From fef73a1a1011126d4df2da2279caae9cef7984d1 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 18 Aug 2011 14:32:09 -0700 Subject: BulletSim: add runtime setting of physics parameters. Update default values. --- .../Region/Physics/BulletSPlugin/BSCharacter.cs | 31 ++- OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 10 +- OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 223 +++++++++++++++++++-- .../Region/Physics/BulletSPlugin/BulletSimAPI.cs | 20 +- 4 files changed, 256 insertions(+), 28 deletions(-) (limited to 'OpenSim/Region/Physics/BulletSPlugin') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 9f9ebcc..682eb80 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -49,8 +49,9 @@ public class BSCharacter : PhysicsActor private bool _grabbed; private bool _selected; private Vector3 _position; - private float _mass = 80f; - public float _density = 60f; + private float _mass; + public float _density; + public float _avatarVolume; private Vector3 _force; private Vector3 _velocity; private Vector3 _torque; @@ -90,13 +91,13 @@ public class BSCharacter : PhysicsActor _scene = parent_scene; _position = pos; _size = size; + _flying = isFlying; _orientation = Quaternion.Identity; _velocity = Vector3.Zero; - _buoyancy = 0f; // characters return a buoyancy of zero + _buoyancy = isFlying ? 1f : 0f; _scale = new Vector3(1f, 1f, 1f); - float AVvolume = (float) (Math.PI*Math.Pow(_scene.Params.avatarCapsuleRadius, 2)*_scene.Params.avatarCapsuleHeight); _density = _scene.Params.avatarDensity; - _mass = _density*AVvolume; + ComputeAvatarVolumeAndMass(); // set _avatarVolume and _mass based on capsule size, _density and _scale ShapeData shapeData = new ShapeData(); shapeData.ID = _localID; @@ -106,7 +107,7 @@ public class BSCharacter : PhysicsActor shapeData.Velocity = _velocity; shapeData.Scale = _scale; shapeData.Mass = _mass; - shapeData.Buoyancy = isFlying ? 1f : 0f; + shapeData.Buoyancy = _buoyancy; shapeData.Static = ShapeData.numericFalse; shapeData.Friction = _scene.Params.avatarFriction; shapeData.Restitution = _scene.Params.defaultRestitution; @@ -212,6 +213,7 @@ public class BSCharacter : PhysicsActor get { return _velocity; } set { _velocity = value; + // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); _scene.TaintedObject(delegate() { BulletSimAPI.SetObjectVelocity(_scene.WorldID, _localID, _velocity); @@ -235,6 +237,7 @@ public class BSCharacter : PhysicsActor get { return _orientation; } set { _orientation = value; + // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); _scene.TaintedObject(delegate() { // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); @@ -300,7 +303,6 @@ public class BSCharacter : PhysicsActor set { _buoyancy = value; _scene.TaintedObject(delegate() { - // simulate flying by changing the effect of gravity BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, LocalID, _buoyancy); }); } @@ -344,6 +346,7 @@ public class BSCharacter : PhysicsActor _force.X += force.X; _force.Y += force.Y; _force.Z += force.Z; + // m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force); _scene.TaintedObject(delegate() { BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); @@ -370,6 +373,17 @@ public class BSCharacter : PhysicsActor return (_subscribedEventsMs > 0); } + // set _avatarVolume and _mass based on capsule size, _density and _scale + private void ComputeAvatarVolumeAndMass() + { + _avatarVolume = (float)( + Math.PI + * _scene.Params.avatarCapsuleRadius * _scale.X + * _scene.Params.avatarCapsuleRadius * _scale.Y + * _scene.Params.avatarCapsuleHeight * _scale.Z); + _mass = _density * _avatarVolume; + } + // The physics engine says that properties have updated. Update same and inform // the world that things have changed. public void UpdateProperties(EntityProperties entprop) @@ -403,6 +417,9 @@ public class BSCharacter : PhysicsActor } if (changed) { + // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); + // Avatar movement is not done by generating this event. There is a system that + // checks for avatar updates each heartbeat loop. // base.RequestPhysicsterseUpdate(); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index c4999f6..cc414e9 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -1013,8 +1013,8 @@ public sealed class BSPrim : PhysicsActor // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid={1}, key={2}, hulls={3}", LogHeader, _localID, _hullKey, hullCount); BulletSimAPI.CreateHull(_scene.WorldID, _hullKey, hullCount, convHulls); _shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL; - // Let the object be scaled by Bullet (the mesh was created as a unit mesh) - _scale = _size; + // meshes are already scaled by the meshmerizer + _scale = new OMV.Vector3(1f, 1f, 1f); } return; } @@ -1138,9 +1138,7 @@ public sealed class BSPrim : PhysicsActor if (_scene.NeedsMeshing(_pbs)) // linksets with constraints don't need a root mesh { // m_log.DebugFormat("{0}: RecreateGeomAndObject: creating mesh", LogHeader); - // Make the mesh scaled to 1 and use Bullet's scaling feature to scale it in world - OMV.Vector3 scaleFactor = new OMV.Vector3(1.0f, 1.0f, 1.0f); - _mesh = _scene.mesher.CreateMesh(_avName, _pbs, scaleFactor, _scene.MeshLOD, _isPhysical); + _mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, _scene.MeshLOD, _isPhysical); } else { @@ -1225,6 +1223,8 @@ public sealed class BSPrim : PhysicsActor else { // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. + + // Only updates only for individual prims and for the root object of a linkset. if (this._parentPrim == null) { // Assign to the local variables so the normal set action does not happen diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index de86d59..518be09 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -58,11 +58,13 @@ using OpenSim.Region.Framework; // namespace OpenSim.Region.Physics.BulletSPlugin { -public class BSScene : PhysicsScene +public class BSScene : PhysicsScene, IPhysicsParameters { private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS SCENE]"; + public string BulletSimVersion = "?"; + private Dictionary m_avatars = new Dictionary(); private Dictionary m_prims = new Dictionary(); private List m_vehicles = new List(); @@ -127,7 +129,7 @@ public class BSScene : PhysicsScene ConfigurationParameters[] m_params; GCHandle m_paramsHandle; - private BulletSimAPI.DebugLogCallback debugLogCallbackHandle; + private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle; public BSScene(string identifier) { @@ -149,12 +151,17 @@ public class BSScene : PhysicsScene m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned); + // 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, enable logging from the unmanaged code if (m_log.IsDebugEnabled) { m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); - debugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); - BulletSimAPI.SetDebugLogCallback(debugLogCallbackHandle); + m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); + BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle); } _taintedObjects = new List(); @@ -188,7 +195,7 @@ public class BSScene : PhysicsScene m_maxUpdatesPerFrame = 2048; m_maximumObjectMass = 10000.01f; - parms.defaultFriction = 0.70f; + parms.defaultFriction = 0.5f; parms.defaultDensity = 10.000006836f; // Aluminum g/cm3 parms.defaultRestitution = 0f; parms.collisionMargin = 0.0f; @@ -202,10 +209,10 @@ public class BSScene : PhysicsScene parms.ccdMotionThreshold = 0.5f; // set to zero to disable parms.ccdSweptSphereRadius = 0.2f; - parms.terrainFriction = 0.85f; - parms.terrainHitFriction = 0.8f; - parms.terrainRestitution = 0.2f; - parms.avatarFriction = 0.85f; + parms.terrainFriction = 0.5f; + parms.terrainHitFraction = 0.8f; + parms.terrainRestitution = 0f; + parms.avatarFriction = 0.0f; parms.avatarDensity = 60f; parms.avatarCapsuleRadius = 0.37f; parms.avatarCapsuleHeight = 1.5f; // 2.140599f @@ -213,7 +220,8 @@ public class BSScene : PhysicsScene if (config != null) { // If there are specifications in the ini file, use those values - // WHEN ADDING OR UPDATING THIS SECTION, BE SURE TO ALSO UPDATE OpenSimDefaults.ini + // WHEN ADDING OR UPDATING THIS SECTION, BE SURE TO UPDATE OpenSimDefaults.ini + // ALSO REMEMBER TO UPDATE THE RUNTIME SETTING OF THE PARAMETERS. IConfig pConfig = config.Configs["BulletSim"]; if (pConfig != null) { @@ -243,7 +251,7 @@ public class BSScene : PhysicsScene parms.ccdSweptSphereRadius = pConfig.GetFloat("CcdSweptSphereRadius", parms.ccdSweptSphereRadius); parms.terrainFriction = pConfig.GetFloat("TerrainFriction", parms.terrainFriction); - parms.terrainHitFriction = pConfig.GetFloat("TerrainHitFriction", parms.terrainHitFriction); + parms.terrainHitFraction = pConfig.GetFloat("TerrainHitFraction", parms.terrainHitFraction); parms.terrainRestitution = pConfig.GetFloat("TerrainRestitution", parms.terrainRestitution); parms.avatarFriction = pConfig.GetFloat("AvatarFriction", parms.avatarFriction); parms.avatarDensity = pConfig.GetFloat("AvatarDensity", parms.avatarDensity); @@ -386,7 +394,7 @@ public class BSScene : PhysicsScene } } - // FIX THIS: fps calculation wrong. This calculation always returns about 1 in normal operation. + // TODO: FIX THIS: fps calculation wrong. This calculation always returns about 1 in normal operation. return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f; } @@ -651,5 +659,196 @@ public class BSScene : PhysicsScene } } #endregion Vehicles + + #region Runtime settable parameters + public static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[] + { + new PhysParameterEntry("MeshLOD", "Level of detail to render meshes (Power of two. Default 32)"), + new PhysParameterEntry("MaxSubStep", "In simulation step, maximum number of substeps"), + new PhysParameterEntry("FixedTimeStep", "In simulation step, seconds of one substep (1/60)"), + new PhysParameterEntry("MaxObjectMass", "Maximum object mass (10000.01)"), + + new PhysParameterEntry("DefaultFriction", "Friction factor used on new objects"), + new PhysParameterEntry("DefaultDensity", "Density for new objects" ), + new PhysParameterEntry("DefaultRestitution", "Bouncyness of an object" ), + // new PhysParameterEntry("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!!)" ), + new PhysParameterEntry("Gravity", "Vertical force of gravity (negative means down)" ), + + new PhysParameterEntry("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)" ), + new PhysParameterEntry("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)" ), + new PhysParameterEntry("DeactivationTime", "Seconds before considering an object potentially static" ), + new PhysParameterEntry("LinearSleepingThreshold", "Seconds to measure linear movement before considering static" ), + new PhysParameterEntry("AngularSleepingThreshold", "Seconds to measure angular movement before considering static" ), + // new PhysParameterEntry("CcdMotionThreshold", "" ), + // new PhysParameterEntry("CcdSweptSphereRadius", "" ), + + new PhysParameterEntry("TerrainFriction", "Factor to reduce movement against terrain surface" ), + new PhysParameterEntry("TerrainHitFraction", "Distance to measure hit collisions" ), + new PhysParameterEntry("TerrainRestitution", "Bouncyness" ), + new PhysParameterEntry("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation." ), + new PhysParameterEntry("AvatarDensity", "Density of an avatar. Changed on avatar recreation." ), + new PhysParameterEntry("AvatarRestitution", "Bouncyness. Changed on avatar recreation." ), + new PhysParameterEntry("AvatarCapsuleRadius", "Radius of space around an avatar" ), + new PhysParameterEntry("AvatarCapsuleHeight", "Default height of space around avatar" ) + }; + + #region IPhysicsParameters + // Get the list of parameters this physics engine supports + public PhysParameterEntry[] GetParameterList() + { + return SettableParameters; + } + + // Set parameter on a specific or all instances. + // Return 'false' if not able to set the parameter. + // Setting the value in the m_params block will change the value the physics engine + // will use the next time since it's pinned and shared memory. + // Some of the values require calling into the physics engine to get the new + // value activated ('terrainFriction' for instance). + public bool SetPhysicsParameter(string parm, float val, uint localID) + { + bool ret = true; + string lparm = parm.ToLower(); + switch (lparm) + { + case "meshlod": m_meshLOD = (int)val; break; + case "maxsubstep": m_maxSubSteps = (int)val; break; + case "fixedtimestep": m_fixedTimeStep = val; break; + case "maxobjectmass": m_maximumObjectMass = val; break; + + case "defaultfriction": m_params[0].defaultFriction = val; break; + case "defaultdensity": m_params[0].defaultDensity = val; break; + case "defaultrestitution": m_params[0].defaultRestitution = val; break; + case "collisionmargin": m_params[0].collisionMargin = val; break; + case "gravity": m_params[0].gravity = val; TaintedUpdateParameter(lparm, PhysParameterEntry.APPLY_TO_NONE, val); break; + + case "lineardamping": UpdateParameterPrims(ref m_params[0].linearDamping, lparm, localID, val); break; + case "angulardamping": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break; + case "deactivationtime": UpdateParameterPrims(ref m_params[0].deactivationTime, lparm, localID, val); break; + case "linearsleepingthreshold": UpdateParameterPrims(ref m_params[0].linearSleepingThreshold, lparm, localID, val); break; + case "angularsleepingthreshold": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break; + case "ccdmotionthreshold": UpdateParameterPrims(ref m_params[0].ccdMotionThreshold, lparm, localID, val); break; + case "ccdsweptsphereradius": UpdateParameterPrims(ref m_params[0].ccdSweptSphereRadius, lparm, localID, val); break; + + // set a terrain physical feature and cause terrain to be recalculated + case "terrainfriction": m_params[0].terrainFriction = val; TaintedUpdateParameter("terrain", 0, val); break; + case "terrainhitfraction": m_params[0].terrainHitFraction = val; TaintedUpdateParameter("terrain", 0, val); break; + case "terrainrestitution": m_params[0].terrainRestitution = val; TaintedUpdateParameter("terrain", 0, val); break; + // set an avatar physical feature and cause avatar(s) to be recalculated + case "avatarfriction": UpdateParameterAvatars(ref m_params[0].avatarFriction, "avatar", localID, val); break; + case "avatardensity": UpdateParameterAvatars(ref m_params[0].avatarDensity, "avatar", localID, val); break; + case "avatarrestitution": UpdateParameterAvatars(ref m_params[0].avatarRestitution, "avatar", localID, val); break; + case "avatarcapsuleradius": UpdateParameterAvatars(ref m_params[0].avatarCapsuleRadius, "avatar", localID, val); break; + case "avatarcapsuleheight": UpdateParameterAvatars(ref m_params[0].avatarCapsuleHeight, "avatar", localID, val); break; + + default: ret = false; break; + } + return ret; + } + + // check to see if we are updating a parameter for a particular or all of the prims + private 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 + private void UpdateParameterAvatars(ref float loc, string parm, uint localID, float val) + { + List operateOn; + lock (m_avatars) operateOn = new List(m_avatars.Keys); + UpdateParameterSet(operateOn, ref loc, parm, localID, val); + } + + // update all the localIDs specified + // If the local ID is APPLY_TO_NONE, just change the default value + // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs + // If the localID is a specific object, apply the parameter change to only that object + private void UpdateParameterSet(List lIDs, ref float defaultLoc, string parm, uint localID, float val) + { + switch (localID) + { + case PhysParameterEntry.APPLY_TO_NONE: + defaultLoc = val; // setting only the default value + break; + case PhysParameterEntry.APPLY_TO_ALL: + defaultLoc = val; // setting ALL also sets the default value + List objectIDs = lIDs; + string xparm = parm.ToLower(); + float xval = val; + TaintedObject(delegate() { + foreach (uint lID in objectIDs) + { + BulletSimAPI.UpdateParameter(m_worldID, lID, xparm, xval); + } + }); + break; + default: + // setting only one localID + TaintedUpdateParameter(parm, localID, val); + break; + } + } + + // schedule the actual updating of the paramter to when the phys engine is not busy + private void TaintedUpdateParameter(string parm, uint localID, float val) + { + uint xlocalID = localID; + string xparm = parm.ToLower(); + float xval = val; + TaintedObject(delegate() { + BulletSimAPI.UpdateParameter(m_worldID, xlocalID, xparm, xval); + }); + } + + // Get parameter. + // Return 'false' if not able to get the parameter. + public bool GetPhysicsParameter(string parm, out float value) + { + float val = 0f; + bool ret = true; + switch (parm.ToLower()) + { + case "meshlod": val = (float)m_meshLOD; break; + case "maxsubstep": val = (float)m_maxSubSteps; break; + case "fixedtimestep": val = m_fixedTimeStep; break; + case "maxobjectmass": val = m_maximumObjectMass; break; + + case "defaultfriction": val = m_params[0].defaultFriction; break; + case "defaultdensity": val = m_params[0].defaultDensity; break; + case "defaultrestitution": val = m_params[0].defaultRestitution; break; + case "collisionmargin": val = m_params[0].collisionMargin; break; + case "gravity": val = m_params[0].gravity; break; + + case "lineardamping": val = m_params[0].linearDamping; break; + case "angulardamping": val = m_params[0].angularDamping; break; + case "deactivationtime": val = m_params[0].deactivationTime; break; + case "linearsleepingthreshold": val = m_params[0].linearSleepingThreshold; break; + case "angularsleepingthreshold": val = m_params[0].angularDamping; break; + case "ccdmotionthreshold": val = m_params[0].ccdMotionThreshold; break; + case "ccdsweptsphereradius": val = m_params[0].ccdSweptSphereRadius; break; + + case "terrainfriction": val = m_params[0].terrainFriction; break; + case "terrainhitfraction": val = m_params[0].terrainHitFraction; break; + case "terrainrestitution": val = m_params[0].terrainRestitution; break; + + case "avatarfriction": val = m_params[0].avatarFriction; break; + case "avatardensity": val = m_params[0].avatarDensity; break; + case "avatarrestitution": val = m_params[0].avatarRestitution; break; + case "avatarcapsuleradius": val = m_params[0].avatarCapsuleRadius; break; + case "avatarcapsuleheight": val = m_params[0].avatarCapsuleHeight; break; + default: ret = false; break; + + } + value = val; + return ret; + } + + #endregion IPhysicsParameters + + #endregion Runtime settable parameters + } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 819fce1..bf953df 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -51,9 +51,6 @@ public struct ShapeData SHAPE_SPHERE = 4, SHAPE_HULL = 5 }; - // note that bools are passed as ints since bool size changes by language - public const int numericTrue = 1; - public const int numericFalse = 0; public uint ID; public PhysicsShapeType Type; public Vector3 Position; @@ -67,6 +64,10 @@ public struct ShapeData public float Restitution; public int Collidable; public int Static; // true if a static object. Otherwise gravity, etc. + + // note that bools are passed as ints since bool size changes by language and architecture + public const int numericTrue = 1; + public const int numericFalse = 0; } [StructLayout(LayoutKind.Sequential)] public struct SweepHit @@ -121,23 +122,34 @@ public struct ConfigurationParameters public float ccdSweptSphereRadius; public float terrainFriction; - public float terrainHitFriction; + public float terrainHitFraction; public float terrainRestitution; public float avatarFriction; public float avatarDensity; public float avatarRestitution; public float avatarCapsuleRadius; public float avatarCapsuleHeight; + + public const float numericTrue = 1f; + public const float numericFalse = 0f; } static class BulletSimAPI { [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +[return: MarshalAs(UnmanagedType.LPStr)] +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); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool UpdateParameter(uint worldID, uint localID, + [MarshalAs(UnmanagedType.LPStr)]string paramCode, float value); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void SetHeightmap(uint worldID, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -- cgit v1.1