From 22290ef35aa13edb1501c69b3cce63a885302563 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 20 Sep 2012 10:12:51 -0700 Subject: BulletSim: complete code for managed code shape and body tracking. Not debugged. Eliminate some null exceptions created adding the above code. Add and remove some detailed logging statements. --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 2 +- .../Region/Physics/BulletSPlugin/BSPhysObject.cs | 22 +- OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 121 ++-- .../Physics/BulletSPlugin/BSShapeCollection.cs | 617 ++++++++++++++++++--- .../Region/Physics/BulletSPlugin/BulletSimAPI.cs | 43 +- 5 files changed, 675 insertions(+), 130 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 19eb1e6..014cd99 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -77,7 +77,7 @@ public class BSCharacter : BSPhysObject public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying) { - base.BaseInitialize(parent_scene, localID, avName); + base.BaseInitialize(parent_scene, localID, avName, "BSCharacter"); _physicsActorType = (int)ActorTypes.Agent; _position = pos; _size = size; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index b575e37..70a10b1 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -39,11 +39,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin // unless the difference is significant. public abstract class BSPhysObject : PhysicsActor { - protected void BaseInitialize(BSScene parentScene, uint localID, string name) + protected void BaseInitialize(BSScene parentScene, uint localID, string name, string typeName) { PhysicsScene = parentScene; LocalID = localID; PhysObjectName = name; + TypeName = typeName; Linkset = new BSLinkset(PhysicsScene, this); @@ -56,6 +57,7 @@ public abstract class BSPhysObject : PhysicsActor public BSScene PhysicsScene { get; protected set; } // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor public string PhysObjectName { get; protected set; } + public string TypeName { get; protected set; } public BSLinkset Linkset { get; set; } @@ -63,9 +65,9 @@ public abstract class BSPhysObject : PhysicsActor public abstract float MassRaw { get; } // Reference to the physical body (btCollisionObject) of this object - public BulletBody BSBody { get; protected set; } + public BulletBody BSBody; // Reference to the physical shape (btCollisionShape) of this object - public BulletShape BSShape { get; protected set; } + public BulletShape BSShape; // Stop all physical motion. public abstract void ZeroMotion(); @@ -116,13 +118,11 @@ public abstract class BSPhysObject : PhysicsActor return ret; } - DetailLog("{0},BSPhysObject.Collison,call,with={1}", LocalID, collidingWith); - // if someone has subscribed for collision events.... if (SubscribedEvents()) { CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); - DetailLog("{0},BSPhysObject.Collison.AddCollider,call,with={1},point={2},normal={3},depth={4}", - LocalID, collidingWith, contactPoint, contactNormal, pentrationDepth); + // DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}", + // LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth); ret = true; } return ret; @@ -147,7 +147,7 @@ public abstract class BSPhysObject : PhysicsActor if (CollisionCollection.Count == 0) PhysicsScene.ObjectsWithNoMoreCollisions.Add(this); - DetailLog("{0},SendCollisions.SendCollisionUpdate,call,numCollisions={1}", LocalID, CollisionCollection.Count); + // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count); base.SendCollisionUpdate(CollisionCollection); // The collisionCollection structure is passed around in the simulator. @@ -164,9 +164,8 @@ public abstract class BSPhysObject : PhysicsActor { // make sure first collision happens NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs); - DetailLog("{0},SubscribeEvents,call,ms={1}", LocalID, SubscribedEventsMs); - PhysicsScene.TaintedObject("BSPhysObject.SubscribeEvents", delegate() + PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() { CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); @@ -179,8 +178,7 @@ public abstract class BSPhysObject : PhysicsActor } public override void UnSubscribeEvents() { SubscribedEventsMs = 0; - DetailLog("{0},UnSubscribeEvents,call", LocalID); - PhysicsScene.TaintedObject("BSPhysObject.UnSubscribeEvents", delegate() + PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() { CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 4d17e6c..4d2c70c 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -96,7 +96,7 @@ public sealed class BSPrim : BSPhysObject OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) { // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); - base.BaseInitialize(parent_scene, localID, primName); + base.BaseInitialize(parent_scene, localID, primName, "BSPrim"); _physicsActorType = (int)ActorTypes.Prim; _position = pos; _size = size; @@ -115,17 +115,17 @@ public sealed class BSPrim : BSPhysObject _restitution = PhysicsScene.Params.defaultRestitution; _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness _mass = CalculateMass(); + + // No body or shape yet + BSBody = new BulletBody(LocalID, IntPtr.Zero); + BSShape = new BulletShape(IntPtr.Zero); + DetailLog("{0},BSPrim.constructor,call", LocalID); // do the actual object creation at taint time PhysicsScene.TaintedObject("BSPrim.create", delegate() { CreateGeomAndObject(true); - // Get the pointer to the physical body for this object. - // At the moment, we're still letting BulletSim manage the creation and destruction - // of the object. Someday we'll move that into the C# code. - BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(PhysicsScene.World.Ptr, LocalID)); - BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr)); CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(BSBody.Ptr); }); } @@ -168,17 +168,24 @@ public sealed class BSPrim : BSPhysObject // Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct // scale and margins are set. CreateGeomAndObject(true); - DetailLog("{0}: BSPrim.setSize: size={1}, scale={2}, mass={3}, physical={4}", LocalID, _size, _scale, _mass, IsPhysical); + DetailLog("{0},BSPrim.setSize,size={1},scale={2},mass={3},physical={4}", LocalID, _size, _scale, _mass, IsPhysical); }); } } + // Scale is what we set in the physics engine. It is different than 'size' in that + // 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>. + public OMV.Vector3 Scale + { + get { return _scale; } + set { _scale = value; } + } public override PrimitiveBaseShape Shape { set { _pbs = value; PhysicsScene.TaintedObject("BSPrim.setShape", delegate() { _mass = CalculateMass(); // changing the shape changes the mass - CreateGeomAndObject(false); + CreateGeomAndObject(true); }); } } @@ -191,7 +198,7 @@ public sealed class BSPrim : BSPhysObject _isSelected = value; PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() { - SetObjectDynamic(); + SetObjectDynamic(false); }); } } @@ -371,7 +378,7 @@ public sealed class BSPrim : BSPhysObject PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() { DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); - SetObjectDynamic(); + SetObjectDynamic(true); }); return; } @@ -433,7 +440,7 @@ public sealed class BSPrim : BSPhysObject PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() { DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); - SetObjectDynamic(); + SetObjectDynamic(true); }); } } @@ -445,7 +452,7 @@ public sealed class BSPrim : BSPhysObject } // An object is solid if it's not phantom and if it's not doing VolumeDetect - private bool IsSolid + public bool IsSolid { get { return !IsPhantom && !_isVolumeDetect; } } @@ -457,21 +464,23 @@ public sealed class BSPrim : BSPhysObject // isSolid: other objects bounce off of this object // isVolumeDetect: other objects pass through but can generate collisions // collisionEvents: whether this object returns collision events - private void SetObjectDynamic() + private void SetObjectDynamic(bool forceRebuild) { +#if CSHARP_BODY_MANAGEMENT + // Recreate the physical object if necessary + CreateGeomAndObject(forceRebuild); +#else // If it's becoming dynamic, it will need hullness VerifyCorrectPhysicalShape(); UpdatePhysicalParameters(); +#endif // CSHARP_BODY_MANAGEMENT } private void UpdatePhysicalParameters() { - /* - // Bullet wants static objects to have a mass of zero - float mass = IsStatic ? 0f : _mass; + DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape); - BulletSimAPI.SetObjectProperties(Scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass); - */ + // Mangling all the physical properties requires the object to be out of the physical world BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.Ptr, BSBody.Ptr); // Make solid or not (do things bounce off or pass through this object) @@ -517,8 +526,8 @@ public sealed class BSPrim : BSPhysObject // There can be special things needed for implementing linksets Linkset.MakeStatic(this); // The activation state is 'sleeping' so Bullet will not try to act on it - // BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.ISLAND_SLEEPING); - BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.DISABLE_SIMULATION); + BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.ISLAND_SLEEPING); + // BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.DISABLE_SIMULATION); } else { @@ -560,8 +569,8 @@ public sealed class BSPrim : BSPhysObject // the functions after this one set up the state of a possibly newly created collision body. private void MakeSolid(bool makeSolid) { +#if !CSHARP_BODY_MANAGEMENT CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(BSBody.Ptr); - /* if (makeSolid) { if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0) @@ -569,11 +578,16 @@ public sealed class BSPrim : BSPhysObject // Solid things are made out of rigid bodies. Remove this old body from the world // and use this shape in a new rigid body. BulletBody oldBody = BSBody; - BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.Ptr, BSBody.Ptr); - BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr)); + // Zero out the pointer to the shape in the old body so the shape will not get freed + BSShape.Ptr = BulletSimAPI.GetCollisionShape2(oldBody.Ptr); + BulletSimAPI.SetCollisionShape2(PhysicsScene.World.Ptr, oldBody.Ptr, IntPtr.Zero); + // Get rid of the old body and remove it from BulletSim's object list + BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID); + + // Create the new body with the shape BSBody = new BulletBody(LocalID, BulletSimAPI.CreateBodyFromShape2(PhysicsScene.World.Ptr, BSShape.Ptr, _position, _orientation)); - BulletSimAPI.DestroyObject2(PhysicsScene.World.Ptr, oldBody.Ptr); - BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.Ptr, BSBody.Ptr); + BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); + DetailLog("{0},BSPrim.MakeSolid:rigidBody,body={1},shape={2}", LocalID, BSBody, BSShape); } } else @@ -583,23 +597,20 @@ public sealed class BSPrim : BSPhysObject // Non-solid things are made out of ghost objects. Remove this old body from the world // and use this shape in a new rigid body. BulletBody oldBody = BSBody; - BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.Ptr, BSBody.Ptr); - BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr)); + + // Zero out the pointer to the shape in the old body so the shape will not get freed + BSShape.Ptr = BulletSimAPI.GetCollisionShape2(oldBody.Ptr); + BulletSimAPI.SetCollisionShape2(PhysicsScene.World.Ptr, oldBody.Ptr, IntPtr.Zero); + // Get rid of the old body and remove it from BulletSim's object list + BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID); + BSBody = new BulletBody(LocalID, BulletSimAPI.CreateGhostFromShape2(PhysicsScene.World.Ptr, BSShape.Ptr, _position, _orientation)); - if (BSBody.Ptr == IntPtr.Zero) - { - m_log.ErrorFormat("{0} BSPrim.MakeSolid: failed creation of ghost object. LocalID=[1}", LogHeader, LocalID); - BSBody = oldBody; - } - else - { - BulletSimAPI.DestroyObject2(PhysicsScene.World.Ptr, oldBody.Ptr); - BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.Ptr, BSBody.Ptr); - } + CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); + DetailLog("{0},BSPrim.MakeGhostBody,body={1},shape={2}", LocalID, BSBody, BSShape); } } - */ +#endif } // Turn on or off the flag controlling whether collision events are returned to the simulator. @@ -1067,6 +1078,7 @@ public sealed class BSPrim : BSPhysObject }// end CalculateMass #endregion Mass Calculation +#if !CSHARP_BODY_MANAGEMENT // Create the geometry information in Bullet for later use. // The objects needs a hull if it's physical otherwise a mesh is enough. // No locking here because this is done when we know physics is not simulating. @@ -1095,6 +1107,7 @@ public sealed class BSPrim : BSPhysObject { DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild); _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; + _meshKey = (ulong)ShapeData.FixedShapeKey.KEY_SPHERE; // Bullet native objects are scaled by the Bullet engine so pass the size in _scale = _size; // TODO: do we need to check for and destroy a mesh or hull that might have been left from before? @@ -1109,6 +1122,7 @@ public sealed class BSPrim : BSPhysObject { DetailLog("{0},BSPrim.CreateGeom,box (force={1})", LocalID, forceRebuild); _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; + _meshKey = (ulong)ShapeData.FixedShapeKey.KEY_BOX; _scale = _size; // TODO: do we need to check for and destroy a mesh or hull that might have been left from before? ret = true; @@ -1136,6 +1150,7 @@ public sealed class BSPrim : BSPhysObject } } } + return ret; } @@ -1345,12 +1360,9 @@ public sealed class BSPrim : BSPhysObject // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, LocalID, shape.Type); bool ret = BulletSimAPI.CreateObject(PhysicsScene.WorldID, shape); - // the CreateObject() may have recreated the rigid body. Make sure we have the latest address. - BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(PhysicsScene.World.Ptr, LocalID)); - BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr)); - return ret; } +#endif // !CSHARP_BODY_MANAGEMENT // Copy prim's info into the BulletSim shape description structure public void FillShapeInfo(out ShapeData shape) @@ -1369,22 +1381,45 @@ public sealed class BSPrim : BSPhysObject shape.Restitution = _restitution; shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; + shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue; + shape.Size = _size; } - // Rebuild the geometry and object. // This is called when the shape changes so we need to recreate the mesh/hull. // No locking here because this is done when the physics engine is not simulating private void CreateGeomAndObject(bool forceRebuild) { +#if CSHARP_BODY_MANAGEMENT + ShapeData shapeData; + FillShapeInfo(out shapeData); + + // Create the correct physical representation for this type of object. + // Updates BSBody and BSShape with the new information. + if (PhysicsScene.Shapes.GetBodyAndShape(forceRebuild, PhysicsScene.World, this, shapeData, _pbs)) + { + // Make sure the properties are set on the new object + UpdatePhysicalParameters(); + } +#else // m_log.DebugFormat("{0}: CreateGeomAndObject. lID={1}, force={2}", LogHeader, LocalID, forceRebuild); // Create the geometry that will make up the object if (CreateGeom(forceRebuild)) { // Create the object and place it into the world CreateObject(); + + // the CreateObject() may have recreated the rigid body. Make sure we have the latest address. + BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(PhysicsScene.World.Ptr, LocalID)); + BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr), _shapeType); + BSShape.shapeKey = _meshKey; + DetailLog("{0},BSPrim.CreateGeomAndObject,body={1},shape={2}", LocalID, BSBody, BSShape); + // Make sure the properties are set on the new object UpdatePhysicalParameters(); } + + +#endif // CSHARP_BODY_MANAGEMENT return; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index eb4b2ad..7470d23 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -1,70 +1,547 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyrightD - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -using System; -using System.Collections.Generic; -using System.Text; -using OMV = OpenMetaverse; - -namespace OpenSim.Region.Physics.BulletSPlugin -{ -public class BSShapeCollection : IDisposable -{ - protected BSScene PhysicsScene { get; set; } - - public BSShapeCollection(BSScene physScene) - { - PhysicsScene = physScene; - } - - public void Dispose() - { - } - - // Track another user of a body - public void ReferenceBody(BulletBody shape) - { - } - - // Release the usage of a body - public void DereferenceBody(BulletBody shape) - { - } - - // Track another user of the shape - public void ReferenceShape(BulletShape shape) - { - } - - // Release the usage of a shape - public void DereferenceShape(BulletShape shape) - { - } - - - -} -} +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OMV = OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSShapeCollection : IDisposable +{ + protected BSScene PhysicsScene { get; set; } + + private Object m_shapeActivityLock = new Object(); + + private struct MeshDesc + { + public IntPtr Ptr; + public int referenceCount; + public DateTime lastReferenced; + public IMesh meshData; + } + + private struct HullDesc + { + public IntPtr Ptr; + public int referenceCount; + public DateTime lastReferenced; + } + + private Dictionary Meshes = new Dictionary(); + private Dictionary Hulls = new Dictionary(); + + public BSShapeCollection(BSScene physScene) + { + PhysicsScene = physScene; + } + + public void Dispose() + { + } + + // 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) + { + bool ret = false; + + // Do we have the correct geometry for this type of object? + if (CreateGeom(forceRebuild, prim, shapeData, pbs)) + { + // If we had to select a new shape geometry for the object, + // rebuild the body around it. + CreateObject(true, prim, PhysicsScene.World, prim.BSShape, shapeData); + ret = true; + } + + return ret; + } + + // Track another user of a body + public void ReferenceBody(BulletBody shape) + { + } + + // Release the usage of a body + public void DereferenceBody(BulletBody shape) + { + } + + // Track another user of the shape + public void ReferenceShape(BulletShape shape) + { + ReferenceShape(shape, null); + } + + // Track the datastructures and use count for a shape. + // When creating a hull, this is called first to reference the mesh + // and then again to reference the hull. + // Meshes and hulls for the same shape have the same hash key. + private void ReferenceShape(BulletShape shape, IMesh meshData) + { + switch (shape.type) + { + case ShapeData.PhysicsShapeType.SHAPE_MESH: + MeshDesc meshDesc; + if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) + { + // There is an existing instance of this mesh. + meshDesc.referenceCount++; + } + else + { + // This is a new reference to a mesh + meshDesc.Ptr = shape.Ptr; + meshDesc.meshData = meshData; + meshDesc.referenceCount = 1; + + } + meshDesc.lastReferenced = System.DateTime.Now; + Meshes[shape.shapeKey] = meshDesc; + break; + case ShapeData.PhysicsShapeType.SHAPE_HULL: + HullDesc hullDesc; + if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) + { + // There is an existing instance of this mesh. + hullDesc.referenceCount++; + } + else + { + // This is a new reference to a mesh + hullDesc.Ptr = shape.Ptr; + hullDesc.referenceCount = 1; + + } + hullDesc.lastReferenced = System.DateTime.Now; + Hulls[shape.shapeKey] = hullDesc; + break; + default: + break; + } + } + + // Release the usage of a shape + public void DereferenceShape(BulletShape shape) + { + switch (shape.type) + { + case ShapeData.PhysicsShapeType.SHAPE_HULL: + DereferenceHull(shape); + // Hulls also include a mesh + DereferenceMesh(shape); + break; + case ShapeData.PhysicsShapeType.SHAPE_MESH: + DereferenceMesh(shape); + break; + default: + break; + } + } + + // Count down the reference count for a mesh shape + private void DereferenceMesh(BulletShape shape) + { + MeshDesc meshDesc; + if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) + { + meshDesc.referenceCount--; + // TODO: release the Bullet storage + meshDesc.lastReferenced = System.DateTime.Now; + Meshes[shape.shapeKey] = meshDesc; + } + } + + // Count down the reference count for a hull shape + private void DereferenceHull(BulletShape shape) + { + HullDesc hullDesc; + if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) + { + hullDesc.referenceCount--; + // TODO: release the Bullet storage (aging old entries?) + hullDesc.lastReferenced = System.DateTime.Now; + Hulls[shape.shapeKey] = hullDesc; + } + } + + // Create the geometry information in Bullet for later use. + // The objects needs a hull if it's physical otherwise a mesh is enough. + // No locking here because this is done when we know physics is not simulating. + // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used. + // Returns 'true' if the geometry was rebuilt. + // Called at taint-time! + private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) + { + bool ret = false; + bool haveShape = false; + bool nativeShapePossible = true; + + BulletShape newShape = new BulletShape(IntPtr.Zero); + + // If the object is dynamic, it must have a hull shape + if (prim.IsPhysical) + nativeShapePossible = false; + + // If the prim attributes are simple, this could be a simple Bullet native shape + if (nativeShapePossible + && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) + || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 + && pbs.ProfileHollow == 0 + && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 + && pbs.PathBegin == 0 && pbs.PathEnd == 0 + && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 + && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 + && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) + { + if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) + { + haveShape = true; + if (forceRebuild || (prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE)) + { + DetailLog("{0},BSShapeCollection.CreateGeom,sphere (force={1}", prim.LocalID, forceRebuild); + newShape = AddNativeShapeToPrim(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE); + + ret = true; + } + } + else + { + // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size); + haveShape = true; + if (forceRebuild || (prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX)) + { + DetailLog("{0},BSShapeCollection.CreateGeom,box (force={1})", prim.LocalID, forceRebuild); + newShape = AddNativeShapeToPrim(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX); + + ret = true; + } + } + } + // If a simple shape isn't happening, create a mesh and possibly a hull + if (!haveShape) + { + if (prim.IsPhysical) + { + if (forceRebuild || !Hulls.ContainsKey(prim.BSShape.shapeKey)) + { + // physical objects require a hull for interaction. + // This also creates the mesh if it doesn't already exist + ret = CreateGeomHull(prim, shapeData, pbs); + } + } + else + { + if (forceRebuild || !Meshes.ContainsKey(prim.BSShape.shapeKey)) + { + // Static (non-physical) objects only need a mesh for bumping into + ret = CreateGeomMesh(prim, shapeData, pbs); + } + } + } + return ret; + } + + private BulletShape AddNativeShapeToPrim(BSPrim prim, ShapeData shapeData, ShapeData.PhysicsShapeType shapeType) + { + BulletShape newShape; + + // Bullet native objects are scaled by the Bullet engine so pass the size in + prim.Scale = shapeData.Size; + + // release any previous shape + DereferenceShape(prim.BSShape); + + MeshDesc existingShapeDesc; + if (Meshes.TryGetValue(shapeData.MeshKey, out existingShapeDesc)) + { + // If there is an existing allocated shape, use it + newShape = new BulletShape(existingShapeDesc.Ptr, shapeType); + } + else + { + // Shape of this discriptioin is not allocated. Create new. + newShape = new BulletShape( + BulletSimAPI.BuildNativeShape2(PhysicsScene.World.Ptr, + (float)shapeType, + PhysicsScene.Params.collisionMargin, + prim.Scale), + shapeType); + } + newShape.shapeKey = shapeData.MeshKey; + ReferenceShape(newShape); + prim.BSShape = newShape; + return newShape; + } + + // No locking here because this is done when we know physics is not simulating + // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs). + // Called at taint-time! + private bool CreateGeomMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) + { + BulletShape newShape = new BulletShape(IntPtr.Zero); + + // level of detail based on size and type of the object + float lod = PhysicsScene.MeshLOD; + if (pbs.SculptEntry) + lod = PhysicsScene.SculptLOD; + + float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); + if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) + lod = PhysicsScene.MeshMegaPrimLOD; + + ulong newMeshKey = (ulong)pbs.GetMeshKey(shapeData.Size, lod); + // m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, LocalID, _meshKey, newMeshKey); + + // if this new shape is the same as last time, don't recreate the mesh + if (prim.BSShape.shapeKey == newMeshKey) return false; + + DetailLog("{0},BSShapeCollection.CreateGeomMesh,create,key={1}", prim.LocalID, newMeshKey); + + // Since we're recreating new, get rid of the reference to the previous shape + DereferenceShape(prim.BSShape); + + IMesh meshData = null; + IntPtr meshPtr; + MeshDesc meshDesc; + if (Meshes.TryGetValue(newMeshKey, out meshDesc)) + { + // If the mesh has already been built just use it. + meshPtr = meshDesc.Ptr; + } + else + { + // always pass false for physicalness as this creates some sort of bounding box which we don't need + meshData = PhysicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, shapeData.Size, lod, false); + + int[] indices = meshData.getIndexListAsInt(); + List vertices = meshData.getVertexList(); + + float[] verticesAsFloats = new float[vertices.Count * 3]; + int vi = 0; + foreach (OMV.Vector3 vv in vertices) + { + verticesAsFloats[vi++] = vv.X; + verticesAsFloats[vi++] = vv.Y; + verticesAsFloats[vi++] = vv.Z; + } + + // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", + // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); + + meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.Ptr, + indices.GetLength(0), indices, vertices.Count, verticesAsFloats); + } + newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); + newShape.shapeKey = newMeshKey; + + ReferenceShape(newShape, meshData); + + // meshes are already scaled by the meshmerizer + prim.Scale = new OMV.Vector3(1f, 1f, 1f); + prim.BSShape = newShape; + return true; // 'true' means a new shape has been added to this prim + } + + // No locking here because this is done when we know physics is not simulating + // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs). + List m_hulls; + private bool CreateGeomHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs) + { + BulletShape newShape; + + float lod = pbs.SculptEntry ? PhysicsScene.SculptLOD : PhysicsScene.MeshLOD; + ulong newHullKey = (ulong)pbs.GetMeshKey(shapeData.Size, lod); + // m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, LocalID, _hullKey, newHullKey); + + // if the hull hasn't changed, don't rebuild it + if (newHullKey == prim.BSShape.shapeKey) return false; + + DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", prim.LocalID, newHullKey, newHullKey); + + // remove references to any previous shape + DereferenceShape(prim.BSShape); + + // Make sure the underlying mesh exists and is correct + // Since we're in the hull code, we know CreateGeomMesh() will not create a native shape. + CreateGeomMesh(prim, shapeData, pbs); + MeshDesc meshDesc = Meshes[newHullKey]; + + IntPtr hullPtr; + HullDesc hullDesc; + if (Hulls.TryGetValue(newHullKey, out hullDesc)) + { + hullPtr = hullDesc.Ptr; + } + else + { + int[] indices = meshDesc.meshData.getIndexListAsInt(); + List vertices = meshDesc.meshData.getVertexList(); + + //format conversion from IMesh format to DecompDesc format + List convIndices = new List(); + List convVertices = new List(); + for (int ii = 0; ii < indices.GetLength(0); ii++) + { + convIndices.Add(indices[ii]); + } + foreach (OMV.Vector3 vv in vertices) + { + convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); + } + + // setup and do convex hull conversion + m_hulls = new List(); + DecompDesc dcomp = new DecompDesc(); + dcomp.mIndices = convIndices; + dcomp.mVertices = convVertices; + ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); + // create the hull into the _hulls variable + convexBuilder.process(dcomp); + + // Convert the vertices and indices for passing to unmanaged. + // The hull information is passed as a large floating point array. + // The format is: + // convHulls[0] = number of hulls + // convHulls[1] = number of vertices in first hull + // convHulls[2] = hull centroid X coordinate + // convHulls[3] = hull centroid Y coordinate + // convHulls[4] = hull centroid Z coordinate + // convHulls[5] = first hull vertex X + // convHulls[6] = first hull vertex Y + // convHulls[7] = first hull vertex Z + // convHulls[8] = second hull vertex X + // ... + // convHulls[n] = number of vertices in second hull + // convHulls[n+1] = second hull centroid X coordinate + // ... + // + // TODO: is is very inefficient. Someday change the convex hull generator to return + // data structures that do not need to be converted in order to pass to Bullet. + // And maybe put the values directly into pinned memory rather than marshaling. + int hullCount = m_hulls.Count; + int totalVertices = 1; // include one for the count of the hulls + foreach (ConvexResult cr in m_hulls) + { + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += cr.HullIndices.Count * 3; // we pass just triangles + } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + foreach (ConvexResult cr in m_hulls) + { + // copy vertices for index access + float3[] verts = new float3[cr.HullVertices.Count]; + int kk = 0; + foreach (float3 ff in cr.HullVertices) + { + verts[kk++] = ff; + } + + // add to the array one hull's worth of data + convHulls[jj++] = cr.HullIndices.Count; + convHulls[jj++] = 0f; // centroid x,y,z + convHulls[jj++] = 0f; + convHulls[jj++] = 0f; + foreach (int ind in cr.HullIndices) + { + convHulls[jj++] = verts[ind].x; + convHulls[jj++] = verts[ind].y; + convHulls[jj++] = verts[ind].z; + } + } + // create the hull data structure in Bullet + // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid={1}, key={2}, hulls={3}", LogHeader, LocalID, _hullKey, hullCount); + hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.Ptr, hullCount, convHulls); + } + newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); + newShape.shapeKey = newHullKey; + + ReferenceShape(newShape); + + // meshes are already scaled by the meshmerizer + prim.Scale = new OMV.Vector3(1f, 1f, 1f); + prim.BSShape = newShape; + return true; // 'true' means a new shape has been added to this prim + } + + // Callback from convex hull creater with a newly created hull. + // Just add it to the collection of hulls for this shape. + private void HullReturn(ConvexResult result) + { + m_hulls.Add(result); + return; + } + + // Create an object in Bullet if it has not already been created + // No locking here because this is done when the physics engine is not simulating + // Returns 'true' if an object was actually created. + private bool CreateObject(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, ShapeData shapeData) + { + // the mesh or hull must have already been created in Bullet + // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, LocalID, shape.Type); + + DereferenceBody(prim.BSBody); + + BulletBody aBody; + IntPtr bodyPtr = IntPtr.Zero; + if (prim.IsSolid) + { + bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.Ptr, shape.Ptr, shapeData.Position, shapeData.Rotation); + } + else + { + bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.Ptr, shape.Ptr, shapeData.Position, shapeData.Rotation); + } + aBody = new BulletBody(shapeData.ID, bodyPtr); + + ReferenceBody(aBody); + + prim.BSBody = aBody; + return true; + } + + private void DetailLog(string msg, params Object[] args) + { + PhysicsScene.PhysicsLogging.Write(msg, args); + } + + + + + +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 52c8a24..47875b0 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -40,7 +40,9 @@ public struct BulletSim { public BulletSim(uint worldId, BSScene bss, IntPtr xx) { - worldID = worldId; scene = bss; Ptr = xx; + worldID = worldId; + scene = bss; + Ptr = xx; } public uint worldID; // The scene is only in here so very low level routines have a handle to print debug/error messages @@ -58,6 +60,16 @@ public struct BulletBody } public IntPtr Ptr; public uint ID; + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append(""); + return buff.ToString(); + } } public struct BulletShape @@ -66,17 +78,29 @@ public struct BulletShape { Ptr = xx; type=ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; - hashKey = 0; + shapeKey = 0; } public BulletShape(IntPtr xx, ShapeData.PhysicsShapeType typ) { Ptr = xx; type = typ; - hashKey = 0; + shapeKey = 0; } public IntPtr Ptr; public ShapeData.PhysicsShapeType type; - public ulong hashKey; + public ulong shapeKey; + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append(""); + return buff.ToString(); + } } // An allocated Bullet btConstraint @@ -155,10 +179,21 @@ public struct ShapeData public float Restitution; public float Collidable; // true of things bump into this public float Static; // true if a static object. Otherwise gravity, etc. + public float Solid; // true if object cannot be passed through + public Vector3 Size; // note that bools are passed as floats since bool size changes by language and architecture public const float numericTrue = 1f; public const float numericFalse = 0f; + + // The native shapes have predefined shape hash keys + public enum FixedShapeKey : ulong + { + KEY_BOX = 1, + KEY_SPHERE = 2, + KEY_CONE = 3, + KEY_CYLINDER = 4, + } } [StructLayout(LayoutKind.Sequential)] public struct SweepHit -- cgit v1.1