diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index d206fc1278..4d132bba1e 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -299,6 +299,13 @@ namespace OpenSim.Framework x; } + // Inclusive, within range test (true if equal to the endpoints) + public static bool InRange(T x, T min, T max) + where T : IComparable + { + return x.CompareTo(max) <= 0 && x.CompareTo(min) >= 0; + } + public static uint GetNextXferID() { uint id = 0; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 57c5898e84..8e059eec19 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -88,8 +88,8 @@ public sealed class BSCharacter : BSPhysObject // Old versions of ScenePresence passed only the height. If width and/or depth are zero, // replace with the default values. _size = size; - if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth; - if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth; + if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; + if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; // A motor to control the acceleration and deceleration of the avatar movement. // _velocityMotor = new BSVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f); @@ -108,8 +108,8 @@ public sealed class BSCharacter : BSPhysObject _velocity = OMV.Vector3.Zero; _appliedVelocity = OMV.Vector3.Zero; _buoyancy = ComputeBuoyancyFromFlying(isFlying); - _currentFriction = PhysicsScene.Params.avatarStandingFriction; - _avatarDensity = PhysicsScene.Params.avatarDensity; + _currentFriction = BSParam.AvatarStandingFriction; + _avatarDensity = BSParam.AvatarDensity; // The dimensions of the avatar capsule are kept in the scale. // Physics creates a unit capsule which is scaled by the physics engine. @@ -134,6 +134,8 @@ public sealed class BSCharacter : BSPhysObject // called when this character is being destroyed and the resources should be released public override void Destroy() { + base.Destroy(); + DetailLog("{0},BSCharacter.Destroy", LocalID); PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() { @@ -156,19 +158,20 @@ public sealed class BSCharacter : BSPhysObject _velocityMotor.Reset(); _velocityMotor.SetCurrent(_velocity); _velocityMotor.SetTarget(_velocity); + _velocityMotor.Enabled = false; // This will enable or disable the flying buoyancy of the avatar. // Needs to be reset especially when an avatar is recreated after crossing a region boundry. Flying = _flying; - BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.avatarRestitution); + BulletSimAPI.SetRestitution2(PhysBody.ptr, BSParam.AvatarRestitution); BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin); BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale); - BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold); - if (PhysicsScene.Params.ccdMotionThreshold > 0f) + BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold); + if (BSParam.CcdMotionThreshold > 0f) { - BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); - BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); + BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold); + BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius); } UpdatePhysicalMassProperties(RawMass); @@ -208,8 +211,8 @@ public sealed class BSCharacter : BSPhysObject _size = value; // Old versions of ScenePresence passed only the height. If width and/or depth are zero, // replace with the default values. - if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth; - if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth; + if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; + if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; ComputeAvatarScale(_size); ComputeAvatarVolumeAndMass(); @@ -433,13 +436,13 @@ public sealed class BSCharacter : BSPhysObject OMV.Vector3 targetVel = value; PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate() { - float timeStep = 0.089f; // DEBUG DEBUG FIX FIX FIX _velocityMotor.Reset(); _velocityMotor.SetTarget(targetVel); _velocityMotor.SetCurrent(_velocity); - // Compute a velocity value and make sure it gets pushed into the avatar. - // This makes sure the avatar will start from a stop. - ForceVelocity = _velocityMotor.Step(timeStep); + _velocityMotor.Enabled = true; + + // Make sure a property update happens next step so the motor gets incorporated. + BulletSimAPI.PushUpdate2(PhysBody.ptr); }); } } @@ -448,12 +451,15 @@ public sealed class BSCharacter : BSPhysObject get { return _velocity; } set { _velocity = value; - _velocityMotor.Reset(); - _velocityMotor.SetCurrent(_velocity); - _velocityMotor.SetTarget(_velocity); // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() { + _velocityMotor.Reset(); + _velocityMotor.SetCurrent(_velocity); + _velocityMotor.SetTarget(_velocity); + // Even though the motor is initialized, it's not used and the velocity goes straight into the avatar. + _velocityMotor.Enabled = false; + DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); ForceVelocity = _velocity; }); @@ -464,27 +470,27 @@ public sealed class BSCharacter : BSPhysObject set { PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity"); + _velocity = value; // Depending on whether the avatar is moving or not, change the friction // to keep the avatar from slipping around if (_velocity.Length() == 0) { - if (_currentFriction != PhysicsScene.Params.avatarStandingFriction) + if (_currentFriction != BSParam.AvatarStandingFriction) { - _currentFriction = PhysicsScene.Params.avatarStandingFriction; + _currentFriction = BSParam.AvatarStandingFriction; if (PhysBody.HasPhysicalBody) BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); } } else { - if (_currentFriction != PhysicsScene.Params.avatarFriction) + if (_currentFriction != BSParam.AvatarFriction) { - _currentFriction = PhysicsScene.Params.avatarFriction; + _currentFriction = BSParam.AvatarFriction; if (PhysBody.HasPhysicalBody) BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); } } - _velocity = value; // Remember the set velocity so we can suppress the reduction by friction, ... _appliedVelocity = value; @@ -561,12 +567,6 @@ public sealed class BSCharacter : BSPhysObject set { _flying = value; - // Velocity movement is different when flying: flying velocity degrades over time. - if (_flying) - _velocityMotor.TargetValueDecayTimeScale = 1f; - else - _velocityMotor.TargetValueDecayTimeScale = BSMotor.Infinite; - // simulate flying by changing the effect of gravity Buoyancy = ComputeBuoyancyFromFlying(_flying); } @@ -750,39 +750,42 @@ public sealed class BSCharacter : BSPhysObject _velocity = entprop.Velocity; _acceleration = entprop.Acceleration; _rotationalVelocity = entprop.RotationalVelocity; + // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. PositionSanityCheck(true); + if (_velocityMotor.Enabled) + { + // TODO: Decide if the step parameters should be changed depending on the avatar's + // state (flying, colliding, ...). + + OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep); + + // If falling, we keep the world's downward vector no matter what the other axis specify. + if (!Flying && !IsColliding) + { + stepVelocity.Z = entprop.Velocity.Z; + DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity); + } + + // If the user has said stop and we've stopped applying velocity correction, + // the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer. + if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero) + { + stepVelocity = OMV.Vector3.Zero; + _velocityMotor.Enabled = false; + DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor); + } + + _velocity = stepVelocity; + entprop.Velocity = _velocity; + BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); + } + // remember the current and last set values LastEntityProperties = CurrentEntityProperties; CurrentEntityProperties = entprop; - // Avatars don't respond to world friction, etc. They only go the speed I tell them too. - // Special kludge here for falling. Even though the target velocity might not have a - // Z component, the avatar could be falling (walked off a ledge, stopped flying, ...) - // and that velocity component must be retained. - float timeStep = 0.089f; // DEBUG DEBUG FIX FIX FIX - OMV.Vector3 stepVelocity = _velocityMotor.Step(timeStep); - // Remove next line so avatars don't fly up forever. DEBUG DEBUG this is only temporary. - // stepVelocity.Z += entprop.Velocity.Z; - _velocity = stepVelocity; - BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); - /* - OMV.Vector3 stepVelocity = _velocityMotor.Step(timeStep); - OMV.Vector3 avVel = new OMV.Vector3(stepVelocity.X, stepVelocity.Y, entprop.Velocity.Z); - _velocity = avVel; - BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, avVel); - - if (entprop.Velocity != LastEntityProperties.Velocity) - { - // Changes in the velocity are suppressed in avatars. - // That's just the way they are defined. - OMV.Vector3 avVel = new OMV.Vector3(_appliedVelocity.X, _appliedVelocity.Y, entprop.Velocity.Z); - _velocity = avVel; - BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, avVel); - } - */ - // Tell the linkset about value changes Linkset.UpdateProperties(this, true); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs index 6b1e304207..e77fb50206 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs @@ -122,7 +122,7 @@ public abstract class BSConstraint : IDisposable // Setting an object's mass to zero (making it static like when it's selected) // automatically disables the constraints. // If the link is enabled, be sure to set the constraint itself to enabled. - BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, m_world.physicsScene.NumericBool(true)); + BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, BSParam.NumericBool(true)); } else { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index a5acd86f89..3fde57bed4 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -563,7 +563,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Moderate angular movement introduced by Bullet. // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle. // Maybe compute linear and angular factor and damping from params. - float angularDamping = PhysicsScene.Params.vehicleAngularDamping; + float angularDamping = BSParam.VehicleAngularDamping; BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping); // Vehicles report collision events so we know when it's on the ground @@ -634,28 +634,33 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_knownHas = 0; m_knownChanged = 0; } + // Push all the changed values back into the physics engine private void PushKnownChanged() { if (m_knownChanged != 0) { if ((m_knownChanged & m_knownChangedPosition) != 0) - Prim.ForcePosition = VehiclePosition; + Prim.ForcePosition = m_knownPosition; + if ((m_knownChanged & m_knownChangedOrientation) != 0) - Prim.ForceOrientation = VehicleOrientation; + Prim.ForceOrientation = m_knownOrientation; + if ((m_knownChanged & m_knownChangedVelocity) != 0) { - Prim.ForceVelocity = VehicleVelocity; + Prim.ForceVelocity = m_knownVelocity; BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity); } + if ((m_knownChanged & m_knownChangedForce) != 0) Prim.AddForce((Vector3)m_knownForce, false, true); if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0) { - Prim.ForceRotationalVelocity = VehicleRotationalVelocity; + Prim.ForceRotationalVelocity = m_knownRotationalVelocity; // Fake out Bullet by making it think the velocity is the same as last time. - BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, VehicleRotationalVelocity); + BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, m_knownRotationalVelocity); } + if ((m_knownChanged & m_knownChangedRotationalForce) != 0) Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true); @@ -667,7 +672,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } // Since the computation of terrain height can be a little involved, this routine - // is used ot fetch the height only once for each vehicle simulation step. + // is used to fetch the height only once for each vehicle simulation step. private float GetTerrainHeight(Vector3 pos) { if ((m_knownHas & m_knownChangedTerrainHeight) == 0) @@ -699,12 +704,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_knownPosition = Prim.ForcePosition; m_knownHas |= m_knownChangedPosition; } - return (Vector3)m_knownPosition; + return m_knownPosition; } set { m_knownPosition = value; m_knownChanged |= m_knownChangedPosition; + m_knownHas |= m_knownChangedPosition; } } @@ -717,12 +723,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_knownOrientation = Prim.ForceOrientation; m_knownHas |= m_knownChangedOrientation; } - return (Quaternion)m_knownOrientation; + return m_knownOrientation; } set { m_knownOrientation = value; m_knownChanged |= m_knownChangedOrientation; + m_knownHas |= m_knownChangedOrientation; } } @@ -741,13 +748,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin { m_knownVelocity = value; m_knownChanged |= m_knownChangedVelocity; + m_knownHas |= m_knownChangedVelocity; } } private void VehicleAddForce(Vector3 aForce) { + if ((m_knownHas & m_knownChangedForce) == 0) + { + m_knownForce = Vector3.Zero; + } m_knownForce += aForce; m_knownChanged |= m_knownChangedForce; + m_knownHas |= m_knownChangedForce; } private Vector3 VehicleRotationalVelocity @@ -765,12 +778,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin { m_knownRotationalVelocity = value; m_knownChanged |= m_knownChangedRotationalVelocity; + m_knownHas |= m_knownChangedRotationalVelocity; } } private void VehicleAddAngularForce(Vector3 aForce) { + if ((m_knownHas & m_knownChangedRotationalForce) == 0) + { + m_knownRotationalForce = Vector3.Zero; + } m_knownRotationalForce += aForce; m_knownChanged |= m_knownChangedRotationalForce; + m_knownHas |= m_knownChangedRotationalForce; } // Vehicle relative forward velocity private Vector3 VehicleForwardVelocity @@ -782,7 +801,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_knownForwardVelocity = VehicleVelocity * Quaternion.Inverse(Quaternion.Normalize(VehicleOrientation)); m_knownHas |= m_knownChangedForwardVelocity; } - return (Vector3)m_knownForwardVelocity; + return m_knownForwardVelocity; } } private float VehicleForwardSpeed diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 2017fa5656..8580928f15 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -56,7 +56,7 @@ public abstract class BSLinkset { BSLinkset ret = null; - switch ((int)physScene.Params.linksetImplementation) + switch ((int)BSParam.LinksetImplementation) { case (int)LinksetImplementation.Constraint: ret = new BSLinksetConstraints(physScene, parent); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs index 8c36c3153a..d95f223b71 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs @@ -226,14 +226,14 @@ public sealed class BSLinksetConstraints : BSLinkset constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); // tweek the constraint to increase stability - constrain.UseFrameOffset(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintUseFrameOffset)); - constrain.TranslationalLimitMotor(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintEnableTransMotor), - PhysicsScene.Params.linkConstraintTransMotorMaxVel, - PhysicsScene.Params.linkConstraintTransMotorMaxForce); - constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP); - if (PhysicsScene.Params.linkConstraintSolverIterations != 0f) + constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset)); + constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor), + BSParam.LinkConstraintTransMotorMaxVel, + BSParam.LinkConstraintTransMotorMaxForce); + constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP); + if (BSParam.LinkConstraintSolverIterations != 0f) { - constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); + constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations); } return constrain; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs index 34a87c6c37..9e1a9ba6e3 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs @@ -43,7 +43,9 @@ public abstract class BSMotor { UseName = useName; PhysicsScene = null; + Enabled = true; } + public virtual bool Enabled { get; set; } public virtual void Reset() { } public virtual void Zero() { } public virtual void GenerateTestOutput(float timeStep) { } @@ -98,6 +100,12 @@ public class BSVMotor : BSMotor public virtual Vector3 CurrentValue { get; protected set; } public virtual Vector3 LastError { get; protected set; } + public virtual bool ErrorIsZero + { get { + return (LastError == Vector3.Zero || LastError.LengthSquared() <= ErrorZeroThreshold); + } + } + public BSVMotor(string useName) : base(useName) { @@ -105,7 +113,7 @@ public class BSVMotor : BSMotor Efficiency = 1f; FrictionTimescale = BSMotor.InfiniteVector; CurrentValue = TargetValue = Vector3.Zero; - ErrorZeroThreshold = 0.01f; + ErrorZeroThreshold = 0.001f; } public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) : this(useName) @@ -133,6 +141,8 @@ public class BSVMotor : BSMotor // Compute the next step and return the new current value public virtual Vector3 Step(float timeStep) { + if (!Enabled) return TargetValue; + Vector3 origTarget = TargetValue; // DEBUG Vector3 origCurrVal = CurrentValue; // DEBUG @@ -178,7 +188,7 @@ public class BSVMotor : BSMotor { // Difference between what we have and target is small. Motor is done. CurrentValue = TargetValue; - MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={2}", + MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}", BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue); } @@ -186,6 +196,8 @@ public class BSVMotor : BSMotor } public virtual Vector3 Step(float timeStep, Vector3 error) { + if (!Enabled) return Vector3.Zero; + LastError = error; Vector3 returnCorrection = Vector3.Zero; if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) @@ -268,12 +280,14 @@ public class BSPIDVMotor : BSVMotor public Vector3 proportionFactor { get; set; } public Vector3 integralFactor { get; set; } public Vector3 derivFactor { get; set; } + // Arbritrary factor range. // EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct. public float EfficiencyHigh = 0.4f; public float EfficiencyLow = 4.0f; - Vector3 IntegralFactor { get; set; } + // Running integration of the error + Vector3 RunningIntegration { get; set; } public BSPIDVMotor(string useName) : base(useName) @@ -281,7 +295,7 @@ public class BSPIDVMotor : BSVMotor proportionFactor = new Vector3(1.00f, 1.00f, 1.00f); integralFactor = new Vector3(1.00f, 1.00f, 1.00f); derivFactor = new Vector3(1.00f, 1.00f, 1.00f); - IntegralFactor = Vector3.Zero; + RunningIntegration = Vector3.Zero; LastError = Vector3.Zero; } @@ -311,15 +325,21 @@ public class BSPIDVMotor : BSVMotor // Ignore Current and Target Values and just advance the PID computation on this error. public override Vector3 Step(float timeStep, Vector3 error) { + if (!Enabled) return Vector3.Zero; + // Add up the error so we can integrate over the accumulated errors - IntegralFactor += error * timeStep; + RunningIntegration += error * timeStep; // A simple derivitive is the rate of change from the last error. Vector3 derivFactor = (error - LastError) * timeStep; LastError = error; // Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError) - Vector3 ret = -(error * proportionFactor + IntegralFactor * integralFactor + derivFactor * derivFactor); + Vector3 ret = -( + error * proportionFactor + + RunningIntegration * integralFactor + + derivFactor * derivFactor + ); return ret; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs new file mode 100755 index 0000000000..5558ad5927 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -0,0 +1,559 @@ +/* + * 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.Region.Physics.Manager; + +using OpenMetaverse; +using Nini.Config; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public static class BSParam +{ + // Level of Detail values kept as float because that's what the Meshmerizer wants + public static float MeshLOD { get; private set; } + public static float MeshMegaPrimLOD { get; private set; } + public static float MeshMegaPrimThreshold { get; private set; } + public static float SculptLOD { get; private set; } + + public static float MinimumObjectMass { get; private set; } + public static float MaximumObjectMass { get; private set; } + + public static float LinearDamping { get; private set; } + public static float AngularDamping { get; private set; } + public static float DeactivationTime { get; private set; } + public static float LinearSleepingThreshold { get; private set; } + public static float AngularSleepingThreshold { get; private set; } + public static float CcdMotionThreshold { get; private set; } + public static float CcdSweptSphereRadius { get; private set; } + public static float ContactProcessingThreshold { get; private set; } + + public static bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed + public static bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes + public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects + + public static float TerrainImplementation { get; private set; } + public static float TerrainFriction { get; private set; } + public static float TerrainHitFraction { get; private set; } + public static float TerrainRestitution { get; private set; } + public static float TerrainCollisionMargin { get; private set; } + + // Avatar parameters + public static float AvatarFriction { get; private set; } + public static float AvatarStandingFriction { get; private set; } + public static float AvatarDensity { get; private set; } + public static float AvatarRestitution { get; private set; } + public static float AvatarCapsuleWidth { get; private set; } + public static float AvatarCapsuleDepth { get; private set; } + public static float AvatarCapsuleHeight { get; private set; } + public static float AvatarContactProcessingThreshold { get; private set; } + + public static float VehicleAngularDamping { get; private set; } + + public static float LinksetImplementation { get; private set; } + public static float LinkConstraintUseFrameOffset { get; private set; } + public static float LinkConstraintEnableTransMotor { get; private set; } + public static float LinkConstraintTransMotorMaxVel { get; private set; } + public static float LinkConstraintTransMotorMaxForce { get; private set; } + public static float LinkConstraintERP { get; private set; } + public static float LinkConstraintCFM { get; private set; } + public static float LinkConstraintSolverIterations { get; private set; } + + public static float PID_D { get; private set; } // derivative + public static float PID_P { get; private set; } // proportional + + public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); + public delegate float ParamGet(BSScene scene); + public delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); + public delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val); + + public struct ParameterDefn + { + public string name; // string name of the parameter + public string desc; // a short description of what the parameter means + public float defaultValue; // default value if not specified anywhere else + public ParamUser userParam; // get the value from the configuration file + public ParamGet getter; // return the current value stored for this parameter + public ParamSet setter; // set the current value for this parameter + public SetOnObject onObject; // set the value on an object in the physical domain + public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) + { + name = n; + desc = d; + defaultValue = v; + userParam = u; + getter = g; + setter = s; + onObject = null; + } + public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o) + { + name = n; + desc = d; + defaultValue = v; + userParam = u; + getter = g; + setter = s; + onObject = o; + } + } + + // List of all of the externally visible parameters. + // For each parameter, this table maps a text name to getter and setters. + // To add a new externally referencable/settable parameter, add the paramter storage + // location somewhere in the program and make an entry in this table with the + // getters and setters. + // It is easiest to find an existing definition and copy it. + // Parameter values are floats. Booleans are converted to a floating value. + // + // A ParameterDefn() takes the following parameters: + // -- the text name of the parameter. This is used for console input and ini file. + // -- a short text description of the parameter. This shows up in the console listing. + // -- a default value (float) + // -- a delegate for fetching the parameter from the ini file. + // Should handle fetching the right type from the ini file and converting it. + // -- a delegate for getting the value as a float + // -- a delegate for setting the value from a float + // -- an optional delegate to update the value in the world. Most often used to + // push the new value to an in-world object. + // + // The single letter parameters for the delegates are: + // s = BSScene + // o = BSPhysObject + // p = string parameter name + // l = localID of referenced object + // v = value (float) + // cf = parameter configuration class (for fetching values from ini file) + private static ParameterDefn[] ParameterDefinitions = + { + new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { ShouldMeshSculptedPrim = cf.GetBoolean(p, BSParam.BoolNumeric(v)); }, + (s) => { return BSParam.NumericBool(ShouldMeshSculptedPrim); }, + (s,p,l,v) => { ShouldMeshSculptedPrim = BSParam.BoolNumeric(v); } ), + new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { ShouldForceSimplePrimMeshing = cf.GetBoolean(p, BSParam.BoolNumeric(v)); }, + (s) => { return BSParam.NumericBool(ShouldForceSimplePrimMeshing); }, + (s,p,l,v) => { ShouldForceSimplePrimMeshing = BSParam.BoolNumeric(v); } ), + new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, BSParam.BoolNumeric(v)); }, + (s) => { return BSParam.NumericBool(ShouldUseHullsForPhysicalObjects); }, + (s,p,l,v) => { ShouldUseHullsForPhysicalObjects = BSParam.BoolNumeric(v); } ), + + new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", + 8f, + (s,cf,p,v) => { MeshLOD = (float)cf.GetInt(p, (int)v); }, + (s) => { return MeshLOD; }, + (s,p,l,v) => { MeshLOD = v; } ), + new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters", + 16f, + (s,cf,p,v) => { MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); }, + (s) => { return MeshMegaPrimLOD; }, + (s,p,l,v) => { MeshMegaPrimLOD = v; } ), + new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD", + 10f, + (s,cf,p,v) => { MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); }, + (s) => { return MeshMegaPrimThreshold; }, + (s,p,l,v) => { MeshMegaPrimThreshold = v; } ), + new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", + 32f, + (s,cf,p,v) => { SculptLOD = (float)cf.GetInt(p, (int)v); }, + (s) => { return SculptLOD; }, + (s,p,l,v) => { SculptLOD = v; } ), + + new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", + 10f, + (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_maxSubSteps; }, + (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ), + new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)", + 1f / 60f, + (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); }, + (s) => { return (float)s.m_fixedTimeStep; }, + (s,p,l,v) => { s.m_fixedTimeStep = v; } ), + new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame", + 2048f, + (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_maxCollisionsPerFrame; }, + (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ), + new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame", + 8000f, + (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_maxUpdatesPerFrame; }, + (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), + new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step", + 500f, + (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_taintsToProcessPerStep; }, + (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ), + new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)", + 0.0001f, + (s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); }, + (s) => { return (float)MinimumObjectMass; }, + (s,p,l,v) => { MinimumObjectMass = v; } ), + new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", + 10000.01f, + (s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); }, + (s) => { return (float)MaximumObjectMass; }, + (s,p,l,v) => { MaximumObjectMass = v; } ), + + new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", + 2200f, + (s,cf,p,v) => { PID_D = cf.GetFloat(p, v); }, + (s) => { return (float)PID_D; }, + (s,p,l,v) => { PID_D = v; } ), + new ParameterDefn("PID_P", "Parameteric factor for motion smoothing", + 900f, + (s,cf,p,v) => { PID_P = cf.GetFloat(p, v); }, + (s) => { return (float)PID_P; }, + (s,p,l,v) => { PID_P = v; } ), + + new ParameterDefn("DefaultFriction", "Friction factor used on new objects", + 0.2f, + (s,cf,p,v) => { s.UnmanagedParams[0].defaultFriction = cf.GetFloat(p, v); }, + (s) => { return s.UnmanagedParams[0].defaultFriction; }, + (s,p,l,v) => { s.UnmanagedParams[0].defaultFriction = v; } ), + new ParameterDefn("DefaultDensity", "Density for new objects" , + 10.000006836f, // Aluminum g/cm3 + (s,cf,p,v) => { s.UnmanagedParams[0].defaultDensity = cf.GetFloat(p, v); }, + (s) => { return s.UnmanagedParams[0].defaultDensity; }, + (s,p,l,v) => { s.UnmanagedParams[0].defaultDensity = v; } ), + new ParameterDefn("DefaultRestitution", "Bouncyness of an object" , + 0f, + (s,cf,p,v) => { s.UnmanagedParams[0].defaultRestitution = cf.GetFloat(p, v); }, + (s) => { return s.UnmanagedParams[0].defaultRestitution; }, + (s,p,l,v) => { s.UnmanagedParams[0].defaultRestitution = v; } ), + new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", + 0.04f, + (s,cf,p,v) => { s.UnmanagedParams[0].collisionMargin = cf.GetFloat(p, v); }, + (s) => { return s.UnmanagedParams[0].collisionMargin; }, + (s,p,l,v) => { s.UnmanagedParams[0].collisionMargin = v; } ), + new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)", + -9.80665f, + (s,cf,p,v) => { s.UnmanagedParams[0].gravity = cf.GetFloat(p, v); }, + (s) => { return s.UnmanagedParams[0].gravity; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{s.UnmanagedParams[0].gravity=x;}, p, PhysParameterEntry.APPLY_TO_NONE, v); }, + (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ), + + + new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", + 0f, + (s,cf,p,v) => { LinearDamping = cf.GetFloat(p, v); }, + (s) => { return LinearDamping; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearDamping=x;}, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, AngularDamping); } ), + new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", + 0f, + (s,cf,p,v) => { AngularDamping = cf.GetFloat(p, v); }, + (s) => { return AngularDamping; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularDamping=x;}, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, LinearDamping, v); } ), + new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", + 0.2f, + (s,cf,p,v) => { DeactivationTime = cf.GetFloat(p, v); }, + (s) => { return DeactivationTime; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{DeactivationTime=x;}, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ), + new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", + 0.8f, + (s,cf,p,v) => { LinearSleepingThreshold = cf.GetFloat(p, v); }, + (s) => { return LinearSleepingThreshold; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearSleepingThreshold=x;}, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ), + new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", + 1.0f, + (s,cf,p,v) => { AngularSleepingThreshold = cf.GetFloat(p, v); }, + (s) => { return AngularSleepingThreshold; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularSleepingThreshold=x;}, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ), + new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , + 0f, // set to zero to disable + (s,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); }, + (s) => { return CcdMotionThreshold; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ), + new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , + 0f, + (s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); }, + (s) => { return CcdSweptSphereRadius; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdSweptSphereRadius=x;}, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ), + new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , + 0.1f, + (s,cf,p,v) => { ContactProcessingThreshold = cf.GetFloat(p, v); }, + (s) => { return ContactProcessingThreshold; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{ContactProcessingThreshold=x;}, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ), + + new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)", + (float)BSTerrainPhys.TerrainImplementation.Mesh, + (s,cf,p,v) => { TerrainImplementation = cf.GetFloat(p,v); }, + (s) => { return TerrainImplementation; }, + (s,p,l,v) => { TerrainImplementation = v; } ), + new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , + 0.3f, + (s,cf,p,v) => { TerrainFriction = cf.GetFloat(p, v); }, + (s) => { return TerrainFriction; }, + (s,p,l,v) => { TerrainFriction = v; /* TODO: set on real terrain */} ), + new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , + 0.8f, + (s,cf,p,v) => { TerrainHitFraction = cf.GetFloat(p, v); }, + (s) => { return TerrainHitFraction; }, + (s,p,l,v) => { TerrainHitFraction = v; /* TODO: set on real terrain */ } ), + new ParameterDefn("TerrainRestitution", "Bouncyness" , + 0f, + (s,cf,p,v) => { TerrainRestitution = cf.GetFloat(p, v); }, + (s) => { return TerrainRestitution; }, + (s,p,l,v) => { TerrainRestitution = v; /* TODO: set on real terrain */ } ), + new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" , + 0.04f, + (s,cf,p,v) => { TerrainCollisionMargin = cf.GetFloat(p, v); }, + (s) => { return TerrainCollisionMargin; }, + (s,p,l,v) => { TerrainCollisionMargin = v; /* TODO: set on real terrain */ } ), + + new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", + 0.2f, + (s,cf,p,v) => { AvatarFriction = cf.GetFloat(p, v); }, + (s) => { return AvatarFriction; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarFriction=x;}, p, l, v); } ), + new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", + 10.0f, + (s,cf,p,v) => { AvatarStandingFriction = cf.GetFloat(p, v); }, + (s) => { return AvatarStandingFriction; }, + (s,p,l,v) => { AvatarStandingFriction = v; } ), + new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", + 60f, + (s,cf,p,v) => { AvatarDensity = cf.GetFloat(p, v); }, + (s) => { return AvatarDensity; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarDensity=x;}, p, l, v); } ), + new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", + 0f, + (s,cf,p,v) => { AvatarRestitution = cf.GetFloat(p, v); }, + (s) => { return AvatarRestitution; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarRestitution=x;}, p, l, v); } ), + new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule", + 0.6f, + (s,cf,p,v) => { AvatarCapsuleWidth = cf.GetFloat(p, v); }, + (s) => { return AvatarCapsuleWidth; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleWidth=x;}, p, l, v); } ), + new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule", + 0.45f, + (s,cf,p,v) => { AvatarCapsuleDepth = cf.GetFloat(p, v); }, + (s) => { return AvatarCapsuleDepth; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleDepth=x;}, p, l, v); } ), + new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", + 1.5f, + (s,cf,p,v) => { AvatarCapsuleHeight = cf.GetFloat(p, v); }, + (s) => { return AvatarCapsuleHeight; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleHeight=x;}, p, l, v); } ), + new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", + 0.1f, + (s,cf,p,v) => { AvatarContactProcessingThreshold = cf.GetFloat(p, v); }, + (s) => { return AvatarContactProcessingThreshold; }, + (s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarContactProcessingThreshold=x;}, p, l, v); } ), + + new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)", + 0.95f, + (s,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); }, + (s) => { return VehicleAngularDamping; }, + (s,p,l,v) => { VehicleAngularDamping = v; } ), + + new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", + 0f, + (s,cf,p,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, + (s) => { return s.UnmanagedParams[0].maxPersistantManifoldPoolSize; }, + (s,p,l,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ), + new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", + 0f, + (s,cf,p,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, + (s) => { return s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize; }, + (s,p,l,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ), + new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s) => { return s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation; }, + (s,p,l,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = v; } ), + new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s) => { return s.UnmanagedParams[0].shouldForceUpdateAllAabbs; }, + (s,p,l,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = v; } ), + new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s) => { return s.UnmanagedParams[0].shouldRandomizeSolverOrder; }, + (s,p,l,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = v; } ), + new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s) => { return s.UnmanagedParams[0].shouldSplitSimulationIslands; }, + (s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ), + new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; }, + (s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ), + new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)", + 0f, // zero says use Bullet default + (s,cf,p,v) => { s.UnmanagedParams[0].numberOfSolverIterations = cf.GetFloat(p, v); }, + (s) => { return s.UnmanagedParams[0].numberOfSolverIterations; }, + (s,p,l,v) => { s.UnmanagedParams[0].numberOfSolverIterations = v; } ), + + new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", + (float)BSLinkset.LinksetImplementation.Compound, + (s,cf,p,v) => { LinksetImplementation = cf.GetFloat(p,v); }, + (s) => { return LinksetImplementation; }, + (s,p,l,v) => { LinksetImplementation = v; } ), + new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { LinkConstraintUseFrameOffset = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s) => { return LinkConstraintUseFrameOffset; }, + (s,p,l,v) => { LinkConstraintUseFrameOffset = v; } ), + new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { LinkConstraintEnableTransMotor = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); }, + (s) => { return LinkConstraintEnableTransMotor; }, + (s,p,l,v) => { LinkConstraintEnableTransMotor = v; } ), + new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints", + 5.0f, + (s,cf,p,v) => { LinkConstraintTransMotorMaxVel = cf.GetFloat(p, v); }, + (s) => { return LinkConstraintTransMotorMaxVel; }, + (s,p,l,v) => { LinkConstraintTransMotorMaxVel = v; } ), + new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints", + 0.1f, + (s,cf,p,v) => { LinkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, + (s) => { return LinkConstraintTransMotorMaxForce; }, + (s,p,l,v) => { LinkConstraintTransMotorMaxForce = v; } ), + new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", + 0.1f, + (s,cf,p,v) => { LinkConstraintCFM = cf.GetFloat(p, v); }, + (s) => { return LinkConstraintCFM; }, + (s,p,l,v) => { LinkConstraintCFM = v; } ), + new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", + 0.1f, + (s,cf,p,v) => { LinkConstraintERP = cf.GetFloat(p, v); }, + (s) => { return LinkConstraintERP; }, + (s,p,l,v) => { LinkConstraintERP = v; } ), + new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)", + 40, + (s,cf,p,v) => { LinkConstraintSolverIterations = cf.GetFloat(p, v); }, + (s) => { return LinkConstraintSolverIterations; }, + (s,p,l,v) => { LinkConstraintSolverIterations = v; } ), + + new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)", + 0f, + (s,cf,p,v) => { s.UnmanagedParams[0].physicsLoggingFrames = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.UnmanagedParams[0].physicsLoggingFrames; }, + (s,p,l,v) => { s.UnmanagedParams[0].physicsLoggingFrames = (int)v; } ), + }; + + // Convert a boolean to our numeric true and false values + public static float NumericBool(bool b) + { + return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse); + } + + // Convert numeric true and false values to a boolean + public static bool BoolNumeric(float b) + { + return (b == ConfigurationParameters.numericTrue ? true : false); + } + + // Search through the parameter definitions and return the matching + // ParameterDefn structure. + // Case does not matter as names are compared after converting to lower case. + // Returns 'false' if the parameter is not found. + internal static bool TryGetParameter(string paramName, out ParameterDefn defn) + { + bool ret = false; + ParameterDefn foundDefn = new ParameterDefn(); + string pName = paramName.ToLower(); + + foreach (ParameterDefn parm in ParameterDefinitions) + { + if (pName == parm.name.ToLower()) + { + foundDefn = parm; + ret = true; + break; + } + } + defn = foundDefn; + return ret; + } + + // Pass through the settable parameters and set the default values + internal static void SetParameterDefaultValues(BSScene physicsScene) + { + foreach (ParameterDefn parm in ParameterDefinitions) + { + parm.setter(physicsScene, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue); + } + } + + // Get user set values out of the ini file. + internal static void SetParameterConfigurationValues(BSScene physicsScene, IConfig cfg) + { + foreach (ParameterDefn parm in ParameterDefinitions) + { + parm.userParam(physicsScene, cfg, parm.name, parm.defaultValue); + } + } + + internal static 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. + internal static void BuildParameterTable() + { + if (SettableParameters.Length < ParameterDefinitions.Length) + { + List entries = new List(); + for (int ii = 0; ii < ParameterDefinitions.Length; ii++) + { + ParameterDefn pd = ParameterDefinitions[ii]; + entries.Add(new PhysParameterEntry(pd.name, pd.desc)); + } + + // make the list in alphabetical order for estetic reasons + entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2) + { + return ppe1.name.CompareTo(ppe2.name); + }); + + SettableParameters = entries.ToArray(); + } + } + + +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index 92a5f2f054..c76f869d81 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -45,6 +45,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin * ForceVariableName: direct reference (store and fetch) to the value in the physics engine. * The last two (and certainly the last one) should be referenced only in taint-time. */ + +/* + * As of 20121221, the following are the call sequences (going down) for different script physical functions: + * llApplyImpulse llApplyRotImpulse llSetTorque llSetForce + * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce + * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse + * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v + * BS.ApplyCentralForce BS.ApplyTorque + */ + public abstract class BSPhysObject : PhysicsActor { protected BSPhysObject() @@ -69,6 +79,12 @@ public abstract class BSPhysObject : PhysicsActor CollidingGroundStep = 0; } + // Tell the object to clean up. + public virtual void Destroy() + { + UnRegisterAllPreStepActions(); + } + public BSScene PhysicsScene { get; protected set; } // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor public string PhysObjectName { get; protected set; } @@ -130,9 +146,6 @@ public abstract class BSPhysObject : PhysicsActor // Update the physical location and motion of the object. Called with data from Bullet. public abstract void UpdateProperties(EntityProperties entprop); - // Tell the object to clean up. - public abstract void Destroy(); - public abstract OMV.Vector3 RawPosition { get; set; } public abstract OMV.Vector3 ForcePosition { get; set; } @@ -140,7 +153,7 @@ public abstract class BSPhysObject : PhysicsActor public abstract OMV.Quaternion ForceOrientation { get; set; } // The system is telling us the velocity it wants to move at. - protected OMV.Vector3 m_targetVelocity; + // protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor public override OMV.Vector3 TargetVelocity { get { return m_targetVelocity; } @@ -280,11 +293,53 @@ public abstract class BSPhysObject : PhysicsActor #endregion // Collisions + #region Per Simulation Step actions + // There are some actions that must be performed for a physical object before each simulation step. + // These actions are optional so, rather than scanning all the physical objects and asking them + // if they have anything to do, a physical object registers for an event call before the step is performed. + // This bookkeeping makes it easy to add, remove and clean up after all these registrations. + private Dictionary RegisteredActions = new Dictionary(); + protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn) + { + string identifier = op + "-" + id.ToString(); + RegisteredActions[identifier] = actn; + PhysicsScene.BeforeStep += actn; + DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier); + } + + // Unregister a pre step action. Safe to call if the action has not been registered. + protected void UnRegisterPreStepAction(string op, uint id) + { + string identifier = op + "-" + id.ToString(); + bool removed = false; + if (RegisteredActions.ContainsKey(identifier)) + { + PhysicsScene.BeforeStep -= RegisteredActions[identifier]; + RegisteredActions.Remove(identifier); + removed = true; + } + DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed); + } + + protected void UnRegisterAllPreStepActions() + { + foreach (KeyValuePair kvp in RegisteredActions) + { + PhysicsScene.BeforeStep -= kvp.Value; + } + RegisteredActions.Clear(); + DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID); + } + + + #endregion // Per Simulation Step actions + // High performance detailed logging routine used by the physical objects. protected void DetailLog(string msg, params Object[] args) { if (PhysicsScene.PhysicsLogging.Enabled) PhysicsScene.DetailLog(msg, args); } + } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 68a0db6c82..26b8df5e06 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -129,6 +129,7 @@ public sealed class BSPrim : BSPhysObject public override void Destroy() { // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); + base.Destroy(); // Undo any links between me and any other object BSPhysObject parentBefore = Linkset.LinksetRoot; @@ -434,12 +435,26 @@ public sealed class BSPrim : BSPhysObject get { return _force; } set { _force = value; - PhysicsScene.TaintedObject("BSPrim.setForce", delegate() + if (_force != OMV.Vector3.Zero) { - // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); - if (PhysBody.HasPhysicalBody) - BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); - }); + // If the force is non-zero, it must be reapplied each tick because + // Bullet clears the forces applied last frame. + RegisterPreStepAction("BSPrim.setForce", LocalID, + delegate(float timeStep) + { + DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force); + if (PhysBody.HasPhysicalBody) + { + BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, _force); + ActivateIfPhysical(false); + } + } + ); + } + else + { + UnRegisterPreStepAction("BSPrim.setForce", LocalID); + } } } @@ -450,15 +465,18 @@ public sealed class BSPrim : BSPhysObject set { Vehicle type = (Vehicle)value; - // Tell the scene about the vehicle so it will get processing each frame. - PhysicsScene.VehicleInSceneTypeChanged(this, type); - PhysicsScene.TaintedObject("setVehicleType", delegate() { // Done at taint time so we're sure the physics engine is not using the variables // Vehicle code changes the parameters for this vehicle type. _vehicle.ProcessTypeChange(type); ActivateIfPhysical(false); + + // If an active vehicle, register the vehicle code to be called before each step + if (_vehicle.Type == Vehicle.TYPE_NONE) + UnRegisterPreStepAction("BSPrim.Vehicle", LocalID); + else + RegisterPreStepAction("BSPrim.Vehicle", LocalID, _vehicle.Step); }); } } @@ -494,23 +512,6 @@ 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 override void StepVehicle(float timeStep) - { - if (IsPhysical && _vehicle.IsActive) - { - _vehicle.Step(timeStep); - /* // TEST TEST DEBUG DEBUG -- trying to reduce the extra action of Bullet simulation step - PhysicsScene.PostTaintObject("BSPrim.StepVehicles", LocalID, delegate() - { - // This resets the interpolation values and recomputes the tensor variables - BulletSimAPI.SetCenterOfMassByPosRot2(BSBody.ptr, ForcePosition, ForceOrientation); - }); - */ - } - } - // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more public override void SetVolumeDetect(int param) { bool newValue = (param != 0); @@ -543,14 +544,32 @@ public sealed class BSPrim : BSPhysObject _velocity = value; if (PhysBody.HasPhysicalBody) + { BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); + ActivateIfPhysical(false); + } } } public override OMV.Vector3 Torque { get { return _torque; } set { _torque = value; - AddAngularForce(_torque, false, false); + if (_torque != OMV.Vector3.Zero) + { + // If the torque is non-zero, it must be reapplied each tick because + // Bullet clears the forces applied last frame. + RegisterPreStepAction("BSPrim.setTorque", LocalID, + delegate(float timeStep) + { + if (PhysBody.HasPhysicalBody) + AddAngularForce(_torque, false, true); + } + ); + } + else + { + UnRegisterPreStepAction("BSPrim.setTorque", LocalID); + } // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); } } @@ -720,10 +739,10 @@ public sealed class BSPrim : BSPhysObject // Mass is zero which disables a bunch of physics stuff in Bullet UpdatePhysicalMassProperties(0f); // Set collision detection parameters - if (PhysicsScene.Params.ccdMotionThreshold > 0f) + if (BSParam.CcdMotionThreshold > 0f) { - BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); - BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); + BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold); + BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius); } // The activation state is 'disabled' so Bullet will not try to act on it. @@ -761,17 +780,17 @@ public sealed class BSPrim : BSPhysObject UpdatePhysicalMassProperties(RawMass); // Set collision detection parameters - if (PhysicsScene.Params.ccdMotionThreshold > 0f) + if (BSParam.CcdMotionThreshold > 0f) { - BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); - BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); + BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold); + BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius); } // Various values for simulation limits - BulletSimAPI.SetDamping2(PhysBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping); - BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, PhysicsScene.Params.deactivationTime); - BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); - BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold); + BulletSimAPI.SetDamping2(PhysBody.ptr, BSParam.LinearDamping, BSParam.AngularDamping); + BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, BSParam.DeactivationTime); + BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); + BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold); // This collides like an object. PhysBody.collisionType = CollisionType.Dynamic; @@ -819,7 +838,7 @@ public sealed class BSPrim : BSPhysObject // Called in taint-time!! private void ActivateIfPhysical(bool forceIt) { - if (IsPhysical) + if (IsPhysical && PhysBody.HasPhysicalBody) BulletSimAPI.Activate2(PhysBody.ptr, forceIt); } @@ -893,8 +912,7 @@ public sealed class BSPrim : BSPhysObject PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() { DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); - if (PhysBody.HasPhysicalBody) - BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); + ForceRotationalVelocity = _rotationalVelocity; }); } } @@ -904,7 +922,11 @@ public sealed class BSPrim : BSPhysObject } set { _rotationalVelocity = value; - BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); + if (PhysBody.HasPhysicalBody) + { + BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); + ActivateIfPhysical(false); + } } } public override bool Kinematic { @@ -933,6 +955,7 @@ public sealed class BSPrim : BSPhysObject { float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); + ActivateIfPhysical(false); } } } @@ -969,56 +992,35 @@ public sealed class BSPrim : BSPhysObject public override float APIDStrength { set { return; } } public override float APIDDamping { set { return; } } - private List m_accumulatedForces = new List(); public override void AddForce(OMV.Vector3 force, bool pushforce) { AddForce(force, pushforce, false); } // Applying a force just adds this to the total force on the object. + // This added force will only last the next simulation tick. public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { // for an object, doesn't matter if force is a pushforce or not if (force.IsFinite()) { - // _force += force; - lock (m_accumulatedForces) - m_accumulatedForces.Add(new OMV.Vector3(force)); + OMV.Vector3 addForce = force; + DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); + PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() + { + // Bullet adds this central force to the total force for this tick + DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); + if (PhysBody.HasPhysicalBody) + { + BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, addForce); + ActivateIfPhysical(false); + } + }); } else { m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); return; } - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() - { - OMV.Vector3 fSum = OMV.Vector3.Zero; - lock (m_accumulatedForces) - { - // Sum the accumulated additional forces for one big force to apply once. - foreach (OMV.Vector3 v in m_accumulatedForces) - { - fSum += v; - } - m_accumulatedForces.Clear(); - } - DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum); - if (fSum != OMV.Vector3.Zero) - if (PhysBody.HasPhysicalBody) - BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum); - }); } - // An impulse force is scaled by the mass of the object. - public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime) - { - OMV.Vector3 applyImpulse = impulse; - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate() - { - DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse); - if (PhysBody.HasPhysicalBody) - BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse); - }); - } - - private List m_accumulatedAngularForces = new List(); public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { AddAngularForce(force, pushforce, false); } @@ -1026,36 +1028,23 @@ public sealed class BSPrim : BSPhysObject { if (force.IsFinite()) { - // _force += force; - lock (m_accumulatedAngularForces) - m_accumulatedAngularForces.Add(new OMV.Vector3(force)); + OMV.Vector3 angForce = force; + PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() + { + if (PhysBody.HasPhysicalBody) + { + BulletSimAPI.ApplyTorque2(PhysBody.ptr, angForce); + ActivateIfPhysical(false); + } + }); } else { m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); return; } - PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() - { - OMV.Vector3 fSum = OMV.Vector3.Zero; - lock (m_accumulatedAngularForces) - { - // Sum the accumulated additional forces for one big force to apply once. - foreach (OMV.Vector3 v in m_accumulatedAngularForces) - { - fSum += v; - } - m_accumulatedAngularForces.Clear(); - } - DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum); - if (fSum != OMV.Vector3.Zero) - { - if (PhysBody.HasPhysicalBody) - BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum); - _torque = fSum; - } - }); } + // A torque impulse. // ApplyTorqueImpulse adds torque directly to the angularVelocity. // AddAngularForce accumulates the force and applied it to the angular velocity all at once. @@ -1066,7 +1055,10 @@ public sealed class BSPrim : BSPhysObject PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() { if (PhysBody.HasPhysicalBody) + { BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse); + ActivateIfPhysical(false); + } }); } @@ -1361,11 +1353,7 @@ public sealed class BSPrim : BSPhysObject } */ - if (returnMass <= 0) - returnMass = 0.0001f; - - if (returnMass > PhysicsScene.MaximumObjectMass) - returnMass = PhysicsScene.MaximumObjectMass; + returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass); return returnMass; }// end CalculateMass diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 2ca4912c8c..e8e0d50b57 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -69,20 +69,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters // every tick so OpenSim will update its animation. private HashSet m_avatars = new HashSet(); - // 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; } } public IMesher mesher; - // Level of Detail values kept as float because that's what the Meshmerizer wants - public float MeshLOD { get; private set; } - public float MeshMegaPrimLOD { get; private set; } - public float MeshMegaPrimThreshold { get; private set; } - public float SculptLOD { get; private set; } - public uint WorldID { get; private set; } public BulletSim World { get; private set; } @@ -90,24 +80,18 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public BSConstraintCollection Constraints { get; private set; } // Simulation parameters - private int m_maxSubSteps; - private float m_fixedTimeStep; - private long m_simulationStep = 0; + internal int m_maxSubSteps; + internal float m_fixedTimeStep; + internal long m_simulationStep = 0; public long SimulationStep { get { return m_simulationStep; } } - private int m_taintsToProcessPerStep; - - // Avatar parameters - public float ParamAvatarFriction { get; private set; } - public float ParamAvatarStandingFriction { get; private set; } - public float ParamAvatarDensity { get; private set; } - public float ParamAvatarRestitution { get; private set; } - public float ParamAvatarCapsuleWidth { get; private set; } - public float ParamAvatarCapsuleDepth { get; private set; } - public float ParamAvatarCapsuleHeight { get; private set; } - public float ParamAvatarContactProcessingThreshold { get; private set; } + internal int m_taintsToProcessPerStep; + internal float LastTimeStep { get; private set; } + // Physical objects can register for prestep or poststep events public delegate void PreStepAction(float timeStep); + public delegate void PostStepAction(float timeStep); public event PreStepAction BeforeStep; + public event PreStepAction AfterStep; // 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 @@ -121,20 +105,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public bool InTaintTime { get; private set; } // Pinned memory used to pass step information between managed and unmanaged - private int m_maxCollisionsPerFrame; - private CollisionDesc[] m_collisionArray; - private GCHandle m_collisionArrayPinnedHandle; + internal int m_maxCollisionsPerFrame; + internal CollisionDesc[] m_collisionArray; + internal GCHandle m_collisionArrayPinnedHandle; - private int m_maxUpdatesPerFrame; - private EntityProperties[] m_updateArray; - private GCHandle m_updateArrayPinnedHandle; - - public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed - public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes - public bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects - - public float PID_D { get; private set; } // derivative - public float PID_P { get; private set; } // proportional + internal int m_maxUpdatesPerFrame; + internal EntityProperties[] m_updateArray; + internal GCHandle m_updateArrayPinnedHandle; public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero public const uint GROUNDPLANE_ID = 1; @@ -145,7 +122,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public ConfigurationParameters Params { - get { return m_params[0]; } + get { return UnmanagedParams[0]; } } public Vector3 DefaultGravity { @@ -157,8 +134,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters get { return Params.gravity; } } - 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 @@ -181,7 +156,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters // 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; + internal ConfigurationParameters[] UnmanagedParams; GCHandle m_paramsHandle; // Handle to the callback used by the unmanaged code to call into the managed code. @@ -218,8 +193,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters Shapes = new BSShapeCollection(this); // Allocate pinned memory to pass parameters. - m_params = new ConfigurationParameters[1]; - m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned); + UnmanagedParams = new ConfigurationParameters[1]; + m_paramsHandle = GCHandle.Alloc(UnmanagedParams, GCHandleType.Pinned); // Set default values for physics parameters plus any overrides from the ini file GetInitialParameterValues(config); @@ -277,7 +252,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters TerrainManager = new BSTerrainManager(this); TerrainManager.CreateInitialGroundPlaneAndTerrain(); - m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)Params.linksetImplementation); + m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); InTaintTime = false; m_initialized = true; @@ -288,9 +263,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters private void GetInitialParameterValues(IConfigSource config) { ConfigurationParameters parms = new ConfigurationParameters(); - m_params[0] = parms; + UnmanagedParams[0] = parms; - SetParameterDefaultValues(); + BSParam.SetParameterDefaultValues(this); if (config != null) { @@ -298,7 +273,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters IConfig pConfig = config.Configs["BulletSim"]; if (pConfig != null) { - SetParameterConfigurationValues(pConfig); + BSParam.SetParameterConfigurationValues(this, pConfig); // Very detailed logging for physics debugging m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); @@ -492,6 +467,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters // Simulate one timestep public override float Simulate(float timeStep) { + // prevent simulation until we've been initialized + if (!m_initialized) return 5.0f; + + LastTimeStep = timeStep; + int updatedEntityCount = 0; IntPtr updatedEntitiesPtr; int collidersCount = 0; @@ -500,26 +480,27 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters int beforeTime = 0; int simTime = 0; - // prevent simulation until we've been initialized - if (!m_initialized) return 5.0f; - // update the prim states while we know the physics engine is not busy int numTaints = _taintOperations.Count; + + InTaintTime = true; // Only used for debugging so locking is not necessary. + ProcessTaints(); - // Some of the prims operate with special vehicle properties - DoPreStepActions(timeStep); + // Some of the physical objects requre individual, pre-step calls + TriggerPreStepEvent(timeStep); // the prestep actions might have added taints ProcessTaints(); + InTaintTime = false; // Only used for debugging so locking is not necessary. + // step the physical world one interval m_simulationStep++; int numSubSteps = 0; try { - if (VehiclePhysicalLoggingEnabled) DumpVehicles(); // DEBUG if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, @@ -529,7 +510,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); - if (VehiclePhysicalLoggingEnabled) DumpVehicles(); // DEBUG } catch (Exception e) { @@ -608,7 +588,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters } } - ProcessPostStepTaints(); + TriggerPostStepEvent(timeStep); // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. // Only enable this in a limited test world with few objects. @@ -700,6 +680,15 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public override bool IsThreaded { get { return false; } } #region Taints + // The simulation execution order is: + // Simulate() + // DoOneTimeTaints + // TriggerPreStepEvent + // DoOneTimeTaints + // Step() + // ProcessAndForwardCollisions + // ProcessAndForwardPropertyUpdates + // TriggerPostStepEvent // Calls to the PhysicsActors can't directly call into the physics engine // because it might be busy. We delay changes to a known time. @@ -726,58 +715,35 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters TaintedObject(ident, callback); } + private void TriggerPreStepEvent(float timeStep) + { + PreStepAction actions = BeforeStep; + if (actions != null) + actions(timeStep); + + } + + private void TriggerPostStepEvent(float timeStep) + { + PreStepAction actions = AfterStep; + if (actions != null) + actions(timeStep); + + } + // When someone tries to change a property on a BSPrim or BSCharacter, the object queues // a callback into itself to do the actual property change. That callback is called // here just before the physics engine is called to step the simulation. public void ProcessTaints() { - InTaintTime = true; // Only used for debugging so locking is not necessary. ProcessRegularTaints(); ProcessPostTaintTaints(); - InTaintTime = false; } private void ProcessRegularTaints() { if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process { - /* - // Code to limit the number of taints processed per step. Meant to limit step time. - // Unsure if a good idea as code assumes that taints are done before the step. - int taintCount = m_taintsToProcessPerStep; - TaintCallbackEntry oneCallback = new TaintCallbackEntry(); - while (_taintOperations.Count > 0 && taintCount-- > 0) - { - bool gotOne = false; - lock (_taintLock) - { - if (_taintOperations.Count > 0) - { - oneCallback = _taintOperations[0]; - _taintOperations.RemoveAt(0); - gotOne = true; - } - } - if (gotOne) - { - try - { - DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident); - oneCallback.callback(); - } - catch (Exception e) - { - DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG - m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e); - } - } - } - if (_taintOperations.Count > 0) - { - DetailLog("{0},BSScene.ProcessTaints,leftTaintsOnList,numNotProcessed={1}", DetailLogZero, _taintOperations.Count); - } - */ - // swizzle a new list into the list location so we can process what's there List oldList; lock (_taintLock) @@ -816,6 +782,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters return; } + // Taints that happen after the normal taint processing but before the simulation step. private void ProcessPostTaintTaints() { if (_postTaintOperations.Count > 0) @@ -843,45 +810,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters } } - public void PostStepTaintObject(String ident, TaintCallback callback) - { - if (!m_initialized) return; - - lock (_taintLock) - { - _postStepOperations.Add(new TaintCallbackEntry(ident, callback)); - } - - return; - } - - private void ProcessPostStepTaints() - { - if (_postStepOperations.Count > 0) - { - List oldList; - lock (_taintLock) - { - oldList = _postStepOperations; - _postStepOperations = new List(); - } - - foreach (TaintCallbackEntry tcbe in oldList) - { - try - { - DetailLog("{0},BSScene.ProcessPostStepTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG - tcbe.callback(); - } - catch (Exception e) - { - m_log.ErrorFormat("{0}: ProcessPostStepTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); - } - } - oldList.Clear(); - } - } - // Only used for debugging. Does not change state of anything so locking is not necessary. public bool AssertInTaintTime(string whereFrom) { @@ -889,540 +817,21 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters { DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); - Util.PrintCallStack(); // Prints the stack into the DEBUG log file. + Util.PrintCallStack(DetailLog); } return InTaintTime; } #endregion // Taints - #region Vehicles - - public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType) - { - RemoveVehiclePrim(vehic); - if (newType != Vehicle.TYPE_NONE) - { - // make it so the scene will call us each tick to do vehicle things - AddVehiclePrim(vehic); - } - } - - // Make so the scene will call this prim for vehicle actions each tick. - // Safe to call if prim is already in the vehicle list. - public void AddVehiclePrim(BSPrim vehicle) - { - lock (m_vehicles) - { - if (!m_vehicles.Contains(vehicle)) - { - m_vehicles.Add(vehicle); - } - } - } - - // Remove a prim from our list of vehicles. - // Safe to call if the prim is not in the vehicle list. - public void RemoveVehiclePrim(BSPrim vehicle) - { - lock (m_vehicles) - { - if (m_vehicles.Contains(vehicle)) - { - m_vehicles.Remove(vehicle); - } - } - } - - private void DoPreStepActions(float timeStep) - { - ProcessVehicles(timeStep); - - PreStepAction actions = BeforeStep; - if (actions != null) - actions(timeStep); - - } - - // Some prims have extra vehicle actions - // Called at taint time! - private void ProcessVehicles(float timeStep) - { - foreach (BSPhysObject pobj in m_vehicles) - { - pobj.StepVehicle(timeStep); - } - } - #endregion Vehicles - #region INI and command line parameter processing - delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); - delegate float ParamGet(BSScene scene); - delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); - delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val); - - private struct ParameterDefn - { - public string name; // string name of the parameter - public string desc; // a short description of what the parameter means - public float defaultValue; // default value if not specified anywhere else - public ParamUser userParam; // get the value from the configuration file - public ParamGet getter; // return the current value stored for this parameter - public ParamSet setter; // set the current value for this parameter - public SetOnObject onObject; // set the value on an object in the physical domain - public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) - { - name = n; - desc = d; - defaultValue = v; - userParam = u; - getter = g; - setter = s; - onObject = null; - } - public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o) - { - name = n; - desc = d; - defaultValue = v; - userParam = u; - getter = g; - setter = s; - onObject = o; - } - } - - // List of all of the externally visible parameters. - // For each parameter, this table maps a text name to getter and setters. - // To add a new externally referencable/settable parameter, add the paramter storage - // location somewhere in the program and make an entry in this table with the - // getters and setters. - // It is easiest to find an existing definition and copy it. - // Parameter values are floats. Booleans are converted to a floating value. - // - // A ParameterDefn() takes the following parameters: - // -- the text name of the parameter. This is used for console input and ini file. - // -- a short text description of the parameter. This shows up in the console listing. - // -- a delegate for fetching the parameter from the ini file. - // Should handle fetching the right type from the ini file and converting it. - // -- a delegate for getting the value as a float - // -- a delegate for setting the value from a float - // -- an optional delegate to update the value in the world. Most often used to - // push the new value to an in-world object. - // - // The single letter parameters for the delegates are: - // s = BSScene - // o = BSPhysObject - // p = string parameter name - // l = localID of referenced object - // v = float value - // cf = parameter configuration class (for fetching values from ini file) - private ParameterDefn[] ParameterDefinitions = - { - new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, - (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); }, - (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ), - new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, - (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); }, - (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ), - new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); }, - (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); }, - (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ), - - new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", - 8f, - (s,cf,p,v) => { s.MeshLOD = (float)cf.GetInt(p, (int)v); }, - (s) => { return s.MeshLOD; }, - (s,p,l,v) => { s.MeshLOD = v; } ), - new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters", - 16f, - (s,cf,p,v) => { s.MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); }, - (s) => { return s.MeshMegaPrimLOD; }, - (s,p,l,v) => { s.MeshMegaPrimLOD = v; } ), - new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD", - 10f, - (s,cf,p,v) => { s.MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); }, - (s) => { return s.MeshMegaPrimThreshold; }, - (s,p,l,v) => { s.MeshMegaPrimThreshold = v; } ), - new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", - 32f, - (s,cf,p,v) => { s.SculptLOD = (float)cf.GetInt(p, (int)v); }, - (s) => { return s.SculptLOD; }, - (s,p,l,v) => { s.SculptLOD = v; } ), - - new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", - 10f, - (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_maxSubSteps; }, - (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ), - new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)", - 1f / 60f, - (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); }, - (s) => { return (float)s.m_fixedTimeStep; }, - (s,p,l,v) => { s.m_fixedTimeStep = v; } ), - new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame", - 2048f, - (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_maxCollisionsPerFrame; }, - (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ), - new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame", - 8000f, - (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_maxUpdatesPerFrame; }, - (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), - new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step", - 500f, - (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_taintsToProcessPerStep; }, - (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ), - new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", - 10000.01f, - (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, - (s,cf,p,v) => { s.PID_D = cf.GetFloat(p, v); }, - (s) => { return (float)s.PID_D; }, - (s,p,l,v) => { s.PID_D = v; } ), - new ParameterDefn("PID_P", "Parameteric factor for motion smoothing", - 900f, - (s,cf,p,v) => { s.PID_P = cf.GetFloat(p, v); }, - (s) => { return (float)s.PID_P; }, - (s,p,l,v) => { s.PID_P = v; } ), - - new ParameterDefn("DefaultFriction", "Friction factor used on new objects", - 0.2f, - (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].defaultFriction; }, - (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ), - new ParameterDefn("DefaultDensity", "Density for new objects" , - 10.000006836f, // Aluminum g/cm3 - (s,cf,p,v) => { s.m_params[0].defaultDensity = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].defaultDensity; }, - (s,p,l,v) => { s.m_params[0].defaultDensity = v; } ), - new ParameterDefn("DefaultRestitution", "Bouncyness of an object" , - 0f, - (s,cf,p,v) => { s.m_params[0].defaultRestitution = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].defaultRestitution; }, - (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ), - new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", - 0.04f, - (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].collisionMargin; }, - (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ), - new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)", - -9.80665f, - (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].gravity; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); }, - (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ), - - - new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", - 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.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); }, - (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, s.m_params[0].angularDamping); } ), - 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.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); }, - (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, s.m_params[0].linearDamping, 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.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); }, - (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, 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.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); }, - (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, 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.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); }, - (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, 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.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); }, - (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, 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.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); }, - (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, 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.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, - (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ), - - new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)", - (float)BSTerrainPhys.TerrainImplementation.Mesh, - (s,cf,p,v) => { s.m_params[0].terrainImplementation = cf.GetFloat(p,v); }, - (s) => { return s.m_params[0].terrainImplementation; }, - (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ), - new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , - 0.3f, - (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].terrainFriction; }, - (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), - new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , - 0.8f, - (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].terrainHitFraction; }, - (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ), - new ParameterDefn("TerrainRestitution", "Bouncyness" , - 0f, - (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].terrainRestitution; }, - (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), - new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" , - 0.04f, - (s,cf,p,v) => { s.m_params[0].terrainCollisionMargin = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].terrainCollisionMargin; }, - (s,p,l,v) => { s.m_params[0].terrainCollisionMargin = v; /* TODO: set on real terrain */ } ), - - new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", - 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); } ), - new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", - 10.0f, - (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].avatarStandingFriction; }, - (s,p,l,v) => { s.m_params[0].avatarStandingFriction = 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.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.UpdateParameterObject(ref s.m_params[0].avatarRestitution, p, l, v); } ), - new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule", - 0.6f, - (s,cf,p,v) => { s.m_params[0].avatarCapsuleWidth = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].avatarCapsuleWidth; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleWidth, p, l, v); } ), - new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule", - 0.45f, - (s,cf,p,v) => { s.m_params[0].avatarCapsuleDepth = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].avatarCapsuleDepth; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarCapsuleDepth, 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.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.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), - - new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)", - 0.95f, - (s,cf,p,v) => { s.m_params[0].vehicleAngularDamping = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].vehicleAngularDamping; }, - (s,p,l,v) => { s.m_params[0].vehicleAngularDamping = v; } ), - - new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", - 0f, - (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; }, - (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ), - new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", - 0f, - (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; }, - (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ), - new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, - (s) => { return s.m_params[0].shouldDisableContactPoolDynamicAllocation; }, - (s,p,l,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = v; } ), - new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { s.m_params[0].shouldForceUpdateAllAabbs = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, - (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; }, - (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ), - new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, - (s) => { return s.m_params[0].shouldRandomizeSolverOrder; }, - (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), - new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, - (s) => { return s.m_params[0].shouldSplitSimulationIslands; }, - (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ), - new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { s.m_params[0].shouldEnableFrictionCaching = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, - (s) => { return s.m_params[0].shouldEnableFrictionCaching; }, - (s,p,l,v) => { s.m_params[0].shouldEnableFrictionCaching = v; } ), - new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)", - 0f, // zero says use Bullet default - (s,cf,p,v) => { s.m_params[0].numberOfSolverIterations = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].numberOfSolverIterations; }, - (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), - - new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", - (float)BSLinkset.LinksetImplementation.Compound, - (s,cf,p,v) => { s.m_params[0].linksetImplementation = cf.GetFloat(p,v); }, - (s) => { return s.m_params[0].linksetImplementation; }, - (s,p,l,v) => { s.m_params[0].linksetImplementation = v; } ), - new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", - ConfigurationParameters.numericFalse, - (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, - (s) => { return s.m_params[0].linkConstraintUseFrameOffset; }, - (s,p,l,v) => { s.m_params[0].linkConstraintUseFrameOffset = v; } ), - new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints", - ConfigurationParameters.numericTrue, - (s,cf,p,v) => { s.m_params[0].linkConstraintEnableTransMotor = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, - (s) => { return s.m_params[0].linkConstraintEnableTransMotor; }, - (s,p,l,v) => { s.m_params[0].linkConstraintEnableTransMotor = v; } ), - new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints", - 5.0f, - (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].linkConstraintTransMotorMaxVel; }, - (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = v; } ), - new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints", - 0.1f, - (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, - (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), - new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", - 0.1f, - (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].linkConstraintCFM; }, - (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), - new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", - 0.1f, - (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].linkConstraintERP; }, - (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), - new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)", - 40, - (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); }, - (s) => { return s.m_params[0].linkConstraintSolverIterations; }, - (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ), - - new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)", - 0f, - (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_params[0].physicsLoggingFrames; }, - (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ), - }; - - // Convert a boolean to our numeric true and false values - public float NumericBool(bool b) - { - return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse); - } - - // Convert numeric true and false values to a boolean - public bool BoolNumeric(float b) - { - return (b == ConfigurationParameters.numericTrue ? true : false); - } - - // Search through the parameter definitions and return the matching - // ParameterDefn structure. - // Case does not matter as names are compared after converting to lower case. - // Returns 'false' if the parameter is not found. - private bool TryGetParameter(string paramName, out ParameterDefn defn) - { - bool ret = false; - ParameterDefn foundDefn = new ParameterDefn(); - string pName = paramName.ToLower(); - - foreach (ParameterDefn parm in ParameterDefinitions) - { - if (pName == parm.name.ToLower()) - { - foundDefn = parm; - ret = true; - break; - } - } - defn = foundDefn; - return ret; - } - - // Pass through the settable parameters and set the default values - private void SetParameterDefaultValues() - { - foreach (ParameterDefn parm in ParameterDefinitions) - { - parm.setter(this, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue); - } - } - - // Get user set values out of the ini file. - private void SetParameterConfigurationValues(IConfig cfg) - { - foreach (ParameterDefn parm in ParameterDefinitions) - { - parm.userParam(this, cfg, parm.name, parm.defaultValue); - } - } - - 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) - { - List entries = new List(); - for (int ii = 0; ii < ParameterDefinitions.Length; ii++) - { - ParameterDefn pd = ParameterDefinitions[ii]; - entries.Add(new PhysParameterEntry(pd.name, pd.desc)); - } - - // make the list in alphabetical order for estetic reasons - entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2) - { - return ppe1.name.CompareTo(ppe2.name); - }); - - SettableParameters = entries.ToArray(); - } - } - - #region IPhysicsParameters // Get the list of parameters this physics engine supports public PhysParameterEntry[] GetParameterList() { - BuildParameterTable(); - return SettableParameters; + BSParam.BuildParameterTable(); + return BSParam.SettableParameters; } // Set parameter on a specific or all instances. @@ -1434,8 +843,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters public bool SetPhysicsParameter(string parm, float val, uint localID) { bool ret = false; - ParameterDefn theParam; - if (TryGetParameter(parm, out theParam)) + BSParam.ParameterDefn theParam; + if (BSParam.TryGetParameter(parm, out theParam)) { theParam.setter(this, parm, localID, val); ret = true; @@ -1447,19 +856,20 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters // 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 UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val) + internal delegate void AssignVal(float x); + internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val) { List objectIDs = new List(); switch (localID) { case PhysParameterEntry.APPLY_TO_NONE: - defaultLoc = val; // setting only the default value + setDefault(val); // setting only the default value // This will cause a call into the physical world if some operation is specified (SetOnObject). objectIDs.Add(TERRAIN_ID); TaintedUpdateParameter(parm, objectIDs, val); break; case PhysParameterEntry.APPLY_TO_ALL: - defaultLoc = val; // setting ALL also sets the default value + setDefault(val); // setting ALL also sets the default value lock (PhysObjects) objectIDs = new List(PhysObjects.Keys); TaintedUpdateParameter(parm, objectIDs, val); break; @@ -1478,8 +888,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters List xlIDs = lIDs; string xparm = parm; TaintedObject("BSScene.UpdateParameterSet", delegate() { - ParameterDefn thisParam; - if (TryGetParameter(xparm, out thisParam)) + BSParam.ParameterDefn thisParam; + if (BSParam.TryGetParameter(xparm, out thisParam)) { if (thisParam.onObject != null) { @@ -1500,8 +910,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters { float val = 0f; bool ret = false; - ParameterDefn theParam; - if (TryGetParameter(parm, out theParam)) + BSParam.ParameterDefn theParam; + if (BSParam.TryGetParameter(parm, out theParam)) { val = theParam.getter(this); ret = true; @@ -1514,16 +924,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters #endregion Runtime settable parameters - // Debugging routine for dumping detailed physical information for vehicle prims - private void DumpVehicles() - { - foreach (BSPrim prim in m_vehicles) - { - BulletSimAPI.DumpRigidBody2(World.ptr, prim.PhysBody.ptr); - BulletSimAPI.DumpCollisionShape2(World.ptr, prim.PhysShape.ptr); - } - } - // Invoke the detailed logger and output something if it's enabled. public void DetailLog(string msg, params Object[] args) { diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index d6e2fe9f47..855be0ba8a 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -456,7 +456,7 @@ public sealed class BSShapeCollection : IDisposable if (!haveShape && pbs != null && nativeShapePossible - && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) + && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 && pbs.ProfileHollow == 0 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 @@ -520,7 +520,7 @@ public sealed class BSShapeCollection : IDisposable bool ret = false; // Note that if it's a native shape, the check for physical/non-physical is not // made. Native shapes work in either case. - if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects) + if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) { // Update prim.BSShape to reference a hull of this shape. ret = GetReferenceToHull(prim,shapeCallback); @@ -836,14 +836,14 @@ public sealed class BSShapeCollection : IDisposable private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) { // level of detail based on size and type of the object - float lod = PhysicsScene.MeshLOD; + float lod = BSParam.MeshLOD; if (pbs.SculptEntry) - lod = PhysicsScene.SculptLOD; + lod = BSParam.SculptLOD; // Mega prims usually get more detail because one can interact with shape approximations at this size. float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); - if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) - lod = PhysicsScene.MeshMegaPrimLOD; + if (maxAxis > BSParam.MeshMegaPrimThreshold) + lod = BSParam.MeshMegaPrimLOD; retLod = lod; return pbs.GetMeshKey(size, lod); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs index 2b120d60db..07a9fd899c 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs @@ -93,7 +93,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys { m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID, m_mapInfo.minCoords, m_mapInfo.maxCoords, - m_mapInfo.heightMap, PhysicsScene.Params.terrainCollisionMargin); + m_mapInfo.heightMap, BSParam.TerrainCollisionMargin); // Create the terrain shape from the mapInfo m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr), @@ -110,9 +110,9 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys m_mapInfo.ID, centerPos, Quaternion.Identity)); // Set current terrain attributes - BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction); - BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction); - BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution); + BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainFriction); + BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainHitFraction); + BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, BSParam.TerrainRestitution); BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT); // Return the new terrain to the world of physical objects diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 3428b9c0fa..86ccfbbf33 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -135,7 +135,7 @@ public sealed class BSTerrainManager : IDisposable // 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, - PhysicsScene.Params.terrainCollisionMargin), + BSParam.TerrainCollisionMargin), BSPhysicsShapeType.SHAPE_GROUNDPLANE); m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, @@ -309,9 +309,9 @@ public sealed class BSTerrainManager : IDisposable { PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", LogHeader, PhysicsScene.RegionName, terrainRegionBase, - (BSTerrainPhys.TerrainImplementation)PhysicsScene.Params.terrainImplementation); + (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation); BSTerrainPhys newTerrainPhys = null; - switch ((int)PhysicsScene.Params.terrainImplementation) + switch ((int)BSParam.TerrainImplementation) { case (int)BSTerrainPhys.TerrainImplementation.Heightmap: newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id, @@ -324,8 +324,8 @@ public sealed class BSTerrainManager : IDisposable default: PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", LogHeader, - (int)PhysicsScene.Params.terrainImplementation, - PhysicsScene.Params.terrainImplementation, + (int)BSParam.TerrainImplementation, + BSParam.TerrainImplementation, PhysicsScene.RegionName, terrainRegionBase); break; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs index 6dc0d92857..061e232997 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs @@ -116,9 +116,9 @@ public sealed class BSTerrainMesh : BSTerrainPhys } // Set current terrain attributes - BulletSimAPI.SetFriction2(m_terrainBody.ptr, PhysicsScene.Params.terrainFriction); - BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, PhysicsScene.Params.terrainHitFraction); - BulletSimAPI.SetRestitution2(m_terrainBody.ptr, PhysicsScene.Params.terrainRestitution); + BulletSimAPI.SetFriction2(m_terrainBody.ptr, BSParam.TerrainFriction); + BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, BSParam.TerrainHitFraction); + BulletSimAPI.SetRestitution2(m_terrainBody.ptr, BSParam.TerrainRestitution); BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT); // Static objects are not very massive. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 962b5408f0..7857eaa411 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -141,6 +141,8 @@ public struct EntityProperties } // Format of this structure must match the definition in the C++ code +// NOTE: adding the X causes compile breaks if used. These are unused symbols +// that can be removed from both here and the unmanaged definition of this structure. [StructLayout(LayoutKind.Sequential)] public struct ConfigurationParameters { @@ -150,31 +152,31 @@ public struct ConfigurationParameters public float collisionMargin; public float gravity; - public float linearDamping; - public float angularDamping; - public float deactivationTime; - public float linearSleepingThreshold; - public float angularSleepingThreshold; - public float ccdMotionThreshold; - public float ccdSweptSphereRadius; - public float contactProcessingThreshold; + public float XlinearDamping; + public float XangularDamping; + public float XdeactivationTime; + public float XlinearSleepingThreshold; + public float XangularSleepingThreshold; + public float XccdMotionThreshold; + public float XccdSweptSphereRadius; + public float XcontactProcessingThreshold; - public float terrainImplementation; - public float terrainFriction; - public float terrainHitFraction; - public float terrainRestitution; - public float terrainCollisionMargin; + public float XterrainImplementation; + public float XterrainFriction; + public float XterrainHitFraction; + public float XterrainRestitution; + public float XterrainCollisionMargin; - public float avatarFriction; - public float avatarStandingFriction; - public float avatarDensity; - public float avatarRestitution; - public float avatarCapsuleWidth; - public float avatarCapsuleDepth; - public float avatarCapsuleHeight; - public float avatarContactProcessingThreshold; + public float XavatarFriction; + public float XavatarStandingFriction; + public float XavatarDensity; + public float XavatarRestitution; + public float XavatarCapsuleWidth; + public float XavatarCapsuleDepth; + public float XavatarCapsuleHeight; + public float XavatarContactProcessingThreshold; - public float vehicleAngularDamping; + public float XvehicleAngularDamping; public float maxPersistantManifoldPoolSize; public float maxCollisionAlgorithmPoolSize; @@ -185,14 +187,14 @@ public struct ConfigurationParameters public float shouldEnableFrictionCaching; public float numberOfSolverIterations; - public float linksetImplementation; - public float linkConstraintUseFrameOffset; - public float linkConstraintEnableTransMotor; - public float linkConstraintTransMotorMaxVel; - public float linkConstraintTransMotorMaxForce; - public float linkConstraintERP; - public float linkConstraintCFM; - public float linkConstraintSolverIterations; + public float XlinksetImplementation; + public float XlinkConstraintUseFrameOffset; + public float XlinkConstraintEnableTransMotor; + public float XlinkConstraintTransMotorMaxVel; + public float XlinkConstraintTransMotorMaxForce; + public float XlinkConstraintERP; + public float XlinkConstraintCFM; + public float XlinkConstraintSolverIterations; public float physicsLoggingFrames; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt index c084ab4e2b..9a7e965246 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt @@ -112,6 +112,9 @@ Test avatar walking up stairs. How does compare with SL. Debounce avatar contact so legs don't keep folding up when standing. Implement LSL physics controls. Like STATUS_ROTATE_X. Add border extensions to terrain to help region crossings and objects leaving region. +Use a different capsule shape for avatar when sitting + LL uses a pyrimidal shape scaled by the avatar's bounding box + http://wiki.secondlife.com/wiki/File:Avmeshforms.png Performance test with lots of avatars. Can BulletSim support a thousand? Optimize collisions in C++: only send up to the object subscribed to collisions.