BulletSim: add parameters and API calls for setting ERP and CFM.

Set ERP and CFM in linkset constraints.
Reorder rebuilding of object bodies so they are not rebuilt everytime
   something is linked and unlinked.
0.7.4.1
Robert Adams 2012-07-31 09:23:05 -07:00
parent 6b1d12edcb
commit 50dbb9ffe4
6 changed files with 164 additions and 29 deletions

View File

@ -50,7 +50,8 @@ public class BSConstraint : IDisposable
m_body2 = obj2; m_body2 = obj2;
m_constraint = new BulletConstraint(BulletSimAPI.CreateConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr, m_constraint = new BulletConstraint(BulletSimAPI.CreateConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr,
frame1, frame1rot, frame1, frame1rot,
frame2, frame2rot)); frame2, frame2rot,
true /*useLinearReferenceFrameA*/, true /*disableCollisionsBetweenLinkedBodies*/));
m_enabled = true; m_enabled = true;
} }
@ -83,6 +84,15 @@ public class BSConstraint : IDisposable
return ret; return ret;
} }
public bool SetCFMAndERP(float cfm, float erp)
{
bool ret = false;
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
return ret;
}
public bool UseFrameOffset(bool useOffset) public bool UseFrameOffset(bool useOffset)
{ {
bool ret = false; bool ret = false;

View File

@ -83,7 +83,8 @@ public class BSConstraintCollection : IDisposable
return true; return true;
} }
// Get the constraint between two bodies. There can be only one the way we're using them. // Get the constraint between two bodies. There can be only one.
// Return 'true' if a constraint was found.
public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint) public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint)
{ {
bool found = false; bool found = false;
@ -105,6 +106,9 @@ public class BSConstraintCollection : IDisposable
return found; return found;
} }
// Remove any constraint between the passed bodies.
// Presumed there is only one such constraint possible.
// Return 'true' if a constraint was found and destroyed.
public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2) public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2)
{ {
// return BulletSimAPI.RemoveConstraint(m_world.ID, obj1.ID, obj2.ID); // return BulletSimAPI.RemoveConstraint(m_world.ID, obj1.ID, obj2.ID);
@ -125,6 +129,8 @@ public class BSConstraintCollection : IDisposable
return ret; return ret;
} }
// Remove all constraints that reference the passed body.
// Return 'true' if any constraints were destroyed.
public bool RemoveAndDestroyConstraint(BulletBody body1) public bool RemoveAndDestroyConstraint(BulletBody body1)
{ {
// return BulletSimAPI.RemoveConstraintByID(m_world.ID, obj.ID); // return BulletSimAPI.RemoveConstraintByID(m_world.ID, obj.ID);

View File

@ -117,10 +117,50 @@ public class BSLinkset
} }
// An existing linkset had one of its members rebuilt or something. // An existing linkset had one of its members rebuilt or something.
// Undo all the physical linking and rebuild the physical linkset. // Go through the linkset and rebuild the pointers to the bodies of the linkset members.
public bool RefreshLinkset(BSPrim requestor) public BSLinkset RefreshLinkset(BSPrim requestor)
{ {
return true; BSLinkset ret = requestor.Linkset;
lock (m_linksetActivityLock)
{
System.IntPtr aPtr = BulletSimAPI.GetBodyHandle2(m_scene.World.Ptr, m_linksetRoot.LocalID);
if (aPtr == System.IntPtr.Zero)
{
// That's odd. We can't find the root of the linkset.
// The linkset is somehow dead. The requestor is now a member of a linkset of one.
DetailLog("{0},RefreshLinkset.RemoveRoot,child={1}", m_linksetRoot.LocalID, m_linksetRoot.LocalID);
ret = RemoveMeFromLinkset(m_linksetRoot);
}
else
{
// Reconstruct the pointer to the body of the linkset root.
DetailLog("{0},RefreshLinkset.RebuildRoot,rootID={1},ptr={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, aPtr);
m_linksetRoot.Body = new BulletBody(m_linksetRoot.LocalID, aPtr);
List<BSPrim> toRemove = new List<BSPrim>();
foreach (BSPrim bsp in m_children)
{
aPtr = BulletSimAPI.GetBodyHandle2(m_scene.World.Ptr, bsp.LocalID);
if (aPtr == System.IntPtr.Zero)
{
toRemove.Add(bsp);
}
else
{
// Reconstruct the pointer to the body of the linkset root.
DetailLog("{0},RefreshLinkset.RebuildChild,rootID={1},ptr={2}", bsp.LocalID, m_linksetRoot.LocalID, aPtr);
bsp.Body = new BulletBody(bsp.LocalID, aPtr);
}
}
foreach (BSPrim bsp in toRemove)
{
RemoveChildFromLinkset(bsp);
}
}
}
return ret;
} }
@ -256,10 +296,13 @@ public class BSLinkset
DetailLog("{0},LinkAChildToMe,taint,root={1},child={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, childPrim.LocalID); DetailLog("{0},LinkAChildToMe,taint,root={1},child={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, childPrim.LocalID);
BSConstraint constrain = m_scene.Constraints.CreateConstraint( BSConstraint constrain = m_scene.Constraints.CreateConstraint(
m_scene.World, m_linksetRoot.Body, childPrim.Body, m_scene.World, m_linksetRoot.Body, childPrim.Body,
childRelativePosition, // childRelativePosition,
childRelativeRotation, // childRelativeRotation,
OMV.Vector3.Zero, OMV.Vector3.Zero,
OMV.Quaternion.Identity); OMV.Quaternion.Identity,
OMV.Vector3.Zero,
OMV.Quaternion.Identity
);
constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
@ -268,6 +311,7 @@ public class BSLinkset
constrain.TranslationalLimitMotor(m_scene.BoolNumeric(m_scene.Params.linkConstraintEnableTransMotor), constrain.TranslationalLimitMotor(m_scene.BoolNumeric(m_scene.Params.linkConstraintEnableTransMotor),
m_scene.Params.linkConstraintTransMotorMaxVel, m_scene.Params.linkConstraintTransMotorMaxVel,
m_scene.Params.linkConstraintTransMotorMaxForce); m_scene.Params.linkConstraintTransMotorMaxForce);
constrain.SetCFMAndERP(m_scene.Params.linkConstraintCFM, m_scene.Params.linkConstraintERP);
} }

View File

@ -103,7 +103,10 @@ public sealed class BSPrim : PhysicsActor
long _collidingGroundStep; long _collidingGroundStep;
private BulletBody m_body; private BulletBody m_body;
public BulletBody Body { get { return m_body; } } public BulletBody Body {
get { return m_body; }
set { m_body = value; }
}
private BSDynamics _vehicle; private BSDynamics _vehicle;
@ -477,9 +480,11 @@ public sealed class BSPrim : PhysicsActor
// Only called at taint time so it is save to call into Bullet. // Only called at taint time so it is save to call into Bullet.
private void SetObjectDynamic() private void SetObjectDynamic()
{ {
// m_log.DebugFormat("{0}: ID={1}, SetObjectDynamic: IsStatic={2}, IsSolid={3}", LogHeader, _localID, IsStatic, IsSolid); // RA: remove this for the moment.
// The problem is that dynamic objects are hulls so if we are becoming physical
RecreateGeomAndObject(); // the shape has to be checked and possibly built.
// Maybe a VerifyCorrectPhysicalShape() routine?
// RecreateGeomAndObject();
float mass = _mass; float mass = _mass;
// Bullet wants static objects have a mass of zero // Bullet wants static objects have a mass of zero
@ -971,21 +976,23 @@ public sealed class BSPrim : PhysicsActor
{ {
DetailLog("{0},CreateGeom,sphere", LocalID); DetailLog("{0},CreateGeom,sphere", LocalID);
_shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
ret = true;
// Bullet native objects are scaled by the Bullet engine so pass the size in // Bullet native objects are scaled by the Bullet engine so pass the size in
_scale = _size; _scale = _size;
// TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
ret = true;
} }
} }
} }
else else
{ {
// m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, size={2}", LogHeader, LocalID, _size); // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size);
if (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX) if (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX)
{ {
DetailLog("{0},CreateGeom,box", LocalID); DetailLog("{0},CreateGeom,box", LocalID);
_shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX;
ret = true;
_scale = _size; _scale = _size;
// TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
ret = true;
} }
} }
} }
@ -1203,11 +1210,9 @@ public sealed class BSPrim : PhysicsActor
FillShapeInfo(out shape); FillShapeInfo(out shape);
// m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type); // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type);
BulletSimAPI.CreateObject(_scene.WorldID, shape); BulletSimAPI.CreateObject(_scene.WorldID, shape);
// the CreateObject() may have recreated the rigid body. Make sure we have the latest. // the CreateObject() may have recreated the rigid body. Make sure we have the latest.
m_body.Ptr = BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID); m_body.Ptr = BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID);
// The root object could have been recreated. Make sure everything linksety is up to date.
_linkset.RefreshLinkset(this);
} }
// Copy prim's info into the BulletSim shape description structure // Copy prim's info into the BulletSim shape description structure
@ -1236,7 +1241,7 @@ public sealed class BSPrim : PhysicsActor
private void RecreateGeomAndObject() private void RecreateGeomAndObject()
{ {
// m_log.DebugFormat("{0}: RecreateGeomAndObject. lID={1}", LogHeader, _localID); // m_log.DebugFormat("{0}: RecreateGeomAndObject. lID={1}", LogHeader, _localID);
CreateGeom(true); if (CreateGeom(true))
CreateObject(); CreateObject();
return; return;
} }
@ -1322,6 +1327,15 @@ public sealed class BSPrim : PhysicsActor
base.RequestPhysicsterseUpdate(); base.RequestPhysicsterseUpdate();
} }
/*
else
{
// For debugging, we can also report the movement of children
DetailLog("{0},UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
entprop.Acceleration, entprop.RotationalVelocity);
}
*/
} }
// I've collided with something // I've collided with something

View File

@ -315,6 +315,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying) public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
{ {
// m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
if (!m_initialized) return null;
BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
lock (m_avatars) m_avatars.Add(localID, actor); lock (m_avatars) m_avatars.Add(localID, actor);
return actor; return actor;
@ -323,6 +326,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override void RemoveAvatar(PhysicsActor actor) public override void RemoveAvatar(PhysicsActor actor)
{ {
// m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
if (!m_initialized) return;
BSCharacter bsactor = actor as BSCharacter; BSCharacter bsactor = actor as BSCharacter;
if (bsactor != null) if (bsactor != null)
{ {
@ -341,6 +347,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override void RemovePrim(PhysicsActor prim) public override void RemovePrim(PhysicsActor prim)
{ {
if (!m_initialized) return;
BSPrim bsprim = prim as BSPrim; BSPrim bsprim = prim as BSPrim;
if (bsprim != null) if (bsprim != null)
{ {
@ -366,6 +374,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
Vector3 size, Quaternion rotation, bool isPhysical, uint localID) Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
{ {
// m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
if (!m_initialized) return null;
BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
lock (m_prims) m_prims.Add(localID, prim); lock (m_prims) m_prims.Add(localID, prim);
return prim; return prim;
@ -807,6 +818,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// List of all of the externally visible parameters. // List of all of the externally visible parameters.
// For each parameter, this table maps a text name to getter and setters. // 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.
// To add a new variable, 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: // A ParameterDefn() takes the following parameters:
// -- the text name of the parameter. This is used for console input and ini file. // -- 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 short text description of the parameter. This shows up in the console listing.
@ -815,7 +832,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// -- a delegate for getting the value as a float // -- a delegate for getting the value as a float
// -- a delegate for setting the value from a float // -- a delegate for setting the value from a float
// //
// To add a new variable, it is best to find an existing definition and copy it. // The single letter parameters for the delegates are:
// s = BSScene
// 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 = private ParameterDefn[] ParameterDefinitions =
{ {
new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
@ -1048,6 +1070,16 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
(s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=none, 1=all. Default=0",
0.0f,
(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.2f,
(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("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)",
0f, 0f,

View File

@ -66,13 +66,14 @@ public struct ShapeData
{ {
public enum PhysicsShapeType public enum PhysicsShapeType
{ {
SHAPE_AVATAR = 0, SHAPE_UNKNOWN = 0,
SHAPE_BOX = 1, SHAPE_AVATAR = 1,
SHAPE_CONE = 2, SHAPE_BOX = 2,
SHAPE_CYLINDER = 3, SHAPE_CONE = 3,
SHAPE_SPHERE = 4, SHAPE_CYLINDER = 4,
SHAPE_MESH = 5, SHAPE_SPHERE = 5,
SHAPE_HULL = 6 SHAPE_MESH = 6,
SHAPE_HULL = 7
}; };
public uint ID; public uint ID;
public PhysicsShapeType Type; public PhysicsShapeType Type;
@ -168,6 +169,8 @@ public struct ConfigurationParameters
public float linkConstraintEnableTransMotor; public float linkConstraintEnableTransMotor;
public float linkConstraintTransMotorMaxVel; public float linkConstraintTransMotorMaxVel;
public float linkConstraintTransMotorMaxForce; public float linkConstraintTransMotorMaxForce;
public float linkConstraintERP;
public float linkConstraintCFM;
public const float numericTrue = 1f; public const float numericTrue = 1f;
public const float numericFalse = 0f; public const float numericFalse = 0f;
@ -189,6 +192,28 @@ public enum CollisionFlags : uint
PHYSICAL_OBJECT = 1 << 12, PHYSICAL_OBJECT = 1 << 12,
}; };
// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0
// ERP controls amount of correction per tick. Usable range=0.1..0.8. Default=0.2.
public enum ConstraintParams : int
{
BT_CONSTRAINT_ERP = 1, // this one is not used in Bullet as of 20120730
BT_CONSTRAINT_STOP_ERP,
BT_CONSTRAINT_CFM,
BT_CONSTRAINT_STOP_CFM,
};
public enum ConstraintParamAxis : int
{
AXIS_LINEAR_X = 0,
AXIS_LINEAR_Y,
AXIS_LINEAR_Z,
AXIS_ANGULAR_X,
AXIS_ANGULAR_Y,
AXIS_ANGULAR_Z,
AXIS_LINEAR_ALL = 20, // these last three added by BulletSim so we don't have to do zillions of calls
AXIS_ANGULAR_ALL,
AXIS_ALL
};
// =============================================================================== // ===============================================================================
static class BulletSimAPI { static class BulletSimAPI {
@ -380,7 +405,8 @@ public static extern IntPtr CreateObject2(IntPtr sim, ShapeData shapeData);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateConstraint2(IntPtr sim, IntPtr obj1, IntPtr obj2, public static extern IntPtr CreateConstraint2(IntPtr sim, IntPtr obj1, IntPtr obj2,
Vector3 frame1loc, Quaternion frame1rot, Vector3 frame1loc, Quaternion frame1rot,
Vector3 frame2loc, Quaternion frame2rot); Vector3 frame2loc, Quaternion frame2rot,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetLinearLimits2(IntPtr constrain, Vector3 low, Vector3 hi); public static extern bool SetLinearLimits2(IntPtr constrain, Vector3 low, Vector3 hi);
@ -397,6 +423,9 @@ public static extern bool TranslationalLimitMotor2(IntPtr constrain, float enabl
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CalculateTransforms2(IntPtr constrain); public static extern bool CalculateTransforms2(IntPtr constrain);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetConstraintParam2(IntPtr constrain, ConstraintParams paramIndex, float value, ConstraintParamAxis axis);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DestroyConstraint2(IntPtr sim, IntPtr constrain); public static extern bool DestroyConstraint2(IntPtr sim, IntPtr constrain);