From 0376b8ddbc4101627aa045a1deb18202fb51fffe Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Fri, 24 Aug 2012 10:10:55 -0700 Subject: BulletSim: add new interface for mesh, hull and terrain creation that will move nearly all of the logic into the C# code. --- .../Region/Physics/BulletSPlugin/BulletSimAPI.cs | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 504bd3c..dab2420 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -392,23 +392,36 @@ public static extern int PhysicsStep2(IntPtr world, float timeStep, int maxSubSt [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool PushUpdate2(IntPtr obj); -/* +// ===================================================================================== +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateMeshShape2(IntPtr world, + int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices, + int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices ); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateHullShape2(IntPtr world, + int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls); + [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateMesh2(IntPtr world, int indicesCount, int* indices, int verticesCount, float* vertices ); +public static extern IntPtr BuildHullShape2(IntPtr world, IntPtr meshShape); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool BuildHull2(IntPtr world, IntPtr mesh); +public static extern IntPtr BuildNativeShape2(IntPtr world, + float shapeType, float collisionMargin, Vector3 scale); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool ReleaseHull2(IntPtr world, IntPtr mesh); +public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape); +// ===================================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool DestroyMesh2(IntPtr world, IntPtr mesh); +public static extern IntPtr CreateGroundPlaneBody2(uint id, Vector3 center, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateObject2(IntPtr world, ShapeData shapeData); -*/ +public static extern IntPtr CreateTerrainBody2(uint id, + Vector3 minCoords, Vector3 maxCoords, float collisionMargin, + [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); +// ===================================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern IntPtr Create6DofConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, Vector3 frame1loc, Quaternion frame1rot, -- cgit v1.1 From 7b6987ce83d16871f6070f3cc7d56280ad3d5dbe Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Fri, 24 Aug 2012 12:58:42 -0700 Subject: BulletSim: unify physical objects under BSPhysObjects. Now BSScene and BSLinkset only know of BSPhysObject's and there is only one list to search in BSScene. --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 31 +++-- OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs | 50 ++++---- .../Region/Physics/BulletSPlugin/BSPhysObject.cs | 58 +++++++++ OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 84 ++++++------- OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 137 +++++++++------------ 5 files changed, 200 insertions(+), 160 deletions(-) create mode 100755 OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 1b23a36..784076d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -34,7 +34,7 @@ using OpenSim.Region.Physics.Manager; namespace OpenSim.Region.Physics.BulletSPlugin { -public class BSCharacter : PhysicsActor +public class BSCharacter : BSPhysObject { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS CHAR]"; @@ -74,11 +74,8 @@ public class BSCharacter : PhysicsActor private bool _kinematic; private float _buoyancy; - private BulletBody m_body; - public BulletBody Body { - get { return m_body; } - set { m_body = value; } - } + public override BulletBody Body { get; set; } + public override BSLinkset Linkset { get; set; } private int _subscribedEventsMs = 0; private int _nextCollisionOkTime = 0; @@ -108,6 +105,8 @@ public class BSCharacter : PhysicsActor _density = _scene.Params.avatarDensity; ComputeAvatarVolumeAndMass(); // set _avatarVolume and _mass based on capsule size, _density and _scale + Linkset = new BSLinkset(_scene, this); + ShapeData shapeData = new ShapeData(); shapeData.ID = _localID; shapeData.Type = ShapeData.PhysicsShapeType.SHAPE_AVATAR; @@ -130,7 +129,7 @@ public class BSCharacter : PhysicsActor // Set the buoyancy for flying. This will be refactored when all the settings happen in C# BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, LocalID, _buoyancy); - m_body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); + Body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); // avatars get all collisions no matter what (makes walking on ground and such work) BulletSimAPI.AddToCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); @@ -139,7 +138,7 @@ public class BSCharacter : PhysicsActor } // called when this character is being destroyed and the resources should be released - public void Destroy() + public override void Destroy() { DetailLog("{0},BSCharacter.Destroy", LocalID); _scene.TaintedObject("BSCharacter.destroy", delegate() @@ -245,6 +244,10 @@ public class BSCharacter : PhysicsActor return _mass; } } + + // used when we only want this prim's mass and not the linkset thing + public override float MassRaw { get {return _mass; } } + public override Vector3 Force { get { return _force; } set { @@ -448,6 +451,12 @@ public class BSCharacter : PhysicsActor }); } } + + public override void ZeroMotion() + { + return; + } + // Stop collision events public override void UnSubscribeEvents() { _subscribedEventsMs = 0; @@ -481,7 +490,7 @@ public class BSCharacter : PhysicsActor // The physics engine says that properties have updated. Update same and inform // the world that things have changed. - public void UpdateProperties(EntityProperties entprop) + public override void UpdateProperties(EntityProperties entprop) { _position = entprop.Position; _orientation = entprop.Rotation; @@ -500,7 +509,7 @@ public class BSCharacter : PhysicsActor // The collision, if it should be reported to the character, is placed in a collection // that will later be sent to the simulator when SendCollisions() is called. CollisionEventUpdate collisionCollection = null; - public void Collide(uint collidingWith, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth) + public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth) { // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); @@ -525,7 +534,7 @@ public class BSCharacter : PhysicsActor } } - public void SendCollisions() + public override void SendCollisions() { /* if (collisionCollection != null && collisionCollection.Count > 0) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 9e3f0db..b04e1b6 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -36,8 +36,8 @@ public class BSLinkset { private static string LogHeader = "[BULLETSIM LINKSET]"; - private BSPrim m_linksetRoot; - public BSPrim LinksetRoot { get { return m_linksetRoot; } } + private BSPhysObject m_linksetRoot; + public BSPhysObject LinksetRoot { get { return m_linksetRoot; } } private BSScene m_physicsScene; public BSScene PhysicsScene { get { return m_physicsScene; } } @@ -46,7 +46,7 @@ public class BSLinkset public int LinksetID { get; private set; } // The children under the root in this linkset - private List m_children; + private List m_children; // We lock the diddling of linkset classes to prevent any badness. // This locks the modification of the instances of this class. Changes @@ -74,7 +74,7 @@ public class BSLinkset get { return ComputeLinksetGeometricCenter(); } } - public BSLinkset(BSScene scene, BSPrim parent) + public BSLinkset(BSScene scene, BSPhysObject parent) { // A simple linkset of one (no children) LinksetID = m_nextLinksetID++; @@ -83,14 +83,14 @@ public class BSLinkset m_nextLinksetID = 1; m_physicsScene = scene; m_linksetRoot = parent; - m_children = new List(); + m_children = new List(); m_mass = parent.MassRaw; } // Link to a linkset where the child knows the parent. // Parent changing should not happen so do some sanity checking. // We return the parent's linkset so the child can track its membership. - public BSLinkset AddMeToLinkset(BSPrim child) + public BSLinkset AddMeToLinkset(BSPhysObject child) { lock (m_linksetActivityLock) { @@ -102,7 +102,7 @@ public class BSLinkset // Remove a child from a linkset. // Returns a new linkset for the child which is a linkset of one (just the // orphened child). - public BSLinkset RemoveMeFromLinkset(BSPrim child) + public BSLinkset RemoveMeFromLinkset(BSPhysObject child) { lock (m_linksetActivityLock) { @@ -129,7 +129,7 @@ public class BSLinkset } // Return 'true' if the passed object is the root object of this linkset - public bool IsRoot(BSPrim requestor) + public bool IsRoot(BSPhysObject requestor) { return (requestor.LocalID == m_linksetRoot.LocalID); } @@ -140,12 +140,12 @@ public class BSLinkset public bool HasAnyChildren { get { return (m_children.Count > 0); } } // Return 'true' if this child is in this linkset - public bool HasChild(BSPrim child) + public bool HasChild(BSPhysObject child) { bool ret = false; lock (m_linksetActivityLock) { - foreach (BSPrim bp in m_children) + foreach (BSPhysObject bp in m_children) { if (child.LocalID == bp.LocalID) { @@ -160,7 +160,7 @@ public class BSLinkset private float ComputeLinksetMass() { float mass = m_linksetRoot.MassRaw; - foreach (BSPrim bp in m_children) + foreach (BSPhysObject bp in m_children) { mass += bp.MassRaw; } @@ -174,7 +174,7 @@ public class BSLinkset lock (m_linksetActivityLock) { - foreach (BSPrim bp in m_children) + foreach (BSPhysObject bp in m_children) { com += bp.Position * bp.MassRaw; totalMass += bp.MassRaw; @@ -192,7 +192,7 @@ public class BSLinkset lock (m_linksetActivityLock) { - foreach (BSPrim bp in m_children) + foreach (BSPhysObject bp in m_children) { com += bp.Position * bp.MassRaw; } @@ -204,7 +204,7 @@ public class BSLinkset // When physical properties are changed the linkset needs to recalculate // its internal properties. - public void Refresh(BSPrim requestor) + public void Refresh(BSPhysObject requestor) { // If there are no children, there aren't any constraints to recompute if (!HasAnyChildren) @@ -230,7 +230,7 @@ public class BSLinkset float linksetMass = LinksetMass; lock (m_linksetActivityLock) { - foreach (BSPrim child in m_children) + foreach (BSPhysObject child in m_children) { BSConstraint constrain; if (m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.Body, child.Body, out constrain)) @@ -255,14 +255,14 @@ public class BSLinkset // I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. - private void AddChildToLinkset(BSPrim child) + private void AddChildToLinkset(BSPhysObject child) { if (!HasChild(child)) { m_children.Add(child); - BSPrim rootx = LinksetRoot; // capture the root as of now - BSPrim childx = child; + BSPhysObject rootx = LinksetRoot; // capture the root as of now + BSPhysObject childx = child; m_physicsScene.TaintedObject("AddChildToLinkset", delegate() { DetailLog("{0},AddChildToLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID); @@ -277,7 +277,7 @@ public class BSLinkset // it's still connected to the linkset. // Normal OpenSimulator operation will never do this because other SceneObjectPart information // has to be updated also (like pointer to prim's parent). - private void RemoveChildFromOtherLinkset(BSPrim pchild) + private void RemoveChildFromOtherLinkset(BSPhysObject pchild) { pchild.Linkset = new BSLinkset(m_physicsScene, pchild); RemoveChildFromLinkset(pchild); @@ -285,12 +285,12 @@ public class BSLinkset // I am the root of a linkset and one of my children is being removed. // Safe to call even if the child is not really in my linkset. - private void RemoveChildFromLinkset(BSPrim child) + private void RemoveChildFromLinkset(BSPhysObject child) { if (m_children.Remove(child)) { - BSPrim rootx = LinksetRoot; // capture the root as of now - BSPrim childx = child; + BSPhysObject rootx = LinksetRoot; // capture the root as of now + BSPhysObject childx = child; m_physicsScene.TaintedObject("RemoveChildFromLinkset", delegate() { DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID); @@ -310,7 +310,7 @@ public class BSLinkset // Create a constraint between me (root of linkset) and the passed prim (the child). // Called at taint time! - private void PhysicallyLinkAChildToRoot(BSPrim rootPrim, BSPrim childPrim) + private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) { // Zero motion for children so they don't interpolate childPrim.ZeroMotion(); @@ -383,7 +383,7 @@ public class BSLinkset // Remove linkage between myself and a particular child // Called at taint time! - private void PhysicallyUnlinkAChildFromRoot(BSPrim rootPrim, BSPrim childPrim) + private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) { DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); @@ -396,7 +396,7 @@ public class BSLinkset // Remove linkage between myself and any possible children I might have // Called at taint time! - private void PhysicallyUnlinkAllChildrenFromRoot(BSPrim rootPrim) + private void PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) { DetailLog("{0},PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs new file mode 100755 index 0000000..6e205a9 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -0,0 +1,58 @@ +/* + * 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; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +// Class to wrap all objects. +// The rest of BulletSim doesn't need to keep checking for avatars or prims +// unless the difference is significant. +public abstract class BSPhysObject : PhysicsActor +{ + public abstract BSLinkset Linkset { get; set; } + + public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, + OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth); + public abstract void SendCollisions(); + + // Return the object mass without calculating it or side effects + public abstract float MassRaw { get; } + + public abstract BulletBody Body { get; set; } + public abstract void ZeroMotion(); + + public abstract void UpdateProperties(EntityProperties entprop); + + public abstract void Destroy(); +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index d3f1e9c..036fd4f 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -37,7 +37,7 @@ using OpenSim.Region.Physics.ConvexDecompositionDotNet; namespace OpenSim.Region.Physics.BulletSPlugin { [Serializable] -public sealed class BSPrim : PhysicsActor +public sealed class BSPrim : BSPhysObject { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS PRIM]"; @@ -88,23 +88,14 @@ public sealed class BSPrim : PhysicsActor private float _buoyancy; // Membership in a linkset is controlled by this class. - private BSLinkset _linkset; - public BSLinkset Linkset - { - get { return _linkset; } - set { _linkset = value; } - } + public override BSLinkset Linkset { get; set; } private int _subscribedEventsMs = 0; private int _nextCollisionOkTime = 0; long _collidingStep; long _collidingGroundStep; - private BulletBody m_body; - public BulletBody Body { - get { return m_body; } - set { m_body = value; } - } + public override BulletBody Body { get; set; } private BSDynamics _vehicle; @@ -139,7 +130,7 @@ public sealed class BSPrim : PhysicsActor _friction = _scene.Params.defaultFriction; // TODO: compute based on object material _density = _scene.Params.defaultDensity; // TODO: compute based on object material _restitution = _scene.Params.defaultRestitution; - _linkset = new BSLinkset(Scene, this); // a linkset of one + Linkset = new BSLinkset(Scene, this); // a linkset of one _vehicle = new BSDynamics(Scene, this); // add vehicleness _mass = CalculateMass(); // do the actual object creation at taint time @@ -151,23 +142,23 @@ public sealed class BSPrim : PhysicsActor // 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. - m_body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); + Body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); }); } // called when this prim is being destroyed and we should free all the resources - public void Destroy() + public override void Destroy() { // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); // Undo any links between me and any other object - BSPrim parentBefore = _linkset.LinksetRoot; - int childrenBefore = _linkset.NumberOfChildren; + BSPhysObject parentBefore = Linkset.LinksetRoot; + int childrenBefore = Linkset.NumberOfChildren; - _linkset = _linkset.RemoveMeFromLinkset(this); + Linkset = Linkset.RemoveMeFromLinkset(this); DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}", - LocalID, parentBefore.LocalID, childrenBefore, _linkset.LinksetRoot.LocalID, _linkset.NumberOfChildren); + LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); // Undo any vehicle properties this.VehicleType = (int)Vehicle.TYPE_NONE; @@ -230,13 +221,13 @@ public sealed class BSPrim : PhysicsActor BSPrim parent = obj as BSPrim; if (parent != null) { - BSPrim parentBefore = _linkset.LinksetRoot; - int childrenBefore = _linkset.NumberOfChildren; + BSPhysObject parentBefore = Linkset.LinksetRoot; + int childrenBefore = Linkset.NumberOfChildren; - _linkset = parent.Linkset.AddMeToLinkset(this); + Linkset = parent.Linkset.AddMeToLinkset(this); DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}", - LocalID, parentBefore.LocalID, childrenBefore, _linkset.LinksetRoot.LocalID, _linkset.NumberOfChildren); + LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); } return; } @@ -246,13 +237,13 @@ public sealed class BSPrim : PhysicsActor // TODO: decide if this parent checking needs to happen at taint time // Race condition here: if link() and delink() in same simulation tick, the delink will not happen - BSPrim parentBefore = _linkset.LinksetRoot; - int childrenBefore = _linkset.NumberOfChildren; + BSPhysObject parentBefore = Linkset.LinksetRoot; + int childrenBefore = Linkset.NumberOfChildren; - _linkset = _linkset.RemoveMeFromLinkset(this); + Linkset = Linkset.RemoveMeFromLinkset(this); DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", - LocalID, parentBefore.LocalID, childrenBefore, _linkset.LinksetRoot.LocalID, _linkset.NumberOfChildren); + LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); return; } @@ -260,7 +251,7 @@ public sealed class BSPrim : PhysicsActor // Do it to the properties so the values get set in the physics engine. // Push the setting of the values to the viewer. // Called at taint time! - public void ZeroMotion() + public override void ZeroMotion() { _velocity = OMV.Vector3.Zero; _acceleration = OMV.Vector3.Zero; @@ -281,7 +272,7 @@ public sealed class BSPrim : PhysicsActor public override OMV.Vector3 Position { get { - if (!_linkset.IsRoot(this)) + if (!Linkset.IsRoot(this)) // child prims move around based on their parent. Need to get the latest location _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); @@ -306,23 +297,23 @@ public sealed class BSPrim : PhysicsActor { get { - return _linkset.LinksetMass; + return Linkset.LinksetMass; } } // used when we only want this prim's mass and not the linkset thing - public float MassRaw { get { return _mass; } } + public override float MassRaw { get { return _mass; } } // Is this used? public override OMV.Vector3 CenterOfMass { - get { return _linkset.CenterOfMass; } + get { return Linkset.CenterOfMass; } } // Is this used? public override OMV.Vector3 GeometricCenter { - get { return _linkset.GeometricCenter; } + get { return Linkset.GeometricCenter; } } public override OMV.Vector3 Force { @@ -431,7 +422,7 @@ public sealed class BSPrim : PhysicsActor } public override OMV.Quaternion Orientation { get { - if (!_linkset.IsRoot(this)) + if (!Linkset.IsRoot(this)) { // Children move around because tied to parent. Get a fresh value. _orientation = BulletSimAPI.GetObjectOrientation(_scene.WorldID, LocalID); @@ -490,7 +481,7 @@ public sealed class BSPrim : PhysicsActor BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass); // recompute any linkset parameters - _linkset.Refresh(this); + Linkset.Refresh(this); CollisionFlags cf = BulletSimAPI.GetCollisionFlags2(Body.Ptr); DetailLog("{0},BSPrim.SetObjectDynamic,taint,static={1},solid={2},mass={3}, cf={4}", LocalID, IsStatic, IsSolid, mass, cf); @@ -1299,7 +1290,7 @@ public sealed class BSPrim : PhysicsActor const float ACCELERATION_TOLERANCE = 0.01f; const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f; - public void UpdateProperties(EntityProperties entprop) + public override void UpdateProperties(EntityProperties entprop) { /* UpdatedProperties changed = 0; @@ -1347,7 +1338,7 @@ public sealed class BSPrim : PhysicsActor // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. // Updates only for individual prims and for the root object of a linkset. - if (_linkset.IsRoot(this)) + if (Linkset.IsRoot(this)) { // Assign to the local variables so the normal set action does not happen _position = entprop.Position; @@ -1375,7 +1366,7 @@ public sealed class BSPrim : PhysicsActor // I've collided with something // Called at taint time from within the Step() function CollisionEventUpdate collisionCollection; - public void Collide(uint collidingWith, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) + public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) { // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); @@ -1387,18 +1378,15 @@ public sealed class BSPrim : PhysicsActor } // DetailLog("{0},BSPrim.Collison,call,with={1}", LocalID, collidingWith); - BSPrim collidingWithPrim; - if (_scene.Prims.TryGetValue(collidingWith, out collidingWithPrim)) + + // prims in the same linkset cannot collide with each other + if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID)) { - // prims in the same linkset cannot collide with each other - if (this.Linkset.LinksetID == collidingWithPrim.Linkset.LinksetID) - { - return; - } + return; } - // if someone is subscribed to collision events.... - if (_subscribedEventsMs != 0) { + // if someone has subscribed for collision events.... + if (SubscribedEvents()) { // throttle the collisions to the number of milliseconds specified in the subscription int nowTime = _scene.SimulationNowTime; if (nowTime >= _nextCollisionOkTime) { @@ -1412,7 +1400,7 @@ public sealed class BSPrim : PhysicsActor } // The scene is telling us it's time to pass our collected collisions into the simulator - public void SendCollisions() + public override void SendCollisions() { if (collisionCollection != null && collisionCollection.Count > 0) { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 56924aa..ce64b9b 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -78,14 +78,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters public string BulletSimVersion = "?"; - private Dictionary m_avatars = new Dictionary(); - public Dictionary Characters { get { return m_avatars; } } - - private Dictionary m_prims = new Dictionary(); - public Dictionary Prims { get { return m_prims; } } + public Dictionary PhysObjects = new Dictionary(); + private HashSet m_objectsWithCollisions = new HashSet(); + // Following is a kludge and can be removed when avatar animation updating is + // moved to a better place. private HashSet m_avatarsWithCollisions = new HashSet(); - private HashSet m_primsWithCollisions = new HashSet(); private List m_vehicles = new List(); @@ -235,7 +233,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // BulletSimVersion = BulletSimAPI.GetVersion(); // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion); - // if Debug, enable logging from the unmanaged code + // If Debug logging level, enable logging from the unmanaged code if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) { m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); @@ -243,13 +241,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog); else m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); - // the handle is saved in a variable to make sure it doesn't get freed after this call + // The handle is saved in a variable to make sure it doesn't get freed after this call BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle); } _taintedObjects = new List(); mesher = meshmerizer; + // The bounding box for the simulated world Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f); @@ -337,7 +336,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters if (!m_initialized) return null; BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); - lock (m_avatars) m_avatars.Add(localID, actor); + lock (PhysObjects) PhysObjects.Add(localID, actor); + + // Remove kludge someday + lock (m_avatarsWithCollisions) m_avatarsWithCollisions.Add(actor); + return actor; } @@ -352,7 +355,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters { try { - lock (m_avatars) m_avatars.Remove(actor.LocalID); + lock (PhysObjects) PhysObjects.Remove(actor.LocalID); + // Remove kludge someday + lock (m_avatarsWithCollisions) m_avatarsWithCollisions.Remove(bsactor); } catch (Exception e) { @@ -374,7 +379,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); try { - lock (m_prims) m_prims.Remove(bsprim.LocalID); + lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID); } catch (Exception e) { @@ -399,7 +404,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters DetailLog("{0},AddPrimShape,call", localID); BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); - lock (m_prims) m_prims.Add(localID, prim); + lock (PhysObjects) PhysObjects.Add(localID, prim); return prim; } @@ -470,19 +475,16 @@ public class BSScene : PhysicsScene, IPhysicsParameters // The above SendCollision's batch up the collisions on the objects. // Now push the collisions into the simulator. - foreach (BSPrim bsp in m_primsWithCollisions) + foreach (BSPhysObject bsp in m_objectsWithCollisions) bsp.SendCollisions(); - m_primsWithCollisions.Clear(); + m_objectsWithCollisions.Clear(); // This is a kludge to get avatar movement updated. - // Don't send collisions only if there were collisions -- send everytime. // ODE sends collisions even if there are none and this is used to update // avatar animations and stuff. - // foreach (BSCharacter bsc in m_avatarsWithCollisions) - // bsc.SendCollisions(); - foreach (KeyValuePair kvp in m_avatars) - kvp.Value.SendCollisions(); - m_avatarsWithCollisions.Clear(); + foreach (BSPhysObject bpo in m_avatarsWithCollisions) + bpo.SendCollisions(); + // m_avatarsWithCollisions.Clear(); // If any of the objects had updated properties, tell the object it has been changed by the physics engine if (updatedEntityCount > 0) @@ -490,16 +492,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters for (int ii = 0; ii < updatedEntityCount; ii++) { EntityProperties entprop = m_updateArray[ii]; - BSPrim prim; - if (m_prims.TryGetValue(entprop.ID, out prim)) - { - prim.UpdateProperties(entprop); - continue; - } - BSCharacter actor; - if (m_avatars.TryGetValue(entprop.ID, out actor)) + BSPhysObject pobj; + if (PhysObjects.TryGetValue(entprop.ID, out pobj)) { - actor.UpdateProperties(entprop); + pobj.UpdateProperties(entprop); continue; } } @@ -529,33 +525,36 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // Something has collided - private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penitration) + private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration) { if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) { return; // don't send collisions to the terrain } + BSPhysObject collider = PhysObjects[localID]; + // TODO: as of this code, terrain was not in the physical object list. + // When BSTerrain is created and it will be in the list, we can remove + // the possibility that it's not there and just fetch the collidee. + BSPhysObject collidee = null; + ActorTypes type = ActorTypes.Prim; if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) + { type = ActorTypes.Ground; - else if (m_avatars.ContainsKey(collidingWith)) - type = ActorTypes.Agent; + } + else + { + collidee = PhysObjects[collidingWith]; + if (collidee is BSCharacter) + type = ActorTypes.Agent; + } // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); - BSPrim prim; - if (m_prims.TryGetValue(localID, out prim)) { - prim.Collide(collidingWith, type, collidePoint, collideNormal, penitration); - m_primsWithCollisions.Add(prim); - return; - } - BSCharacter actor; - if (m_avatars.TryGetValue(localID, out actor)) { - actor.Collide(collidingWith, type, collidePoint, collideNormal, penitration); - m_avatarsWithCollisions.Add(actor); - return; - } + collider.Collide(collidingWith, collidee, type, collidePoint, collideNormal, penetration); + m_objectsWithCollisions.Add(collider); + return; } @@ -605,17 +604,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters // make sure no stepping happens while we're deleting stuff m_initialized = false; - foreach (KeyValuePair kvp in m_avatars) - { - kvp.Value.Destroy(); - } - m_avatars.Clear(); - - foreach (KeyValuePair kvp in m_prims) + foreach (KeyValuePair kvp in PhysObjects) { kvp.Value.Destroy(); } - m_prims.Clear(); + PhysObjects.Clear(); // Now that the prims are all cleaned up, there should be no constraints left if (m_constraintCollection != null) @@ -996,42 +989,42 @@ public class BSScene : PhysicsScene, IPhysicsParameters 0f, (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].linearDamping; }, - (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearDamping, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); } ), new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", 0f, (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].angularDamping; }, - (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularDamping, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); } ), new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", 0.2f, (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].deactivationTime; }, - (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].deactivationTime, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); } ), new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", 0.8f, (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].linearSleepingThreshold; }, - (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ), new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", 1.0f, (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].angularSleepingThreshold; }, - (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ), new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , 0f, // set to zero to disable (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].ccdMotionThreshold; }, - (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ), new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , 0f, (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].ccdSweptSphereRadius; }, - (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ), new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , 0.1f, (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].contactProcessingThreshold; }, - (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ), new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 0.5f, @@ -1052,32 +1045,32 @@ public class BSScene : PhysicsScene, IPhysicsParameters 0.5f, (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarFriction; }, - (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarFriction, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", 60f, (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarDensity; }, - (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarDensity, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarDensity, p, l, v); } ), new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", 0f, (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarRestitution; }, - (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarRestitution, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ), new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar", 0.37f, (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarCapsuleRadius; }, - (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", 1.5f, (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarCapsuleHeight; }, - (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ), new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", 0.1f, (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, - (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", @@ -1264,18 +1257,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // check to see if we are updating a parameter for a particular or all of the prims - protected void UpdateParameterPrims(ref float loc, string parm, uint localID, float val) - { - List operateOn; - lock (m_prims) operateOn = new List(m_prims.Keys); - UpdateParameterSet(operateOn, ref loc, parm, localID, val); - } - - // check to see if we are updating a parameter for a particular or all of the avatars - protected void UpdateParameterAvatars(ref float loc, string parm, uint localID, float val) + protected void UpdateParameterObject(ref float loc, string parm, uint localID, float val) { List operateOn; - lock (m_avatars) operateOn = new List(m_avatars.Keys); + lock (PhysObjects) operateOn = new List(PhysObjects.Keys); UpdateParameterSet(operateOn, ref loc, parm, localID, val); } -- cgit v1.1 From 7c140570db3b01eb83efc0d42a47715d3047e376 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sat, 25 Aug 2012 23:18:46 -0700 Subject: BulletSim: Changes to terrain storage and management so mega-regions work. Moved all terrain code out of BSScene and into new BSTerrainManager. Added logic to manage multiple terrains for mega-regions. Added new functions to BulletSimAPI to match the library. Moved all of the terrain creation and setup logic from C++ code to C# code. The unused code has not yet been removed from either place. Soon. Moved checks for avatar above ground and in bounds into BSCharacter. --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 37 ++- .../Region/Physics/BulletSPlugin/BSConstraint.cs | 3 +- OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs | 9 +- .../Region/Physics/BulletSPlugin/BSPhysObject.cs | 118 ++++---- OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 2 +- OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 245 ++++++++-------- .../Physics/BulletSPlugin/BSTerrainManager.cs | 307 +++++++++++++++++++++ .../Region/Physics/BulletSPlugin/BulletSimAPI.cs | 61 +++- 8 files changed, 597 insertions(+), 185 deletions(-) create mode 100755 OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 784076d..e76d8a4 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -226,16 +226,37 @@ public class BSCharacter : BSPhysObject bool ret = false; // If below the ground, move the avatar up - float terrainHeight = Scene.GetTerrainHeightAtXYZ(_position); - if (_position.Z < terrainHeight) + float terrainHeight = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); + if (Position.Z < terrainHeight) { - DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},orient={2}", LocalID, _position, _orientation); - _position.Z = terrainHeight + 2.0f; + DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); + Vector3 newPos = _position; + newPos.Z = terrainHeight + 2.0f; + _position = newPos; ret = true; } // TODO: check for out of bounds + return ret; + } + // A version of the sanity check that also makes sure a new position value is + // pushed back to the physics engine. This routine would be used by anyone + // who is not already pushing the value. + private bool PositionSanityCheck2() + { + bool ret = false; + if (PositionSanityCheck()) + { + // The new position value must be pushed into the physics engine but we can't + // just assign to "Position" because of potential call loops. + _scene.TaintedObject("BSCharacter.PositionSanityCheck", delegate() + { + DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + }); + ret = true; + } return ret; } @@ -500,9 +521,13 @@ public class BSCharacter : BSPhysObject // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. // base.RequestPhysicsterseUpdate(); - DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", + // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. + PositionSanityCheck2(); + + float heightHere = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); // just for debug + DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5},terrain={6}", LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, - entprop.Acceleration, entprop.RotationalVelocity); + entprop.Acceleration, entprop.RotationalVelocity, heightHere); } // Called by the scene when a collision with this object is reported diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs index 25084d8..d9270d1 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs @@ -48,11 +48,10 @@ public abstract class BSConstraint : IDisposable { if (m_enabled) { - // BulletSimAPI.RemoveConstraint(m_world.ID, m_body1.ID, m_body2.ID); + m_enabled = false; bool success = BulletSimAPI.DestroyConstraint2(m_world.Ptr, m_constraint.Ptr); m_world.scene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success); m_constraint.Ptr = System.IntPtr.Zero; - m_enabled = false; } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index d7213fc..8169e99 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -465,6 +465,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } }//end SetDefaultsForType + // One step of the vehicle properties for the next 'pTimestep' seconds. internal void Step(float pTimestep) { if (m_type == Vehicle.TYPE_NONE) return; @@ -592,9 +593,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin } // If below the terrain, move us above the ground a little. - if (pos.Z < m_prim.Scene.GetTerrainHeightAtXYZ(pos)) + if (pos.Z < m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos)) { - pos.Z = m_prim.Scene.GetTerrainHeightAtXYZ(pos) + 2; + pos.Z = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2; m_prim.Position = pos; VDetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos); } @@ -609,7 +610,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) { - m_VhoverTargetHeight = m_prim.Scene.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight; + m_VhoverTargetHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight; } if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) { @@ -673,7 +674,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { grav.Z = (float)(grav.Z * 1.125); } - float terraintemp = m_prim.Scene.GetTerrainHeightAtXYZ(pos); + float terraintemp = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos); float postemp = (pos.Z - terraintemp); if (postemp > 2.5f) { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index 6e205a9..ef463ca 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -1,58 +1,60 @@ -/* - * 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; - -namespace OpenSim.Region.Physics.BulletSPlugin -{ -// Class to wrap all objects. -// The rest of BulletSim doesn't need to keep checking for avatars or prims -// unless the difference is significant. -public abstract class BSPhysObject : PhysicsActor -{ - public abstract BSLinkset Linkset { get; set; } - - public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, - OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth); - public abstract void SendCollisions(); - - // Return the object mass without calculating it or side effects - public abstract float MassRaw { get; } - - public abstract BulletBody Body { get; set; } - public abstract void ZeroMotion(); - - public abstract void UpdateProperties(EntityProperties entprop); - - public abstract void Destroy(); -} -} +/* + * 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; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +// Class to wrap all objects. +// The rest of BulletSim doesn't need to keep checking for avatars or prims +// unless the difference is significant. +public abstract class BSPhysObject : PhysicsActor +{ + public abstract BSLinkset Linkset { get; set; } + + public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, + OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth); + public abstract void SendCollisions(); + + // Return the object mass without calculating it or side effects + public abstract float MassRaw { get; } + + public abstract BulletBody Body { get; set; } + public abstract void ZeroMotion(); + + public virtual void StepVehicle(float timeStep) { } + + public abstract void UpdateProperties(EntityProperties entprop); + + public abstract void Destroy(); +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 036fd4f..6bfce5c 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -377,7 +377,7 @@ public sealed class BSPrim : BSPhysObject // Called each simulation step to advance vehicle characteristics. // Called from Scene when doing simulation step so we're in taint processing time. - public void StepVehicle(float timeStep) + public override void StepVehicle(float timeStep) { if (IsPhysical) _vehicle.Step(timeStep); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index ce64b9b..f80304d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -39,8 +39,6 @@ using log4net; using OpenMetaverse; // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) -// Debug linkset -// Test with multiple regions in one simulator // Adjust character capsule size when height is adjusted (ScenePresence.SetHeight) // Test sculpties // Compute physics FPS reasonably @@ -54,10 +52,8 @@ using OpenMetaverse; // Use collision masks for collision with terrain and phantom objects // Check out llVolumeDetect. Must do something for that. // Should prim.link() and prim.delink() membership checking happen at taint time? -// changing the position and orientation of a linked prim must rebuild the constraint with the root. // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect -// Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions) // Implement LockAngularMotion // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) // Does NeedsMeshing() really need to exclude all the different shapes? @@ -85,18 +81,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters // moved to a better place. private HashSet m_avatarsWithCollisions = new HashSet(); - private List m_vehicles = new List(); - - private float[] m_heightMap; - private float m_waterLevel; - private uint m_worldID; - public uint WorldID { get { return m_worldID; } } + // List of all the objects that have vehicle properties and should be called + // to update each physics step. + private List m_vehicles = new List(); // let my minuions use my logger public ILog Logger { get { return m_log; } } - private bool m_initialized = false; - + // If non-zero, the number of simulation steps between calls to the physics + // engine to output detailed physics stats. Debug logging level must be on also. private int m_detailedStatsStep = 0; public IMesher mesher; @@ -106,29 +99,31 @@ public class BSScene : PhysicsScene, IPhysicsParameters public float MeshMegaPrimThreshold { get; private set; } public float SculptLOD { get; private set; } - private BulletSim m_worldSim; - public BulletSim World - { - get { return m_worldSim; } - } - private BSConstraintCollection m_constraintCollection; - public BSConstraintCollection Constraints - { - get { return m_constraintCollection; } - } + public uint WorldID { get; private set; } + public BulletSim World { get; private set; } + + // All the constraints that have been allocated in this instance. + public BSConstraintCollection Constraints { get; private set; } + // Simulation parameters private int m_maxSubSteps; private float m_fixedTimeStep; private long m_simulationStep = 0; public long SimulationStep { get { return m_simulationStep; } } + // The length of the last timestep we were asked to simulate. + // This is used by the vehicle code. Since the vehicle code is called + // once per simulation step, its constants need to be scaled by this. public float LastSimulatedTimestep { get; private set; } // A value of the time now so all the collision and update routines do not have to get their own - // Set to 'now' just before all the prims and actors are called for collisions and updates - private int m_simulationNowTime; - public int SimulationNowTime { get { return m_simulationNowTime; } } + // Set to 'now' just before all the prims and actors are called for collisions and updates + public int SimulationNowTime { get; private set; } + + // True if initialized and ready to do simulation steps + private bool m_initialized = false; + // Pinned memory used to pass step information between managed and unmanaged private int m_maxCollisionsPerFrame; private CollisionDesc[] m_collisionArray; private GCHandle m_collisionArrayPinnedHandle; @@ -145,6 +140,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero public const uint GROUNDPLANE_ID = 1; + public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here + + private float m_waterLevel; + public BSTerrainManager TerrainManager { get; private set; } public ConfigurationParameters Params { @@ -155,12 +154,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters get { return new Vector3(0f, 0f, Params.gravity); } } - private float m_maximumObjectMass; - public float MaximumObjectMass - { - get { return m_maximumObjectMass; } - } + public float MaximumObjectMass { get; private set; } + // When functions in the unmanaged code must be called, it is only + // done at a known time just before the simulation step. The taint + // system saves all these function calls and executes them in + // order before the simulation. public delegate void TaintCallback(); private struct TaintCallbackEntry { @@ -176,6 +175,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters private Object _taintLock = new Object(); // A pointer to an instance if this structure is passed to the C++ code + // Used to pass basic configuration values to the unmanaged code. ConfigurationParameters[] m_params; GCHandle m_paramsHandle; @@ -189,11 +189,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters private bool m_physicsLoggingEnabled; private string m_physicsLoggingDir; private string m_physicsLoggingPrefix; - private int m_physicsLoggingFileMinutes; - - private bool m_vehicleLoggingEnabled; - public bool VehicleLoggingEnabled { get { return m_vehicleLoggingEnabled; } } + private int m_physicsLoggingFileMinutes; + // 'true' of the vehicle code is to log lots of details + public bool VehicleLoggingEnabled { get; private set; } + #region Construction and Initialization public BSScene(string identifier) { m_initialized = false; @@ -216,6 +216,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned); + mesher = meshmerizer; + _taintedObjects = new List(); + // Enable very detailed logging. // By creating an empty logger when not logging, the log message invocation code // can be left in and every call doesn't have to check for null. @@ -228,11 +231,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters PhysicsLogging = new Logging.LogWriter(); } - // Get the version of the DLL - // TODO: this doesn't work yet. Something wrong with marshaling the returned string. - // BulletSimVersion = BulletSimAPI.GetVersion(); - // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion); - // If Debug logging level, enable logging from the unmanaged code if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) { @@ -245,22 +243,32 @@ public class BSScene : PhysicsScene, IPhysicsParameters BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle); } - _taintedObjects = new List(); - - mesher = meshmerizer; + // Get the version of the DLL + // TODO: this doesn't work yet. Something wrong with marshaling the returned string. + // BulletSimVersion = BulletSimAPI.GetVersion(); + // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion); - // The bounding box for the simulated world + // The bounding box for the simulated world. The origin is 0,0,0 unless we're + // a child in a mega-region. + // Turns out that Bullet really doesn't care about the extents of the simulated + // area. It tracks active objects no matter where they are. Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f); // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); - m_worldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(), + WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(), m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject()); // Initialization to support the transition to a new API which puts most of the logic // into the C# code so it is easier to modify and add to. - m_worldSim = new BulletSim(m_worldID, this, BulletSimAPI.GetSimHandle2(m_worldID)); - m_constraintCollection = new BSConstraintCollection(World); + World = new BulletSim(WorldID, this, BulletSimAPI.GetSimHandle2(WorldID)); + + Constraints = new BSConstraintCollection(World); + + // Note: choose one of the two following lines + // BulletSimAPI.CreateInitialGroundPlaneAndTerrain(WorldID); + TerrainManager = new BSTerrainManager(this); + TerrainManager.CreateInitialGroundPlaneAndTerrain(); m_initialized = true; } @@ -288,7 +296,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); // Very detailed logging for vehicle debugging - m_vehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); + VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); // Do any replacements in the parameters m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); @@ -323,6 +331,38 @@ public class BSScene : PhysicsScene, IPhysicsParameters PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg); } + public override void Dispose() + { + // m_log.DebugFormat("{0}: Dispose()", LogHeader); + + // make sure no stepping happens while we're deleting stuff + m_initialized = false; + + TerrainManager.ReleaseGroundPlaneAndTerrain(); + + foreach (KeyValuePair kvp in PhysObjects) + { + kvp.Value.Destroy(); + } + PhysObjects.Clear(); + + // Now that the prims are all cleaned up, there should be no constraints left + if (Constraints != null) + { + Constraints.Dispose(); + Constraints = null; + } + + // Anything left in the unmanaged code should be cleaned out + BulletSimAPI.Shutdown(WorldID); + + // Not logging any more + PhysicsLogging.Close(); + } + #endregion // Construction and Initialization + + #region Prim and Avatar addition and removal + public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) { m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); @@ -413,6 +453,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters // information call is not needed. public override void AddPhysicsActorTaint(PhysicsActor prim) { } + #endregion // Prim and Avatar addition and removal + + #region Simulation // Simulate one timestep public override float Simulate(float timeStep) { @@ -428,7 +471,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters int simulateStartTime = Util.EnvironmentTickCount(); - // update the prim states while we know the physics engine is not busy + // update the prim states while we know the physics engine is not busy + int numTaints = _taintedObjects.Count; ProcessTaints(); // Some of the prims operate with special vehicle properties @@ -440,14 +484,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters int numSubSteps = 0; try { - numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep, + numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); - DetailLog("{0},Simulate,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount); + DetailLog("{0},Simulate,call, nTaints= {1}, substeps={2}, updates={3}, colliders={4}", + DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); } catch (Exception e) { - m_log.WarnFormat("{0},PhysicsStep Exception: substeps={1}, updates={2}, colliders={3}, e={4}", LogHeader, numSubSteps, updatedEntityCount, collidersCount, e); - // DetailLog("{0},PhysicsStepException,call, substeps={1}, updates={2}, colliders={3}", DetailLogZero, numSubSteps, updatedEntityCount, collidersCount); + m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", + LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); + DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", + DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); updatedEntityCount = 0; collidersCount = 0; } @@ -456,7 +503,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in // Get a value for 'now' so all the collision and update routines don't have to get their own - m_simulationNowTime = Util.EnvironmentTickCount(); + SimulationNowTime = Util.EnvironmentTickCount(); // If there were collisions, process them by sending the event to the prim. // Collisions must be processed before updates. @@ -527,7 +574,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // Something has collided private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration) { - if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) + if (localID <= TerrainManager.HighestTerrainID) { return; // don't send collisions to the terrain } @@ -539,7 +586,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters BSPhysObject collidee = null; ActorTypes type = ActorTypes.Prim; - if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) + if (collidingWith <= TerrainManager.HighestTerrainID) { type = ActorTypes.Ground; } @@ -558,28 +605,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters return; } + #endregion // Simulation + public override void GetResults() { } - public override void SetTerrain(float[] heightMap) { - m_heightMap = heightMap; - this.TaintedObject("BSScene.SetTerrain", delegate() - { - BulletSimAPI.SetHeightmap(m_worldID, m_heightMap); - }); - } + #region Terrain - // Someday we will have complex terrain with caves and tunnels - // For the moment, it's flat and convex - public float GetTerrainHeightAtXYZ(Vector3 loc) - { - return GetTerrainHeightAtXY(loc.X, loc.Y); - } - - public float GetTerrainHeightAtXY(float tX, float tY) - { - if (tX < 0 || tX >= Constants.RegionSize || tY < 0 || tY >= Constants.RegionSize) - return 30; - return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)]; + public override void SetTerrain(float[] heightMap) { + TerrainManager.SetTerrain(heightMap); } public override void SetWaterLevel(float baseheight) @@ -595,35 +628,29 @@ public class BSScene : PhysicsScene, IPhysicsParameters public override void DeleteTerrain() { // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); + } + + // Although no one seems to check this, I do support combining. + public override bool SupportsCombining() + { + return TerrainManager.SupportsCombining(); } - - public override void Dispose() - { - // m_log.DebugFormat("{0}: Dispose()", LogHeader); - - // make sure no stepping happens while we're deleting stuff - m_initialized = false; - - foreach (KeyValuePair kvp in PhysObjects) - { - kvp.Value.Destroy(); - } - PhysObjects.Clear(); - - // Now that the prims are all cleaned up, there should be no constraints left - if (m_constraintCollection != null) - { - m_constraintCollection.Dispose(); - m_constraintCollection = null; - } - - // Anything left in the unmanaged code should be cleaned out - BulletSimAPI.Shutdown(WorldID); - - // Not logging any more - PhysicsLogging.Close(); + // This call says I am a child to region zero in a mega-region. 'pScene' is that + // of region zero, 'offset' is my offset from regions zero's origin, and + // 'extents' is the largest XY that is handled in my region. + public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) + { + TerrainManager.Combine(pScene, offset, extents); + } + + // Unhook all the combining that I know about. + public override void UnCombine(PhysicsScene pScene) + { + TerrainManager.UnCombine(pScene); } + #endregion // Terrain + public override Dictionary GetTopColliders() { return new Dictionary(); @@ -833,14 +860,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters // no locking because only called when physics engine is not busy private void ProcessVehicles(float timeStep) { - foreach (BSPrim prim in m_vehicles) + foreach (BSPhysObject pobj in m_vehicles) { - prim.StepVehicle(timeStep); + pobj.StepVehicle(timeStep); } } #endregion Vehicles - #region Parameters + #region INI and command line parameter processing delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); delegate float ParamGet(BSScene scene); @@ -943,9 +970,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", 10000.01f, - (s,cf,p,v) => { s.m_maximumObjectMass = cf.GetFloat(p, v); }, - (s) => { return (float)s.m_maximumObjectMass; }, - (s,p,l,v) => { s.m_maximumObjectMass = v; } ), + (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); }, + (s) => { return (float)s.MaximumObjectMass; }, + (s,p,l,v) => { s.MaximumObjectMass = v; } ), new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", 2200f, @@ -1207,6 +1234,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1]; + // This creates an array in the correct format for returning the list of + // parameters. This is used by the 'list' option of the 'physics' command. private void BuildParameterTable() { if (SettableParameters.Length < ParameterDefinitions.Length) @@ -1283,7 +1312,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters TaintedObject("BSScene.UpdateParameterSet", delegate() { foreach (uint lID in objectIDs) { - BulletSimAPI.UpdateParameter(m_worldID, lID, xparm, xval); + BulletSimAPI.UpdateParameter(WorldID, lID, xparm, xval); } }); break; @@ -1301,7 +1330,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters string xparm = parm.ToLower(); float xval = val; TaintedObject("BSScene.TaintedUpdateParameter", delegate() { - BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval); + BulletSimAPI.UpdateParameter(WorldID, xlocalID, xparm, xval); }); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs new file mode 100755 index 0000000..28c1940 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -0,0 +1,307 @@ +/* + * 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 OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.CoreModules; +using OpenSim.Region.Physics.Manager; + +using Nini.Config; +using log4net; + +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSTerrainManager +{ + static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; + + BSScene m_physicsScene; + + private BulletBody m_groundPlane; + + // If doing mega-regions, if we're region zero we will be managing multiple + // region terrains since region zero does the physics for the whole mega-region. + private Dictionary m_terrains; + private Dictionary m_heightMaps; + + // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. + // This is incremented before assigning to new region so it is the last ID allocated. + private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1; + public uint HighestTerrainID { get {return m_terrainCount; } } + + // If doing mega-regions, this holds our offset from region zero of + // the mega-regions. "parentScene" points to the PhysicsScene of region zero. + private Vector3 m_worldOffset = Vector3.Zero; + public Vector2 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize); + private PhysicsScene m_parentScene = null; + + public BSTerrainManager(BSScene physicsScene) + { + m_physicsScene = physicsScene; + m_terrains = new Dictionary(); + m_heightMaps = new Dictionary(); + } + + // Create the initial instance of terrain and the underlying ground plane. + // The objects are allocated in the unmanaged space and the pointers are tracked + // by the managed code. + // The terrains and the groundPlane are not added to the list of PhysObjects. + // This is called from the initialization routine so we presume it is + // safe to call Bullet in real time. We hope no one is moving around prim yet. + public void CreateInitialGroundPlaneAndTerrain() + { + // The ground plane is here to catch things that are trying to drop to negative infinity + m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, + BulletSimAPI.CreateGroundPlaneBody2(BSScene.GROUNDPLANE_ID, 1f, 0.4f)); + BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); + + Vector3 minTerrainCoords = new Vector3(0f, 0f, 24f); + Vector3 maxTerrainCoords = new Vector3(Constants.RegionSize, Constants.RegionSize, 25f); + int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; + float[] initialMap = new float[totalHeights]; + for (int ii = 0; ii < totalHeights; ii++) + { + initialMap[ii] = 25f; + } + CreateNewTerrainSegment(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords); + } + + public void ReleaseGroundPlaneAndTerrain() + { + if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr)) + { + BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); + } + m_groundPlane.Ptr = IntPtr.Zero; + + foreach (KeyValuePair kvp in m_terrains) + { + if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.Ptr)) + { + BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.Ptr); + BulletSimAPI.ReleaseHeightmapInfo2(m_heightMaps[kvp.Key].Ptr); + } + } + m_terrains.Clear(); + m_heightMaps.Clear(); + } + + // Create a new terrain description. This is used for mega-regions where + // the children of region zero give region zero all of the terrain + // segments since region zero does all the physics for the mega-region. + // Call at taint time!! + public void CreateNewTerrainSegment(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) + { + // The Z coordinates are recalculated to be the min and max height of the terrain + // itself. The caller may have passed us the real region extent. + float minZ = float.MaxValue; + float maxZ = float.MinValue; + int hSize = heightMap.Length; + for (int ii = 0; ii < hSize; ii++) + { + minZ = heightMap[ii] < minZ ? heightMap[ii] : minZ; + maxZ = heightMap[ii] > maxZ ? heightMap[ii] : maxZ; + } + minCoords.Z = minZ; + maxCoords.Z = maxZ; + // If the terrain is flat, make a difference so we get a good bounding box + if (minZ == maxZ) + minZ -= 0.2f; + Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); + + // Create the heightmap data structure in the unmanaged space + BulletHeightMapInfo mapInfo = new BulletHeightMapInfo( + BulletSimAPI.CreateHeightmap2(minCoords, maxCoords, heightMap), heightMap); + mapInfo.terrainRegionBase = terrainRegionBase; + mapInfo.maxRegionExtent = maxCoords; + mapInfo.minZ = minZ; + mapInfo.maxZ = maxZ; + mapInfo.sizeX = maxCoords.X - minCoords.X; + mapInfo.sizeY = maxCoords.Y - minCoords.Y; + + DetailLog("{0},BSScene.CreateNewTerrainSegment,call,minZ={1},maxZ={2},hMapPtr={3},minC={4},maxC={5}", + BSScene.DetailLogZero, minZ, maxZ, mapInfo.Ptr, minCoords, maxCoords); + // Create the terrain body from that heightmap + BulletBody terrainBody = new BulletBody(id, BulletSimAPI.CreateTerrainBody2(id, mapInfo.Ptr, 0.01f)); + + BulletSimAPI.SetFriction2(terrainBody.Ptr, m_physicsScene.Params.terrainFriction); + BulletSimAPI.SetHitFraction2(terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction); + BulletSimAPI.SetRestitution2(terrainBody.Ptr, m_physicsScene.Params.terrainRestitution); + BulletSimAPI.SetCollisionFlags2(terrainBody.Ptr, CollisionFlags.CF_STATIC_OBJECT); + BulletSimAPI.Activate2(terrainBody.Ptr, true); + + // Add the new terrain to the dynamics world + BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, terrainBody.Ptr); + BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, terrainBody.Ptr); + + + // Add the created terrain to the management set. If we are doing mega-regions, + // the terrains of our children will be added. + m_terrains.Add(terrainRegionBase, terrainBody); + m_heightMaps.Add(terrainRegionBase, mapInfo); + } + + public void SetTerrain(float[] heightMap) { + if (m_worldOffset != Vector3.Zero && m_parentScene != null) + { + // If doing the mega-prim stuff and we are the child of the zero region, + // the terrain is really added to our parent + if (m_parentScene is BSScene) + { + ((BSScene)m_parentScene).TerrainManager.SetTerrain(heightMap, m_worldOffset); + } + } + else + { + // if not doing the mega-prim thing, just change the terrain + SetTerrain(heightMap, m_worldOffset); + } + } + + private void SetTerrain(float[] heightMap, Vector3 tOffset) + { + float minZ = float.MaxValue; + float maxZ = float.MinValue; + + // Copy heightMap local and compute some statistics. + // Not really sure if we need to do this deep copy but, given + // the magic that happens to make the closure for taint + // below, I don't want there to be any problem with sharing + // locations of there are multiple calls to this routine + // within one tick. + int heightMapSize = heightMap.Length; + float[] localHeightMap = new float[heightMapSize]; + for (int ii = 0; ii < heightMapSize; ii++) + { + float height = heightMap[ii]; + if (height < minZ) minZ = height; + if (height > maxZ) maxZ = height; + localHeightMap[ii] = height; + } + + Vector2 terrainRegionBase = new Vector2(tOffset.X, tOffset.Y); + BulletHeightMapInfo mapInfo; + if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo)) + { + // If this is terrain we know about, it's easy to update + mapInfo.heightMap = localHeightMap; + m_physicsScene.TaintedObject("BSScene.SetTerrain:UpdateExisting", delegate() + { + DetailLog("{0},SetTerrain:UpdateExisting,baseX={1},baseY={2},minZ={3},maxZ={4}", + BSScene.DetailLogZero, tOffset.X, tOffset.Y, minZ, maxZ); + BulletSimAPI.UpdateHeightMap2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.heightMap); + }); + } + else + { + // Our mega-prim child is giving us a new terrain to add to the phys world + uint newTerrainID = ++m_terrainCount; + + Vector3 minCoords = tOffset; + minCoords.Z = minZ; + Vector3 maxCoords = new Vector3(tOffset.X + Constants.RegionSize, + tOffset.Y + Constants.RegionSize, + maxZ); + m_physicsScene.TaintedObject("BSScene.SetTerrain:NewTerrain", delegate() + { + DetailLog("{0},SetTerrain:NewTerrain,baseX={1},baseY={2}", BSScene.DetailLogZero, tOffset.X, tOffset.Y); + CreateNewTerrainSegment(newTerrainID, heightMap, minCoords, maxCoords); + }); + } + } + + // Someday we will have complex terrain with caves and tunnels + // For the moment, it's flat and convex + public float GetTerrainHeightAtXYZ(Vector3 loc) + { + return GetTerrainHeightAtXY(loc.X, loc.Y); + } + + // Given an X and Y, find the height of the terrain. + // Since we could be handling multiple terrains for a mega-region, + // the base of the region is calcuated assuming all regions are + // the same size and that is the default. + // Once the heightMapInfo is found, we have all the information to + // compute the offset into the array. + public float GetTerrainHeightAtXY(float tX, float tY) + { + float ret = 30f; + + int offsetX = ((int)(tX / (int)Constants.RegionSize)) * (int)Constants.RegionSize; + int offsetY = ((int)(tY / (int)Constants.RegionSize)) * (int)Constants.RegionSize; + Vector2 terrainBaseXY = new Vector2(offsetX, offsetY); + + BulletHeightMapInfo mapInfo; + if (m_heightMaps.TryGetValue(terrainBaseXY, out mapInfo)) + { + float regionX = tX - offsetX; + float regionY = tY - offsetY; + regionX = regionX > mapInfo.sizeX ? 0 : regionX; + regionY = regionY > mapInfo.sizeY ? 0 : regionY; + ret = mapInfo.heightMap[(int)(regionX * mapInfo.sizeX + regionY)]; + } + else + { + m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: x={1}, y={2}", + LogHeader, tX, tY); + } + return ret; + } + + // Although no one seems to check this, I do support combining. + public bool SupportsCombining() + { + return true; + } + // This call says I am a child to region zero in a mega-region. 'pScene' is that + // of region zero, 'offset' is my offset from regions zero's origin, and + // 'extents' is the largest XY that is handled in my region. + public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) + { + m_worldOffset = offset; + WorldExtents = new Vector2(extents.X, extents.Y); + m_parentScene = pScene; + } + + // Unhook all the combining that I know about. + public void UnCombine(PhysicsScene pScene) + { + // Just like ODE, for the moment a NOP + } + + + private void DetailLog(string msg, params Object[] args) + { + m_physicsScene.PhysicsLogging.Write(msg, args); + } +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index dab2420..3b319fb 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -33,6 +33,9 @@ using OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { // Classes to allow some type checking for the API +// These hold pointers to allocated objects in the unmanaged space. + +// The physics engine controller class created at initialization public struct BulletSim { public BulletSim(uint id, BSScene bss, IntPtr xx) { ID = id; scene = bss; Ptr = xx; } @@ -42,6 +45,7 @@ public struct BulletSim public IntPtr Ptr; } +// An allocated Bullet btRigidBody public struct BulletBody { public BulletBody(uint id, IntPtr xx) { ID = id; Ptr = xx; } @@ -49,12 +53,35 @@ public struct BulletBody public uint ID; } +// An allocated Bullet btConstraint public struct BulletConstraint { public BulletConstraint(IntPtr xx) { Ptr = xx; } public IntPtr Ptr; } +// An allocated HeightMapThing which hold various heightmap info +// Made a class rather than a struct so there would be only one +// instance of this and C# will pass around pointers rather +// than making copies. +public class BulletHeightMapInfo +{ + public BulletHeightMapInfo(IntPtr xx, float[] hm) { + Ptr = xx; + heightMap = hm; + terrainRegionBase = new Vector2(0f, 0f); + maxRegionExtent = new Vector3(100f, 100f, 25f); + minZ = maxZ = 0f; + sizeX = sizeY = 256f; + } + public IntPtr Ptr; + public float[] heightMap; + public Vector2 terrainRegionBase; + public Vector3 maxRegionExtent; + public float sizeX, sizeY; + public float minZ, maxZ; +} + // =============================================================================== [StructLayout(LayoutKind.Sequential)] public struct ConvexHull @@ -231,6 +258,9 @@ public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, int maxUpdates, IntPtr updateArray); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void CreateInitialGroundPlaneAndTerrain(uint worldID); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void SetHeightmap(uint worldID, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -414,12 +444,23 @@ public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape); // ===================================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateGroundPlaneBody2(uint id, Vector3 center, float collisionMargin); +public static extern IntPtr CreateGroundPlaneBody2(uint id, float height, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern IntPtr CreateTerrainBody2(uint id, - Vector3 minCoords, Vector3 maxCoords, float collisionMargin, - [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); + IntPtr heightMapInfo, + float collisionMargin); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateHeightmap2(Vector3 minCoords, Vector3 maxCoords, + [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool ReleaseHeightmapInfo2(IntPtr heightMapInfo); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void UpdateHeightMap2(IntPtr world, IntPtr heightMapInfo, + [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); // ===================================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -473,11 +514,16 @@ public static extern bool SetConstraintParam2(IntPtr constrain, ConstraintParams [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool DestroyConstraint2(IntPtr world, IntPtr constrain); +// ===================================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern Vector3 AddObjectToWorld2(IntPtr world, IntPtr obj); +public static extern bool AddObjectToWorld2(IntPtr world, IntPtr obj); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern Vector3 RemoveObjectFromWorld2(IntPtr world, IntPtr obj); +public static extern bool RemoveObjectFromWorld2(IntPtr world, IntPtr obj); + +// ===================================================================================== +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void Activate2(IntPtr obj, bool forceActivation); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern Vector3 GetPosition2(IntPtr obj); @@ -522,6 +568,9 @@ public static extern bool SetContactProcessingThreshold2(IntPtr obj, float val); public static extern bool SetFriction2(IntPtr obj, float val); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetHitFraction2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool SetRestitution2(IntPtr obj, float val); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -564,7 +613,7 @@ public static extern bool SetMargin2(IntPtr obj, float val); public static extern bool UpdateSingleAabb2(IntPtr world, IntPtr obj); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool DestroyObject2(IntPtr world, uint id); +public static extern bool DestroyObject2(IntPtr world, IntPtr obj); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void DumpPhysicsStatistics2(IntPtr sim); -- cgit v1.1 From d3adf9b2b3bd8e0e76168cd6eb6414f3123c5f02 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sat, 25 Aug 2012 23:25:29 -0700 Subject: BulletSim: fix line endings. --- OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 44 ++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index f80304d..7b4802e 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -102,7 +102,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters public uint WorldID { get; private set; } public BulletSim World { get; private set; } - // All the constraints that have been allocated in this instance. + // All the constraints that have been allocated in this instance. public BSConstraintCollection Constraints { get; private set; } // Simulation parameters @@ -117,7 +117,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters public float LastSimulatedTimestep { get; private set; } // A value of the time now so all the collision and update routines do not have to get their own - // Set to 'now' just before all the prims and actors are called for collisions and updates + // Set to 'now' just before all the prims and actors are called for collisions and updates public int SimulationNowTime { get; private set; } // True if initialized and ready to do simulation steps @@ -140,8 +140,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero public const uint GROUNDPLANE_ID = 1; - public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here - + public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here + private float m_waterLevel; public BSTerrainManager TerrainManager { get; private set; } @@ -189,8 +189,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters private bool m_physicsLoggingEnabled; private string m_physicsLoggingDir; private string m_physicsLoggingPrefix; - private int m_physicsLoggingFileMinutes; - // 'true' of the vehicle code is to log lots of details + private int m_physicsLoggingFileMinutes; + // 'true' of the vehicle code is to log lots of details public bool VehicleLoggingEnabled { get; private set; } #region Construction and Initialization @@ -266,7 +266,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters Constraints = new BSConstraintCollection(World); // Note: choose one of the two following lines - // BulletSimAPI.CreateInitialGroundPlaneAndTerrain(WorldID); + // BulletSimAPI.CreateInitialGroundPlaneAndTerrain(WorldID); TerrainManager = new BSTerrainManager(this); TerrainManager.CreateInitialGroundPlaneAndTerrain(); @@ -471,7 +471,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters int simulateStartTime = Util.EnvironmentTickCount(); - // update the prim states while we know the physics engine is not busy + // update the prim states while we know the physics engine is not busy int numTaints = _taintedObjects.Count; ProcessTaints(); @@ -611,7 +611,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters #region Terrain - public override void SetTerrain(float[] heightMap) { + public override void SetTerrain(float[] heightMap) { TerrainManager.SetTerrain(heightMap); } @@ -628,25 +628,25 @@ public class BSScene : PhysicsScene, IPhysicsParameters public override void DeleteTerrain() { // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); - } - - // Although no one seems to check this, I do support combining. - public override bool SupportsCombining() - { - return TerrainManager.SupportsCombining(); + } + + // Although no one seems to check this, I do support combining. + public override bool SupportsCombining() + { + return TerrainManager.SupportsCombining(); } // This call says I am a child to region zero in a mega-region. 'pScene' is that // of region zero, 'offset' is my offset from regions zero's origin, and // 'extents' is the largest XY that is handled in my region. public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) - { + { TerrainManager.Combine(pScene, offset, extents); - } - - // Unhook all the combining that I know about. - public override void UnCombine(PhysicsScene pScene) - { - TerrainManager.UnCombine(pScene); + } + + // Unhook all the combining that I know about. + public override void UnCombine(PhysicsScene pScene) + { + TerrainManager.UnCombine(pScene); } #endregion // Terrain -- cgit v1.1 From ae852bb8738c7bce60c8fee9fbf6038288bd9363 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Wed, 29 Aug 2012 09:20:09 -0700 Subject: BulletSim: clean up some variable naming for consistancy. Update DLL API for new terrain and shape/body pattern methods. Terrain creation and modification uses new shape/body pattern. Move debug logging callback set to initialization call so logging is per physics engine. --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 78 ++++++------ OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 13 +- .../Physics/BulletSPlugin/BSTerrainManager.cs | 139 +++++++++++++++------ .../Region/Physics/BulletSPlugin/BulletSimAPI.cs | 55 +++++--- 4 files changed, 183 insertions(+), 102 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index e76d8a4..fa21233 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -39,8 +39,7 @@ public class BSCharacter : BSPhysObject private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS CHAR]"; - private BSScene _scene; - public BSScene Scene { get { return _scene; } } + public BSScene Scene { get; private set; } private String _avName; // private bool _stopped; private Vector3 _size; @@ -92,7 +91,7 @@ public class BSCharacter : BSPhysObject { _localID = localID; _avName = avName; - _scene = parent_scene; + Scene = parent_scene; _position = pos; _size = size; _flying = isFlying; @@ -101,11 +100,11 @@ public class BSCharacter : BSPhysObject _buoyancy = ComputeBuoyancyFromFlying(isFlying); // The dimensions of the avatar capsule are kept in the scale. // Physics creates a unit capsule which is scaled by the physics engine. - _scale = new Vector3(_scene.Params.avatarCapsuleRadius, _scene.Params.avatarCapsuleRadius, size.Z); - _density = _scene.Params.avatarDensity; + _scale = new Vector3(Scene.Params.avatarCapsuleRadius, Scene.Params.avatarCapsuleRadius, size.Z); + _density = Scene.Params.avatarDensity; ComputeAvatarVolumeAndMass(); // set _avatarVolume and _mass based on capsule size, _density and _scale - Linkset = new BSLinkset(_scene, this); + Linkset = new BSLinkset(Scene, this); ShapeData shapeData = new ShapeData(); shapeData.ID = _localID; @@ -117,19 +116,19 @@ public class BSCharacter : BSPhysObject shapeData.Mass = _mass; shapeData.Buoyancy = _buoyancy; shapeData.Static = ShapeData.numericFalse; - shapeData.Friction = _scene.Params.avatarFriction; - shapeData.Restitution = _scene.Params.avatarRestitution; + shapeData.Friction = Scene.Params.avatarFriction; + shapeData.Restitution = Scene.Params.avatarRestitution; // do actual create at taint time - _scene.TaintedObject("BSCharacter.create", delegate() + Scene.TaintedObject("BSCharacter.create", delegate() { DetailLog("{0},BSCharacter.create", _localID); - BulletSimAPI.CreateObject(parent_scene.WorldID, shapeData); + BulletSimAPI.CreateObject(Scene.WorldID, shapeData); // Set the buoyancy for flying. This will be refactored when all the settings happen in C# - BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, LocalID, _buoyancy); + BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy); - Body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); + Body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(Scene.World.Ptr, LocalID)); // avatars get all collisions no matter what (makes walking on ground and such work) BulletSimAPI.AddToCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); }); @@ -141,9 +140,9 @@ public class BSCharacter : BSPhysObject public override void Destroy() { DetailLog("{0},BSCharacter.Destroy", LocalID); - _scene.TaintedObject("BSCharacter.destroy", delegate() + Scene.TaintedObject("BSCharacter.destroy", delegate() { - BulletSimAPI.DestroyObject(_scene.WorldID, _localID); + BulletSimAPI.DestroyObject(Scene.WorldID, _localID); }); } @@ -172,9 +171,9 @@ public class BSCharacter : BSPhysObject ComputeAvatarVolumeAndMass(); - _scene.TaintedObject("BSCharacter.setSize", delegate() + Scene.TaintedObject("BSCharacter.setSize", delegate() { - BulletSimAPI.SetObjectScaleMass(_scene.WorldID, LocalID, _scale, _mass, true); + BulletSimAPI.SetObjectScaleMass(Scene.WorldID, LocalID, _scale, _mass, true); }); } @@ -203,17 +202,17 @@ public class BSCharacter : BSPhysObject public override Vector3 Position { get { - // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + // _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); return _position; } set { _position = value; PositionSanityCheck(); - _scene.TaintedObject("BSCharacter.setPosition", delegate() + Scene.TaintedObject("BSCharacter.setPosition", delegate() { DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation); }); } } @@ -229,10 +228,8 @@ public class BSCharacter : BSPhysObject float terrainHeight = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); if (Position.Z < terrainHeight) { - DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); - Vector3 newPos = _position; - newPos.Z = terrainHeight + 2.0f; - _position = newPos; + DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); + _position.Z = terrainHeight + 2.0f; ret = true; } @@ -250,10 +247,10 @@ public class BSCharacter : BSPhysObject { // The new position value must be pushed into the physics engine but we can't // just assign to "Position" because of potential call loops. - _scene.TaintedObject("BSCharacter.PositionSanityCheck", delegate() + Scene.TaintedObject("BSCharacter.PositionSanityCheck", delegate() { DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation); }); ret = true; } @@ -301,10 +298,10 @@ public class BSCharacter : BSPhysObject set { _velocity = value; // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); - _scene.TaintedObject("BSCharacter.setVelocity", delegate() + Scene.TaintedObject("BSCharacter.setVelocity", delegate() { DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); - BulletSimAPI.SetObjectVelocity(_scene.WorldID, _localID, _velocity); + BulletSimAPI.SetObjectVelocity(Scene.WorldID, _localID, _velocity); }); } } @@ -327,10 +324,10 @@ public class BSCharacter : BSPhysObject set { _orientation = value; // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); - _scene.TaintedObject("BSCharacter.setOrientation", delegate() + Scene.TaintedObject("BSCharacter.setOrientation", delegate() { - // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); - BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + // _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); + BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation); }); } } @@ -367,11 +364,11 @@ public class BSCharacter : BSPhysObject set { _throttleUpdates = value; } } public override bool IsColliding { - get { return (_collidingStep == _scene.SimulationStep); } + get { return (_collidingStep == Scene.SimulationStep); } set { _isColliding = value; } } public override bool CollidingGround { - get { return (_collidingGroundStep == _scene.SimulationStep); } + get { return (_collidingGroundStep == Scene.SimulationStep); } set { _collidingGround = value; } } public override bool CollidingObj { @@ -393,10 +390,10 @@ public class BSCharacter : BSPhysObject public override float Buoyancy { get { return _buoyancy; } set { _buoyancy = value; - _scene.TaintedObject("BSCharacter.setBuoyancy", delegate() + Scene.TaintedObject("BSCharacter.setBuoyancy", delegate() { DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); - BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, LocalID, _buoyancy); + BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy); }); } } @@ -440,7 +437,7 @@ public class BSCharacter : BSPhysObject _force.Y += force.Y; _force.Z += force.Z; // m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force); - _scene.TaintedObject("BSCharacter.AddForce", delegate() + Scene.TaintedObject("BSCharacter.AddForce", delegate() { DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force); BulletSimAPI.AddObjectForce2(Body.Ptr, _force); @@ -524,10 +521,9 @@ public class BSCharacter : BSPhysObject // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. PositionSanityCheck2(); - float heightHere = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); // just for debug + float heightHere = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); // only for debug DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5},terrain={6}", - LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, - entprop.Acceleration, entprop.RotationalVelocity, heightHere); + LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity, heightHere); } // Called by the scene when a collision with this object is reported @@ -539,16 +535,16 @@ public class BSCharacter : BSPhysObject // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); // The following makes IsColliding() and IsCollidingGround() work - _collidingStep = _scene.SimulationStep; + _collidingStep = Scene.SimulationStep; if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) { - _collidingGroundStep = _scene.SimulationStep; + _collidingGroundStep = Scene.SimulationStep; } // DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith); // throttle collisions to the rate specified in the subscription if (_subscribedEventsMs != 0) { - int nowTime = _scene.SimulationNowTime; + int nowTime = Scene.SimulationNowTime; if (nowTime >= _nextCollisionOkTime) { _nextCollisionOkTime = nowTime + _subscribedEventsMs; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 7b4802e..2f55ba4 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -232,15 +232,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // If Debug logging level, enable logging from the unmanaged code + m_DebugLogCallbackHandle = null; if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) { m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); if (PhysicsLogging.Enabled) + // The handle is saved in a variable to make sure it doesn't get freed after this call m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog); else m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); - // The handle is saved in a variable to make sure it doesn't get freed after this call - BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle); } // Get the version of the DLL @@ -257,7 +257,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(), m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), - m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject()); + m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(), + m_DebugLogCallbackHandle); // Initialization to support the transition to a new API which puts most of the logic // into the C# code so it is easier to modify and add to. @@ -265,8 +266,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters Constraints = new BSConstraintCollection(World); - // Note: choose one of the two following lines - // BulletSimAPI.CreateInitialGroundPlaneAndTerrain(WorldID); TerrainManager = new BSTerrainManager(this); TerrainManager.CreateInitialGroundPlaneAndTerrain(); @@ -378,7 +377,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); lock (PhysObjects) PhysObjects.Add(localID, actor); - // Remove kludge someday + // TODO: Remove kludge someday. + // We must generate a collision for avatars whether they collide or not. + // This is required by OpenSim to update avatar animations, etc. lock (m_avatarsWithCollisions) m_avatarsWithCollisions.Add(actor); return actor; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 28c1940..733d9c2 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -44,8 +44,22 @@ public class BSTerrainManager { static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; + // These height values are fractional so the odd values will be + // noticable when debugging. + public const float HEIGHT_INITIALIZATION = 24.987f; + public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f; + public const float HEIGHT_GETHEIGHT_RET = 24.765f; + + // If the min and max height are equal, we reduce the min by this + // amount to make sure that a bounding box is built for the terrain. + public const float HEIGHT_EQUAL_FUDGE = 0.2f; + + public const float TERRAIN_COLLISION_MARGIN = 0.2f; + + // The scene that I am part of BSScene m_physicsScene; + // The ground plane created to keep thing from falling to infinity. private BulletBody m_groundPlane; // If doing mega-regions, if we're region zero we will be managing multiple @@ -53,6 +67,10 @@ public class BSTerrainManager private Dictionary m_terrains; private Dictionary m_heightMaps; + // True of the terrain has been modified. + // Used to force recalculation of terrain height after terrain has been modified + private bool m_terrainModified; + // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. // This is incremented before assigning to new region so it is the last ID allocated. private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1; @@ -69,6 +87,7 @@ public class BSTerrainManager m_physicsScene = physicsScene; m_terrains = new Dictionary(); m_heightMaps = new Dictionary(); + m_terrainModified = false; } // Create the initial instance of terrain and the underlying ground plane. @@ -80,17 +99,18 @@ public class BSTerrainManager public void CreateInitialGroundPlaneAndTerrain() { // The ground plane is here to catch things that are trying to drop to negative infinity + BulletShape groundPlaneShape = new BulletShape(BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN)); m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, - BulletSimAPI.CreateGroundPlaneBody2(BSScene.GROUNDPLANE_ID, 1f, 0.4f)); + BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.Ptr, Vector3.Zero, Quaternion.Identity)); BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); - Vector3 minTerrainCoords = new Vector3(0f, 0f, 24f); - Vector3 maxTerrainCoords = new Vector3(Constants.RegionSize, Constants.RegionSize, 25f); + Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE); + Vector3 maxTerrainCoords = new Vector3(Constants.RegionSize, Constants.RegionSize, HEIGHT_INITIALIZATION); int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; float[] initialMap = new float[totalHeights]; for (int ii = 0; ii < totalHeights; ii++) { - initialMap[ii] = 25f; + initialMap[ii] = HEIGHT_INITIALIZATION; } CreateNewTerrainSegment(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords); } @@ -108,7 +128,7 @@ public class BSTerrainManager if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.Ptr)) { BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.Ptr); - BulletSimAPI.ReleaseHeightmapInfo2(m_heightMaps[kvp.Key].Ptr); + BulletSimAPI.ReleaseHeightMapInfo2(m_heightMaps[kvp.Key].Ptr); } } m_terrains.Clear(); @@ -128,30 +148,41 @@ public class BSTerrainManager int hSize = heightMap.Length; for (int ii = 0; ii < hSize; ii++) { - minZ = heightMap[ii] < minZ ? heightMap[ii] : minZ; - maxZ = heightMap[ii] > maxZ ? heightMap[ii] : maxZ; + float height = heightMap[ii]; + if (height < minZ) minZ = height; + if (height > maxZ) maxZ = height; } + // If the terrain is flat, make a difference so we get a bounding box + if (minZ == maxZ) + minZ -= HEIGHT_EQUAL_FUDGE; + minCoords.Z = minZ; maxCoords.Z = maxZ; - // If the terrain is flat, make a difference so we get a good bounding box - if (minZ == maxZ) - minZ -= 0.2f; Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); // Create the heightmap data structure in the unmanaged space - BulletHeightMapInfo mapInfo = new BulletHeightMapInfo( - BulletSimAPI.CreateHeightmap2(minCoords, maxCoords, heightMap), heightMap); + BulletHeightMapInfo mapInfo = new BulletHeightMapInfo(id, heightMap, + BulletSimAPI.CreateHeightMapInfo2(id, minCoords, maxCoords, heightMap, TERRAIN_COLLISION_MARGIN)); mapInfo.terrainRegionBase = terrainRegionBase; - mapInfo.maxRegionExtent = maxCoords; + mapInfo.minCoords = minCoords; + mapInfo.maxCoords = maxCoords; mapInfo.minZ = minZ; mapInfo.maxZ = maxZ; mapInfo.sizeX = maxCoords.X - minCoords.X; mapInfo.sizeY = maxCoords.Y - minCoords.Y; + Vector3 centerPos; + centerPos.X = minCoords.X + (mapInfo.sizeX / 2f); + centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f); + centerPos.Z = minZ + (maxZ - minZ) / 2f; + DetailLog("{0},BSScene.CreateNewTerrainSegment,call,minZ={1},maxZ={2},hMapPtr={3},minC={4},maxC={5}", BSScene.DetailLogZero, minZ, maxZ, mapInfo.Ptr, minCoords, maxCoords); - // Create the terrain body from that heightmap - BulletBody terrainBody = new BulletBody(id, BulletSimAPI.CreateTerrainBody2(id, mapInfo.Ptr, 0.01f)); + // Create the terrain shape from the mapInfo + BulletShape terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); + + BulletBody terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2(terrainShape.Ptr, + centerPos, Quaternion.Identity)); BulletSimAPI.SetFriction2(terrainBody.Ptr, m_physicsScene.Params.terrainFriction); BulletSimAPI.SetHitFraction2(terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction); @@ -163,11 +194,12 @@ public class BSTerrainManager BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, terrainBody.Ptr); BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, terrainBody.Ptr); - // Add the created terrain to the management set. If we are doing mega-regions, // the terrains of our children will be added. m_terrains.Add(terrainRegionBase, terrainBody); m_heightMaps.Add(terrainRegionBase, mapInfo); + + m_terrainModified = true; } public void SetTerrain(float[] heightMap) { @@ -191,34 +223,57 @@ public class BSTerrainManager { float minZ = float.MaxValue; float maxZ = float.MinValue; + Vector2 terrainRegionBase = new Vector2(tOffset.X, tOffset.Y); - // Copy heightMap local and compute some statistics. - // Not really sure if we need to do this deep copy but, given - // the magic that happens to make the closure for taint - // below, I don't want there to be any problem with sharing - // locations of there are multiple calls to this routine - // within one tick. int heightMapSize = heightMap.Length; - float[] localHeightMap = new float[heightMapSize]; for (int ii = 0; ii < heightMapSize; ii++) { float height = heightMap[ii]; if (height < minZ) minZ = height; if (height > maxZ) maxZ = height; - localHeightMap[ii] = height; } - Vector2 terrainRegionBase = new Vector2(tOffset.X, tOffset.Y); + // The shape of the terrain is from its base to its extents. + Vector3 minCoords, maxCoords; + minCoords = tOffset; + minCoords.Z = minZ; + maxCoords = tOffset; + maxCoords.X += Constants.RegionSize; + maxCoords.Y += Constants.RegionSize; + maxCoords.Z = maxZ; + + BulletBody terrainBody; BulletHeightMapInfo mapInfo; if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo)) { + terrainBody = m_terrains[terrainRegionBase]; + // Copy heightMap local and compute some statistics. + for (int ii = 0; ii < heightMapSize; ii++) + { + mapInfo.heightMap[ii] = heightMap[ii]; + } + // If this is terrain we know about, it's easy to update - mapInfo.heightMap = localHeightMap; m_physicsScene.TaintedObject("BSScene.SetTerrain:UpdateExisting", delegate() { DetailLog("{0},SetTerrain:UpdateExisting,baseX={1},baseY={2},minZ={3},maxZ={4}", BSScene.DetailLogZero, tOffset.X, tOffset.Y, minZ, maxZ); - BulletSimAPI.UpdateHeightMap2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.heightMap); + // Fill the existing height map info with the new location and size information + BulletSimAPI.FillHeightMapInfo2(mapInfo.Ptr, mapInfo.ID, minCoords, maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); + + // Create a terrain shape based on the new info + BulletShape terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); + + // Swap the shape in the terrain body (this also deletes the old shape) + bool success = BulletSimAPI.ReplaceBodyShape2(m_physicsScene.World.Ptr, terrainBody.Ptr, terrainShape.Ptr); + + if (!success) + { + DetailLog("{0},SetTerrain:UpdateExisting,Failed", BSScene.DetailLogZero); + m_physicsScene.Logger.ErrorFormat("{0} Failed updating terrain heightmap. Region={1}", + LogHeader, m_physicsScene.RegionName); + + } }); } else @@ -226,11 +281,6 @@ public class BSTerrainManager // Our mega-prim child is giving us a new terrain to add to the phys world uint newTerrainID = ++m_terrainCount; - Vector3 minCoords = tOffset; - minCoords.Z = minZ; - Vector3 maxCoords = new Vector3(tOffset.X + Constants.RegionSize, - tOffset.Y + Constants.RegionSize, - maxZ); m_physicsScene.TaintedObject("BSScene.SetTerrain:NewTerrain", delegate() { DetailLog("{0},SetTerrain:NewTerrain,baseX={1},baseY={2}", BSScene.DetailLogZero, tOffset.X, tOffset.Y); @@ -240,9 +290,9 @@ public class BSTerrainManager } // Someday we will have complex terrain with caves and tunnels - // For the moment, it's flat and convex public float GetTerrainHeightAtXYZ(Vector3 loc) { + // For the moment, it's flat and convex return GetTerrainHeightAtXY(loc.X, loc.Y); } @@ -252,9 +302,19 @@ public class BSTerrainManager // the same size and that is the default. // Once the heightMapInfo is found, we have all the information to // compute the offset into the array. + private float lastHeightTX = 999999f; + private float lastHeightTY = 999999f; + private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; public float GetTerrainHeightAtXY(float tX, float tY) { - float ret = 30f; + // You'd be surprized at the number of times this routine is called + // with the same parameters as last time. + if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) + return lastHeight; + + lastHeightTX = tX; + lastHeightTY = tY; + float ret = HEIGHT_GETHEIGHT_RET; int offsetX = ((int)(tX / (int)Constants.RegionSize)) * (int)Constants.RegionSize; int offsetY = ((int)(tY / (int)Constants.RegionSize)) * (int)Constants.RegionSize; @@ -265,15 +325,20 @@ public class BSTerrainManager { float regionX = tX - offsetX; float regionY = tY - offsetY; - regionX = regionX > mapInfo.sizeX ? 0 : regionX; - regionY = regionY > mapInfo.sizeY ? 0 : regionY; - ret = mapInfo.heightMap[(int)(regionX * mapInfo.sizeX + regionY)]; + if (regionX > mapInfo.sizeX) regionX = 0; + if (regionY > mapInfo.sizeY) regionY = 0; + int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX; + ret = mapInfo.heightMap[mapIndex]; + m_terrainModified = false; + DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}", + BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret); } else { m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: x={1}, y={2}", LogHeader, tX, tY); } + lastHeight = ret; return ret; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 3b319fb..804d2ea 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -38,13 +38,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin { // The physics engine controller class created at initialization public struct BulletSim { - public BulletSim(uint id, BSScene bss, IntPtr xx) { ID = id; scene = bss; Ptr = xx; } - public uint ID; + public BulletSim(uint worldId, BSScene bss, IntPtr 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 public BSScene scene; public IntPtr Ptr; } +public struct BulletShape +{ + public BulletShape(IntPtr xx) { Ptr = xx; } + public IntPtr Ptr; +} + // An allocated Bullet btRigidBody public struct BulletBody { @@ -66,18 +72,22 @@ public struct BulletConstraint // than making copies. public class BulletHeightMapInfo { - public BulletHeightMapInfo(IntPtr xx, float[] hm) { + public BulletHeightMapInfo(uint id, float[] hm, IntPtr xx) { + ID = id; Ptr = xx; heightMap = hm; terrainRegionBase = new Vector2(0f, 0f); - maxRegionExtent = new Vector3(100f, 100f, 25f); + minCoords = new Vector3(100f, 100f, 25f); + maxCoords = new Vector3(101f, 101f, 26f); minZ = maxZ = 0f; sizeX = sizeY = 256f; } + public uint ID; public IntPtr Ptr; public float[] heightMap; public Vector2 terrainRegionBase; - public Vector3 maxRegionExtent; + public Vector3 minCoords; + public Vector3 maxCoords; public float sizeX, sizeY; public float minZ, maxZ; } @@ -248,6 +258,10 @@ public enum ConstraintParamAxis : int // =============================================================================== static class BulletSimAPI { +// Link back to the managed code for outputting log messages +[UnmanagedFunctionPointer(CallingConvention.Cdecl)] +public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); + [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.LPStr)] public static extern string GetVersion(); @@ -255,7 +269,8 @@ public static extern string GetVersion(); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, int maxCollisions, IntPtr collisionArray, - int maxUpdates, IntPtr updateArray); + int maxUpdates, IntPtr updateArray, + DebugLogCallback logRoutine); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void CreateInitialGroundPlaneAndTerrain(uint worldID); @@ -372,8 +387,6 @@ public static extern Vector3 RecoverFromPenetration(uint worldID, uint id); public static extern void DumpBulletStatistics(); // Log a debug message -[UnmanagedFunctionPointer(CallingConvention.Cdecl)] -public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void SetDebugLogCallback(DebugLogCallback callback); @@ -407,7 +420,7 @@ public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms, public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern void SetHeightmap2(IntPtr world, float[] heightmap); +public static extern void SetHeightMap2(IntPtr world, float[] heightmap); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void Shutdown2(IntPtr sim); @@ -442,25 +455,31 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vector3 pos, Quaternion rot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool ReplaceBodyShape2(IntPtr sim, IntPtr obj, IntPtr shape); // ===================================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateGroundPlaneBody2(uint id, float height, float collisionMargin); +public static extern IntPtr CreateHeightMapInfo2(uint id, Vector3 minCoords, Vector3 maxCoords, + [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateTerrainBody2(uint id, - IntPtr heightMapInfo, - float collisionMargin); +public static extern IntPtr FillHeightMapInfo2(IntPtr mapInfo, uint id, Vector3 minCoords, Vector3 maxCoords, + [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateHeightmap2(Vector3 minCoords, Vector3 maxCoords, - [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); +public static extern bool ReleaseHeightMapInfo2(IntPtr heightMapInfo); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool ReleaseHeightmapInfo2(IntPtr heightMapInfo); +public static extern IntPtr CreateGroundPlaneShape2(uint id, float height, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern void UpdateHeightMap2(IntPtr world, IntPtr heightMapInfo, - [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); +public static extern IntPtr CreateTerrainShape2(IntPtr mapInfo); // ===================================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -- cgit v1.1 From ffdc7987207de279116a077e2042ed3a1f381a5f Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Fri, 31 Aug 2012 11:33:36 -0700 Subject: BulletSim: Update BulletSimAPI to match the DLL interface. Major rework of terrain management which finally makes mega-regions work. Update heightmap of terrain by rebuilding the terrain's body and shape. There is a problem with just replacing the shape so this workaround will do for the moment but it will need to be resolved for mesh and hull switching. --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 2 +- OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 2 +- .../Physics/BulletSPlugin/BSTerrainManager.cs | 356 +++++++++++++-------- .../Region/Physics/BulletSPlugin/BulletSimAPI.cs | 8 +- 4 files changed, 231 insertions(+), 137 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index fa21233..747ae71 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -228,7 +228,7 @@ public class BSCharacter : BSPhysObject float terrainHeight = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); if (Position.Z < terrainHeight) { - DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); + DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); _position.Z = terrainHeight + 2.0f; ret = true; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 2f55ba4..4a468af 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -1070,7 +1070,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters (s) => { return s.m_params[0].terrainRestitution; }, (s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ), new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", - 0.5f, + 0.2f, (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarFriction; }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 733d9c2..ab45f8f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -54,17 +54,19 @@ public class BSTerrainManager // amount to make sure that a bounding box is built for the terrain. public const float HEIGHT_EQUAL_FUDGE = 0.2f; - public const float TERRAIN_COLLISION_MARGIN = 0.2f; + public const float TERRAIN_COLLISION_MARGIN = 0.0f; + + // Until the whole simulator is changed to pass us the region size, we rely on constants. + public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, 0f); // The scene that I am part of - BSScene m_physicsScene; + private BSScene m_physicsScene; // The ground plane created to keep thing from falling to infinity. private BulletBody m_groundPlane; // If doing mega-regions, if we're region zero we will be managing multiple // region terrains since region zero does the physics for the whole mega-region. - private Dictionary m_terrains; private Dictionary m_heightMaps; // True of the terrain has been modified. @@ -78,16 +80,22 @@ public class BSTerrainManager // If doing mega-regions, this holds our offset from region zero of // the mega-regions. "parentScene" points to the PhysicsScene of region zero. - private Vector3 m_worldOffset = Vector3.Zero; - public Vector2 WorldExtents = new Vector2((int)Constants.RegionSize, (int)Constants.RegionSize); - private PhysicsScene m_parentScene = null; + private Vector3 m_worldOffset; + // If the parent region (region 0), this is the extent of the combined regions + // relative to the origin of region zero + private Vector3 m_worldMax; + private PhysicsScene m_parentScene; public BSTerrainManager(BSScene physicsScene) { m_physicsScene = physicsScene; - m_terrains = new Dictionary(); m_heightMaps = new Dictionary(); m_terrainModified = false; + + // Assume one region of default size + m_worldOffset = Vector3.Zero; + m_worldMax = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, 4096f); + m_parentScene = null; } // Create the initial instance of terrain and the underlying ground plane. @@ -95,7 +103,7 @@ public class BSTerrainManager // by the managed code. // The terrains and the groundPlane are not added to the list of PhysObjects. // This is called from the initialization routine so we presume it is - // safe to call Bullet in real time. We hope no one is moving around prim yet. + // safe to call Bullet in real time. We hope no one is moving prims around yet. public void CreateInitialGroundPlaneAndTerrain() { // The ground plane is here to catch things that are trying to drop to negative infinity @@ -105,125 +113,91 @@ public class BSTerrainManager BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE); - Vector3 maxTerrainCoords = new Vector3(Constants.RegionSize, Constants.RegionSize, HEIGHT_INITIALIZATION); + Vector3 maxTerrainCoords = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, HEIGHT_INITIALIZATION); int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; float[] initialMap = new float[totalHeights]; for (int ii = 0; ii < totalHeights; ii++) { initialMap[ii] = HEIGHT_INITIALIZATION; } - CreateNewTerrainSegment(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords); + UpdateOrCreateTerrain(BSScene.TERRAIN_ID, initialMap, minTerrainCoords, maxTerrainCoords, true); } + // Release all the terrain structures we might have allocated public void ReleaseGroundPlaneAndTerrain() { - if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr)) + if (m_groundPlane.Ptr != IntPtr.Zero) { - BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); - } - m_groundPlane.Ptr = IntPtr.Zero; - - foreach (KeyValuePair kvp in m_terrains) - { - if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.Ptr)) + if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr)) { - BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.Ptr); - BulletSimAPI.ReleaseHeightMapInfo2(m_heightMaps[kvp.Key].Ptr); + BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); } + m_groundPlane.Ptr = IntPtr.Zero; } - m_terrains.Clear(); - m_heightMaps.Clear(); + + ReleaseTerrain(); } - // Create a new terrain description. This is used for mega-regions where - // the children of region zero give region zero all of the terrain - // segments since region zero does all the physics for the mega-region. - // Call at taint time!! - public void CreateNewTerrainSegment(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) + // Release all the terrain we have allocated + public void ReleaseTerrain() { - // The Z coordinates are recalculated to be the min and max height of the terrain - // itself. The caller may have passed us the real region extent. - float minZ = float.MaxValue; - float maxZ = float.MinValue; - int hSize = heightMap.Length; - for (int ii = 0; ii < hSize; ii++) + foreach (KeyValuePair kvp in m_heightMaps) { - float height = heightMap[ii]; - if (height < minZ) minZ = height; - if (height > maxZ) maxZ = height; + if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.terrainBody.Ptr)) + { + BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.terrainBody.Ptr); + BulletSimAPI.ReleaseHeightMapInfo2(kvp.Value.Ptr); + } } - // If the terrain is flat, make a difference so we get a bounding box - if (minZ == maxZ) - minZ -= HEIGHT_EQUAL_FUDGE; - - minCoords.Z = minZ; - maxCoords.Z = maxZ; - Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); - - // Create the heightmap data structure in the unmanaged space - BulletHeightMapInfo mapInfo = new BulletHeightMapInfo(id, heightMap, - BulletSimAPI.CreateHeightMapInfo2(id, minCoords, maxCoords, heightMap, TERRAIN_COLLISION_MARGIN)); - mapInfo.terrainRegionBase = terrainRegionBase; - mapInfo.minCoords = minCoords; - mapInfo.maxCoords = maxCoords; - mapInfo.minZ = minZ; - mapInfo.maxZ = maxZ; - mapInfo.sizeX = maxCoords.X - minCoords.X; - mapInfo.sizeY = maxCoords.Y - minCoords.Y; - - Vector3 centerPos; - centerPos.X = minCoords.X + (mapInfo.sizeX / 2f); - centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f); - centerPos.Z = minZ + (maxZ - minZ) / 2f; - - DetailLog("{0},BSScene.CreateNewTerrainSegment,call,minZ={1},maxZ={2},hMapPtr={3},minC={4},maxC={5}", - BSScene.DetailLogZero, minZ, maxZ, mapInfo.Ptr, minCoords, maxCoords); - // Create the terrain shape from the mapInfo - BulletShape terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); - - BulletBody terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2(terrainShape.Ptr, - centerPos, Quaternion.Identity)); - - BulletSimAPI.SetFriction2(terrainBody.Ptr, m_physicsScene.Params.terrainFriction); - BulletSimAPI.SetHitFraction2(terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction); - BulletSimAPI.SetRestitution2(terrainBody.Ptr, m_physicsScene.Params.terrainRestitution); - BulletSimAPI.SetCollisionFlags2(terrainBody.Ptr, CollisionFlags.CF_STATIC_OBJECT); - BulletSimAPI.Activate2(terrainBody.Ptr, true); - - // Add the new terrain to the dynamics world - BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, terrainBody.Ptr); - BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, terrainBody.Ptr); - - // Add the created terrain to the management set. If we are doing mega-regions, - // the terrains of our children will be added. - m_terrains.Add(terrainRegionBase, terrainBody); - m_heightMaps.Add(terrainRegionBase, mapInfo); - - m_terrainModified = true; + m_heightMaps.Clear(); } + // The simulator wants to set a new heightmap for the terrain. public void SetTerrain(float[] heightMap) { if (m_worldOffset != Vector3.Zero && m_parentScene != null) { + // If a child of a mega-region, we shouldn't have any terrain allocated for us + ReleaseGroundPlaneAndTerrain(); // If doing the mega-prim stuff and we are the child of the zero region, - // the terrain is really added to our parent + // the terrain is added to our parent if (m_parentScene is BSScene) { - ((BSScene)m_parentScene).TerrainManager.SetTerrain(heightMap, m_worldOffset); + DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", + BSScene.DetailLogZero, m_worldOffset, m_worldMax); + ((BSScene)m_parentScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID, + heightMap, m_worldOffset, m_worldOffset+DefaultRegionSize, false); } } else { - // if not doing the mega-prim thing, just change the terrain - SetTerrain(heightMap, m_worldOffset); + // If not doing the mega-prim thing, just change the terrain + DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); + + UpdateOrCreateTerrain(BSScene.TERRAIN_ID, heightMap, m_worldOffset, m_worldOffset+DefaultRegionSize, false); } } - private void SetTerrain(float[] heightMap, Vector3 tOffset) + // If called with no mapInfo for the terrain, this will create a new mapInfo and terrain + // based on the passed information. The 'id' should be either the terrain id or + // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. + // The latter feature is for creating child terrains for mega-regions. + // If called with a mapInfo in m_heightMaps but the terrain has no body yet (mapInfo.terrainBody.Ptr == 0) + // then a new body and shape is created and the mapInfo is filled. + // This call is used for doing the initial terrain creation. + // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new + // terrain shape is created and added to the body. + // This call is most often used to update the heightMap and parameters of the terrain. + // The 'doNow' boolean says whether to do all the unmanaged activities right now (like when + // calling this routine from initialization or taint-time routines) or whether to delay + // all the unmanaged activities to taint-time. + private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool doNow) { + DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},doNow={3}", + BSScene.DetailLogZero, minCoords, maxCoords, doNow); + float minZ = float.MaxValue; float maxZ = float.MinValue; - Vector2 terrainRegionBase = new Vector2(tOffset.X, tOffset.Y); + Vector2 terrainRegionBase = new Vector2(minCoords.X, minCoords.Y); int heightMapSize = heightMap.Length; for (int ii = 0; ii < heightMapSize; ii++) @@ -234,58 +208,162 @@ public class BSTerrainManager } // The shape of the terrain is from its base to its extents. - Vector3 minCoords, maxCoords; - minCoords = tOffset; minCoords.Z = minZ; - maxCoords = tOffset; - maxCoords.X += Constants.RegionSize; - maxCoords.Y += Constants.RegionSize; maxCoords.Z = maxZ; - BulletBody terrainBody; BulletHeightMapInfo mapInfo; if (m_heightMaps.TryGetValue(terrainRegionBase, out mapInfo)) { - terrainBody = m_terrains[terrainRegionBase]; - // Copy heightMap local and compute some statistics. - for (int ii = 0; ii < heightMapSize; ii++) - { - mapInfo.heightMap[ii] = heightMap[ii]; - } - // If this is terrain we know about, it's easy to update - m_physicsScene.TaintedObject("BSScene.SetTerrain:UpdateExisting", delegate() + + mapInfo.heightMap = heightMap; + mapInfo.minCoords = minCoords; + mapInfo.maxCoords = maxCoords; + mapInfo.minZ = minZ; + mapInfo.maxZ = maxZ; + mapInfo.sizeX = maxCoords.X - minCoords.X; + mapInfo.sizeY = maxCoords.Y - minCoords.Y; + DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}", + BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY); + + BSScene.TaintCallback rebuildOperation = delegate() { - DetailLog("{0},SetTerrain:UpdateExisting,baseX={1},baseY={2},minZ={3},maxZ={4}", - BSScene.DetailLogZero, tOffset.X, tOffset.Y, minZ, maxZ); - // Fill the existing height map info with the new location and size information - BulletSimAPI.FillHeightMapInfo2(mapInfo.Ptr, mapInfo.ID, minCoords, maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); + if (m_parentScene != null) + { + // It's possible that Combine() was called after this code was queued. + // If we are a child of combined regions, we don't create any terrain for us. + DetailLog("{0},UpdateOrCreateTerrain:AmACombineChild,taint", BSScene.DetailLogZero); - // Create a terrain shape based on the new info - BulletShape terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); + // Get rid of any terrain that may have been allocated for us. + ReleaseGroundPlaneAndTerrain(); - // Swap the shape in the terrain body (this also deletes the old shape) - bool success = BulletSimAPI.ReplaceBodyShape2(m_physicsScene.World.Ptr, terrainBody.Ptr, terrainShape.Ptr); + // I hate doing this, but just bail + return; + } - if (!success) + if (mapInfo.terrainBody.Ptr != IntPtr.Zero) { - DetailLog("{0},SetTerrain:UpdateExisting,Failed", BSScene.DetailLogZero); - m_physicsScene.Logger.ErrorFormat("{0} Failed updating terrain heightmap. Region={1}", - LogHeader, m_physicsScene.RegionName); - + // Updating an existing terrain. + DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}", + BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY); + + // Remove from the dynamics world because we're going to mangle this object + BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr); + + // Get rid of the old terrain + BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr); + BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr); + mapInfo.Ptr = IntPtr.Zero; + + /* + // NOTE: This routine is half here because I can't get the terrain shape replacement + // to work. In the short term, the above three lines completely delete the old + // terrain and the code below recreates one from scratch. + // Hopefully the Bullet community will help me out on this one. + + // First, release the old collision shape (there is only one terrain) + BulletSimAPI.DeleteCollisionShape2(m_physicsScene.World.Ptr, mapInfo.terrainShape.Ptr); + + // Fill the existing height map info with the new location and size information + BulletSimAPI.FillHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.Ptr, mapInfo.ID, + mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); + + // Create a terrain shape based on the new info + mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); + + // Stuff the shape into the existing terrain body + BulletSimAPI.SetBodyShape2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr, mapInfo.terrainShape.Ptr); + */ } - }); + // else + { + // Creating a new terrain. + DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}", + BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ); + + mapInfo.ID = id; + mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.ID, + mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); + + // The terrain object initial position is at the center of the object + Vector3 centerPos; + centerPos.X = minCoords.X + (mapInfo.sizeX / 2f); + centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f); + centerPos.Z = minZ + ((maxZ - minZ) / 2f); + + // Create the terrain shape from the mapInfo + mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); + + mapInfo.terrainBody = new BulletBody(mapInfo.ID, + BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.Ptr, + centerPos, Quaternion.Identity)); + } + + // Make sure the entry is in the heightmap table + m_heightMaps[terrainRegionBase] = mapInfo; + + // Set current terrain attributes + BulletSimAPI.SetFriction2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainFriction); + BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction); + BulletSimAPI.SetRestitution2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainRestitution); + BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.Ptr, CollisionFlags.CF_STATIC_OBJECT); + + BulletSimAPI.SetMassProps2(mapInfo.terrainBody.Ptr, 0f, Vector3.Zero); + BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.Ptr); + + // Return the new terrain to the world of physical objects + BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr); + + // redo its bounding box now that it is in the world + BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr); + + // Make sure the new shape is processed. + BulletSimAPI.Activate2(mapInfo.terrainBody.Ptr, true); + }; + + // There is the option to do the changes now (we're already in 'taint time'), or + // to do the Bullet operations later. + if (doNow) + rebuildOperation(); + else + m_physicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation); } else { - // Our mega-prim child is giving us a new terrain to add to the phys world - uint newTerrainID = ++m_terrainCount; + // We don't know about this terrain so either we are creating a new terrain or + // our mega-prim child is giving us a new terrain to add to the phys world - m_physicsScene.TaintedObject("BSScene.SetTerrain:NewTerrain", delegate() + // if this is a child terrain, calculate a unique terrain id + uint newTerrainID = id; + if (newTerrainID >= BSScene.CHILDTERRAIN_ID) + newTerrainID = ++m_terrainCount; + + float[] heightMapX = heightMap; + Vector3 minCoordsX = minCoords; + Vector3 maxCoordsX = maxCoords; + + DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", + BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); + + // Code that must happen at taint-time + BSScene.TaintCallback createOperation = delegate() { - DetailLog("{0},SetTerrain:NewTerrain,baseX={1},baseY={2}", BSScene.DetailLogZero, tOffset.X, tOffset.Y); - CreateNewTerrainSegment(newTerrainID, heightMap, minCoords, maxCoords); - }); + DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y); + // Create a new mapInfo that will be filled with the new info + mapInfo = new BulletHeightMapInfo(id, heightMapX, + BulletSimAPI.CreateHeightMapInfo2(m_physicsScene.World.Ptr, newTerrainID, + minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN)); + // Put the unfilled heightmap info into the collection of same + m_heightMaps.Add(terrainRegionBase, mapInfo); + // Build the terrain + UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true); + }; + + // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time. + if (doNow) + createOperation(); + else + m_physicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation); } } @@ -316,8 +394,8 @@ public class BSTerrainManager lastHeightTY = tY; float ret = HEIGHT_GETHEIGHT_RET; - int offsetX = ((int)(tX / (int)Constants.RegionSize)) * (int)Constants.RegionSize; - int offsetY = ((int)(tY / (int)Constants.RegionSize)) * (int)Constants.RegionSize; + int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; + int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; Vector2 terrainBaseXY = new Vector2(offsetX, offsetY); BulletHeightMapInfo mapInfo; @@ -335,8 +413,8 @@ public class BSTerrainManager } else { - m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: x={1}, y={2}", - LogHeader, tX, tY); + m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", + LogHeader, m_physicsScene.RegionName, tX, tY); } lastHeight = ret; return ret; @@ -347,20 +425,34 @@ public class BSTerrainManager { return true; } - // This call says I am a child to region zero in a mega-region. 'pScene' is that - // of region zero, 'offset' is my offset from regions zero's origin, and - // 'extents' is the largest XY that is handled in my region. + + // This routine is called two ways: + // One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum + // extent of the combined regions. This is to inform the parent of the size + // of the combined regions. + // and one with 'offset' as the offset of the child region to the base region, + // 'pScene' pointing to the parent and 'extents' of zero. This informs the + // child of its relative base and new parent. public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) { m_worldOffset = offset; - WorldExtents = new Vector2(extents.X, extents.Y); + m_worldMax = extents; m_parentScene = pScene; + if (pScene != null) + { + // We are a child. + // We want m_worldMax to be the highest coordinate of our piece of terrain. + m_worldMax = offset + DefaultRegionSize; + } + DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}", + BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax); } // Unhook all the combining that I know about. public void UnCombine(PhysicsScene pScene) { // Just like ODE, for the moment a NOP + DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 804d2ea..a0bad3a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -90,6 +90,8 @@ public class BulletHeightMapInfo public Vector3 maxCoords; public float sizeX, sizeY; public float minZ, maxZ; + public BulletShape terrainShape; + public BulletBody terrainBody; } // =============================================================================== @@ -462,14 +464,14 @@ public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vecto public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool ReplaceBodyShape2(IntPtr sim, IntPtr obj, IntPtr shape); +public static extern bool SetBodyShape2(IntPtr sim, IntPtr obj, IntPtr shape); // ===================================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateHeightMapInfo2(uint id, Vector3 minCoords, Vector3 maxCoords, +public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr FillHeightMapInfo2(IntPtr mapInfo, uint id, Vector3 minCoords, Vector3 maxCoords, +public static extern IntPtr FillHeightMapInfo2(IntPtr sim, IntPtr mapInfo, uint id, Vector3 minCoords, Vector3 maxCoords, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -- cgit v1.1