Merge branch 'master' into careminster

avinationmerge
Melanie 2012-12-23 18:05:17 +00:00
commit a126097d6b
17 changed files with 964 additions and 908 deletions

View File

@ -299,6 +299,13 @@ namespace OpenSim.Framework
x;
}
// Inclusive, within range test (true if equal to the endpoints)
public static bool InRange<T>(T x, T min, T max)
where T : IComparable<T>
{
return x.CompareTo(max) <= 0 && x.CompareTo(min) >= 0;
}
public static uint GetNextXferID()
{
uint id = 0;

View File

@ -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);

View File

@ -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
{

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<PhysParameterEntry> entries = new List<PhysParameterEntry>();
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();
}
}
}
}

View File

@ -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<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>();
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<string, BSScene.PreStepAction> 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);
}
}
}

View File

@ -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<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>();
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<OMV.Vector3> m_accumulatedAngularForces = new List<OMV.Vector3>();
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

View File

@ -69,20 +69,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
// every tick so OpenSim will update its animation.
private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
// List of all the objects that have vehicle properties and should be called
// to update each physics step.
private List<BSPhysObject> m_vehicles = new List<BSPhysObject>();
// 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<TaintCallbackEntry> 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<TaintCallbackEntry> oldList;
lock (_taintLock)
{
oldList = _postStepOperations;
_postStepOperations = new List<TaintCallbackEntry>();
}
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<PhysParameterEntry> entries = new List<PhysParameterEntry>();
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<uint> objectIDs = new List<uint>();
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<uint>(PhysObjects.Keys);
TaintedUpdateParameter(parm, objectIDs, val);
break;
@ -1478,8 +888,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
List<uint> 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)
{

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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.

View File

@ -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;

View File

@ -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.