Merge branch 'master' into careminster

avinationmerge
Melanie 2012-11-04 22:44:42 +00:00
commit c623e35805
22 changed files with 1624 additions and 847 deletions

View File

@ -182,12 +182,14 @@ what it is today.
This software uses components from the following developers: This software uses components from the following developers:
* Sleepycat Software (Berkeley DB) * Sleepycat Software (Berkeley DB)
* Aurora-Sim (http://aurora-sim.org)
* SQLite (Public Domain) * SQLite (Public Domain)
* XmlRpcCS (http://xmlrpccs.sf.net/) * XmlRpcCS (http://xmlrpccs.sf.net/)
* MySQL, Inc. (MySQL Connector/NET) * MySQL, Inc. (MySQL Connector/NET)
* NUnit (http://www.nunit.org) * NUnit (http://www.nunit.org)
* AGEIA Inc. (PhysX) * AGEIA Inc. (PhysX)
* Russel L. Smith (ODE) * Russel L. Smith (ODE)
* Erwin Coumans (Bullet)
* Prebuild (http://sourceforge.net/projects/dnpb/) * Prebuild (http://sourceforge.net/projects/dnpb/)
* LibOpenMetaverse (http://lib.openmetaverse.org/) * LibOpenMetaverse (http://lib.openmetaverse.org/)
* DotNetOpenMail v0.5.8b (http://dotnetopenmail.sourceforge.net) * DotNetOpenMail v0.5.8b (http://dotnetopenmail.sourceforge.net)

View File

@ -34,7 +34,7 @@ using OpenSim.Region.Physics.Manager;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BSCharacter : BSPhysObject public sealed class BSCharacter : BSPhysObject
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[BULLETS CHAR]"; private static readonly string LogHeader = "[BULLETS CHAR]";
@ -97,28 +97,14 @@ public class BSCharacter : BSPhysObject
// set _avatarVolume and _mass based on capsule size, _density and Scale // set _avatarVolume and _mass based on capsule size, _density and Scale
ComputeAvatarVolumeAndMass(); ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
LocalID, _size, Scale, _avatarDensity, _avatarVolume, MassRaw); LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
ShapeData shapeData = new ShapeData();
shapeData.ID = LocalID;
shapeData.Type = ShapeData.PhysicsShapeType.SHAPE_AVATAR;
shapeData.Position = _position;
shapeData.Rotation = _orientation;
shapeData.Velocity = _velocity;
shapeData.Size = Scale; // capsule is a native shape but scale is not just <1,1,1>
shapeData.Scale = Scale;
shapeData.Mass = _mass;
shapeData.Buoyancy = _buoyancy;
shapeData.Static = ShapeData.numericFalse;
shapeData.Friction = PhysicsScene.Params.avatarStandingFriction;
shapeData.Restitution = PhysicsScene.Params.avatarRestitution;
// do actual create at taint time // do actual create at taint time
PhysicsScene.TaintedObject("BSCharacter.create", delegate() PhysicsScene.TaintedObject("BSCharacter.create", delegate()
{ {
DetailLog("{0},BSCharacter.create,taint", LocalID); DetailLog("{0},BSCharacter.create,taint", LocalID);
// New body and shape into BSBody and BSShape // New body and shape into BSBody and BSShape
PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, shapeData, null, null, null); PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, null, null);
SetPhysicalProperties(); SetPhysicalProperties();
}); });
@ -131,46 +117,45 @@ public class BSCharacter : BSPhysObject
DetailLog("{0},BSCharacter.Destroy", LocalID); DetailLog("{0},BSCharacter.Destroy", LocalID);
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
{ {
PhysicsScene.Shapes.DereferenceBody(BSBody, true, null); PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
PhysicsScene.Shapes.DereferenceShape(BSShape, true, null); PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
}); });
} }
private void SetPhysicalProperties() private void SetPhysicalProperties()
{ {
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, BSBody.ptr); BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
ZeroMotion(); ZeroMotion();
ForcePosition = _position; ForcePosition = _position;
// Set the velocity and compute the proper friction // Set the velocity and compute the proper friction
ForceVelocity = _velocity; ForceVelocity = _velocity;
BulletSimAPI.SetRestitution2(BSBody.ptr, PhysicsScene.Params.avatarRestitution); BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.avatarRestitution);
BulletSimAPI.SetMargin2(BSShape.ptr, PhysicsScene.Params.collisionMargin); BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin);
BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale); BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold); BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
if (PhysicsScene.Params.ccdMotionThreshold > 0f) if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{ {
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold); BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
} }
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw); UpdatePhysicalMassProperties(RawMass);
BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia);
// Make so capsule does not fall over // Make so capsule does not fall over
BulletSimAPI.SetAngularFactorV2(BSBody.ptr, OMV.Vector3.Zero); BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero);
BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT); BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, BSBody.ptr); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
// BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG); // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_DEACTIVATION); BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_DEACTIVATION);
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, BSBody.ptr); BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
// Do this after the object has been added to the world // Do this after the object has been added to the world
BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr, BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr,
(uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarFilter,
(uint)CollisionFilterGroups.AvatarMask); (uint)CollisionFilterGroups.AvatarMask);
} }
@ -196,13 +181,12 @@ public class BSCharacter : BSPhysObject
ComputeAvatarScale(_size); ComputeAvatarScale(_size);
ComputeAvatarVolumeAndMass(); ComputeAvatarVolumeAndMass();
DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}", DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}",
LocalID, Scale, _avatarDensity, _avatarVolume, MassRaw); LocalID, Scale, _avatarDensity, _avatarVolume, RawMass);
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
{ {
BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale); BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw); UpdatePhysicalMassProperties(RawMass);
BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia);
}); });
} }
@ -214,6 +198,11 @@ public class BSCharacter : BSPhysObject
{ {
set { BaseShape = value; } set { BaseShape = value; }
} }
// I want the physics engine to make an avatar capsule
public override ShapeData.PhysicsShapeType PreferredPhysicalShape
{
get {return ShapeData.PhysicsShapeType.SHAPE_AVATAR; }
}
public override bool Grabbed { public override bool Grabbed {
set { _grabbed = value; } set { _grabbed = value; }
@ -236,16 +225,22 @@ public class BSCharacter : BSPhysObject
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
// Zero some other properties directly into the physics engine // Zero some other properties directly into the physics engine
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, OMV.Vector3.Zero); BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
BulletSimAPI.SetAngularVelocity2(BSBody.ptr, OMV.Vector3.Zero); BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
BulletSimAPI.SetInterpolationVelocity2(BSBody.ptr, OMV.Vector3.Zero, OMV.Vector3.Zero); BulletSimAPI.SetInterpolationVelocity2(PhysBody.ptr, OMV.Vector3.Zero, OMV.Vector3.Zero);
BulletSimAPI.ClearForces2(BSBody.ptr); BulletSimAPI.ClearForces2(PhysBody.ptr);
} }
public override void LockAngularMotion(OMV.Vector3 axis) { return; } public override void LockAngularMotion(OMV.Vector3 axis) { return; }
public override OMV.Vector3 RawPosition
{
get { return _position; }
set { _position = value; }
}
public override OMV.Vector3 Position { public override OMV.Vector3 Position {
get { get {
// Don't refetch the position because this function is called a zillion times
// _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID); // _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
return _position; return _position;
} }
@ -256,19 +251,19 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
{ {
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
}); });
} }
} }
public override OMV.Vector3 ForcePosition { public override OMV.Vector3 ForcePosition {
get { get {
_position = BulletSimAPI.GetPosition2(BSBody.ptr); _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
return _position; return _position;
} }
set { set {
_position = value; _position = value;
PositionSanityCheck(); PositionSanityCheck();
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
} }
} }
@ -312,15 +307,11 @@ public class BSCharacter : BSPhysObject
{ {
// The new position value must be pushed into the physics engine but we can't // The new position value must be pushed into the physics engine but we can't
// just assign to "Position" because of potential call loops. // just assign to "Position" because of potential call loops.
BSScene.TaintCallback sanityOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
{ {
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
}; });
if (inTaintTime)
sanityOperation();
else
PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation);
ret = true; ret = true;
} }
return ret; return ret;
@ -329,7 +320,14 @@ public class BSCharacter : BSPhysObject
public override float Mass { get { return _mass; } } public override float Mass { get { return _mass; } }
// used when we only want this prim's mass and not the linkset thing // used when we only want this prim's mass and not the linkset thing
public override float MassRaw { get {return _mass; } } public override float RawMass {
get {return _mass; }
}
public override void UpdatePhysicalMassProperties(float physMass)
{
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
}
public override OMV.Vector3 Force { public override OMV.Vector3 Force {
get { return _force; } get { return _force; }
@ -339,7 +337,7 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
{ {
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
BulletSimAPI.SetObjectForce2(BSBody.ptr, _force); BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
}); });
} }
} }
@ -378,7 +376,7 @@ public class BSCharacter : BSPhysObject
if (_currentFriction != PhysicsScene.Params.avatarStandingFriction) if (_currentFriction != PhysicsScene.Params.avatarStandingFriction)
{ {
_currentFriction = PhysicsScene.Params.avatarStandingFriction; _currentFriction = PhysicsScene.Params.avatarStandingFriction;
BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction); BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
} }
} }
else else
@ -386,15 +384,15 @@ public class BSCharacter : BSPhysObject
if (_currentFriction != PhysicsScene.Params.avatarFriction) if (_currentFriction != PhysicsScene.Params.avatarFriction)
{ {
_currentFriction = PhysicsScene.Params.avatarFriction; _currentFriction = PhysicsScene.Params.avatarFriction;
BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction); BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
} }
} }
_velocity = value; _velocity = value;
// Remember the set velocity so we can suppress the reduction by friction, ... // Remember the set velocity so we can suppress the reduction by friction, ...
_appliedVelocity = value; _appliedVelocity = value;
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity); BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
BulletSimAPI.Activate2(BSBody.ptr, true); BulletSimAPI.Activate2(PhysBody.ptr, true);
} }
} }
public override OMV.Vector3 Torque { public override OMV.Vector3 Torque {
@ -411,6 +409,11 @@ public class BSCharacter : BSPhysObject
get { return _acceleration; } get { return _acceleration; }
set { _acceleration = value; } set { _acceleration = value; }
} }
public override OMV.Quaternion RawOrientation
{
get { return _orientation; }
set { _orientation = value; }
}
public override OMV.Quaternion Orientation { public override OMV.Quaternion Orientation {
get { return _orientation; } get { return _orientation; }
set { set {
@ -419,7 +422,7 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
{ {
// _position = BulletSimAPI.GetPosition2(BSBody.ptr); // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
}); });
} }
} }
@ -428,13 +431,13 @@ public class BSCharacter : BSPhysObject
{ {
get get
{ {
_orientation = BulletSimAPI.GetOrientation2(BSBody.ptr); _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
return _orientation; return _orientation;
} }
set set
{ {
_orientation = value; _orientation = value;
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
} }
} }
public override int PhysicsActorType { public override int PhysicsActorType {
@ -493,9 +496,9 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
{ {
if (_floatOnWater) if (_floatOnWater)
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
else else
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
}); });
} }
} }
@ -528,7 +531,7 @@ public class BSCharacter : BSPhysObject
DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
// Buoyancy is faked by changing the gravity applied to the object // Buoyancy is faked by changing the gravity applied to the object
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav)); BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
} }
} }
@ -574,7 +577,7 @@ public class BSCharacter : BSPhysObject
PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate() PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
{ {
DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force); DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
BulletSimAPI.SetObjectForce2(BSBody.ptr, _force); BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
}); });
} }
else else
@ -642,7 +645,7 @@ public class BSCharacter : BSPhysObject
// That's just the way they are defined. // That's just the way they are defined.
OMV.Vector3 avVel = new OMV.Vector3(_appliedVelocity.X, _appliedVelocity.Y, entprop.Velocity.Z); OMV.Vector3 avVel = new OMV.Vector3(_appliedVelocity.X, _appliedVelocity.Y, entprop.Velocity.Z);
_velocity = avVel; _velocity = avVel;
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, avVel); BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, avVel);
} }
// Tell the linkset about value changes // Tell the linkset about value changes

View File

@ -42,6 +42,12 @@ public abstract class BSConstraint : IDisposable
protected BulletConstraint m_constraint; protected BulletConstraint m_constraint;
protected bool m_enabled = false; protected bool m_enabled = false;
public BulletBody Body1 { get { return m_body1; } }
public BulletBody Body2 { get { return m_body2; } }
public BulletConstraint Constraint { get { return m_constraint; } }
public abstract ConstraintType Type { get; }
public bool IsEnabled { get { return m_enabled; } }
public BSConstraint() public BSConstraint()
{ {
} }
@ -64,12 +70,6 @@ public abstract class BSConstraint : IDisposable
} }
} }
public BulletBody Body1 { get { return m_body1; } }
public BulletBody Body2 { get { return m_body2; } }
public BulletConstraint Constraint { get { return m_constraint; } }
public abstract ConstraintType Type { get; }
public virtual bool SetLinearLimits(Vector3 low, Vector3 high) public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
{ {
bool ret = false; bool ret = false;

View File

@ -32,14 +32,14 @@ using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BS6DofConstraint : BSConstraint public sealed class BSConstraint6Dof : BSConstraint
{ {
private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]"; private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } } public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
// Create a btGeneric6DofConstraint // Create a btGeneric6DofConstraint
public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
Vector3 frame1, Quaternion frame1rot, Vector3 frame1, Quaternion frame1rot,
Vector3 frame2, Quaternion frame2rot, Vector3 frame2, Quaternion frame2rot,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
@ -58,7 +58,7 @@ public class BS6DofConstraint : BSConstraint
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
} }
public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
Vector3 joinPoint, Vector3 joinPoint,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
{ {
@ -71,8 +71,7 @@ public class BS6DofConstraint : BSConstraint
BSScene.DetailLogZero, world.worldID, BSScene.DetailLogZero, world.worldID,
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
"[BULLETSIM 6DOF CONSTRAINT]", world.worldID, LogHeader, world.worldID, obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
m_enabled = false; m_enabled = false;
} }
else else
@ -135,7 +134,11 @@ public class BS6DofConstraint : BSConstraint
bool ret = false; bool ret = false;
float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
if (m_enabled) if (m_enabled)
{
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce); ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
m_world.physicsScene.DetailLog("{0},BS6DOFConstraint,TransLimitMotor,enable={1},vel={2},maxForce={3}",
BSScene.DetailLogZero, enable, targetVelocity, maxMotorForce);
}
return ret; return ret;
} }

View File

@ -33,7 +33,7 @@ using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BSConstraintCollection : IDisposable public sealed class BSConstraintCollection : IDisposable
{ {
// private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); // private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
// private static readonly string LogHeader = "[CONSTRAINT COLLECTION]"; // private static readonly string LogHeader = "[CONSTRAINT COLLECTION]";
@ -143,8 +143,6 @@ public class BSConstraintCollection : IDisposable
// Return 'true' if any constraints were destroyed. // 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);
List<BSConstraint> toRemove = new List<BSConstraint>(); List<BSConstraint> toRemove = new List<BSConstraint>();
uint lookingID = body1.ID; uint lookingID = body1.ID;
lock (m_constraints) lock (m_constraints)

View File

@ -32,11 +32,11 @@ using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
class BSHingeConstraint : BSConstraint public sealed class BSConstraintHinge : BSConstraint
{ {
public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } } public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, public BSConstraintHinge(BulletSim world, BulletBody obj1, BulletBody obj2,
Vector3 pivotInA, Vector3 pivotInB, Vector3 pivotInA, Vector3 pivotInB,
Vector3 axisInA, Vector3 axisInB, Vector3 axisInA, Vector3 axisInB,
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)

View File

@ -52,7 +52,7 @@ using OpenSim.Region.Physics.Manager;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BSDynamics public sealed class BSDynamics
{ {
private BSScene PhysicsScene { get; set; } private BSScene PhysicsScene { get; set; }
// the prim this dynamic controller belongs to // the prim this dynamic controller belongs to
@ -72,8 +72,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// LIMIT_ROLL_ONLY // LIMIT_ROLL_ONLY
private Vector3 m_BlockingEndPoint = Vector3.Zero; private Vector3 m_BlockingEndPoint = Vector3.Zero;
private Quaternion m_RollreferenceFrame = Quaternion.Identity; private Quaternion m_RollreferenceFrame = Quaternion.Identity;
private Quaternion m_referenceFrame = Quaternion.Identity;
// Linear properties // Linear properties
private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center
private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body
private Vector3 m_linearFrictionTimescale = Vector3.Zero; private Vector3 m_linearFrictionTimescale = Vector3.Zero;
@ -86,7 +89,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
//Angular properties //Angular properties
private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
private int m_angularMotorApply = 0; // application frame counter // private int m_angularMotorApply = 0; // application frame counter
private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity
private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate
private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
@ -95,19 +98,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin
private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
//Deflection properties //Deflection properties
// private float m_angularDeflectionEfficiency = 0; private float m_angularDeflectionEfficiency = 0;
// private float m_angularDeflectionTimescale = 0; private float m_angularDeflectionTimescale = 0;
// private float m_linearDeflectionEfficiency = 0; private float m_linearDeflectionEfficiency = 0;
// private float m_linearDeflectionTimescale = 0; private float m_linearDeflectionTimescale = 0;
//Banking properties //Banking properties
// private float m_bankingEfficiency = 0; private float m_bankingEfficiency = 0;
// private float m_bankingMix = 0; private float m_bankingMix = 0;
// private float m_bankingTimescale = 0; private float m_bankingTimescale = 0;
//Hover and Buoyancy properties //Hover and Buoyancy properties
private float m_VhoverHeight = 0f; private float m_VhoverHeight = 0f;
// private float m_VhoverEfficiency = 0f; private float m_VhoverEfficiency = 0f;
private float m_VhoverTimescale = 0f; private float m_VhoverTimescale = 0f;
private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height
private float m_VehicleBuoyancy = 0f; //KF: m_VehicleBuoyancy is set by VEHICLE_BUOYANCY for a vehicle. private float m_VehicleBuoyancy = 0f; //KF: m_VehicleBuoyancy is set by VEHICLE_BUOYANCY for a vehicle.
@ -138,10 +141,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
switch (pParam) switch (pParam)
{ {
case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY: case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY:
// m_angularDeflectionEfficiency = Math.Max(pValue, 0.01f); m_angularDeflectionEfficiency = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.ANGULAR_DEFLECTION_TIMESCALE: case Vehicle.ANGULAR_DEFLECTION_TIMESCALE:
// m_angularDeflectionTimescale = Math.Max(pValue, 0.01f); m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f); m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f);
@ -150,20 +153,20 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_angularMotorTimescale = Math.Max(pValue, 0.01f); m_angularMotorTimescale = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.BANKING_EFFICIENCY: case Vehicle.BANKING_EFFICIENCY:
// m_bankingEfficiency = Math.Max(pValue, 0.01f); m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f));
break; break;
case Vehicle.BANKING_MIX: case Vehicle.BANKING_MIX:
// m_bankingMix = Math.Max(pValue, 0.01f); m_bankingMix = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.BANKING_TIMESCALE: case Vehicle.BANKING_TIMESCALE:
// m_bankingTimescale = Math.Max(pValue, 0.01f); m_bankingTimescale = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.BUOYANCY: case Vehicle.BUOYANCY:
m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f)); m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f));
break; break;
// case Vehicle.HOVER_EFFICIENCY: case Vehicle.HOVER_EFFICIENCY:
// m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f)); m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f));
// break; break;
case Vehicle.HOVER_HEIGHT: case Vehicle.HOVER_HEIGHT:
m_VhoverHeight = pValue; m_VhoverHeight = pValue;
break; break;
@ -171,10 +174,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_VhoverTimescale = Math.Max(pValue, 0.01f); m_VhoverTimescale = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.LINEAR_DEFLECTION_EFFICIENCY: case Vehicle.LINEAR_DEFLECTION_EFFICIENCY:
// m_linearDeflectionEfficiency = Math.Max(pValue, 0.01f); m_linearDeflectionEfficiency = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.LINEAR_DEFLECTION_TIMESCALE: case Vehicle.LINEAR_DEFLECTION_TIMESCALE:
// m_linearDeflectionTimescale = Math.Max(pValue, 0.01f); m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
break; break;
case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f); m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f);
@ -196,7 +199,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
break; break;
case Vehicle.ANGULAR_MOTOR_DIRECTION: case Vehicle.ANGULAR_MOTOR_DIRECTION:
m_angularMotorDirection = new Vector3(pValue, pValue, pValue); m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
m_angularMotorApply = 10; // m_angularMotorApply = 100;
break; break;
case Vehicle.LINEAR_FRICTION_TIMESCALE: case Vehicle.LINEAR_FRICTION_TIMESCALE:
m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
@ -206,7 +209,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue); m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue);
break; break;
case Vehicle.LINEAR_MOTOR_OFFSET: case Vehicle.LINEAR_MOTOR_OFFSET:
// m_linearMotorOffset = new Vector3(pValue, pValue, pValue); m_linearMotorOffset = new Vector3(pValue, pValue, pValue);
break; break;
} }
@ -221,15 +224,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
break; break;
case Vehicle.ANGULAR_MOTOR_DIRECTION: case Vehicle.ANGULAR_MOTOR_DIRECTION:
m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
// Limit requested angular speed to 2 rps= 4 pi rads/sec // Limit requested angular speed to 2 rps= 4 pi rads/sec
if (m_angularMotorDirection.X > 12.56f) m_angularMotorDirection.X = 12.56f; pValue.X = Math.Max(-12.56f, Math.Min(pValue.X, 12.56f));
if (m_angularMotorDirection.X < - 12.56f) m_angularMotorDirection.X = - 12.56f; pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f));
if (m_angularMotorDirection.Y > 12.56f) m_angularMotorDirection.Y = 12.56f; pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f));
if (m_angularMotorDirection.Y < - 12.56f) m_angularMotorDirection.Y = - 12.56f; m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
if (m_angularMotorDirection.Z > 12.56f) m_angularMotorDirection.Z = 12.56f; // m_angularMotorApply = 100;
if (m_angularMotorDirection.Z < - 12.56f) m_angularMotorDirection.Z = - 12.56f;
m_angularMotorApply = 10;
break; break;
case Vehicle.LINEAR_FRICTION_TIMESCALE: case Vehicle.LINEAR_FRICTION_TIMESCALE:
m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
@ -239,7 +239,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z);
break; break;
case Vehicle.LINEAR_MOTOR_OFFSET: case Vehicle.LINEAR_MOTOR_OFFSET:
// m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z);
break; break;
case Vehicle.BLOCK_EXIT: case Vehicle.BLOCK_EXIT:
m_BlockingEndPoint = new Vector3(pValue.X, pValue.Y, pValue.Z); m_BlockingEndPoint = new Vector3(pValue.X, pValue.Y, pValue.Z);
@ -253,7 +253,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
switch (pParam) switch (pParam)
{ {
case Vehicle.REFERENCE_FRAME: case Vehicle.REFERENCE_FRAME:
// m_referenceFrame = pValue; m_referenceFrame = pValue;
break; break;
case Vehicle.ROLL_FRAME: case Vehicle.ROLL_FRAME:
m_RollreferenceFrame = pValue; m_RollreferenceFrame = pValue;
@ -265,21 +265,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", Prim.LocalID, pParam, remove); VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", Prim.LocalID, pParam, remove);
VehicleFlag parm = (VehicleFlag)pParam; VehicleFlag parm = (VehicleFlag)pParam;
if (remove) if (pParam == -1)
m_flags = (VehicleFlag)0;
else
{ {
if (pParam == -1) if (remove)
{
m_flags = (VehicleFlag)0;
}
else
{
m_flags &= ~parm; m_flags &= ~parm;
} else
m_flags |= parm;
} }
else { }
m_flags |= parm;
}
}//end ProcessVehicleFlags
internal void ProcessTypeChange(Vehicle pType) internal void ProcessTypeChange(Vehicle pType)
{ {
@ -288,99 +283,142 @@ namespace OpenSim.Region.Physics.BulletSPlugin
Type = pType; Type = pType;
switch (pType) switch (pType)
{ {
case Vehicle.TYPE_NONE: case Vehicle.TYPE_NONE:
m_linearFrictionTimescale = new Vector3(0, 0, 0);
m_angularFrictionTimescale = new Vector3(0, 0, 0);
m_linearMotorDirection = Vector3.Zero; m_linearMotorDirection = Vector3.Zero;
m_linearMotorTimescale = 0; m_linearMotorTimescale = 0;
m_linearMotorDecayTimescale = 0; m_linearMotorDecayTimescale = 0;
m_linearFrictionTimescale = new Vector3(0, 0, 0);
m_angularMotorDirection = Vector3.Zero; m_angularMotorDirection = Vector3.Zero;
m_angularMotorTimescale = 0;
m_angularMotorDecayTimescale = 0; m_angularMotorDecayTimescale = 0;
m_angularMotorTimescale = 0;
m_angularFrictionTimescale = new Vector3(0, 0, 0);
m_VhoverHeight = 0; m_VhoverHeight = 0;
m_VhoverEfficiency = 0;
m_VhoverTimescale = 0; m_VhoverTimescale = 0;
m_VehicleBuoyancy = 0; m_VehicleBuoyancy = 0;
m_linearDeflectionEfficiency = 1;
m_linearDeflectionTimescale = 1;
m_angularDeflectionEfficiency = 0;
m_angularDeflectionTimescale = 1000;
m_verticalAttractionEfficiency = 0;
m_verticalAttractionTimescale = 0;
m_bankingEfficiency = 0;
m_bankingTimescale = 1000;
m_bankingMix = 1;
m_referenceFrame = Quaternion.Identity;
m_flags = (VehicleFlag)0; m_flags = (VehicleFlag)0;
break; break;
case Vehicle.TYPE_SLED: case Vehicle.TYPE_SLED:
m_linearFrictionTimescale = new Vector3(30, 1, 1000);
m_angularFrictionTimescale = new Vector3(1000, 1000, 1000);
m_linearMotorDirection = Vector3.Zero; m_linearMotorDirection = Vector3.Zero;
m_linearMotorTimescale = 1000; m_linearMotorTimescale = 1000;
m_linearMotorDecayTimescale = 120; m_linearMotorDecayTimescale = 120;
m_linearFrictionTimescale = new Vector3(30, 1, 1000);
m_angularMotorDirection = Vector3.Zero; m_angularMotorDirection = Vector3.Zero;
m_angularMotorTimescale = 1000; m_angularMotorTimescale = 1000;
m_angularMotorDecayTimescale = 120; m_angularMotorDecayTimescale = 120;
m_angularFrictionTimescale = new Vector3(1000, 1000, 1000);
m_VhoverHeight = 0; m_VhoverHeight = 0;
// m_VhoverEfficiency = 1; m_VhoverEfficiency = 10; // TODO: this looks wrong!!
m_VhoverTimescale = 10; m_VhoverTimescale = 10;
m_VehicleBuoyancy = 0; m_VehicleBuoyancy = 0;
// m_linearDeflectionEfficiency = 1;
// m_linearDeflectionTimescale = 1; m_linearDeflectionEfficiency = 1;
// m_angularDeflectionEfficiency = 1; m_linearDeflectionTimescale = 1;
// m_angularDeflectionTimescale = 1000;
// m_bankingEfficiency = 0; m_angularDeflectionEfficiency = 1;
// m_bankingMix = 1; m_angularDeflectionTimescale = 1000;
// m_bankingTimescale = 10;
// m_referenceFrame = Quaternion.Identity; m_verticalAttractionEfficiency = 0;
m_verticalAttractionTimescale = 0;
m_bankingEfficiency = 0;
m_bankingTimescale = 10;
m_bankingMix = 1;
m_referenceFrame = Quaternion.Identity;
m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP);
m_flags &= m_flags &=
~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY |
VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY);
break; break;
case Vehicle.TYPE_CAR: case Vehicle.TYPE_CAR:
m_linearFrictionTimescale = new Vector3(100, 2, 1000);
m_angularFrictionTimescale = new Vector3(1000, 1000, 1000);
m_linearMotorDirection = Vector3.Zero; m_linearMotorDirection = Vector3.Zero;
m_linearMotorTimescale = 1; m_linearMotorTimescale = 1;
m_linearMotorDecayTimescale = 60; m_linearMotorDecayTimescale = 60;
m_linearFrictionTimescale = new Vector3(100, 2, 1000);
m_angularMotorDirection = Vector3.Zero; m_angularMotorDirection = Vector3.Zero;
m_angularMotorTimescale = 1; m_angularMotorTimescale = 1;
m_angularMotorDecayTimescale = 0.8f; m_angularMotorDecayTimescale = 0.8f;
m_angularFrictionTimescale = new Vector3(1000, 1000, 1000);
m_VhoverHeight = 0; m_VhoverHeight = 0;
// m_VhoverEfficiency = 0; m_VhoverEfficiency = 0;
m_VhoverTimescale = 1000; m_VhoverTimescale = 1000;
m_VehicleBuoyancy = 0; m_VehicleBuoyancy = 0;
// // m_linearDeflectionEfficiency = 1;
// // m_linearDeflectionTimescale = 2; m_linearDeflectionEfficiency = 1;
// // m_angularDeflectionEfficiency = 0; m_linearDeflectionTimescale = 2;
// m_angularDeflectionTimescale = 10;
m_angularDeflectionEfficiency = 0;
m_angularDeflectionTimescale = 10;
m_verticalAttractionEfficiency = 1f; m_verticalAttractionEfficiency = 1f;
m_verticalAttractionTimescale = 10f; m_verticalAttractionTimescale = 10f;
// m_bankingEfficiency = -0.2f;
// m_bankingMix = 1; m_bankingEfficiency = -0.2f;
// m_bankingTimescale = 1; m_bankingMix = 1;
// m_referenceFrame = Quaternion.Identity; m_bankingTimescale = 1;
m_referenceFrame = Quaternion.Identity;
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
| VehicleFlag.HOVER_TERRAIN_ONLY
| VehicleFlag.HOVER_GLOBAL_HEIGHT);
m_flags |= (VehicleFlag.NO_DEFLECTION_UP m_flags |= (VehicleFlag.NO_DEFLECTION_UP
| VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_ROLL_ONLY
| VehicleFlag.LIMIT_MOTOR_UP); | VehicleFlag.LIMIT_MOTOR_UP
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT); | VehicleFlag.HOVER_UP_ONLY);
m_flags |= (VehicleFlag.HOVER_UP_ONLY);
break; break;
case Vehicle.TYPE_BOAT: case Vehicle.TYPE_BOAT:
m_linearFrictionTimescale = new Vector3(10, 3, 2);
m_angularFrictionTimescale = new Vector3(10,10,10);
m_linearMotorDirection = Vector3.Zero; m_linearMotorDirection = Vector3.Zero;
m_linearMotorTimescale = 5; m_linearMotorTimescale = 5;
m_linearMotorDecayTimescale = 60; m_linearMotorDecayTimescale = 60;
m_linearFrictionTimescale = new Vector3(10, 3, 2);
m_angularMotorDirection = Vector3.Zero; m_angularMotorDirection = Vector3.Zero;
m_angularMotorTimescale = 4; m_angularMotorTimescale = 4;
m_angularMotorDecayTimescale = 4; m_angularMotorDecayTimescale = 4;
m_angularFrictionTimescale = new Vector3(10,10,10);
m_VhoverHeight = 0; m_VhoverHeight = 0;
// m_VhoverEfficiency = 0.5f; m_VhoverEfficiency = 0.5f;
m_VhoverTimescale = 2; m_VhoverTimescale = 2;
m_VehicleBuoyancy = 1; m_VehicleBuoyancy = 1;
// m_linearDeflectionEfficiency = 0.5f;
// m_linearDeflectionTimescale = 3; m_linearDeflectionEfficiency = 0.5f;
// m_angularDeflectionEfficiency = 0.5f; m_linearDeflectionTimescale = 3;
// m_angularDeflectionTimescale = 5;
m_angularDeflectionEfficiency = 0.5f;
m_angularDeflectionTimescale = 5;
m_verticalAttractionEfficiency = 0.5f; m_verticalAttractionEfficiency = 0.5f;
m_verticalAttractionTimescale = 5f; m_verticalAttractionTimescale = 5f;
// m_bankingEfficiency = -0.3f;
// m_bankingMix = 0.8f; m_bankingEfficiency = -0.3f;
// m_bankingTimescale = 1; m_bankingMix = 0.8f;
// m_referenceFrame = Quaternion.Identity; m_bankingTimescale = 1;
m_referenceFrame = Quaternion.Identity;
m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY
| VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_GLOBAL_HEIGHT
| VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_ROLL_ONLY
@ -390,28 +428,35 @@ namespace OpenSim.Region.Physics.BulletSPlugin
| VehicleFlag.HOVER_WATER_ONLY); | VehicleFlag.HOVER_WATER_ONLY);
break; break;
case Vehicle.TYPE_AIRPLANE: case Vehicle.TYPE_AIRPLANE:
m_linearFrictionTimescale = new Vector3(200, 10, 5);
m_angularFrictionTimescale = new Vector3(20, 20, 20);
m_linearMotorDirection = Vector3.Zero; m_linearMotorDirection = Vector3.Zero;
m_linearMotorTimescale = 2; m_linearMotorTimescale = 2;
m_linearMotorDecayTimescale = 60; m_linearMotorDecayTimescale = 60;
m_linearFrictionTimescale = new Vector3(200, 10, 5);
m_angularMotorDirection = Vector3.Zero; m_angularMotorDirection = Vector3.Zero;
m_angularMotorTimescale = 4; m_angularMotorTimescale = 4;
m_angularMotorDecayTimescale = 4; m_angularMotorDecayTimescale = 4;
m_angularFrictionTimescale = new Vector3(20, 20, 20);
m_VhoverHeight = 0; m_VhoverHeight = 0;
// m_VhoverEfficiency = 0.5f; m_VhoverEfficiency = 0.5f;
m_VhoverTimescale = 1000; m_VhoverTimescale = 1000;
m_VehicleBuoyancy = 0; m_VehicleBuoyancy = 0;
// m_linearDeflectionEfficiency = 0.5f;
// m_linearDeflectionTimescale = 3; m_linearDeflectionEfficiency = 0.5f;
// m_angularDeflectionEfficiency = 1; m_linearDeflectionTimescale = 3;
// m_angularDeflectionTimescale = 2;
m_angularDeflectionEfficiency = 1;
m_angularDeflectionTimescale = 2;
m_verticalAttractionEfficiency = 0.9f; m_verticalAttractionEfficiency = 0.9f;
m_verticalAttractionTimescale = 2f; m_verticalAttractionTimescale = 2f;
// m_bankingEfficiency = 1;
// m_bankingMix = 0.7f; m_bankingEfficiency = 1;
// m_bankingTimescale = 2; m_bankingMix = 0.7f;
// m_referenceFrame = Quaternion.Identity; m_bankingTimescale = 2;
m_referenceFrame = Quaternion.Identity;
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
| VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY
| VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_GLOBAL_HEIGHT
@ -421,28 +466,36 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY);
break; break;
case Vehicle.TYPE_BALLOON: case Vehicle.TYPE_BALLOON:
m_linearFrictionTimescale = new Vector3(5, 5, 5);
m_angularFrictionTimescale = new Vector3(10, 10, 10);
m_linearMotorDirection = Vector3.Zero; m_linearMotorDirection = Vector3.Zero;
m_linearMotorTimescale = 5; m_linearMotorTimescale = 5;
m_linearFrictionTimescale = new Vector3(5, 5, 5);
m_linearMotorDecayTimescale = 60; m_linearMotorDecayTimescale = 60;
m_angularMotorDirection = Vector3.Zero; m_angularMotorDirection = Vector3.Zero;
m_angularMotorTimescale = 6; m_angularMotorTimescale = 6;
m_angularFrictionTimescale = new Vector3(10, 10, 10);
m_angularMotorDecayTimescale = 10; m_angularMotorDecayTimescale = 10;
m_VhoverHeight = 5; m_VhoverHeight = 5;
// m_VhoverEfficiency = 0.8f; m_VhoverEfficiency = 0.8f;
m_VhoverTimescale = 10; m_VhoverTimescale = 10;
m_VehicleBuoyancy = 1; m_VehicleBuoyancy = 1;
// m_linearDeflectionEfficiency = 0;
// m_linearDeflectionTimescale = 5; m_linearDeflectionEfficiency = 0;
// m_angularDeflectionEfficiency = 0; m_linearDeflectionTimescale = 5;
// m_angularDeflectionTimescale = 5;
m_angularDeflectionEfficiency = 0;
m_angularDeflectionTimescale = 5;
m_verticalAttractionEfficiency = 1f; m_verticalAttractionEfficiency = 1f;
m_verticalAttractionTimescale = 100f; m_verticalAttractionTimescale = 100f;
// m_bankingEfficiency = 0;
// m_bankingMix = 0.7f; m_bankingEfficiency = 0;
// m_bankingTimescale = 5; m_bankingMix = 0.7f;
// m_referenceFrame = Quaternion.Identity; m_bankingTimescale = 5;
m_referenceFrame = Quaternion.Identity;
m_referenceFrame = Quaternion.Identity;
m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
| VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY
| VehicleFlag.HOVER_UP_ONLY | VehicleFlag.HOVER_UP_ONLY
@ -452,21 +505,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
| VehicleFlag.HOVER_GLOBAL_HEIGHT); | VehicleFlag.HOVER_GLOBAL_HEIGHT);
break; break;
} }
}//end SetDefaultsForType }
// Some of the properties of this prim may have changed. // Some of the properties of this prim may have changed.
// Do any updating needed for a vehicle // Do any updating needed for a vehicle
public void Refresh() public void Refresh()
{ {
if (!IsActive) if (IsActive)
return; {
// Friction effects are handled by this vehicle code
// Set the prim's inertia to zero. The vehicle code handles that and this BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, 0f);
// removes the motion and torque actions introduced by Bullet. BulletSimAPI.SetHitFraction2(Prim.PhysBody.ptr, 0f);
Vector3 inertia = Vector3.Zero; }
// comment out for DEBUG test
// BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia);
// BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr);
} }
// One step of the vehicle properties for the next 'pTimestep' seconds. // One step of the vehicle properties for the next 'pTimestep' seconds.
@ -474,10 +524,22 @@ namespace OpenSim.Region.Physics.BulletSPlugin
{ {
if (!IsActive) return; if (!IsActive) return;
// DEBUG
// Because Bullet does apply forces to the vehicle, our last computed
// linear and angular velocities are not what is happening now.
// Vector3 externalAngularVelocity = Prim.ForceRotationalVelocity - m_lastAngularVelocity;
// m_lastAngularVelocity += (externalAngularVelocity * 0.5f) * pTimestep;
// m_lastAngularVelocity = Prim.ForceRotationalVelocity; // DEBUG: account for what Bullet did last time
// m_lastLinearVelocityVector = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG:
// END DEBUG
MoveLinear(pTimestep); MoveLinear(pTimestep);
MoveAngular(pTimestep); MoveAngular(pTimestep);
LimitRotation(pTimestep); LimitRotation(pTimestep);
// DEBUG: Trying to figure out why Bullet goes crazy when the root prim is moved.
// BulletSimAPI.SetInterpolationVelocity2(Prim.BSBody.ptr, m_newVelocity, m_lastAngularVelocity); // DEBUG DEBUG DEBUG
// remember the position so next step we can limit absolute movement effects // remember the position so next step we can limit absolute movement effects
m_lastPositionVector = Prim.ForcePosition; m_lastPositionVector = Prim.ForcePosition;
@ -489,62 +551,51 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Also does hover and float. // Also does hover and float.
private void MoveLinear(float pTimestep) private void MoveLinear(float pTimestep)
{ {
// m_linearMotorDirection is the direction we are moving relative to the vehicle coordinates // m_linearMotorDirection is the target direction we are moving relative to the vehicle coordinates
// m_lastLinearVelocityVector is the speed we are moving in that direction // m_lastLinearVelocityVector is the current speed we are moving in that direction
if (m_linearMotorDirection.LengthSquared() > 0.001f) if (m_linearMotorDirection.LengthSquared() > 0.001f)
{ {
Vector3 origDir = m_linearMotorDirection; Vector3 origDir = m_linearMotorDirection;
Vector3 origVel = m_lastLinearVelocityVector; Vector3 origVel = m_lastLinearVelocityVector;
Vector3 vehicleVelocity = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG
// add drive to body // add drive to body
// Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale / pTimestep); Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale) * pTimestep;
Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale / pTimestep);
// lastLinearVelocityVector is the current body velocity vector // lastLinearVelocityVector is the current body velocity vector
// RA: Not sure what the *10 is for. A correction for pTimestep?
// m_lastLinearVelocityVector += (addAmount*10);
m_lastLinearVelocityVector += addAmount; m_lastLinearVelocityVector += addAmount;
// Limit the velocity vector to less than the last set linear motor direction float decayFactor = (1.0f / m_linearMotorDecayTimescale) * pTimestep;
if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X)) m_linearMotorDirection *= (1f - decayFactor);
m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X;
if (Math.Abs(m_lastLinearVelocityVector.Y) > Math.Abs(m_linearMotorDirectionLASTSET.Y))
m_lastLinearVelocityVector.Y = m_linearMotorDirectionLASTSET.Y;
if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z))
m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z;
/* Vector3 frictionFactor = (Vector3.One / m_linearFrictionTimescale) * pTimestep;
// decay applied velocity m_lastLinearVelocityVector *= (Vector3.One - frictionFactor);
Vector3 decayfraction = Vector3.One/(m_linearMotorDecayTimescale / pTimestep);
// (RA: do not know where the 0.5f comes from)
m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
*/
float keepfraction = 1.0f - (1.0f / (m_linearMotorDecayTimescale / pTimestep));
m_linearMotorDirection *= keepfraction;
VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},notDecay={4},dir={5},vel={6}", // Rotate new object velocity from vehicle relative to world coordinates
Prim.LocalID, origDir, origVel, addAmount, keepfraction, m_linearMotorDirection, m_lastLinearVelocityVector); m_newVelocity = m_lastLinearVelocityVector * Prim.ForceOrientation;
VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},vehVel={3},add={4},decay={5},frict={6},lmDir={7},lmVel={8},newVel={9}",
Prim.LocalID, origDir, origVel, vehicleVelocity, addAmount, decayFactor, frictionFactor,
m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity);
} }
else else
{ {
// if what remains of direction is very small, zero it. // if what remains of direction is very small, zero it.
m_linearMotorDirection = Vector3.Zero; m_linearMotorDirection = Vector3.Zero;
m_lastLinearVelocityVector = Vector3.Zero; m_lastLinearVelocityVector = Vector3.Zero;
m_newVelocity = Vector3.Zero;
VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID);
} }
// convert requested object velocity to object relative vector // m_newVelocity is velocity computed from linear motor in world coordinates
Quaternion rotq = Prim.ForceOrientation;
m_newVelocity = m_lastLinearVelocityVector * rotq;
// Add the various forces into m_dir which will be our new direction vector (velocity) // Gravity and Buoyancy
// add Gravity and Buoyancy
// There is some gravity, make a gravity force vector that is applied after object velocity. // There is some gravity, make a gravity force vector that is applied after object velocity.
// m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Linkset.LinksetMass * (1f - m_VehicleBuoyancy)); Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy);
/* /*
* RA: Not sure why one would do this * RA: Not sure why one would do this unless we are hoping external forces are doing gravity, ...
// Preserve the current Z velocity // Preserve the current Z velocity
Vector3 vel_now = m_prim.Velocity; Vector3 vel_now = m_prim.Velocity;
m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity
@ -567,6 +618,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
} }
// Check if hovering // Check if hovering
// m_VhoverEfficiency: 0=bouncy, 1=totally damped
// m_VhoverTimescale: time to achieve height
if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
{ {
// We should hover, get the target height // We should hover, get the target height
@ -597,13 +650,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin
} }
else else
{ {
float herr0 = pos.Z - m_VhoverTargetHeight; float verticalError = pos.Z - m_VhoverTargetHeight;
// RA: where does the 50 come from?
float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale);
// Replace Vertical speed with correction figure if significant // Replace Vertical speed with correction figure if significant
if (Math.Abs(herr0) > 0.01f) if (Math.Abs(verticalError) > 0.01f)
{ {
m_newVelocity.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale); m_newVelocity.Z += verticalCorrectionVelocity;
//KF: m_VhoverEfficiency is not yet implemented //KF: m_VhoverEfficiency is not yet implemented
} }
else if (verticalError < -0.01)
{
m_newVelocity.Z -= verticalCorrectionVelocity;
}
else else
{ {
m_newVelocity.Z = 0f; m_newVelocity.Z = 0f;
@ -680,19 +739,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Apply velocity // Apply velocity
Prim.ForceVelocity = m_newVelocity; Prim.ForceVelocity = m_newVelocity;
// apply gravity force // Prim.AddForce(m_newVelocity * Prim.Linkset.LinksetMass, false);
// Why is this set here? The physics engine already does gravity. Prim.AddForce(grav * Prim.Linkset.LinksetMass, false);
Prim.AddForce(grav, false, true);
// Apply friction VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},grav={4}",
Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep)); Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, grav);
m_lastLinearVelocityVector *= keepFraction;
VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},grav={4},1Mdecay={5}",
Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, grav, keepFraction);
} // end MoveLinear() } // end MoveLinear()
// =======================================================================
// Apply the effect of the angular motor. // Apply the effect of the angular motor.
private void MoveAngular(float pTimestep) private void MoveAngular(float pTimestep)
{ {
@ -704,94 +759,150 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// m_angularFrictionTimescale // body angular velocity decay rate // m_angularFrictionTimescale // body angular velocity decay rate
// m_lastAngularVelocity // what was last applied to body // m_lastAngularVelocity // what was last applied to body
// Get what the body is doing, this includes 'external' influences if (m_angularMotorDirection.LengthSquared() > 0.0001)
Vector3 angularVelocity = Prim.ForceRotationalVelocity;
if (m_angularMotorApply > 0)
{ {
// Rather than snapping the angular motor velocity from the old value to
// a newly set velocity, this routine steps the value from the previous
// value (m_angularMotorVelocity) to the requested value (m_angularMotorDirection).
// There are m_angularMotorApply steps.
Vector3 origVel = m_angularMotorVelocity; Vector3 origVel = m_angularMotorVelocity;
Vector3 origDir = m_angularMotorDirection; Vector3 origDir = m_angularMotorDirection;
// ramp up to new value // new velocity += error / ( time to get there / step interval)
// new velocity += error / ( time to get there / step interval) // requested speed - last motor speed
// requested speed - last motor speed m_angularMotorVelocity += (m_angularMotorDirection - m_angularMotorVelocity) / (m_angularMotorTimescale / pTimestep);
m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep); // decay requested direction
m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep); m_angularMotorDirection *= (1.0f - (pTimestep * 1.0f/m_angularMotorDecayTimescale));
m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep);
VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},origDir={5},vel={6}", VDetailLog("{0},MoveAngular,angularMotorApply,angTScale={1},timeStep={2},origvel={3},origDir={4},vel={5}",
Prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origVel, origDir, m_angularMotorVelocity); Prim.LocalID, m_angularMotorTimescale, pTimestep, origVel, origDir, m_angularMotorVelocity);
m_angularMotorApply--;
} }
else else
{ {
// No motor recently applied, keep the body velocity m_angularMotorVelocity = Vector3.Zero;
// and decay the velocity }
m_angularMotorVelocity -= m_angularMotorVelocity / (m_angularMotorDecayTimescale / pTimestep);
if (m_angularMotorVelocity.LengthSquared() < 0.00001) #region Vertical attactor
m_angularMotorVelocity = Vector3.Zero;
} // end motor section
// Vertical attractor section
Vector3 vertattr = Vector3.Zero; Vector3 vertattr = Vector3.Zero;
Vector3 deflection = Vector3.Zero; Vector3 deflection = Vector3.Zero;
Vector3 banking = Vector3.Zero; Vector3 banking = Vector3.Zero;
if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero) if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero)
{ {
float VAservo = 0.2f / (m_verticalAttractionTimescale / pTimestep); float VAservo = pTimestep * 0.2f / m_verticalAttractionTimescale;
if (Prim.Linkset.LinksetIsColliding)
VAservo = pTimestep * 0.05f / (m_verticalAttractionTimescale);
VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
// get present body rotation // Create a vector of the vehicle "up" in world coordinates
Quaternion rotq = Prim.ForceOrientation; Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation;
// vector pointing up // verticalError.X and .Y are the World error amounts. They are 0 when there is no
Vector3 verterr = Vector3.Zero; // error (Vehicle Body is 'vertical'), and .Z will be 1. As the body leans to its
verterr.Z = 1.0f; // side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall
// and .Z will go // negative. Similar for tilt and |.Y|. .X and .Y must be
// rotate it to Body Angle // modulated to prevent a stable inverted body.
verterr = verterr * rotq;
// verterr.X and .Y are the World error amounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1.
// As the body leans to its side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall and .Z will go
// negative. Similar for tilt and |.Y|. .X and .Y must be modulated to prevent a stable inverted body.
// Error is 0 (no error) to +/- 2 (max error) // Error is 0 (no error) to +/- 2 (max error)
if (verterr.Z < 0.0f) if (verticalError.Z < 0.0f)
{ {
verterr.X = 2.0f - verterr.X; verticalError.X = 2.0f - verticalError.X;
verterr.Y = 2.0f - verterr.Y; verticalError.Y = 2.0f - verticalError.Y;
} }
// scale it by VAservo // scale it by VAservo
verterr = verterr * VAservo; verticalError = verticalError * VAservo;
// As the body rotates around the X axis, then verterr.Y increases; Rotated around Y then .X increases, so // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y
// Change Body angular velocity X based on Y, and Y based on X. Z is not changed. // then .X increases, so change Body angular velocity X based on Y, and Y based on X.
vertattr.X = verterr.Y; // Z is not changed.
vertattr.Y = - verterr.X; vertattr.X = verticalError.Y;
vertattr.Y = - verticalError.X;
vertattr.Z = 0f; vertattr.Z = 0f;
// scaling appears better usingsquare-law // scaling appears better usingsquare-law
Vector3 angularVelocity = Prim.ForceRotationalVelocity;
float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
vertattr.X += bounce * angularVelocity.X; vertattr.X += bounce * angularVelocity.X;
vertattr.Y += bounce * angularVelocity.Y; vertattr.Y += bounce * angularVelocity.Y;
VDetailLog("{0},MoveAngular,verticalAttraction,verterr={1},bounce={2},vertattr={3}", VDetailLog("{0},MoveAngular,verticalAttraction,verticalError={1},bounce={2},vertattr={3}",
Prim.LocalID, verterr, bounce, vertattr); Prim.LocalID, verticalError, bounce, vertattr);
} // else vertical attractor is off }
#endregion // Vertical attactor
#region Deflection
//Forward is the prefered direction, but if the reference frame has changed, we need to take this into account as well
if (m_angularDeflectionEfficiency != 0)
{
Vector3 preferredAxisOfMotion =
new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0);
preferredAxisOfMotion *= Quaternion.Add(Prim.ForceOrientation, m_referenceFrame);
deflection = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep;
VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}",
Prim.LocalID, preferredAxisOfMotion, deflection);
}
#endregion
#region Banking
if (m_bankingEfficiency != 0)
{
Vector3 dir = Vector3.One * Prim.ForceOrientation;
float mult = (m_bankingMix*m_bankingMix)*-1*(m_bankingMix < 0 ? -1 : 1);
//Changes which way it banks in and out of turns
//Use the square of the efficiency, as it looks much more how SL banking works
float effSquared = (m_bankingEfficiency*m_bankingEfficiency);
if (m_bankingEfficiency < 0)
effSquared *= -1; //Keep the negative!
float mix = Math.Abs(m_bankingMix);
if (m_angularMotorVelocity.X == 0)
{
/*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f))
{
Vector3 axisAngle;
float angle;
parent.Orientation.GetAxisAngle(out axisAngle, out angle);
Vector3 rotatedVel = parent.Velocity * parent.Orientation;
if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0))
m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10;
else
m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10;
}*/
}
else
banking.Z += (effSquared*(mult*mix))*(m_angularMotorVelocity.X) * 4;
if (!Prim.Linkset.LinksetIsColliding && Math.Abs(m_angularMotorVelocity.X) > mix)
//If they are colliding, we probably shouldn't shove the prim around... probably
{
float angVelZ = m_angularMotorVelocity.X*-1;
/*if(angVelZ > mix)
angVelZ = mix;
else if(angVelZ < -mix)
angVelZ = -mix;*/
//This controls how fast and how far the banking occurs
Vector3 bankingRot = new Vector3(angVelZ*(effSquared*mult), 0, 0);
if (bankingRot.X > 3)
bankingRot.X = 3;
else if (bankingRot.X < -3)
bankingRot.X = -3;
bankingRot *= Prim.ForceOrientation;
banking += bankingRot;
}
m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency;
VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},banking={3}",
Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, banking);
}
#endregion
m_lastVertAttractor = vertattr; m_lastVertAttractor = vertattr;
// Bank section tba
// Deflection section tba
// Sum velocities // Sum velocities
m_lastAngularVelocity = m_angularMotorVelocity + vertattr; // + bank + deflection m_lastAngularVelocity = m_angularMotorVelocity + vertattr + banking + deflection;
if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
{ {
@ -806,52 +917,53 @@ namespace OpenSim.Region.Physics.BulletSPlugin
VDetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); VDetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
} }
// apply friction
Vector3 decayamount = Vector3.One / (m_angularFrictionTimescale / pTimestep);
m_lastAngularVelocity -= m_lastAngularVelocity * decayamount;
// Apply to the body // Apply to the body
Prim.ForceRotationalVelocity = m_lastAngularVelocity; // The above calculates the absolute angular velocity needed
// Prim.ForceRotationalVelocity = m_lastAngularVelocity;
VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity); // Apply a force to overcome current angular velocity
Vector3 applyAngularForce = (m_lastAngularVelocity - Prim.ForceRotationalVelocity) * Prim.Linkset.LinksetMass;
// Vector3 applyAngularForce = (m_lastAngularVelocity - Prim.ForceRotationalVelocity);
// Prim.AddAngularForce(applyAngularForce, false);
Prim.ApplyTorqueImpulse(applyAngularForce, false);
// Apply friction for next time
Vector3 decayamount = (Vector3.One / m_angularFrictionTimescale) * pTimestep;
m_lastAngularVelocity *= Vector3.One - decayamount;
VDetailLog("{0},MoveAngular,done,applyAForce={1},decay={2},lastAngular={3}",
Prim.LocalID, applyAngularForce, decayamount, m_lastAngularVelocity);
} //end MoveAngular } //end MoveAngular
internal void LimitRotation(float timestep) internal void LimitRotation(float timestep)
{ {
Quaternion rotq = Prim.ForceOrientation; Quaternion rotq = Prim.ForceOrientation;
Quaternion m_rot = rotq; Quaternion m_rot = rotq;
bool changed = false;
if (m_RollreferenceFrame != Quaternion.Identity) if (m_RollreferenceFrame != Quaternion.Identity)
{ {
if (rotq.X >= m_RollreferenceFrame.X) if (rotq.X >= m_RollreferenceFrame.X)
{ {
m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2); m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2);
changed = true;
} }
if (rotq.Y >= m_RollreferenceFrame.Y) if (rotq.Y >= m_RollreferenceFrame.Y)
{ {
m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2); m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2);
changed = true;
} }
if (rotq.X <= -m_RollreferenceFrame.X) if (rotq.X <= -m_RollreferenceFrame.X)
{ {
m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2); m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2);
changed = true;
} }
if (rotq.Y <= -m_RollreferenceFrame.Y) if (rotq.Y <= -m_RollreferenceFrame.Y)
{ {
m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2); m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2);
changed = true;
} }
changed = true;
} }
if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0) if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0)
{ {
m_rot.X = 0; m_rot.X = 0;
m_rot.Y = 0; m_rot.Y = 0;
changed = true;
} }
if (changed) if (rotq != m_rot)
{ {
Prim.ForceOrientation = m_rot; Prim.ForceOrientation = m_rot;
VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot); VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);

View File

@ -36,20 +36,29 @@ public abstract class BSLinkset
{ {
// private static string LogHeader = "[BULLETSIM LINKSET]"; // private static string LogHeader = "[BULLETSIM LINKSET]";
public enum LinksetImplementation
{
Constraint = 0, // linkset tied together with constraints
Compound = 1, // linkset tied together as a compound object
Manual = 2 // linkset tied together manually (code moves all the pieces)
}
// Create the correct type of linkset for this child // Create the correct type of linkset for this child
public static BSLinkset Factory(BSScene physScene, BSPhysObject parent) public static BSLinkset Factory(BSScene physScene, BSPhysObject parent)
{ {
BSLinkset ret = null; BSLinkset ret = null;
/*
if (parent.IsPhysical)
ret = new BSLinksetConstraints(physScene, parent);
else
ret = new BSLinksetManual(physScene, parent);
*/
// at the moment, there is only one
ret = new BSLinksetConstraints(physScene, parent);
switch ((int)physScene.Params.linksetImplementation)
{
case (int)LinksetImplementation.Compound:
ret = new BSLinksetCompound(physScene, parent);
break;
case (int)LinksetImplementation.Manual:
// ret = new BSLinksetManual(physScene, parent);
break;
default:
ret = new BSLinksetConstraints(physScene, parent);
break;
}
return ret; return ret;
} }
@ -61,22 +70,27 @@ public abstract class BSLinkset
public int LinksetID { get; private set; } public int LinksetID { get; private set; }
// The children under the root in this linkset. // The children under the root in this linkset.
// There are two lists of children: the current children at runtime
// and the children at taint-time. For instance, if you delink a
// child from the linkset, the child is removed from m_children
// but the constraint won't be removed until taint time.
// Two lists lets this track the 'current' children and
// the physical 'taint' children separately.
// After taint processing and before the simulation step, these
// two lists must be the same.
protected HashSet<BSPhysObject> m_children; protected HashSet<BSPhysObject> m_children;
protected HashSet<BSPhysObject> m_taintChildren;
// We lock the diddling of linkset classes to prevent any badness. // We lock the diddling of linkset classes to prevent any badness.
// This locks the modification of the instances of this class. Changes // This locks the modification of the instances of this class. Changes
// to the physical representation is done via the tainting mechenism. // to the physical representation is done via the tainting mechenism.
protected object m_linksetActivityLock = new Object(); protected object m_linksetActivityLock = new Object();
// Some linksets have a preferred physical shape.
// Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
public virtual ShapeData.PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
{
return ShapeData.PhysicsShapeType.SHAPE_UNKNOWN;
}
// Linksets move around the children so the linkset might need to compute the child position
public virtual OMV.Vector3 Position(BSPhysObject member)
{ return member.RawPosition; }
public virtual OMV.Quaternion Orientation(BSPhysObject member)
{ return member.RawOrientation; }
// TODO: does this need to be done for Velocity and RotationalVelocityy?
// We keep the prim's mass in the linkset structure since it could be dependent on other prims // We keep the prim's mass in the linkset structure since it could be dependent on other prims
protected float m_mass; protected float m_mass;
public float LinksetMass public float LinksetMass
@ -88,6 +102,8 @@ public abstract class BSLinkset
} }
} }
public virtual bool LinksetIsColliding { get { return false; } }
public OMV.Vector3 CenterOfMass public OMV.Vector3 CenterOfMass
{ {
get { return ComputeLinksetCenterOfMass(); } get { return ComputeLinksetCenterOfMass(); }
@ -108,8 +124,7 @@ public abstract class BSLinkset
PhysicsScene = scene; PhysicsScene = scene;
LinksetRoot = parent; LinksetRoot = parent;
m_children = new HashSet<BSPhysObject>(); m_children = new HashSet<BSPhysObject>();
m_taintChildren = new HashSet<BSPhysObject>(); m_mass = parent.RawMass;
m_mass = parent.MassRaw;
} }
// Link to a linkset where the child knows the parent. // Link to a linkset where the child knows the parent.
@ -140,7 +155,6 @@ public abstract class BSLinkset
// Cannot remove the root from a linkset. // Cannot remove the root from a linkset.
return this; return this;
} }
RemoveChildFromLinkset(child); RemoveChildFromLinkset(child);
} }
@ -165,9 +179,8 @@ public abstract class BSLinkset
bool ret = false; bool ret = false;
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
{ {
if (m_children.Contains(child)) ret = m_children.Contains(child);
ret = true; /* Safer version but the above should work
/*
foreach (BSPhysObject bp in m_children) foreach (BSPhysObject bp in m_children)
{ {
if (child.LocalID == bp.LocalID) if (child.LocalID == bp.LocalID)
@ -181,10 +194,36 @@ public abstract class BSLinkset
return ret; return ret;
} }
// Perform an action on each member of the linkset including root prim.
// Depends on the action on whether this should be done at taint time.
public delegate bool ForEachMemberAction(BSPhysObject obj);
public virtual bool ForEachMember(ForEachMemberAction action)
{
bool ret = false;
lock (m_linksetActivityLock)
{
action(LinksetRoot);
foreach (BSPhysObject po in m_children)
{
if (action(po))
break;
}
}
return ret;
}
// I am the root of a linkset and a new child is being added
// Called while LinkActivity is locked.
protected abstract void AddChildToLinkset(BSPhysObject child);
// I am the root of a linkset and one of my children is being removed.
// Safe to call even if the child is not really in my linkset.
protected abstract void RemoveChildFromLinkset(BSPhysObject child);
// When physical properties are changed the linkset needs to recalculate // When physical properties are changed the linkset needs to recalculate
// its internal properties. // its internal properties.
// May be called at runtime or taint-time (just pass the appropriate flag). // May be called at runtime or taint-time.
public abstract void Refresh(BSPhysObject requestor, bool inTaintTime); public abstract void Refresh(BSPhysObject requestor);
// The object is going dynamic (physical). Do any setup necessary // The object is going dynamic (physical). Do any setup necessary
// for a dynamic linkset. // for a dynamic linkset.
@ -218,17 +257,17 @@ public abstract class BSLinkset
public abstract void RestoreBodyDependencies(BSPrim child); public abstract void RestoreBodyDependencies(BSPrim child);
// ================================================================ // ================================================================
// Below this point is internal magic
protected virtual float ComputeLinksetMass() protected virtual float ComputeLinksetMass()
{ {
float mass; float mass = LinksetRoot.RawMass;
lock (m_linksetActivityLock) if (HasAnyChildren)
{ {
mass = LinksetRoot.MassRaw; lock (m_linksetActivityLock)
foreach (BSPhysObject bp in m_taintChildren)
{ {
mass += bp.MassRaw; foreach (BSPhysObject bp in m_children)
{
mass += bp.RawMass;
}
} }
} }
return mass; return mass;
@ -239,13 +278,13 @@ public abstract class BSLinkset
OMV.Vector3 com; OMV.Vector3 com;
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
{ {
com = LinksetRoot.Position * LinksetRoot.MassRaw; com = LinksetRoot.Position * LinksetRoot.RawMass;
float totalMass = LinksetRoot.MassRaw; float totalMass = LinksetRoot.RawMass;
foreach (BSPhysObject bp in m_taintChildren) foreach (BSPhysObject bp in m_children)
{ {
com += bp.Position * bp.MassRaw; com += bp.Position * bp.RawMass;
totalMass += bp.MassRaw; totalMass += bp.RawMass;
} }
if (totalMass != 0f) if (totalMass != 0f)
com /= totalMass; com /= totalMass;
@ -261,31 +300,16 @@ public abstract class BSLinkset
{ {
com = LinksetRoot.Position; com = LinksetRoot.Position;
foreach (BSPhysObject bp in m_taintChildren) foreach (BSPhysObject bp in m_children)
{ {
com += bp.Position * bp.MassRaw; com += bp.Position * bp.RawMass;
} }
com /= (m_taintChildren.Count + 1); com /= (m_children.Count + 1);
} }
return com; return com;
} }
// I am the root of a linkset and a new child is being added
// Called while LinkActivity is locked.
protected abstract void AddChildToLinkset(BSPhysObject child);
// Forcefully removing a child from a linkset.
// This is not being called by the child so we have to make sure the child doesn't think
// it's still connected to the linkset.
// Normal OpenSimulator operation will never do this because other SceneObjectPart information
// also has to be updated (like pointer to prim's parent).
protected abstract void RemoveChildFromOtherLinkset(BSPhysObject pchild);
// I am the root of a linkset and one of my children is being removed.
// Safe to call even if the child is not really in my linkset.
protected abstract void RemoveChildFromLinkset(BSPhysObject child);
// Invoke the detailed logger and output something if it's enabled. // Invoke the detailed logger and output something if it's enabled.
protected void DetailLog(string msg, params Object[] args) protected void DetailLog(string msg, params Object[] args)
{ {

View File

@ -0,0 +1,273 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyrightD
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Text;
using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin
{
public sealed class BSLinksetCompound : BSLinkset
{
private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
public BSLinksetCompound(BSScene scene, BSPhysObject parent)
{
base.Initialize(scene, parent);
}
// For compound implimented linksets, if there are children, use compound shape for the root.
public override ShapeData.PhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
{
ShapeData.PhysicsShapeType ret = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN;
if (IsRoot(requestor) && HasAnyChildren)
{
ret = ShapeData.PhysicsShapeType.SHAPE_COMPOUND;
}
// DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
return ret;
}
// When physical properties are changed the linkset needs to recalculate
// its internal properties.
// This is queued in the 'post taint' queue so the
// refresh will happen once after all the other taints are applied.
public override void Refresh(BSPhysObject requestor)
{
// External request for Refresh (from BSPrim) is not necessary
// InternalRefresh(requestor);
}
private void InternalRefresh(BSPhysObject requestor)
{
DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,requestor={1}", LinksetRoot.LocalID, requestor.LocalID);
// Queue to happen after all the other taint processing
PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", requestor.LocalID, delegate()
{
if (IsRoot(requestor) && HasAnyChildren)
RecomputeLinksetCompound();
});
}
// The object is going dynamic (physical). Do any setup necessary
// for a dynamic linkset.
// Only the state of the passed object can be modified. The rest of the linkset
// has not yet been fully constructed.
// Return 'true' if any properties updated on the passed object.
// Called at taint-time!
public override bool MakeDynamic(BSPhysObject child)
{
bool ret = false;
DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
if (!IsRoot(child))
{
// Physical children are removed from the world as the shape ofthe root compound
// shape takes over.
BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
ret = true;
}
return ret;
}
// The object is going static (non-physical). Do any setup necessary for a static linkset.
// Return 'true' if any properties updated on the passed object.
// This doesn't normally happen -- OpenSim removes the objects from the physical
// world if it is a static linkset.
// Called at taint-time!
public override bool MakeStatic(BSPhysObject child)
{
bool ret = false;
DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
if (!IsRoot(child))
{
// The non-physical children can come back to life.
BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
// Don't force activation so setting of DISABLE_SIMULATION can stay.
BulletSimAPI.Activate2(child.PhysBody.ptr, false);
ret = true;
}
return ret;
}
// Called at taint-time!!
public override void UpdateProperties(BSPhysObject updated)
{
// Nothing to do for constraints on property updates
}
// The children move around in relationship to the root.
// Just grab the current values of wherever it is right now.
public override OMV.Vector3 Position(BSPhysObject member)
{
return BulletSimAPI.GetPosition2(member.PhysBody.ptr);
}
public override OMV.Quaternion Orientation(BSPhysObject member)
{
return BulletSimAPI.GetOrientation2(member.PhysBody.ptr);
}
// Routine called when rebuilding the body of some member of the linkset.
// Since we don't keep in world relationships, do nothing unless it's a child changing.
// Returns 'true' of something was actually removed and would need restoring
// Called at taint-time!!
public override bool RemoveBodyDependencies(BSPrim child)
{
bool ret = false;
DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"), IsRoot(child));
if (!IsRoot(child))
{
// Cause the current shape to be freed and the new one to be built.
InternalRefresh(LinksetRoot);
ret = true;
}
return ret;
}
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
// this routine will restore the removed constraints.
// Called at taint-time!!
public override void RestoreBodyDependencies(BSPrim child)
{
// The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
}
// ================================================================
// Add a new child to the linkset.
// Called while LinkActivity is locked.
protected override void AddChildToLinkset(BSPhysObject child)
{
if (!HasChild(child))
{
m_children.Add(child);
DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
// Cause constraints and assorted properties to be recomputed before the next simulation step.
InternalRefresh(LinksetRoot);
}
return;
}
// Remove the specified child from the linkset.
// Safe to call even if the child is not really in my linkset.
protected override void RemoveChildFromLinkset(BSPhysObject child)
{
if (m_children.Remove(child))
{
DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
child.LocalID,
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"),
child.LocalID, child.PhysBody.ptr.ToString("X"));
// Cause the child's body to be rebuilt and thus restored to normal operation
child.ForceBodyShapeRebuild(false);
if (!HasAnyChildren)
{
// The linkset is now empty. The root needs rebuilding.
LinksetRoot.ForceBodyShapeRebuild(false);
}
else
{
// Schedule a rebuild of the linkset before the next simulation tick.
InternalRefresh(LinksetRoot);
}
}
return;
}
// Called before the simulation step to make sure the compound based linkset
// is all initialized.
// Constraint linksets are rebuilt every time.
// Note that this works for rebuilding just the root after a linkset is taken apart.
// Called at taint time!!
private void RecomputeLinksetCompound()
{
// Cause the root shape to be rebuilt as a compound object with just the root in it
LinksetRoot.ForceBodyShapeRebuild(true);
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
// Add a shape for each of the other children in the linkset
ForEachMember(delegate(BSPhysObject cPrim)
{
if (!IsRoot(cPrim))
{
// Each child position and rotation is given relative to the root.
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, displacementPos, displacementRot);
if (cPrim.PhysShape.isNativeShape)
{
// Native shapes are not shared so we need to create a new one.
// A mesh or hull is created because scale is not available on a native shape.
// (TODO: Bullet does have a btScaledCollisionShape. Can that be used?)
BulletShape saveShape = cPrim.PhysShape;
cPrim.PhysShape.ptr = IntPtr.Zero; // Don't let the create free the child's shape
PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
BulletShape newShape = cPrim.PhysShape;
cPrim.PhysShape = saveShape;
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot);
}
else
{
// For the shared shapes (meshes and hulls), just use the shape in the child.
if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
{
PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
}
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, displacementPos, displacementRot);
}
}
return false; // 'false' says to move onto the next child in the list
});
// With all of the linkset packed into the root prim, it has the mass of everyone.
float linksetMass = LinksetMass;
LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
// DEBUG: see of inter-linkset collisions are causing problems for constraint linksets.
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
}
}
}

View File

@ -32,7 +32,7 @@ using OMV = OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BSLinksetConstraints : BSLinkset public sealed class BSLinksetConstraints : BSLinkset
{ {
// private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
@ -43,23 +43,16 @@ public class BSLinksetConstraints : BSLinkset
// When physical properties are changed the linkset needs to recalculate // When physical properties are changed the linkset needs to recalculate
// its internal properties. // its internal properties.
// May be called at runtime or taint-time (just pass the appropriate flag). // This is queued in the 'post taint' queue so the
public override void Refresh(BSPhysObject requestor, bool inTaintTime) // refresh will happen once after all the other taints are applied.
public override void Refresh(BSPhysObject requestor)
{ {
// If there are no children or not root, I am not the one that recomputes the constraints // Queue to happen after all the other taint processing
if (!HasAnyChildren || !IsRoot(requestor)) PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
return;
BSScene.TaintCallback refreshOperation = delegate()
{ {
RecomputeLinksetConstraintVariables(); if (HasAnyChildren && IsRoot(requestor))
DetailLog("{0},BSLinkset.Refresh,complete,rBody={1}", RecomputeLinksetConstraints();
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); });
};
if (inTaintTime)
refreshOperation();
else
PhysicsScene.TaintedObject("BSLinkSet.Refresh", refreshOperation);
} }
// The object is going dynamic (physical). Do any setup necessary // The object is going dynamic (physical). Do any setup necessary
@ -74,9 +67,10 @@ public class BSLinksetConstraints : BSLinkset
return false; return false;
} }
// The object is going static (non-physical). Do any setup necessary // The object is going static (non-physical). Do any setup necessary for a static linkset.
// for a static linkset.
// Return 'true' if any properties updated on the passed object. // Return 'true' if any properties updated on the passed object.
// This doesn't normally happen -- OpenSim removes the objects from the physical
// world if it is a static linkset.
// Called at taint-time! // Called at taint-time!
public override bool MakeStatic(BSPhysObject child) public override bool MakeStatic(BSPhysObject child)
{ {
@ -90,35 +84,36 @@ public class BSLinksetConstraints : BSLinkset
// Nothing to do for constraints on property updates // Nothing to do for constraints on property updates
} }
// Routine used when rebuilding the body of the root of the linkset // The children of the linkset are moved around by the constraints.
// Destroy all the constraints have have been made to root. // Just grab the current values of wherever it is right now.
// This is called when the root body is changing. public override OMV.Vector3 Position(BSPhysObject member)
// Returns 'true' of something eas actually removed and would need restoring {
return BulletSimAPI.GetPosition2(member.PhysBody.ptr);
}
public override OMV.Quaternion Orientation(BSPhysObject member)
{
return BulletSimAPI.GetOrientation2(member.PhysBody.ptr);
}
// Routine called when rebuilding the body of some member of the linkset.
// Destroy all the constraints have have been made to root and set
// up to rebuild the constraints before the next simulation step.
// Returns 'true' of something was actually removed and would need restoring
// Called at taint-time!! // Called at taint-time!!
public override bool RemoveBodyDependencies(BSPrim child) public override bool RemoveBodyDependencies(BSPrim child)
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"));
lock (m_linksetActivityLock) lock (m_linksetActivityLock)
{ {
if (IsRoot(child)) // Just undo all the constraints for this linkset. Rebuild at the end of the step.
{ ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
// If the one with the dependency is root, must undo all children // Cause the constraints, et al to be rebuilt before the next simulation step.
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", Refresh(LinksetRoot);
child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"));
ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
}
else
{
DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
child.LocalID,
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
child.LocalID, child.BSBody.ptr.ToString("X"));
// ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child);
// Despite the function name, this removes any link to the specified object.
ret = PhysicallyUnlinkAllChildrenFromRoot(child);
}
} }
return ret; return ret;
} }
@ -128,32 +123,12 @@ public class BSLinksetConstraints : BSLinkset
// Called at taint-time!! // Called at taint-time!!
public override void RestoreBodyDependencies(BSPrim child) public override void RestoreBodyDependencies(BSPrim child)
{ {
lock (m_linksetActivityLock) // The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
{
if (IsRoot(child))
{
DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}",
child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
foreach (BSPhysObject bpo in m_taintChildren)
{
PhysicallyLinkAChildToRoot(LinksetRoot, bpo);
}
}
else
{
DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
LinksetRoot.LocalID,
LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
child.LocalID, child.BSBody.ptr.ToString("X"));
PhysicallyLinkAChildToRoot(LinksetRoot, child);
}
}
} }
// ================================================================ // ================================================================
// Below this point is internal magic
// I am the root of a linkset and a new child is being added // Add a new child to the linkset.
// Called while LinkActivity is locked. // Called while LinkActivity is locked.
protected override void AddChildToLinkset(BSPhysObject child) protected override void AddChildToLinkset(BSPhysObject child)
{ {
@ -161,39 +136,15 @@ public class BSLinksetConstraints : BSLinkset
{ {
m_children.Add(child); m_children.Add(child);
BSPhysObject rootx = LinksetRoot; // capture the root as of now DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
BSPhysObject childx = child;
DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); // Cause constraints and assorted properties to be recomputed before the next simulation step.
Refresh(LinksetRoot);
PhysicsScene.TaintedObject("AddChildToLinkset", delegate()
{
DetailLog("{0},AddChildToLinkset,taint,rID={1},rBody={2},cID={3},cBody={4}",
rootx.LocalID,
rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
childx.LocalID, childx.BSBody.ptr.ToString("X"));
// Since this is taint-time, the body and shape could have changed for the child
rootx.ForcePosition = rootx.Position; // DEBUG
childx.ForcePosition = childx.Position; // DEBUG
PhysicallyLinkAChildToRoot(rootx, childx);
m_taintChildren.Add(child);
});
} }
return; return;
} }
// Forcefully removing a child from a linkset. // Remove the specified child from the linkset.
// This is not being called by the child so we have to make sure the child doesn't think
// it's still connected to the linkset.
// Normal OpenSimulator operation will never do this because other SceneObjectPart information
// also has to be updated (like pointer to prim's parent).
protected override void RemoveChildFromOtherLinkset(BSPhysObject pchild)
{
pchild.Linkset = BSLinkset.Factory(PhysicsScene, pchild);
RemoveChildFromLinkset(pchild);
}
// I am the root of a linkset and one of my children is being removed.
// Safe to call even if the child is not really in my linkset. // Safe to call even if the child is not really in my linkset.
protected override void RemoveChildFromLinkset(BSPhysObject child) protected override void RemoveChildFromLinkset(BSPhysObject child)
{ {
@ -202,22 +153,21 @@ public class BSLinksetConstraints : BSLinkset
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
BSPhysObject childx = child; BSPhysObject childx = child;
DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
childx.LocalID, childx.LocalID,
rootx.LocalID, rootx.BSBody.ptr.ToString("X"), rootx.LocalID, rootx.PhysBody.ptr.ToString("X"),
childx.LocalID, childx.BSBody.ptr.ToString("X")); childx.LocalID, childx.PhysBody.ptr.ToString("X"));
PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
{ {
m_taintChildren.Remove(child);
PhysicallyUnlinkAChildFromRoot(rootx, childx); PhysicallyUnlinkAChildFromRoot(rootx, childx);
RecomputeLinksetConstraintVariables();
}); });
// See that the linkset parameters are recomputed at the end of the taint time.
Refresh(LinksetRoot);
} }
else else
{ {
// This will happen if we remove the root of the linkset first. Non-fatal occurance. // Non-fatal occurance.
// PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
} }
return; return;
@ -226,6 +176,12 @@ public class BSLinksetConstraints : BSLinkset
// Create a constraint between me (root of linkset) and the passed prim (the child). // Create a constraint between me (root of linkset) and the passed prim (the child).
// Called at taint time! // Called at taint time!
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
{
// Don't build the constraint when asked. Put it off until just before the simulation step.
Refresh(rootPrim);
}
private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim)
{ {
// Zero motion for children so they don't interpolate // Zero motion for children so they don't interpolate
childPrim.ZeroMotion(); childPrim.ZeroMotion();
@ -237,22 +193,23 @@ public class BSLinksetConstraints : BSLinkset
// real world coordinate of midpoint between the two objects // real world coordinate of midpoint between the two objects
OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
rootPrim.LocalID, rootPrim.LocalID,
rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"), rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString("X"),
childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"), childPrim.LocalID, childPrim.PhysBody.ptr.ToString("X"),
rootPrim.Position, childPrim.Position, midPoint); rootPrim.Position, childPrim.Position, midPoint);
// create a constraint that allows no freedom of movement between the two objects // create a constraint that allows no freedom of movement between the two objects
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
BS6DofConstraint constrain = new BS6DofConstraint( BSConstraint6Dof constrain = new BSConstraint6Dof(
PhysicsScene.World, rootPrim.BSBody, childPrim.BSBody, midPoint, true, true ); PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true );
// PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
/* NOTE: below is an attempt to build constraint with full frame computation, etc. /* NOTE: below is an attempt to build constraint with full frame computation, etc.
* Using the midpoint is easier since it lets the Bullet code manipulate the transforms * Using the midpoint is easier since it lets the Bullet code manipulate the transforms
* of the objects. * of the objects.
* Code left as a warning to future programmers. * Code left for future programmers.
// ================================================================================== // ==================================================================================
// relative position normalized to the root prim // relative position normalized to the root prim
OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
@ -262,20 +219,13 @@ public class BSLinksetConstraints : BSLinkset
OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
// create a constraint that allows no freedom of movement between the two objects DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
BS6DofConstraint constrain = new BS6DofConstraint( BS6DofConstraint constrain = new BS6DofConstraint(
PhysicsScene.World, rootPrim.Body, childPrim.Body, PhysicsScene.World, rootPrim.Body, childPrim.Body,
OMV.Vector3.Zero, OMV.Vector3.Zero,
OMV.Quaternion.Inverse(rootPrim.Orientation), OMV.Quaternion.Inverse(rootPrim.Orientation),
OMV.Vector3.Zero, OMV.Vector3.Zero,
OMV.Quaternion.Inverse(childPrim.Orientation), OMV.Quaternion.Inverse(childPrim.Orientation),
// A point half way between the parent and child
// childRelativePosition/2,
// childRelativeRotation,
// childRelativePosition/2,
// inverseChildRelativeRotation,
true, true,
true true
); );
@ -298,25 +248,26 @@ public class BSLinksetConstraints : BSLinkset
{ {
constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
} }
return constrain;
} }
// Remove linkage between myself and a particular child // Remove linkage between the linkset root and a particular child
// The root and child bodies are passed in because we need to remove the constraint between // The root and child bodies are passed in because we need to remove the constraint between
// the bodies that were at unlink time. // the bodies that were present at unlink time.
// Called at taint time! // Called at taint time!
private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
{ {
bool ret = false; bool ret = false;
DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
rootPrim.LocalID, rootPrim.LocalID,
rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"), rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString("X"),
childPrim.LocalID, childPrim.BSBody.ptr.ToString("X")); childPrim.LocalID, childPrim.PhysBody.ptr.ToString("X"));
// Find the constraint for this link and get rid of it from the overall collection and from my list // Find the constraint for this link and get rid of it from the overall collection and from my list
if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody)) if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody))
{ {
// Make the child refresh its location // Make the child refresh its location
BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); BulletSimAPI.PushUpdate2(childPrim.PhysBody.ptr);
ret = true; ret = true;
} }
@ -324,62 +275,53 @@ public class BSLinksetConstraints : BSLinkset
} }
// Remove linkage between myself and any possible children I might have. // Remove linkage between myself and any possible children I might have.
// Returns 'true' of any constraints were destroyed.
// Called at taint time! // Called at taint time!
private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
{ {
DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
bool ret = false;
if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody)) return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody);
{
ret = true;
}
return ret;
} }
// Call each of the constraints that make up this linkset and recompute the // Call each of the constraints that make up this linkset and recompute the
// various transforms and variables. Used when objects are added or removed // various transforms and variables. Create constraints of not created yet.
// from a linkset to make sure the constraints know about the new mass and // Called before the simulation step to make sure the constraint based linkset
// geometry. // is all initialized.
// Must only be called at taint time!! // Called at taint time!!
private void RecomputeLinksetConstraintVariables() private void RecomputeLinksetConstraints()
{ {
float linksetMass = LinksetMass; float linksetMass = LinksetMass;
foreach (BSPhysObject child in m_taintChildren) LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
// DEBUG: see of inter-linkset collisions are causing problems
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}",
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"), linksetMass);
foreach (BSPhysObject child in m_children)
{ {
// A child in the linkset physically shows the mass of the whole linkset.
// This allows Bullet to apply enough force on the child to move the whole linkset.
// (Also do the mass stuff before recomputing the constraint so mass is not zero.)
child.UpdatePhysicalMassProperties(linksetMass);
BSConstraint constrain; BSConstraint constrain;
if (PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain)) if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
{ {
// DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}", // If constraint doesn't exist yet, create it.
// LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID); constrain = BuildConstraint(LinksetRoot, child);
constrain.RecomputeConstraintVariables(linksetMass);
}
else
{
// Non-fatal error that happens when children are being added to the linkset but
// their constraints have not been created yet.
break;
} }
constrain.RecomputeConstraintVariables(linksetMass);
// DEBUG: see of inter-linkset collisions are causing problems
// BulletSimAPI.SetCollisionFilterMask2(child.BSBody.ptr,
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
// BulletSimAPI.DumpConstraint2(PhysicsScene.World.ptr, constrain.Constraint.ptr); // DEBUG DEBUG
} }
// If the whole linkset is not here, doesn't make sense to recompute linkset wide values
if (m_children.Count == m_taintChildren.Count)
{
// If this is a multiple object linkset, set everybody's center of mass to the set's center of mass
OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr,
centerOfMass, OMV.Quaternion.Identity);
DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,setCenterOfMass,COM={1},rBody={2}",
LinksetRoot.LocalID, centerOfMass, LinksetRoot.BSBody.ptr.ToString("X"));
foreach (BSPhysObject child in m_taintChildren)
{
BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr,
centerOfMass, OMV.Quaternion.Identity);
}
// BulletSimAPI.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG
}
return;
} }
} }
} }

View File

@ -63,12 +63,14 @@ public abstract class BSPhysObject : PhysicsActor
public BSLinkset Linkset { get; set; } public BSLinkset Linkset { get; set; }
// Return the object mass without calculating it or having side effects // Return the object mass without calculating it or having side effects
public abstract float MassRaw { get; } public abstract float RawMass { get; }
// Set the raw mass but also update physical mass properties (inertia, ...)
public abstract void UpdatePhysicalMassProperties(float mass);
// Reference to the physical body (btCollisionObject) of this object // Reference to the physical body (btCollisionObject) of this object
public BulletBody BSBody; public BulletBody PhysBody;
// Reference to the physical shape (btCollisionShape) of this object // Reference to the physical shape (btCollisionShape) of this object
public BulletShape BSShape; public BulletShape PhysShape;
// 'true' if the mesh's underlying asset failed to build. // 'true' if the mesh's underlying asset failed to build.
// This will keep us from looping after the first time the build failed. // This will keep us from looping after the first time the build failed.
@ -76,6 +78,12 @@ public abstract class BSPhysObject : PhysicsActor
// The objects base shape information. Null if not a prim type shape. // The objects base shape information. Null if not a prim type shape.
public PrimitiveBaseShape BaseShape { get; protected set; } public PrimitiveBaseShape BaseShape { get; protected set; }
// Some types of objects have preferred physical representations.
// Returns SHAPE_UNKNOWN if there is no preference.
public virtual ShapeData.PhysicsShapeType PreferredPhysicalShape
{
get { return ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; }
}
// When the physical properties are updated, an EntityProperty holds the update values. // When the physical properties are updated, an EntityProperty holds the update values.
// Keep the current and last EntityProperties to enable computation of differences // Keep the current and last EntityProperties to enable computation of differences
@ -99,8 +107,10 @@ public abstract class BSPhysObject : PhysicsActor
// Tell the object to clean up. // Tell the object to clean up.
public abstract void Destroy(); public abstract void Destroy();
public abstract OMV.Vector3 RawPosition { get; set; }
public abstract OMV.Vector3 ForcePosition { get; set; } public abstract OMV.Vector3 ForcePosition { get; set; }
public abstract OMV.Quaternion RawOrientation { get; set; }
public abstract OMV.Quaternion ForceOrientation { get; set; } public abstract OMV.Quaternion ForceOrientation { get; set; }
public abstract OMV.Vector3 ForceVelocity { get; set; } public abstract OMV.Vector3 ForceVelocity { get; set; }
@ -204,7 +214,7 @@ public abstract class BSPhysObject : PhysicsActor
PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
{ {
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
}); });
} }
else else
@ -218,7 +228,7 @@ public abstract class BSPhysObject : PhysicsActor
SubscribedEventsMs = 0; SubscribedEventsMs = 0;
PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
{ {
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
}); });
} }
// Return 'true' if the simulator wants collision events // Return 'true' if the simulator wants collision events

View File

@ -111,8 +111,8 @@ public sealed class BSPrim : BSPhysObject
_mass = CalculateMass(); _mass = CalculateMass();
// No body or shape yet // No body or shape yet
BSBody = new BulletBody(LocalID, IntPtr.Zero); PhysBody = new BulletBody(LocalID, IntPtr.Zero);
BSShape = new BulletShape(IntPtr.Zero); PhysShape = new BulletShape(IntPtr.Zero);
DetailLog("{0},BSPrim.constructor,call", LocalID); DetailLog("{0},BSPrim.constructor,call", LocalID);
// do the actual object creation at taint time // do the actual object creation at taint time
@ -120,7 +120,7 @@ public sealed class BSPrim : BSPhysObject
{ {
CreateGeomAndObject(true); CreateGeomAndObject(true);
CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(BSBody.ptr); CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(PhysBody.ptr);
}); });
} }
@ -145,8 +145,8 @@ public sealed class BSPrim : BSPhysObject
{ {
DetailLog("{0},BSPrim.Destroy,taint,", LocalID); DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
// If there are physical body and shape, release my use of same. // If there are physical body and shape, release my use of same.
PhysicsScene.Shapes.DereferenceBody(BSBody, true, null); PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
PhysicsScene.Shapes.DereferenceShape(BSShape, true, null); PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
}); });
} }
@ -171,18 +171,18 @@ public sealed class BSPrim : BSPhysObject
ForceBodyShapeRebuild(false); ForceBodyShapeRebuild(false);
} }
} }
// Whatever the linkset wants is what I want.
public override ShapeData.PhysicsShapeType PreferredPhysicalShape
{ get { return Linkset.PreferredPhysicalShape(this); } }
public override bool ForceBodyShapeRebuild(bool inTaintTime) public override bool ForceBodyShapeRebuild(bool inTaintTime)
{ {
LastAssetBuildFailed = false; LastAssetBuildFailed = false;
BSScene.TaintCallback rebuildOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate()
{ {
_mass = CalculateMass(); // changing the shape changes the mass _mass = CalculateMass(); // changing the shape changes the mass
CreateGeomAndObject(true); CreateGeomAndObject(true);
}; });
if (inTaintTime)
rebuildOperation();
else
PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", rebuildOperation);
return true; return true;
} }
public override bool Grabbed { public override bool Grabbed {
@ -242,8 +242,8 @@ public sealed class BSPrim : BSPhysObject
_acceleration = OMV.Vector3.Zero; _acceleration = OMV.Vector3.Zero;
_rotationalVelocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero;
// Zero some other properties directly into the physics engine // Zero some other properties in the physics engine
BulletSimAPI.ClearForces2(BSBody.ptr); BulletSimAPI.ClearAllForces2(PhysBody.ptr);
} }
public override void LockAngularMotion(OMV.Vector3 axis) public override void LockAngularMotion(OMV.Vector3 axis)
@ -252,11 +252,16 @@ public sealed class BSPrim : BSPhysObject
return; return;
} }
public override OMV.Vector3 RawPosition
{
get { return _position; }
set { _position = value; }
}
public override OMV.Vector3 Position { public override OMV.Vector3 Position {
get { get {
// child prims move around based on their parent. Need to get the latest location
if (!Linkset.IsRoot(this)) if (!Linkset.IsRoot(this))
// child prims move around based on their parent. Need to get the latest location _position = Linkset.Position(this);
_position = BulletSimAPI.GetPosition2(BSBody.ptr);
// don't do the GetObjectPosition for root elements because this function is called a zillion times // don't do the GetObjectPosition for root elements because this function is called a zillion times
// _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
@ -274,19 +279,21 @@ public sealed class BSPrim : BSPhysObject
PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
{ {
// DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
ActivateIfPhysical(false);
}); });
} }
} }
public override OMV.Vector3 ForcePosition { public override OMV.Vector3 ForcePosition {
get { get {
_position = BulletSimAPI.GetPosition2(BSBody.ptr); _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
return _position; return _position;
} }
set { set {
_position = value; _position = value;
PositionSanityCheck(); PositionSanityCheck();
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
ActivateIfPhysical(false);
} }
} }
@ -311,6 +318,7 @@ public sealed class BSPrim : BSPhysObject
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
{ {
float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
// TODO: a floating motor so object will bob in the water
if (Position.Z < waterHeight) if (Position.Z < waterHeight)
{ {
_position.Z = waterHeight; _position.Z = waterHeight;
@ -332,16 +340,11 @@ public sealed class BSPrim : BSPhysObject
{ {
// The new position value must be pushed into the physics engine but we can't // The new position value must be pushed into the physics engine but we can't
// just assign to "Position" because of potential call loops. // just assign to "Position" because of potential call loops.
BSScene.TaintCallback sanityOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck", delegate()
{ {
DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
ForcePosition = _position; ForcePosition = _position;
}; });
if (inTaintTime)
sanityOperation();
else
PhysicsScene.TaintedObject("BSPrim.PositionSanityCheck", sanityOperation);
ret = true; ret = true;
} }
return ret; return ret;
@ -353,13 +356,34 @@ public sealed class BSPrim : BSPhysObject
{ {
get get
{ {
// return Linkset.LinksetMass; return Linkset.LinksetMass;
return _mass; // return _mass;
} }
} }
// used when we only want this prim's mass and not the linkset thing // used when we only want this prim's mass and not the linkset thing
public override float MassRaw { get { return _mass; } } public override float RawMass {
get { return _mass; }
}
// Set the physical mass to the passed mass.
// Note that this does not change _mass!
public override void UpdatePhysicalMassProperties(float physMass)
{
if (IsStatic)
{
BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, OMV.Vector3.Zero);
BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
}
else
{
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
// center of mass is at the zero of the object
BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation);
// BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2}", LocalID, physMass, localInertia);
}
}
// Is this used? // Is this used?
public override OMV.Vector3 CenterOfMass public override OMV.Vector3 CenterOfMass
@ -380,7 +404,7 @@ public sealed class BSPrim : BSPhysObject
PhysicsScene.TaintedObject("BSPrim.setForce", delegate() PhysicsScene.TaintedObject("BSPrim.setForce", delegate()
{ {
// DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
BulletSimAPI.SetObjectForce2(BSBody.ptr, _force); BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
}); });
} }
} }
@ -400,6 +424,7 @@ public sealed class BSPrim : BSPhysObject
// Done at taint time so we're sure the physics engine is not using the variables // 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 code changes the parameters for this vehicle type.
_vehicle.ProcessTypeChange(type); _vehicle.ProcessTypeChange(type);
ActivateIfPhysical(false);
}); });
} }
} }
@ -408,6 +433,7 @@ public sealed class BSPrim : BSPhysObject
PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
{ {
_vehicle.ProcessFloatVehicleParam((Vehicle)param, value); _vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
ActivateIfPhysical(false);
}); });
} }
public override void VehicleVectorParam(int param, OMV.Vector3 value) public override void VehicleVectorParam(int param, OMV.Vector3 value)
@ -415,6 +441,7 @@ public sealed class BSPrim : BSPhysObject
PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
{ {
_vehicle.ProcessVectorVehicleParam((Vehicle)param, value); _vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
ActivateIfPhysical(false);
}); });
} }
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
@ -422,6 +449,7 @@ public sealed class BSPrim : BSPhysObject
PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate()
{ {
_vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation);
ActivateIfPhysical(false);
}); });
} }
public override void VehicleFlags(int param, bool remove) public override void VehicleFlags(int param, bool remove)
@ -436,9 +464,16 @@ public sealed class BSPrim : BSPhysObject
// Called from Scene when doing simulation step so we're in taint processing time. // Called from Scene when doing simulation step so we're in taint processing time.
public override void StepVehicle(float timeStep) public override void StepVehicle(float timeStep)
{ {
if (IsPhysical) if (IsPhysical && _vehicle.IsActive)
{ {
_vehicle.Step(timeStep); _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);
});
*/
} }
} }
@ -463,7 +498,7 @@ public sealed class BSPrim : BSPhysObject
PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
{ {
// DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity); BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
}); });
} }
} }
@ -471,12 +506,14 @@ public sealed class BSPrim : BSPhysObject
get { return _velocity; } get { return _velocity; }
set { set {
_velocity = value; _velocity = value;
BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity); BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
} }
} }
public override OMV.Vector3 Torque { public override OMV.Vector3 Torque {
get { return _torque; } get { return _torque; }
set { _torque = value; set {
_torque = value;
AddAngularForce(_torque, false, false);
// DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque);
} }
} }
@ -489,12 +526,17 @@ public sealed class BSPrim : BSPhysObject
get { return _acceleration; } get { return _acceleration; }
set { _acceleration = value; } set { _acceleration = value; }
} }
public override OMV.Quaternion RawOrientation
{
get { return _orientation; }
set { _orientation = value; }
}
public override OMV.Quaternion Orientation { public override OMV.Quaternion Orientation {
get { get {
// Children move around because tied to parent. Get a fresh value.
if (!Linkset.IsRoot(this)) if (!Linkset.IsRoot(this))
{ {
// Children move around because tied to parent. Get a fresh value. _orientation = Linkset.Orientation(this);
_orientation = BulletSimAPI.GetOrientation2(BSBody.ptr);
} }
return _orientation; return _orientation;
} }
@ -507,7 +549,7 @@ public sealed class BSPrim : BSPhysObject
{ {
// _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
// DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
}); });
} }
} }
@ -516,13 +558,13 @@ public sealed class BSPrim : BSPhysObject
{ {
get get
{ {
_orientation = BulletSimAPI.GetOrientation2(BSBody.ptr); _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
return _orientation; return _orientation;
} }
set set
{ {
_orientation = value; _orientation = value;
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
} }
} }
public override int PhysicsActorType { public override int PhysicsActorType {
@ -539,6 +581,8 @@ public sealed class BSPrim : BSPhysObject
{ {
// DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical);
SetObjectDynamic(true); SetObjectDynamic(true);
// whether phys-to-static or static-to-phys, the object is not moving.
ZeroMotion();
}); });
} }
} }
@ -576,7 +620,7 @@ public sealed class BSPrim : BSPhysObject
// Mangling all the physical properties requires the object not be in the physical world. // Mangling all the physical properties requires the object not be in the physical world.
// This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found).
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, BSBody.ptr); BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
// Set up the object physicalness (does gravity and collisions move this object) // Set up the object physicalness (does gravity and collisions move this object)
MakeDynamic(IsStatic); MakeDynamic(IsStatic);
@ -590,24 +634,24 @@ public sealed class BSPrim : BSPhysObject
// Make solid or not (do things bounce off or pass through this object). // Make solid or not (do things bounce off or pass through this object).
MakeSolid(IsSolid); MakeSolid(IsSolid);
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, BSBody.ptr); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
// Rebuild its shape // Rebuild its shape
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, BSBody.ptr); BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
// Collision filter can be set only when the object is in the world // Collision filter can be set only when the object is in the world
if (BSBody.collisionFilter != 0 || BSBody.collisionMask != 0) if (PhysBody.collisionFilter != 0 || PhysBody.collisionMask != 0)
{ {
BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr, (uint)BSBody.collisionFilter, (uint)BSBody.collisionMask); BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, (uint)PhysBody.collisionFilter, (uint)PhysBody.collisionMask);
} }
// Recompute any linkset parameters. // Recompute any linkset parameters.
// When going from non-physical to physical, this re-enables the constraints that // When going from non-physical to physical, this re-enables the constraints that
// had been automatically disabled when the mass was set to zero. // had been automatically disabled when the mass was set to zero.
Linkset.Refresh(this, true); Linkset.Refresh(this);
DetailLog("{0},BSPrim.UpdatePhysicalParameters,exit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}", DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, BSBody, BSShape); LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape);
} }
// "Making dynamic" means changing to and from static. // "Making dynamic" means changing to and from static.
@ -620,75 +664,74 @@ public sealed class BSPrim : BSPhysObject
if (makeStatic) if (makeStatic)
{ {
// Become a Bullet 'static' object type // Become a Bullet 'static' object type
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_STATIC_OBJECT); CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
// Stop all movement // Stop all movement
BulletSimAPI.ClearAllForces2(BSBody.ptr); ZeroMotion();
// Center of mass is at the center of the object // Center of mass is at the center of the object
BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.BSBody.ptr, _position, _orientation); BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
// Mass is zero which disables a bunch of physics stuff in Bullet // Mass is zero which disables a bunch of physics stuff in Bullet
BulletSimAPI.SetMassProps2(BSBody.ptr, 0f, OMV.Vector3.Zero); UpdatePhysicalMassProperties(0f);
// There is no inertia in a static object
BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
// Set collision detection parameters // Set collision detection parameters
if (PhysicsScene.Params.ccdMotionThreshold > 0f) if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{ {
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold); BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
} }
// There can be special things needed for implementing linksets // There can be special things needed for implementing linksets
Linkset.MakeStatic(this); Linkset.MakeStatic(this);
// The activation state is 'disabled' so Bullet will not try to act on it. // The activation state is 'disabled' so Bullet will not try to act on it.
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_SIMULATION); BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
// Start it out sleeping and physical actions could wake it up. // Start it out sleeping and physical actions could wake it up.
// BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING);
BSBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; PhysBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter;
BSBody.collisionMask = CollisionFilterGroups.StaticObjectMask; PhysBody.collisionMask = CollisionFilterGroups.StaticObjectMask;
} }
else else
{ {
// Not a Bullet static object // Not a Bullet static object
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.CF_STATIC_OBJECT); CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
// Set various physical properties so internal dynamic properties will get computed correctly as they are set // Set various physical properties so internal dynamic properties will get computed correctly as they are set
BulletSimAPI.SetFriction2(BSBody.ptr, PhysicsScene.Params.defaultFriction); BulletSimAPI.SetFriction2(PhysBody.ptr, PhysicsScene.Params.defaultFriction);
BulletSimAPI.SetRestitution2(BSBody.ptr, PhysicsScene.Params.defaultRestitution); BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.defaultRestitution);
// per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
BulletSimAPI.ClearAllForces2(BSBody.ptr); // Since this can be called multiple times, only zero forces when becoming physical
// BulletSimAPI.ClearAllForces2(BSBody.ptr);
// For good measure, make sure the transform is set through to the motion state // For good measure, make sure the transform is set through to the motion state
BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
// Center of mass is at the center of the object
BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
// A dynamic object has mass // A dynamic object has mass
IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.ptr); UpdatePhysicalMassProperties(RawMass);
OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Mass);
BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia);
BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr);
// Set collision detection parameters // Set collision detection parameters
if (PhysicsScene.Params.ccdMotionThreshold > 0f) if (PhysicsScene.Params.ccdMotionThreshold > 0f)
{ {
BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold); BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
} }
// Various values for simulation limits // Various values for simulation limits
BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping); BulletSimAPI.SetDamping2(PhysBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping);
BulletSimAPI.SetDeactivationTime2(BSBody.ptr, PhysicsScene.Params.deactivationTime); BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, PhysicsScene.Params.deactivationTime);
BulletSimAPI.SetSleepingThresholds2(BSBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold);
BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold); BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
// There might be special things needed for implementing linksets. // There might be special things needed for implementing linksets.
Linkset.MakeDynamic(this); Linkset.MakeDynamic(this);
// Force activation of the object so Bullet will act on it. // Force activation of the object so Bullet will act on it.
// Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG); BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
// BulletSimAPI.Activate2(BSBody.ptr, true); // BulletSimAPI.Activate2(BSBody.ptr, true);
BSBody.collisionFilter = CollisionFilterGroups.ObjectFilter; PhysBody.collisionFilter = CollisionFilterGroups.ObjectFilter;
BSBody.collisionMask = CollisionFilterGroups.ObjectMask; PhysBody.collisionMask = CollisionFilterGroups.ObjectMask;
} }
} }
@ -698,7 +741,7 @@ public sealed class BSPrim : BSPhysObject
// the functions after this one set up the state of a possibly newly created collision body. // the functions after this one set up the state of a possibly newly created collision body.
private void MakeSolid(bool makeSolid) private void MakeSolid(bool makeSolid)
{ {
CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(BSBody.ptr); CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(PhysBody.ptr);
if (makeSolid) if (makeSolid)
{ {
// Verify the previous code created the correct shape for this type of thing. // Verify the previous code created the correct shape for this type of thing.
@ -706,7 +749,7 @@ public sealed class BSPrim : BSPhysObject
{ {
m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType);
} }
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
} }
else else
{ {
@ -714,22 +757,31 @@ public sealed class BSPrim : BSPhysObject
{ {
m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
} }
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
BSBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter; PhysBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter;
BSBody.collisionMask = CollisionFilterGroups.VolumeDetectMask; PhysBody.collisionMask = CollisionFilterGroups.VolumeDetectMask;
} }
} }
// Enable physical actions. Bullet will keep sleeping non-moving physical objects so
// they need waking up when parameters are changed.
// Called in taint-time!!
private void ActivateIfPhysical(bool forceIt)
{
if (IsPhysical)
BulletSimAPI.Activate2(PhysBody.ptr, forceIt);
}
// Turn on or off the flag controlling whether collision events are returned to the simulator. // Turn on or off the flag controlling whether collision events are returned to the simulator.
private void EnableCollisions(bool wantsCollisionEvents) private void EnableCollisions(bool wantsCollisionEvents)
{ {
if (wantsCollisionEvents) if (wantsCollisionEvents)
{ {
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
} }
else else
{ {
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
} }
} }
@ -774,9 +826,9 @@ public sealed class BSPrim : BSPhysObject
PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate()
{ {
if (_floatOnWater) if (_floatOnWater)
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
else else
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
}); });
} }
} }
@ -798,8 +850,8 @@ public sealed class BSPrim : BSPhysObject
// m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
{ {
// DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
BulletSimAPI.SetAngularVelocity2(BSBody.ptr, _rotationalVelocity); BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
}); });
} }
} }
@ -809,7 +861,7 @@ public sealed class BSPrim : BSPhysObject
} }
set { set {
_rotationalVelocity = value; _rotationalVelocity = value;
BulletSimAPI.SetAngularVelocity2(BSBody.ptr, _rotationalVelocity); BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
} }
} }
public override bool Kinematic { public override bool Kinematic {
@ -835,7 +887,7 @@ public sealed class BSPrim : BSPhysObject
// DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
// Buoyancy is faked by changing the gravity applied to the object // Buoyancy is faked by changing the gravity applied to the object
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav)); BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
} }
} }
@ -888,7 +940,7 @@ public sealed class BSPrim : BSPhysObject
m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
return; return;
} }
BSScene.TaintCallback addForceOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
{ {
OMV.Vector3 fSum = OMV.Vector3.Zero; OMV.Vector3 fSum = OMV.Vector3.Zero;
lock (m_accumulatedForces) lock (m_accumulatedForces)
@ -900,20 +952,60 @@ public sealed class BSPrim : BSPhysObject
} }
m_accumulatedForces.Clear(); m_accumulatedForces.Clear();
} }
// DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum); DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum);
// For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object. if (fSum != OMV.Vector3.Zero)
BulletSimAPI.ApplyCentralForce2(BSBody.ptr, fSum); BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum);
}; });
if (inTaintTime)
addForceOperation();
else
PhysicsScene.TaintedObject("BSPrim.AddForce", addForceOperation);
} }
private List<OMV.Vector3> m_accumulatedAngularForces = new List<OMV.Vector3>();
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
// DetailLog("{0},BSPrim.AddAngularForce,call,angForce={1},push={2}", LocalID, force, pushforce); AddAngularForce(force, pushforce, false);
// m_log.DebugFormat("{0}: AddAngularForce. f={1}, push={2}", LogHeader, force, pushforce);
} }
public void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime)
{
if (force.IsFinite())
{
// _force += force;
lock (m_accumulatedAngularForces)
m_accumulatedAngularForces.Add(new OMV.Vector3(force));
}
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)
{
BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum);
_torque = fSum;
}
});
}
// A torque impulse.
public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
{
OMV.Vector3 applyImpulse = impulse;
PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
{
DetailLog("{0},BSPrim.ApplyTorqueImpulse,taint,tImpulse={1}", LocalID, applyImpulse);
BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse);
});
}
public override void SetMomentum(OMV.Vector3 momentum) { public override void SetMomentum(OMV.Vector3 momentum) {
// DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum); // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum);
} }
@ -1195,9 +1287,7 @@ public sealed class BSPrim : BSPhysObject
returnMass = _density * volume; returnMass = _density * volume;
/* /* Comment out code that computes the mass of the linkset. That is done in the Linkset class.
* This change means each object keeps its own mass and the Mass property
* will return the sum if we're part of a linkset.
if (IsRootOfLinkset) if (IsRootOfLinkset)
{ {
foreach (BSPrim prim in _childrenPrims) foreach (BSPrim prim in _childrenPrims)
@ -1217,34 +1307,11 @@ public sealed class BSPrim : BSPhysObject
}// end CalculateMass }// end CalculateMass
#endregion Mass Calculation #endregion Mass Calculation
// Copy prim's info into the BulletSim shape description structure
public void FillShapeInfo(out ShapeData shape)
{
shape.ID = LocalID;
shape.Type = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN;
shape.Position = _position;
shape.Rotation = _orientation;
shape.Velocity = _velocity;
shape.Size = _size;
shape.Scale = Scale;
shape.Mass = _isPhysical ? _mass : 0f;
shape.Buoyancy = _buoyancy;
shape.HullKey = 0;
shape.MeshKey = 0;
shape.Friction = _friction;
shape.Restitution = _restitution;
shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse;
shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue;
}
// Rebuild the geometry and object. // Rebuild the geometry and object.
// This is called when the shape changes so we need to recreate the mesh/hull. // This is called when the shape changes so we need to recreate the mesh/hull.
// Called at taint-time!!! // Called at taint-time!!!
private void CreateGeomAndObject(bool forceRebuild) private void CreateGeomAndObject(bool forceRebuild)
{ {
ShapeData shapeData;
FillShapeInfo(out shapeData);
// If this prim is part of a linkset, we must remove and restore the physical // If this prim is part of a linkset, we must remove and restore the physical
// links if the body is rebuilt. // links if the body is rebuilt.
bool needToRestoreLinkset = false; bool needToRestoreLinkset = false;
@ -1253,8 +1320,7 @@ public sealed class BSPrim : BSPhysObject
// Updates BSBody and BSShape with the new information. // Updates BSBody and BSShape with the new information.
// Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
// Returns 'true' if either the body or the shape was changed. // Returns 'true' if either the body or the shape was changed.
PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape, PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody)
null, delegate(BulletBody dBody)
{ {
// Called if the current prim body is about to be destroyed. // Called if the current prim body is about to be destroyed.
// Remove all the physical dependencies on the old body. // Remove all the physical dependencies on the old body.
@ -1353,8 +1419,9 @@ public sealed class BSPrim : BSPhysObject
PositionSanityCheck(true); PositionSanityCheck(true);
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation;
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
// BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG

View File

@ -62,7 +62,7 @@ using OpenMetaverse;
// //
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BSScene : PhysicsScene, IPhysicsParameters public sealed class BSScene : PhysicsScene, IPhysicsParameters
{ {
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[BULLETS SCENE]"; private static readonly string LogHeader = "[BULLETS SCENE]";
@ -116,6 +116,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// True if initialized and ready to do simulation steps // True if initialized and ready to do simulation steps
private bool m_initialized = false; private bool m_initialized = false;
// Flag which is true when processing taints.
// Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
public bool InTaintTime { get; private set; }
// Pinned memory used to pass step information between managed and unmanaged // Pinned memory used to pass step information between managed and unmanaged
private int m_maxCollisionsPerFrame; private int m_maxCollisionsPerFrame;
private CollisionDesc[] m_collisionArray; private CollisionDesc[] m_collisionArray;
@ -171,7 +175,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
private Object _taintLock = new Object(); // lock for using the next object private Object _taintLock = new Object(); // lock for using the next object
private List<TaintCallbackEntry> _taintedObjects; private List<TaintCallbackEntry> _taintOperations;
private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
private List<TaintCallbackEntry> _postStepOperations;
// A pointer to an instance if this structure is passed to the C++ code // A pointer to an instance if this structure is passed to the C++ code
// Used to pass basic configuration values to the unmanaged code. // Used to pass basic configuration values to the unmanaged code.
@ -203,7 +209,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override void Initialise(IMesher meshmerizer, IConfigSource config) public override void Initialise(IMesher meshmerizer, IConfigSource config)
{ {
mesher = meshmerizer; mesher = meshmerizer;
_taintedObjects = new List<TaintCallbackEntry>(); _taintOperations = new List<TaintCallbackEntry>();
_postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
_postStepOperations = new List<TaintCallbackEntry>();
PhysObjects = new Dictionary<uint, BSPhysObject>(); PhysObjects = new Dictionary<uint, BSPhysObject>();
Shapes = new BSShapeCollection(this); Shapes = new BSShapeCollection(this);
@ -266,6 +274,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
TerrainManager = new BSTerrainManager(this); TerrainManager = new BSTerrainManager(this);
TerrainManager.CreateInitialGroundPlaneAndTerrain(); TerrainManager.CreateInitialGroundPlaneAndTerrain();
m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)Params.linksetImplementation);
InTaintTime = false;
m_initialized = true; m_initialized = true;
} }
@ -475,23 +486,21 @@ public class BSScene : PhysicsScene, IPhysicsParameters
if (!m_initialized) return 5.0f; if (!m_initialized) return 5.0f;
// update the prim states while we know the physics engine is not busy // update the prim states while we know the physics engine is not busy
int numTaints = _taintedObjects.Count; int numTaints = _taintOperations.Count;
ProcessTaints(); ProcessTaints();
// Some of the prims operate with special vehicle properties // Some of the prims operate with special vehicle properties
ProcessVehicles(timeStep); ProcessVehicles(timeStep);
numTaints += _taintedObjects.Count; numTaints += _taintOperations.Count;
ProcessTaints(); // the vehicles might have added taints ProcessTaints(); // the vehicles might have added taints
// step the physical world one interval // step the physical world one interval
m_simulationStep++; m_simulationStep++;
int numSubSteps = 0; int numSubSteps = 0;
// DEBUG
// DetailLog("{0},BSScene.Simulate,beforeStep,ntaimts={1},step={2}", DetailLogZero, numTaints, m_simulationStep);
try try
{ {
// DumpVehicles(); // DEBUG
if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
@ -500,6 +509,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}",
DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount);
// DumpVehicles(); // DEBUG
} }
catch (Exception e) catch (Exception e)
{ {
@ -575,6 +585,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
ProcessPostStepTaints();
// This causes the unmanaged code to output ALL the values found in ALL the objects in the world. // This 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. // Only enable this in a limited test world with few objects.
// BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
@ -670,6 +682,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override bool IsThreaded { get { return false; } } public override bool IsThreaded { get { return false; } }
#region Taints
// Calls to the PhysicsActors can't directly call into the physics engine // Calls to the PhysicsActors can't directly call into the physics engine
// because it might be busy. We delay changes to a known time. // because it might be busy. We delay changes to a known time.
// We rely on C#'s closure to save and restore the context for the delegate. // We rely on C#'s closure to save and restore the context for the delegate.
@ -679,30 +693,48 @@ public class BSScene : PhysicsScene, IPhysicsParameters
lock (_taintLock) lock (_taintLock)
{ {
_taintedObjects.Add(new TaintCallbackEntry(ident, callback)); _taintOperations.Add(new TaintCallbackEntry(ident, callback));
} }
return; return;
} }
// Sometimes a potentially tainted operation can be used in and out of taint time.
// This routine executes the command immediately if in taint-time otherwise it is queued.
public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
{
if (inTaintTime)
callback();
else
TaintedObject(ident, callback);
}
// When someone tries to change a property on a BSPrim or BSCharacter, the object queues // 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 // 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. // here just before the physics engine is called to step the simulation.
public void ProcessTaints() public void ProcessTaints()
{ {
if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process InTaintTime = true;
ProcessRegularTaints();
ProcessPostTaintTaints();
InTaintTime = false;
}
private void ProcessRegularTaints()
{
if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
{ {
int taintCount = m_taintsToProcessPerStep; int taintCount = m_taintsToProcessPerStep;
TaintCallbackEntry oneCallback = new TaintCallbackEntry(); TaintCallbackEntry oneCallback = new TaintCallbackEntry();
while (_taintedObjects.Count > 0 && taintCount-- > 0) while (_taintOperations.Count > 0 && taintCount-- > 0)
{ {
bool gotOne = false; bool gotOne = false;
lock (_taintLock) lock (_taintLock)
{ {
if (_taintedObjects.Count > 0) if (_taintOperations.Count > 0)
{ {
oneCallback = _taintedObjects[0]; oneCallback = _taintOperations[0];
_taintedObjects.RemoveAt(0); _taintOperations.RemoveAt(0);
gotOne = true; gotOne = true;
} }
} }
@ -746,6 +778,101 @@ public class BSScene : PhysicsScene, IPhysicsParameters
} }
} }
// Schedule an update to happen after all the regular taints are processed.
// Note that new requests for the same operation ("ident") for the same object ("ID")
// will replace any previous operation by the same object.
public void PostTaintObject(String ident, uint ID, TaintCallback callback)
{
if (!m_initialized) return;
string uniqueIdent = ident + "-" + ID.ToString();
lock (_taintLock)
{
_postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
}
return;
}
private void ProcessPostTaintTaints()
{
if (_postTaintOperations.Count > 0)
{
Dictionary<string, TaintCallbackEntry> oldList;
lock (_taintLock)
{
oldList = _postTaintOperations;
_postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
}
foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
{
try
{
DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
kvp.Value.callback();
}
catch (Exception e)
{
m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
}
}
oldList.Clear();
}
}
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();
}
}
public bool AssertInTaintTime(string whereFrom)
{
if (!InTaintTime)
{
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();
}
return InTaintTime;
}
#endregion // Taints
#region Vehicles #region Vehicles
public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType) public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType)
@ -970,49 +1097,49 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linearDamping; }, (s) => { return s.m_params[0].linearDamping; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ), (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, v); } ),
new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
0f, 0f,
(s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].angularDamping; }, (s) => { return s.m_params[0].angularDamping; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ), (s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, v); } ),
new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
0.2f, 0.2f,
(s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].deactivationTime; }, (s) => { return s.m_params[0].deactivationTime; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); },
(s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.BSBody.ptr, v); } ), (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
0.8f, 0.8f,
(s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linearSleepingThreshold; }, (s) => { return s.m_params[0].linearSleepingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ), (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
1.0f, 1.0f,
(s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].angularSleepingThreshold; }, (s) => { return s.m_params[0].angularSleepingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ), (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
0f, // set to zero to disable 0f, // set to zero to disable
(s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].ccdMotionThreshold; }, (s) => { return s.m_params[0].ccdMotionThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.BSBody.ptr, v); } ), (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
0f, 0f,
(s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].ccdSweptSphereRadius; }, (s) => { return s.m_params[0].ccdSweptSphereRadius; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); },
(s,o,v) => { BulletSimAPI.SetCcdSweepSphereRadius2(o.BSBody.ptr, v); } ), (s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
0.1f, 0.1f,
(s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].contactProcessingThreshold; }, (s) => { return s.m_params[0].contactProcessingThreshold; },
(s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); },
(s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.BSBody.ptr, v); } ), (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
0.5f, 0.5f,
@ -1107,6 +1234,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s) => { return s.m_params[0].numberOfSolverIterations; }, (s) => { return s.m_params[0].numberOfSolverIterations; },
(s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), (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.", new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
ConfigurationParameters.numericFalse, ConfigurationParameters.numericFalse,
(s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
@ -1128,12 +1260,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(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=no violation, 1=infinite. Default=0.1", new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
0.001f, 0.1f,
(s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linkConstraintCFM; }, (s) => { return s.m_params[0].linkConstraintCFM; },
(s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), (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", new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
0.8f, 0.1f,
(s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linkConstraintERP; }, (s) => { return s.m_params[0].linkConstraintERP; },
(s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
@ -1259,7 +1391,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// If the local ID is APPLY_TO_NONE, just change the default value // 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 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 // If the localID is a specific object, apply the parameter change to only that object
protected void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val) private void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val)
{ {
List<uint> objectIDs = new List<uint>(); List<uint> objectIDs = new List<uint>();
switch (localID) switch (localID)
@ -1284,7 +1416,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
} }
// schedule the actual updating of the paramter to when the phys engine is not busy // schedule the actual updating of the paramter to when the phys engine is not busy
protected void TaintedUpdateParameter(string parm, List<uint> lIDs, float val) private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
{ {
float xval = val; float xval = val;
List<uint> xlIDs = lIDs; List<uint> xlIDs = lIDs;
@ -1326,12 +1458,22 @@ public class BSScene : PhysicsScene, IPhysicsParameters
#endregion Runtime settable parameters #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. // Invoke the detailed logger and output something if it's enabled.
public void DetailLog(string msg, params Object[] args) public void DetailLog(string msg, params Object[] args)
{ {
PhysicsLogging.Write(msg, args); PhysicsLogging.Write(msg, args);
// Add the Flush() if debugging crashes to get all the messages written out. // Add the Flush() if debugging crashes. Gets all the messages written out.
// PhysicsLogging.Flush(); PhysicsLogging.Flush();
} }
// Used to fill in the LocalID when there isn't one. It's the correct number of characters. // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
public const string DetailLogZero = "0000000000"; public const string DetailLogZero = "0000000000";

View File

@ -34,11 +34,11 @@ using OpenSim.Region.Physics.ConvexDecompositionDotNet;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BSShapeCollection : IDisposable public sealed class BSShapeCollection : IDisposable
{ {
private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
protected BSScene PhysicsScene { get; set; } private BSScene PhysicsScene { get; set; }
private Object m_collectionActivityLock = new Object(); private Object m_collectionActivityLock = new Object();
@ -48,6 +48,7 @@ public class BSShapeCollection : IDisposable
public IntPtr ptr; public IntPtr ptr;
public int referenceCount; public int referenceCount;
public DateTime lastReferenced; public DateTime lastReferenced;
public UInt64 shapeKey;
} }
// Description of a hull. // Description of a hull.
@ -57,6 +58,7 @@ public class BSShapeCollection : IDisposable
public IntPtr ptr; public IntPtr ptr;
public int referenceCount; public int referenceCount;
public DateTime lastReferenced; public DateTime lastReferenced;
public UInt64 shapeKey;
} }
// The sharable set of meshes and hulls. Indexed by their shape hash. // The sharable set of meshes and hulls. Indexed by their shape hash.
@ -90,9 +92,10 @@ public class BSShapeCollection : IDisposable
// remove the physical constraints before the body is destroyed. // remove the physical constraints before the body is destroyed.
// Called at taint-time!! // Called at taint-time!!
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim, public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
ShapeData shapeData, PrimitiveBaseShape pbs,
ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
{ {
PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
bool ret = false; bool ret = false;
// This lock could probably be pushed down lower but building shouldn't take long // This lock could probably be pushed down lower but building shouldn't take long
@ -100,41 +103,38 @@ public class BSShapeCollection : IDisposable
{ {
// Do we have the correct geometry for this type of object? // Do we have the correct geometry for this type of object?
// Updates prim.BSShape with information/pointers to shape. // Updates prim.BSShape with information/pointers to shape.
// CreateGeom returns 'true' of BSShape as changed to a new shape. // Returns 'true' of BSShape is changed to a new shape.
bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback); bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback);
// If we had to select a new shape geometry for the object, // If we had to select a new shape geometry for the object,
// rebuild the body around it. // rebuild the body around it.
// Updates prim.BSBody with information/pointers to requested body // Updates prim.BSBody with information/pointers to requested body
// Returns 'true' if BSBody was changed.
bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
prim.BSShape, shapeData, bodyCallback); prim.PhysShape, bodyCallback);
ret = newGeom || newBody; ret = newGeom || newBody;
} }
DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}", DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
prim.LocalID, forceRebuild, ret, prim.BSBody, prim.BSShape); prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
return ret; return ret;
} }
// Track another user of a body // Track another user of a body.
// We presume the caller has allocated the body. // We presume the caller has allocated the body.
// Bodies only have one user so the body is just put into the world if not already there. // Bodies only have one user so the body is just put into the world if not already there.
public void ReferenceBody(BulletBody body, bool inTaintTime) public void ReferenceBody(BulletBody body, bool inTaintTime)
{ {
lock (m_collectionActivityLock) lock (m_collectionActivityLock)
{ {
DetailLog("{0},BSShapeCollection.ReferenceBody,newBody", body.ID, body); DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
BSScene.TaintCallback createOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
{ {
if (!BulletSimAPI.IsInWorld2(body.ptr)) if (!BulletSimAPI.IsInWorld2(body.ptr))
{ {
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
} }
}; });
if (inTaintTime)
createOperation();
else
PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation);
} }
} }
@ -147,25 +147,23 @@ public class BSShapeCollection : IDisposable
lock (m_collectionActivityLock) lock (m_collectionActivityLock)
{ {
BSScene.TaintCallback removeOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
{ {
DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}", DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
body.ID, body.ptr.ToString("X"), inTaintTime); body.ID, body, inTaintTime);
// If the caller needs to know the old body is going away, pass the event up. // If the caller needs to know the old body is going away, pass the event up.
if (bodyCallback != null) bodyCallback(body); if (bodyCallback != null) bodyCallback(body);
// It may have already been removed from the world in which case the next is a NOOP. if (BulletSimAPI.IsInWorld2(body.ptr))
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); {
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
}
// Zero any reference to the shape so it is not freed when the body is deleted. // Zero any reference to the shape so it is not freed when the body is deleted.
BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero); BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr); BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
}; });
// If already in taint-time, do the operations now. Otherwise queue for later.
if (inTaintTime)
removeOperation();
else
PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
} }
} }
@ -175,7 +173,7 @@ public class BSShapeCollection : IDisposable
// Meshes and hulls for the same shape have the same hash key. // Meshes and hulls for the same shape have the same hash key.
// NOTE that native shapes are not added to the mesh list or removed. // NOTE that native shapes are not added to the mesh list or removed.
// Returns 'true' if this is the initial reference to the shape. Otherwise reused. // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
private bool ReferenceShape(BulletShape shape) public bool ReferenceShape(BulletShape shape)
{ {
bool ret = false; bool ret = false;
switch (shape.type) switch (shape.type)
@ -193,6 +191,7 @@ public class BSShapeCollection : IDisposable
{ {
// This is a new reference to a mesh // This is a new reference to a mesh
meshDesc.ptr = shape.ptr; meshDesc.ptr = shape.ptr;
meshDesc.shapeKey = shape.shapeKey;
// We keep a reference to the underlying IMesh data so a hull can be built // We keep a reference to the underlying IMesh data so a hull can be built
meshDesc.referenceCount = 1; meshDesc.referenceCount = 1;
DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
@ -215,6 +214,7 @@ public class BSShapeCollection : IDisposable
{ {
// This is a new reference to a hull // This is a new reference to a hull
hullDesc.ptr = shape.ptr; hullDesc.ptr = shape.ptr;
hullDesc.shapeKey = shape.shapeKey;
hullDesc.referenceCount = 1; hullDesc.referenceCount = 1;
DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
@ -239,7 +239,7 @@ public class BSShapeCollection : IDisposable
if (shape.ptr == IntPtr.Zero) if (shape.ptr == IntPtr.Zero)
return; return;
BSScene.TaintCallback dereferenceOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
{ {
if (shape.ptr != IntPtr.Zero) if (shape.ptr != IntPtr.Zero)
{ {
@ -261,6 +261,9 @@ public class BSShapeCollection : IDisposable
case ShapeData.PhysicsShapeType.SHAPE_MESH: case ShapeData.PhysicsShapeType.SHAPE_MESH:
DereferenceMesh(shape, shapeCallback); DereferenceMesh(shape, shapeCallback);
break; break;
case ShapeData.PhysicsShapeType.SHAPE_COMPOUND:
DereferenceCompound(shape, shapeCallback);
break;
case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
break; break;
default: default:
@ -268,18 +271,7 @@ public class BSShapeCollection : IDisposable
} }
} }
} }
}; });
if (inTaintTime)
{
lock (m_collectionActivityLock)
{
dereferenceOperation();
}
}
else
{
PhysicsScene.TaintedObject("BSShapeCollection.DereferenceShape", dereferenceOperation);
}
} }
// Count down the reference count for a mesh shape // Count down the reference count for a mesh shape
@ -294,8 +286,8 @@ public class BSShapeCollection : IDisposable
if (shapeCallback != null) shapeCallback(shape); if (shapeCallback != null) shapeCallback(shape);
meshDesc.lastReferenced = System.DateTime.Now; meshDesc.lastReferenced = System.DateTime.Now;
Meshes[shape.shapeKey] = meshDesc; Meshes[shape.shapeKey] = meshDesc;
DetailLog("{0},BSShapeCollection.DereferenceMesh,key={1},refCnt={2}", DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); BSScene.DetailLogZero, shape, meshDesc.referenceCount);
} }
} }
@ -309,11 +301,94 @@ public class BSShapeCollection : IDisposable
{ {
hullDesc.referenceCount--; hullDesc.referenceCount--;
// TODO: release the Bullet storage (aging old entries?) // TODO: release the Bullet storage (aging old entries?)
// Tell upper layers that, if they have dependencies on this shape, this link is going away
if (shapeCallback != null) shapeCallback(shape); if (shapeCallback != null) shapeCallback(shape);
hullDesc.lastReferenced = System.DateTime.Now; hullDesc.lastReferenced = System.DateTime.Now;
Hulls[shape.shapeKey] = hullDesc; Hulls[shape.shapeKey] = hullDesc;
DetailLog("{0},BSShapeCollection.DereferenceHull,key={1},refCnt={2}", DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); BSScene.DetailLogZero, shape, hullDesc.referenceCount);
}
}
// Remove a reference to a compound shape.
// Taking a compound shape apart is a little tricky because if you just delete the
// physical shape, it will free all the underlying children. We can't do that because
// they could be shared. So, this removes each of the children from the compound and
// dereferences them separately before destroying the compound collision object itself.
// Called at taint-time.
private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback)
{
if (!BulletSimAPI.IsCompound2(shape.ptr))
{
// Failed the sanity check!!
PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
LogHeader, shape.type, shape.ptr.ToString("X"));
DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X"));
return;
}
int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
for (int ii = numChildren - 1; ii >= 0; ii--)
{
IntPtr childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii);
DereferenceAnonCollisionShape(childShape);
}
BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
}
// Sometimes we have a pointer to a collision shape but don't know what type it is.
// Figure out type and call the correct dereference routine.
// Called at taint-time.
private void DereferenceAnonCollisionShape(IntPtr cShape)
{
MeshDesc meshDesc;
HullDesc hullDesc;
BulletShape shapeInfo = new BulletShape(cShape);
if (TryGetMeshByPtr(cShape, out meshDesc))
{
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_MESH;
shapeInfo.shapeKey = meshDesc.shapeKey;
}
else
{
if (TryGetHullByPtr(cShape, out hullDesc))
{
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_HULL;
shapeInfo.shapeKey = hullDesc.shapeKey;
}
else
{
if (BulletSimAPI.IsCompound2(cShape))
{
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_COMPOUND;
}
else
{
if (BulletSimAPI.IsNativeShape2(cShape))
{
shapeInfo.isNativeShape = true;
shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
}
}
}
}
DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
if (shapeInfo.type != ShapeData.PhysicsShapeType.SHAPE_UNKNOWN)
{
DereferenceShape(shapeInfo, true, null);
}
else
{
PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}",
LogHeader, PhysicsScene.RegionName, cShape.ToString("X"));
} }
} }
@ -325,22 +400,46 @@ public class BSShapeCollection : IDisposable
// Info in prim.BSShape is updated to the new shape. // Info in prim.BSShape is updated to the new shape.
// Returns 'true' if the geometry was rebuilt. // Returns 'true' if the geometry was rebuilt.
// Called at taint-time! // Called at taint-time!
private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData, private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) {
bool ret = false;
bool haveShape = false;
if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
{
// an avatar capsule is close to a native shape (it is not shared)
ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR,
ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
ret = true;
haveShape = true;
}
// Compound shapes are handled special as they are rebuilt from scratch.
// This isn't too great a hardship since most of the child shapes will already been created.
if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND)
{
ret = GetReferenceToCompoundShape(prim, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
haveShape = true;
}
if (!haveShape)
{
ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback);
}
return ret;
}
// Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
private bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{ {
bool ret = false; bool ret = false;
bool haveShape = false; bool haveShape = false;
bool nativeShapePossible = true; bool nativeShapePossible = true;
PrimitiveBaseShape pbs = prim.BaseShape;
if (shapeData.Type == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
{
// an avatar capsule is close to a native shape (it is not shared)
ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR,
ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.BSShape);
ret = true;
haveShape = true;
}
// If the prim attributes are simple, this could be a simple Bullet native shape // If the prim attributes are simple, this could be a simple Bullet native shape
if (!haveShape if (!haveShape
&& pbs != null && pbs != null
@ -354,98 +453,110 @@ public class BSShapeCollection : IDisposable
&& pbs.PathScaleX == 100 && pbs.PathScaleY == 100 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
&& pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
{ {
// It doesn't look like Bullet scales spheres so make sure the scales are all equal
if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
&& pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
{ {
haveShape = true; haveShape = true;
if (forceRebuild if (forceRebuild
|| prim.Scale != shapeData.Size || prim.Scale != prim.Size
|| prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE || prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE
) )
{ {
ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE, ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_SPHERE,
ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.BSShape); prim.LocalID, forceRebuild, prim.PhysShape);
} }
} }
if (pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
{ {
haveShape = true; haveShape = true;
if (forceRebuild if (forceRebuild
|| prim.Scale != shapeData.Size || prim.Scale != prim.Size
|| prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX || prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX
) )
{ {
ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX, ret = GetReferenceToNativeShape( prim, ShapeData.PhysicsShapeType.SHAPE_BOX,
ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); ShapeData.FixedShapeKey.KEY_BOX, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
prim.LocalID, forceRebuild, prim.BSShape); prim.LocalID, forceRebuild, prim.PhysShape);
} }
} }
} }
// If a simple shape is not happening, create a mesh and possibly a hull. // If a simple shape is not happening, create a mesh and possibly a hull.
// Note that if it's a native shape, the check for physical/non-physical is not
// made. Native shapes are best used in either case.
if (!haveShape && pbs != null) if (!haveShape && pbs != null)
{ {
if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects) ret = CreateGeomMeshOrHull(prim, shapeCallback);
{ }
// Update prim.BSShape to reference a hull of this shape.
ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback); return ret;
DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", }
shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X"));
} public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
else {
{
ret = GetReferenceToMesh(prim, shapeData, pbs, shapeCallback); bool ret = false;
DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", // Note that if it's a native shape, the check for physical/non-physical is not
shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X")); // made. Native shapes work in either case.
} if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects)
{
// Update prim.BSShape to reference a hull of this shape.
ret = GetReferenceToHull(prim,shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
}
else
{
ret = GetReferenceToMesh(prim, shapeCallback);
DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
} }
return ret; return ret;
} }
// Creates a native shape and assignes it to prim.BSShape. // Creates a native shape and assignes it to prim.BSShape.
// "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
private bool GetReferenceToNativeShape(BSPhysObject prim, ShapeData shapeData, private bool GetReferenceToNativeShape(BSPhysObject prim,
ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
ShapeDestructionCallback shapeCallback) ShapeDestructionCallback shapeCallback)
{ {
// release any previous shape // release any previous shape
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.PhysShape, true, shapeCallback);
shapeData.Type = shapeType;
// 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
prim.Scale = shapeData.Size; prim.Scale = prim.Size;
shapeData.Scale = shapeData.Size;
BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey); BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
// Don't need to do a 'ReferenceShape()' here because native shapes are not shared. // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
shapeData.ID, newShape, shapeData.Scale); prim.LocalID, newShape, prim.Scale);
prim.BSShape = newShape; prim.PhysShape = newShape;
return true; return true;
} }
private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType, private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, ShapeData.PhysicsShapeType shapeType,
ShapeData shapeData, ShapeData.FixedShapeKey shapeKey) ShapeData.FixedShapeKey shapeKey)
{ {
BulletShape newShape; BulletShape newShape;
// Need to make sure the passed shape information is for the native type. // Need to make sure the passed shape information is for the native type.
ShapeData nativeShapeData = shapeData; ShapeData nativeShapeData = new ShapeData();
nativeShapeData.Type = shapeType; nativeShapeData.Type = shapeType;
nativeShapeData.ID = prim.LocalID;
nativeShapeData.Scale = prim.Scale;
nativeShapeData.Size = prim.Scale;
nativeShapeData.MeshKey = (ulong)shapeKey; nativeShapeData.MeshKey = (ulong)shapeKey;
nativeShapeData.HullKey = (ulong)shapeKey; nativeShapeData.HullKey = (ulong)shapeKey;
if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
{ {
newShape = new BulletShape( newShape = new BulletShape(
BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, nativeShapeData.Scale) BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
, shapeType); , shapeType);
DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", nativeShapeData.ID, nativeShapeData.Scale); DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
} }
else else
{ {
@ -454,7 +565,7 @@ public class BSShapeCollection : IDisposable
if (newShape.ptr == IntPtr.Zero) if (newShape.ptr == IntPtr.Zero)
{ {
PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
LogHeader, nativeShapeData.ID, nativeShapeData.Type); LogHeader, prim.LocalID, shapeType);
} }
newShape.shapeKey = (System.UInt64)shapeKey; newShape.shapeKey = (System.UInt64)shapeKey;
newShape.isNativeShape = true; newShape.isNativeShape = true;
@ -466,33 +577,32 @@ public class BSShapeCollection : IDisposable
// Dereferences previous shape in BSShape and adds a reference for this new shape. // Dereferences previous shape in BSShape and adds a reference for this new shape.
// Returns 'true' of a mesh was actually built. Otherwise . // Returns 'true' of a mesh was actually built. Otherwise .
// Called at taint-time! // Called at taint-time!
private bool GetReferenceToMesh(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape = new BulletShape(IntPtr.Zero); BulletShape newShape = new BulletShape(IntPtr.Zero);
float lod; float lod;
System.UInt64 newMeshKey = ComputeShapeKey(shapeData, pbs, out lod); System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
// if this new shape is the same as last time, don't recreate the mesh // if this new shape is the same as last time, don't recreate the mesh
if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH)
return false; return false;
DetailLog("{0},BSShapeCollection.CreateGeomMesh,create,oldKey={1},newKey={2}", DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newMeshKey.ToString("X")); prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
// Since we're recreating new, get rid of the reference to the previous shape // Since we're recreating new, get rid of the reference to the previous shape
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.PhysShape, true, shapeCallback);
newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, prim.BaseShape, prim.Size, lod);
// Take evasive action if the mesh was not constructed. // Take evasive action if the mesh was not constructed.
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs); newShape = VerifyMeshCreated(newShape, prim);
ReferenceShape(newShape); ReferenceShape(newShape);
// meshes are already scaled by the meshmerizer // meshes are already scaled by the meshmerizer
prim.Scale = new OMV.Vector3(1f, 1f, 1f); prim.Scale = new OMV.Vector3(1f, 1f, 1f);
prim.BSShape = newShape; prim.PhysShape = newShape;
return true; // 'true' means a new shape has been added to this prim return true; // 'true' means a new shape has been added to this prim
} }
@ -526,7 +636,7 @@ public class BSShapeCollection : IDisposable
verticesAsFloats[vi++] = vv.Z; verticesAsFloats[vi++] = vv.Z;
} }
// m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", // m_log.DebugFormat("{0}: BSShapeCollection.CreatePhysicalMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
// LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
@ -541,32 +651,31 @@ public class BSShapeCollection : IDisposable
// See that hull shape exists in the physical world and update prim.BSShape. // See that hull shape exists in the physical world and update prim.BSShape.
// We could be creating the hull because scale changed or whatever. // We could be creating the hull because scale changed or whatever.
private bool GetReferenceToHull(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
ShapeDestructionCallback shapeCallback)
{ {
BulletShape newShape; BulletShape newShape;
float lod; float lod;
System.UInt64 newHullKey = ComputeShapeKey(shapeData, pbs, out lod); System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
// if the hull hasn't changed, don't rebuild it // if the hull hasn't changed, don't rebuild it
if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL)
return false; return false;
DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X")); prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
// Remove usage of the previous shape. // Remove usage of the previous shape.
DereferenceShape(prim.BSShape, true, shapeCallback); DereferenceShape(prim.PhysShape, true, shapeCallback);
newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod);
newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs); newShape = VerifyMeshCreated(newShape, prim);
ReferenceShape(newShape); ReferenceShape(newShape);
// hulls are already scaled by the meshmerizer // hulls are already scaled by the meshmerizer
prim.Scale = new OMV.Vector3(1f, 1f, 1f); prim.Scale = new OMV.Vector3(1f, 1f, 1f);
prim.BSShape = newShape; prim.PhysShape = newShape;
return true; // 'true' means a new shape has been added to this prim return true; // 'true' means a new shape has been added to this prim
} }
@ -685,9 +794,31 @@ public class BSShapeCollection : IDisposable
return; return;
} }
// Compound shapes are always built from scratch.
// This shouldn't be to bad since most of the parts will be meshes that had been built previously.
private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
{
// Remove reference to the old shape
// Don't need to do this as the shape is freed when the new root shape is created below.
// DereferenceShape(prim.PhysShape, true, shapeCallback);
BulletShape cShape = new BulletShape(
BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), ShapeData.PhysicsShapeType.SHAPE_COMPOUND);
// Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
CreateGeomMeshOrHull(prim, shapeCallback);
BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
prim.LocalID, cShape, prim.PhysShape);
prim.PhysShape = cShape;
return true;
}
// Create a hash of all the shape parameters to be used as a key // Create a hash of all the shape parameters to be used as a key
// for this particular shape. // for this particular shape.
private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod) private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
{ {
// level of detail based on size and type of the object // level of detail based on size and type of the object
float lod = PhysicsScene.MeshLOD; float lod = PhysicsScene.MeshLOD;
@ -695,40 +826,40 @@ public class BSShapeCollection : IDisposable
lod = PhysicsScene.SculptLOD; lod = PhysicsScene.SculptLOD;
// Mega prims usually get more detail because one can interact with shape approximations at this size. // Mega prims usually get more detail because one can interact with shape approximations at this size.
float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) if (maxAxis > PhysicsScene.MeshMegaPrimThreshold)
lod = PhysicsScene.MeshMegaPrimLOD; lod = PhysicsScene.MeshMegaPrimLOD;
retLod = lod; retLod = lod;
return pbs.GetMeshKey(shapeData.Size, lod); return pbs.GetMeshKey(size, lod);
} }
// For those who don't want the LOD // For those who don't want the LOD
private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs) private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs)
{ {
float lod; float lod;
return ComputeShapeKey(shapeData, pbs, out lod); return ComputeShapeKey(size, pbs, out lod);
} }
// The creation of a mesh or hull can fail if an underlying asset is not available. // The creation of a mesh or hull can fail if an underlying asset is not available.
// There are two cases: 1) the asset is not in the cache and it needs to be fetched; // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
// and 2) the asset cannot be converted (like decompressing JPEG2000s). // and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
// The first case causes the asset to be fetched. The second case just requires // The first case causes the asset to be fetched. The second case requires
// us to not loop forever. // us to not loop forever.
// Called after creating a physical mesh or hull. If the physical shape was created, // Called after creating a physical mesh or hull. If the physical shape was created,
// just return. // just return.
private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs) private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
{ {
// If the shape was successfully created, nothing more to do // If the shape was successfully created, nothing more to do
if (newShape.ptr != IntPtr.Zero) if (newShape.ptr != IntPtr.Zero)
return newShape; return newShape;
// If this mesh has an underlying asset and we have not failed getting it before, fetch the asset // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
if (pbs.SculptEntry && !prim.LastAssetBuildFailed && pbs.SculptTexture != OMV.UUID.Zero) if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero)
{ {
prim.LastAssetBuildFailed = true; prim.LastAssetBuildFailed = true;
BSPhysObject xprim = prim; BSPhysObject xprim = prim;
DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}", DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}",
LogHeader, shapeData.ID.ToString("X"), prim.LastAssetBuildFailed); LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
Util.FireAndForget(delegate Util.FireAndForget(delegate
{ {
RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
@ -745,7 +876,7 @@ public class BSShapeCollection : IDisposable
yprim.BaseShape.SculptData = asset.Data; yprim.BaseShape.SculptData = asset.Data;
// This will cause the prim to see that the filler shape is not the right // This will cause the prim to see that the filler shape is not the right
// one and try again to build the object. // one and try again to build the object.
// No race condition with the native sphere setting since the rebuild is at taint time. // No race condition with the normal shape setting since the rebuild is at taint time.
yprim.ForceBodyShapeRebuild(false); yprim.ForceBodyShapeRebuild(false);
}); });
@ -757,13 +888,13 @@ public class BSShapeCollection : IDisposable
if (prim.LastAssetBuildFailed) if (prim.LastAssetBuildFailed)
{ {
PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}", PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}",
LogHeader, shapeData.ID, pbs.SculptTexture); LogHeader, prim.LocalID, prim.BaseShape.SculptTexture);
} }
} }
// While we figure out the real problem, stick a simple native shape on the object. // While we figure out the real problem, stick a simple native shape on the object.
BulletShape fillinShape = BulletShape fillinShape =
BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_BOX, shapeData, ShapeData.FixedShapeKey.KEY_BOX); BuildPhysicalNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX);
return fillinShape; return fillinShape;
} }
@ -773,18 +904,18 @@ public class BSShapeCollection : IDisposable
// Returns 'true' if an object was actually created. // Returns 'true' if an object was actually created.
// Called at taint-time. // Called at taint-time.
private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape, private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
ShapeData shapeData, BodyDestructionCallback bodyCallback) BodyDestructionCallback bodyCallback)
{ {
bool ret = false; bool ret = false;
// the mesh, hull or native shape must have already been created in Bullet // the mesh, hull or native shape must have already been created in Bullet
bool mustRebuild = (prim.BSBody.ptr == IntPtr.Zero); bool mustRebuild = (prim.PhysBody.ptr == IntPtr.Zero);
// If there is an existing body, verify it's of an acceptable type. // If there is an existing body, verify it's of an acceptable type.
// If not a solid object, body is a GhostObject. Otherwise a RigidBody. // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
if (!mustRebuild) if (!mustRebuild)
{ {
CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.BSBody.ptr); CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr);
if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
|| !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
{ {
@ -796,27 +927,27 @@ public class BSShapeCollection : IDisposable
if (mustRebuild || forceRebuild) if (mustRebuild || forceRebuild)
{ {
// Free any old body // Free any old body
DereferenceBody(prim.BSBody, true, bodyCallback); DereferenceBody(prim.PhysBody, true, bodyCallback);
BulletBody aBody; BulletBody aBody;
IntPtr bodyPtr = IntPtr.Zero; IntPtr bodyPtr = IntPtr.Zero;
if (prim.IsSolid) if (prim.IsSolid)
{ {
bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
shapeData.ID, shapeData.Position, shapeData.Rotation); prim.LocalID, prim.RawPosition, prim.RawOrientation);
DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
} }
else else
{ {
bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
shapeData.ID, shapeData.Position, shapeData.Rotation); prim.LocalID, prim.ForcePosition, prim.ForceOrientation);
DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
} }
aBody = new BulletBody(shapeData.ID, bodyPtr); aBody = new BulletBody(prim.LocalID, bodyPtr);
ReferenceBody(aBody, true); ReferenceBody(aBody, true);
prim.BSBody = aBody; prim.PhysBody = aBody;
ret = true; ret = true;
} }
@ -824,6 +955,42 @@ public class BSShapeCollection : IDisposable
return ret; return ret;
} }
private bool TryGetMeshByPtr(IntPtr addr, out MeshDesc outDesc)
{
bool ret = false;
MeshDesc foundDesc = new MeshDesc();
foreach (MeshDesc md in Meshes.Values)
{
if (md.ptr == addr)
{
foundDesc = md;
ret = true;
break;
}
}
outDesc = foundDesc;
return ret;
}
private bool TryGetHullByPtr(IntPtr addr, out HullDesc outDesc)
{
bool ret = false;
HullDesc foundDesc = new HullDesc();
foreach (HullDesc hd in Hulls.Values)
{
if (hd.ptr == addr)
{
foundDesc = hd;
ret = true;
break;
}
}
outDesc = foundDesc;
return ret;
}
private void DetailLog(string msg, params Object[] args) private void DetailLog(string msg, params Object[] args)
{ {
if (PhysicsScene.PhysicsLogging.Enabled) if (PhysicsScene.PhysicsLogging.Enabled)

View File

@ -40,7 +40,7 @@ using OpenMetaverse;
namespace OpenSim.Region.Physics.BulletSPlugin namespace OpenSim.Region.Physics.BulletSPlugin
{ {
public class BSTerrainManager public sealed class BSTerrainManager
{ {
static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
@ -238,7 +238,7 @@ public class BSTerrainManager
DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}", DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,call,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}",
BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY); BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY);
BSScene.TaintCallback rebuildOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:UpdateExisting", delegate()
{ {
if (MegaRegionParentPhysicsScene != null) if (MegaRegionParentPhysicsScene != null)
{ {
@ -337,14 +337,7 @@ public class BSTerrainManager
BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
m_terrainModified = true; m_terrainModified = true;
}; });
// There is the option to do the changes now (we're already in 'taint time'), or
// to do the Bullet operations later.
if (inTaintTime)
rebuildOperation();
else
PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation);
} }
else else
{ {
@ -364,7 +357,7 @@ public class BSTerrainManager
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
// Code that must happen at taint-time // Code that must happen at taint-time
BSScene.TaintCallback createOperation = delegate() PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateOrCreateTerrain:NewTerrain", delegate()
{ {
DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y); DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,taint,baseX={1},baseY={2}", BSScene.DetailLogZero, minCoords.X, minCoords.Y);
// Create a new mapInfo that will be filled with the new info // Create a new mapInfo that will be filled with the new info
@ -377,13 +370,7 @@ public class BSTerrainManager
UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true); UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
m_terrainModified = true; m_terrainModified = true;
}; });
// If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
if (inTaintTime)
createOperation();
else
PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation);
} }
} }

View File

@ -194,6 +194,7 @@ public struct ShapeData
// following defined by BulletSim // following defined by BulletSim
SHAPE_GROUNDPLANE = 20, SHAPE_GROUNDPLANE = 20,
SHAPE_TERRAIN = 21, SHAPE_TERRAIN = 21,
SHAPE_COMPOUND = 22,
}; };
public uint ID; public uint ID;
public PhysicsShapeType Type; public PhysicsShapeType Type;
@ -299,6 +300,7 @@ public struct ConfigurationParameters
public float shouldEnableFrictionCaching; public float shouldEnableFrictionCaching;
public float numberOfSolverIterations; public float numberOfSolverIterations;
public float linksetImplementation;
public float linkConstraintUseFrameOffset; public float linkConstraintUseFrameOffset;
public float linkConstraintEnableTransMotor; public float linkConstraintEnableTransMotor;
public float linkConstraintTransMotorMaxVel; public float linkConstraintTransMotorMaxVel;
@ -378,6 +380,7 @@ public enum CollisionFilterGroups : uint
BTerrainFilter = 1 << 11, BTerrainFilter = 1 << 11,
BRaycastFilter = 1 << 12, BRaycastFilter = 1 << 12,
BSolidFilter = 1 << 13, BSolidFilter = 1 << 13,
BLinksetFilter = 1 << 14,
// The collsion filters and masked are defined in one place -- don't want them scattered // The collsion filters and masked are defined in one place -- don't want them scattered
AvatarFilter = BCharacterFilter, AvatarFilter = BCharacterFilter,
@ -386,6 +389,8 @@ public enum CollisionFilterGroups : uint
ObjectMask = BAllFilter, ObjectMask = BAllFilter,
StaticObjectFilter = BStaticFilter, StaticObjectFilter = BStaticFilter,
StaticObjectMask = BAllFilter, StaticObjectMask = BAllFilter,
LinksetFilter = BLinksetFilter,
LinksetMask = BAllFilter & ~BLinksetFilter,
VolumeDetectFilter = BSensorTrigger, VolumeDetectFilter = BSensorTrigger,
VolumeDetectMask = ~BSensorTrigger, VolumeDetectMask = ~BSensorTrigger,
TerrainFilter = BTerrainFilter, TerrainFilter = BTerrainFilter,
@ -395,8 +400,6 @@ public enum CollisionFilterGroups : uint
}; };
// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0 // 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. // ERP controls amount of correction per tick. Usable range=0.1..0.8. Default=0.2.
public enum ConstraintParams : int public enum ConstraintParams : int
@ -611,13 +614,22 @@ public static extern bool IsNativeShape2(IntPtr shape);
public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateCompoundShape2(IntPtr sim); public static extern IntPtr CreateCompoundShape2(IntPtr sim, bool enableDynamicAabbTree);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void AddChildToCompoundShape2(IntPtr cShape, IntPtr addShape, Vector3 pos, Quaternion rot); public static extern int GetNumberOfCompoundChildren2(IntPtr cShape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void RemoveChildFromCompoundShape2(IntPtr cShape, IntPtr removeShape); public static extern void AddChildShapeToCompoundShape2(IntPtr cShape, IntPtr addShape, Vector3 pos, Quaternion rot);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetChildShapeFromCompoundShapeIndex2(IntPtr cShape, int indx);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr RemoveChildShapeFromCompoundShapeIndex2(IntPtr cShape, int indx);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void RemoveChildShapeFromCompoundShape2(IntPtr cShape, IntPtr removeShape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr DuplicateCollisionShape2(IntPtr sim, IntPtr srcShape, uint id); public static extern IntPtr DuplicateCollisionShape2(IntPtr sim, IntPtr srcShape, uint id);
@ -881,10 +893,10 @@ public static extern float GetCcdMotionThreshold2(IntPtr obj);
public static extern void SetCcdMotionThreshold2(IntPtr obj, float val); public static extern void SetCcdMotionThreshold2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetCcdSweepSphereRadius2(IntPtr obj); public static extern float GetCcdSweptSphereRadius2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetCcdSweepSphereRadius2(IntPtr obj, float val); public static extern void SetCcdSweptSphereRadius2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetUserPointer2(IntPtr obj); public static extern IntPtr GetUserPointer2(IntPtr obj);

View File

@ -460,6 +460,15 @@ namespace OpenSim.Services.HypergridService
if (folders != null && folders.Length > 0) if (folders != null && folders.Length > 0)
return folders[0]; return folders[0];
// OK, so the RootFolder type didn't work. Let's look for any type with parent UUID.Zero.
folders = m_Database.GetFolders(
new string[] { "agentID", "folderName", "parentFolderID" },
new string[] { principalID.ToString(), "My Inventory", UUID.Zero.ToString() });
if (folders != null && folders.Length > 0)
return folders[0];
return null; return null;
} }

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) Contributors, http://aurora-sim.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 copyright
* 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 Aurora-Sim 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.
*/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.