diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 2e6b2dad90..623ac8ff16 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -41,8 +41,6 @@ public class BSCharacter : BSPhysObject // private bool _stopped; private OMV.Vector3 _size; - private OMV.Vector3 _scale; - private PrimitiveBaseShape _pbs; private bool _grabbed; private bool _selected; private OMV.Vector3 _position; @@ -67,6 +65,10 @@ public class BSCharacter : BSPhysObject private bool _kinematic; private float _buoyancy; + // The friction and velocity of the avatar is modified depending on whether walking or not. + private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar + private float _currentFriction; // the friction currently being used (changed by setVelocity). + private OMV.Vector3 _PIDTarget; private bool _usePID; private float _PIDTau; @@ -84,14 +86,18 @@ public class BSCharacter : BSPhysObject _flying = isFlying; _orientation = OMV.Quaternion.Identity; _velocity = OMV.Vector3.Zero; + _appliedVelocity = OMV.Vector3.Zero; _buoyancy = ComputeBuoyancyFromFlying(isFlying); + _currentFriction = PhysicsScene.Params.avatarStandingFriction; + _avatarDensity = PhysicsScene.Params.avatarDensity; // The dimensions of the avatar capsule are kept in the scale. // Physics creates a unit capsule which is scaled by the physics engine. ComputeAvatarScale(_size); - _avatarDensity = PhysicsScene.Params.avatarDensity; - // set _avatarVolume and _mass based on capsule size, _density and _scale + // set _avatarVolume and _mass based on capsule size, _density and Scale ComputeAvatarVolumeAndMass(); + DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", + LocalID, _size, Scale, _avatarDensity, _avatarVolume, MassRaw); ShapeData shapeData = new ShapeData(); shapeData.ID = LocalID; @@ -99,28 +105,22 @@ public class BSCharacter : BSPhysObject shapeData.Position = _position; shapeData.Rotation = _orientation; shapeData.Velocity = _velocity; - shapeData.Scale = _scale; + shapeData.Size = Scale; + shapeData.Scale = Scale; shapeData.Mass = _mass; shapeData.Buoyancy = _buoyancy; shapeData.Static = ShapeData.numericFalse; - shapeData.Friction = PhysicsScene.Params.avatarFriction; + shapeData.Friction = PhysicsScene.Params.avatarStandingFriction; shapeData.Restitution = PhysicsScene.Params.avatarRestitution; // do actual create at taint time PhysicsScene.TaintedObject("BSCharacter.create", delegate() { DetailLog("{0},BSCharacter.create,taint", LocalID); - BulletSimAPI.CreateObject(PhysicsScene.WorldID, shapeData); + // New body and shape into BSBody and BSShape + PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, shapeData, null, null, null); - // Set the buoyancy for flying. This will be refactored when all the settings happen in C#. - // If not set at creation, the avatar will stop flying when created after crossing a region boundry. - BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy); - - BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(PhysicsScene.World.ptr, LocalID)); - - // This works here because CreateObject has already put the character into the physical world. - BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr, - (uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarMask); + SetPhysicalProperties(); }); return; } @@ -131,53 +131,85 @@ public class BSCharacter : BSPhysObject DetailLog("{0},BSCharacter.Destroy", LocalID); PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() { - BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID); + PhysicsScene.Shapes.DereferenceBody(BSBody, true, null); + PhysicsScene.Shapes.DereferenceShape(BSShape, true, null); }); } + private void SetPhysicalProperties() + { + BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, BSBody.ptr); + + ZeroMotion(); + ForcePosition = _position; + // Set the velocity and compute the proper friction + ForceVelocity = _velocity; + BulletSimAPI.SetRestitution2(BSBody.ptr, PhysicsScene.Params.avatarRestitution); + BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale); + BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold); + if (PhysicsScene.Params.ccdMotionThreshold > 0f) + { + BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold); + BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); + } + + OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw); + BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia); + + BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT); + + BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, BSBody.ptr); + + BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG); + BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, BSBody.ptr); + + // Do this after the object has been added to the world + BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr, + (uint)CollisionFilterGroups.AvatarFilter, + (uint)CollisionFilterGroups.AvatarMask); + } + public override void RequestPhysicsterseUpdate() { base.RequestPhysicsterseUpdate(); } // No one calls this method so I don't know what it could possibly mean - public override bool Stopped { - get { return false; } - } + public override bool Stopped { get { return false; } } public override OMV.Vector3 Size { get { // Avatar capsule size is kept in the scale parameter. - return new OMV.Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z); + return _size; } set { - // When an avatar's size is set, only the height is changed - // and that really only depends on the radius. + // When an avatar's size is set, only the height is changed. _size = value; ComputeAvatarScale(_size); - - // TODO: something has to be done with the avatar's vertical position - ComputeAvatarVolumeAndMass(); + DetailLog("{0},BSCharacter.setSize,call,scale={1},density={2},volume={3},mass={4}", + LocalID, Scale, _avatarDensity, _avatarVolume, MassRaw); PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() { - BulletSimAPI.SetObjectScaleMass(PhysicsScene.WorldID, LocalID, _scale, _mass, true); + BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale); + OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw); + BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia); }); } } - public override PrimitiveBaseShape Shape { - set { _pbs = value; - } + public override OMV.Vector3 Scale { get; set; } + public override PrimitiveBaseShape Shape + { + set { BaseShape = value; } } + public override bool Grabbed { - set { _grabbed = value; - } + set { _grabbed = value; } } public override bool Selected { - set { _selected = value; - } + set { _selected = value; } } public override void CrossingFailure() { return; } public override void link(PhysicsActor obj) { return; } @@ -204,7 +236,7 @@ public class BSCharacter : BSPhysObject public override OMV.Vector3 Position { get { - // _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); + // _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID); return _position; } set { @@ -214,7 +246,7 @@ public class BSCharacter : BSPhysObject PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() { DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); }); } } @@ -263,7 +295,7 @@ public class BSCharacter : BSPhysObject // A version of the sanity check that also makes sure a new position value is // pushed back to the physics engine. This routine would be used by anyone // who is not already pushing the value. - private bool PositionSanityCheck2(bool inTaintTime) + private bool PositionSanityCheck(bool inTaintTime) { bool ret = false; if (PositionSanityCheck()) @@ -273,7 +305,7 @@ public class BSCharacter : BSPhysObject BSScene.TaintCallback sanityOperation = delegate() { DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); }; if (inTaintTime) sanityOperation(); @@ -284,11 +316,7 @@ public class BSCharacter : BSPhysObject return ret; } - 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 public override float MassRaw { get {return _mass; } } @@ -301,15 +329,13 @@ public class BSCharacter : BSPhysObject PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() { DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); - BulletSimAPI.SetObjectForce(PhysicsScene.WorldID, LocalID, _force); + BulletSimAPI.SetObjectForce2(BSBody.ptr, _force); }); } } - public override int VehicleType { - get { return 0; } - set { return; } - } + // Avatars don't do vehicles + public override int VehicleType { get { return 0; } set { return; } } public override void VehicleFloatParam(int param, float value) { } public override void VehicleVectorParam(int param, OMV.Vector3 value) {} public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { } @@ -328,15 +354,37 @@ public class BSCharacter : BSPhysObject PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() { DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); - BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity); + ForceVelocity = _velocity; }); } } public override OMV.Vector3 ForceVelocity { get { return _velocity; } set { + // Depending on whether the avatar is moving or not, change the friction + // to keep the avatar from slipping around + if (_velocity.Length() == 0) + { + if (_currentFriction != PhysicsScene.Params.avatarStandingFriction) + { + _currentFriction = PhysicsScene.Params.avatarStandingFriction; + BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction); + } + } + else + { + if (_currentFriction != PhysicsScene.Params.avatarFriction) + { + _currentFriction = PhysicsScene.Params.avatarFriction; + BulletSimAPI.SetFriction2(BSBody.ptr, _currentFriction); + } + } _velocity = value; - BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity); + // Remember the set velocity so we can suppress the reduction by friction, ... + _appliedVelocity = value; + + BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity); + BulletSimAPI.Activate2(BSBody.ptr, true); } } public override OMV.Vector3 Torque { @@ -360,8 +408,8 @@ public class BSCharacter : BSPhysObject // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() { - // _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); - BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); + // _position = BulletSimAPI.GetPosition2(BSBody.ptr); + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); }); } } @@ -389,12 +437,18 @@ public class BSCharacter : BSPhysObject set { _isPhysical = value; } } + public override bool IsSolid { + get { return true; } + } + public override bool IsStatic { + get { return false; } + } public override bool Flying { get { return _flying; } set { _flying = value; // simulate flying by changing the effect of gravity - this.Buoyancy = ComputeBuoyancyFromFlying(_flying); + Buoyancy = ComputeBuoyancyFromFlying(_flying); } } // Flying is implimented by changing the avatar's buoyancy. @@ -454,10 +508,19 @@ public class BSCharacter : BSPhysObject PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate() { DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); - BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy); + ForceBuoyancy = _buoyancy; }); } } + public override float ForceBuoyancy { + get { return _buoyancy; } + set { _buoyancy = value; + DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); + // Buoyancy is faked by changing the gravity applied to the object + float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); + BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav)); + } + } // Used for MoveTo public override OMV.Vector3 PIDTarget { @@ -518,27 +581,32 @@ public class BSCharacter : BSPhysObject private void ComputeAvatarScale(OMV.Vector3 size) { - _scale.X = PhysicsScene.Params.avatarCapsuleRadius; - _scale.Y = PhysicsScene.Params.avatarCapsuleRadius; + // The 'size' given by the simulator is the mid-point of the avatar + // and X and Y are unspecified. - // The 1.15 came from ODE but it seems to cause the avatar to float off the ground - // _scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y); - _scale.Z = (_size.Z) - (_scale.X + _scale.Y); + OMV.Vector3 newScale = OMV.Vector3.Zero; + newScale.X = PhysicsScene.Params.avatarCapsuleRadius; + newScale.Y = PhysicsScene.Params.avatarCapsuleRadius; + + // From the total height, remote the capsule half spheres that are at each end + newScale.Z = (size.Z * 2f) - Math.Min(newScale.X, newScale.Y); + // newScale.Z = (size.Z * 2f); + Scale = newScale; } - // set _avatarVolume and _mass based on capsule size, _density and _scale + // set _avatarVolume and _mass based on capsule size, _density and Scale private void ComputeAvatarVolumeAndMass() { _avatarVolume = (float)( Math.PI - * _scale.X - * _scale.Y // the area of capsule cylinder - * _scale.Z // times height of capsule cylinder + * Scale.X + * Scale.Y // the area of capsule cylinder + * Scale.Z // times height of capsule cylinder + 1.33333333f * Math.PI - * _scale.X - * Math.Min(_scale.X, _scale.Y) - * _scale.Y // plus the volume of the capsule end caps + * Scale.X + * Math.Min(Scale.X, Scale.Y) + * Scale.Y // plus the volume of the capsule end caps ); _mass = _avatarDensity * _avatarVolume; } @@ -553,7 +621,23 @@ public class BSCharacter : BSPhysObject _acceleration = entprop.Acceleration; _rotationalVelocity = entprop.RotationalVelocity; // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. - PositionSanityCheck2(true); + PositionSanityCheck(true); + + // remember the current and last set values + LastEntityProperties = CurrentEntityProperties; + CurrentEntityProperties = entprop; + + if (entprop.Velocity != LastEntityProperties.Velocity) + { + // Changes in the velocity are suppressed in avatars. + // That's just the way they are defined. + OMV.Vector3 avVel = new OMV.Vector3(_appliedVelocity.X, _appliedVelocity.Y, entprop.Velocity.Z); + _velocity = avVel; + BulletSimAPI.SetLinearVelocity2(BSBody.ptr, avVel); + } + + // Tell the linkset about this + Linkset.UpdateProperties(this); // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. // base.RequestPhysicsterseUpdate(); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index cae599c96f..ead6a08f43 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -47,6 +47,7 @@ public abstract class BSPhysObject : PhysicsActor TypeName = typeName; Linkset = new BSLinkset(PhysicsScene, this); + LastAssetBuildFailed = false; CollisionCollection = new CollisionEventUpdate(); SubscribedEventsMs = 0; @@ -69,6 +70,23 @@ public abstract class BSPhysObject : PhysicsActor // Reference to the physical shape (btCollisionShape) of this object public BulletShape BSShape; + // 'true' if the mesh's underlying asset failed to build. + // This will keep us from looping after the first time the build failed. + public bool LastAssetBuildFailed { get; set; } + + // The objects base shape information. Null if not a prim type shape. + public PrimitiveBaseShape BaseShape { get; protected set; } + + // When the physical properties are updated, an EntityProperty holds the update values. + // Keep the current and last EntityProperties to enable computation of differences + // between the current update and the previous values. + public EntityProperties CurrentEntityProperties { get; set; } + public EntityProperties LastEntityProperties { get; set; } + + public abstract OMV.Vector3 Scale { get; set; } + public abstract bool IsSolid { get; } + public abstract bool IsStatic { get; } + // Stop all physical motion. public abstract void ZeroMotion(); @@ -89,6 +107,10 @@ public abstract class BSPhysObject : PhysicsActor public abstract OMV.Vector3 ForceRotationalVelocity { get; set; } + public abstract float ForceBuoyancy { get; set; } + + public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; } + #region Collisions // Requested number of milliseconds between collision events. Zero means disabled. @@ -129,30 +151,28 @@ public abstract class BSPhysObject : PhysicsActor // if someone has subscribed for collision events.... if (SubscribedEvents()) { CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); - // DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}", - // LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth); + DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}", + LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth); ret = true; } return ret; } - // Routine to send the collected collisions into the simulator. - // Also handles removal of this from the collection of objects with collisions if - // there are no collisions from this object. Mechanism is create one last - // collision event to make collision_end work. + // Send the collected collisions into the simulator. // Called at taint time from within the Step() function thus no locking problems // with CollisionCollection and ObjectsWithNoMoreCollisions. // Return 'true' if there were some actual collisions passed up public virtual bool SendCollisions() { bool ret = true; + // If the 'no collision' call, force it to happen right now so quick collision_end + bool force = CollisionCollection.Count == 0; // throttle the collisions to the number of milliseconds specified in the subscription - int nowTime = PhysicsScene.SimulationNowTime; - if (nowTime >= NextCollisionOkTime) + if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime)) { - NextCollisionOkTime = nowTime + SubscribedEventsMs; + NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs; // We are called if we previously had collisions. If there are no collisions // this time, send up one last empty event so OpenSim can sense collision end. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 6a4365c84a..aeeb4dd27b 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -46,12 +46,10 @@ public sealed class BSPrim : BSPhysObject private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS PRIM]"; - private PrimitiveBaseShape _pbs; - - // _size is what the user passed. _scale is what we pass to the physics engine with the mesh. - // Often _scale is unity because the meshmerizer will apply _size when creating the mesh. + // _size is what the user passed. Scale is what we pass to the physics engine with the mesh. + // Often Scale is unity because the meshmerizer will apply _size when creating the mesh. private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user - private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer + // private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer private bool _grabbed; private bool _isSelected; @@ -98,12 +96,12 @@ public sealed class BSPrim : BSPhysObject _physicsActorType = (int)ActorTypes.Prim; _position = pos; _size = size; - _scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type + Scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type _orientation = rotation; _buoyancy = 1f; _velocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero; - _pbs = pbs; + BaseShape = pbs; _isPhysical = pisPhysical; _isVolumeDetect = false; _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material @@ -160,33 +158,32 @@ public sealed class BSPrim : BSPhysObject get { return _size; } set { _size = value; - PhysicsScene.TaintedObject("BSPrim.setSize", delegate() - { - _mass = CalculateMass(); // changing size changes the mass - // Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct - // scale and margins are set. - CreateGeomAndObject(true); - // DetailLog("{0},BSPrim.setSize,size={1},scale={2},mass={3},physical={4}", LocalID, _size, _scale, _mass, IsPhysical); - }); + ForceBodyShapeRebuild(false); } } // Scale is what we set in the physics engine. It is different than 'size' in that // 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>. - public OMV.Vector3 Scale - { - get { return _scale; } - set { _scale = value; } - } + public override OMV.Vector3 Scale { get; set; } + public override PrimitiveBaseShape Shape { set { - _pbs = value; - PhysicsScene.TaintedObject("BSPrim.setShape", delegate() - { - _mass = CalculateMass(); // changing the shape changes the mass - CreateGeomAndObject(true); - }); + BaseShape = value; + ForceBodyShapeRebuild(false); } } + public override bool ForceBodyShapeRebuild(bool inTaintTime) + { + BSScene.TaintCallback rebuildOperation = delegate() + { + _mass = CalculateMass(); // changing the shape changes the mass + CreateGeomAndObject(true); + }; + if (inTaintTime) + rebuildOperation(); + else + PhysicsScene.TaintedObject("BSPrim.ForceBodyShapeRebuild", rebuildOperation); + return true; + } public override bool Grabbed { set { _grabbed = value; } @@ -325,9 +322,9 @@ public sealed class BSPrim : BSPhysObject } // A version of the sanity check that also makes sure a new position value is - // pushed back to the physics engine. This routine would be used by anyone + // pushed to the physics engine. This routine would be used by anyone // who is not already pushing the value. - private bool PositionSanityCheck2(bool inTaintTime) + private bool PositionSanityCheck(bool inTaintTime) { bool ret = false; if (PositionSanityCheck()) @@ -337,7 +334,7 @@ public sealed class BSPrim : BSPhysObject BSScene.TaintCallback sanityOperation = delegate() { DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); + ForcePosition = _position; }; if (inTaintTime) sanityOperation(); @@ -547,13 +544,13 @@ public sealed class BSPrim : BSPhysObject } // An object is static (does not move) if selected or not physical - private bool IsStatic + public override bool IsStatic { get { return _isSelected || !IsPhysical; } } // An object is solid if it's not phantom and if it's not doing VolumeDetect - public bool IsSolid + public override bool IsSolid { get { return !IsPhantom && !_isVolumeDetect; } } @@ -631,6 +628,12 @@ public sealed class BSPrim : BSPhysObject BulletSimAPI.SetMassProps2(BSBody.ptr, 0f, OMV.Vector3.Zero); // There is no inertia in a static object BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); + // Set collision detection parameters + if (PhysicsScene.Params.ccdMotionThreshold > 0f) + { + BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold); + BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); + } // There can be special things needed for implementing linksets Linkset.MakeStatic(this); // The activation state is 'disabled' so Bullet will not try to act on it. @@ -662,6 +665,13 @@ public sealed class BSPrim : BSPhysObject BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia); BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); + // Set collision detection parameters + if (PhysicsScene.Params.ccdMotionThreshold > 0f) + { + BulletSimAPI.SetCcdMotionThreshold2(BSBody.ptr, PhysicsScene.Params.ccdMotionThreshold); + BulletSimAPI.SetCcdSweepSphereRadius2(BSBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); + } + // Various values for simulation limits BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping); BulletSimAPI.SetDeactivationTime2(BSBody.ptr, PhysicsScene.Params.deactivationTime); @@ -813,13 +823,20 @@ public sealed class BSPrim : BSPhysObject _buoyancy = value; PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() { - // DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy); - // Buoyancy is faked by changing the gravity applied to the object - float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); - BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav)); + ForceBuoyancy = _buoyancy; }); } } + public override float ForceBuoyancy { + get { return _buoyancy; } + set { + _buoyancy = value; + // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); + // Buoyancy is faked by changing the gravity applied to the object + float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); + BulletSimAPI.SetGravity2(BSBody.ptr, new OMV.Vector3(0f, 0f, grav)); + } + } // Used for MoveTo public override OMV.Vector3 PIDTarget { @@ -907,19 +924,19 @@ public sealed class BSPrim : BSPhysObject float tmp; float returnMass = 0; - float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; + float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f; float hollowVolume = hollowAmount * hollowAmount; - switch (_pbs.ProfileShape) + switch (BaseShape.ProfileShape) { case ProfileShape.Square: // default box - if (_pbs.PathCurve == (byte)Extrusion.Straight) + if (BaseShape.PathCurve == (byte)Extrusion.Straight) { if (hollowAmount > 0.0) { - switch (_pbs.HollowShape) + switch (BaseShape.HollowShape) { case HollowShape.Square: case HollowShape.Same: @@ -943,19 +960,19 @@ public sealed class BSPrim : BSPhysObject } } - else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) { //a tube - volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); - tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); + volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX); + tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY); volume -= volume*tmp*tmp; if (hollowAmount > 0.0) { hollowVolume *= hollowAmount; - switch (_pbs.HollowShape) + switch (BaseShape.HollowShape) { case HollowShape.Square: case HollowShape.Same: @@ -980,13 +997,13 @@ public sealed class BSPrim : BSPhysObject case ProfileShape.Circle: - if (_pbs.PathCurve == (byte)Extrusion.Straight) + if (BaseShape.PathCurve == (byte)Extrusion.Straight) { volume *= 0.78539816339f; // elipse base if (hollowAmount > 0.0) { - switch (_pbs.HollowShape) + switch (BaseShape.HollowShape) { case HollowShape.Same: case HollowShape.Circle: @@ -1008,10 +1025,10 @@ public sealed class BSPrim : BSPhysObject } } - else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) { - volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); - tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); + volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX); + tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY); volume *= (1.0f - tmp * tmp); if (hollowAmount > 0.0) @@ -1020,7 +1037,7 @@ public sealed class BSPrim : BSPhysObject // calculate the hollow volume by it's shape compared to the prim shape hollowVolume *= hollowAmount; - switch (_pbs.HollowShape) + switch (BaseShape.HollowShape) { case HollowShape.Same: case HollowShape.Circle: @@ -1044,7 +1061,7 @@ public sealed class BSPrim : BSPhysObject break; case ProfileShape.HalfCircle: - if (_pbs.PathCurve == (byte)Extrusion.Curve1) + if (BaseShape.PathCurve == (byte)Extrusion.Curve1) { volume *= 0.52359877559829887307710723054658f; } @@ -1052,7 +1069,7 @@ public sealed class BSPrim : BSPhysObject case ProfileShape.EquilateralTriangle: - if (_pbs.PathCurve == (byte)Extrusion.Straight) + if (BaseShape.PathCurve == (byte)Extrusion.Straight) { volume *= 0.32475953f; @@ -1060,7 +1077,7 @@ public sealed class BSPrim : BSPhysObject { // calculate the hollow volume by it's shape compared to the prim shape - switch (_pbs.HollowShape) + switch (BaseShape.HollowShape) { case HollowShape.Same: case HollowShape.Triangle: @@ -1085,11 +1102,11 @@ public sealed class BSPrim : BSPhysObject volume *= (1.0f - hollowVolume); } } - else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) { volume *= 0.32475953f; - volume *= 0.01f * (float)(200 - _pbs.PathScaleX); - tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); + volume *= 0.01f * (float)(200 - BaseShape.PathScaleX); + tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY); volume *= (1.0f - tmp * tmp); if (hollowAmount > 0.0) @@ -1097,7 +1114,7 @@ public sealed class BSPrim : BSPhysObject hollowVolume *= hollowAmount; - switch (_pbs.HollowShape) + switch (BaseShape.HollowShape) { case HollowShape.Same: case HollowShape.Triangle: @@ -1137,26 +1154,26 @@ public sealed class BSPrim : BSPhysObject float profileBegin; float profileEnd; - if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible) + if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible) { - taperX1 = _pbs.PathScaleX * 0.01f; + taperX1 = BaseShape.PathScaleX * 0.01f; if (taperX1 > 1.0f) taperX1 = 2.0f - taperX1; taperX = 1.0f - taperX1; - taperY1 = _pbs.PathScaleY * 0.01f; + taperY1 = BaseShape.PathScaleY * 0.01f; if (taperY1 > 1.0f) taperY1 = 2.0f - taperY1; taperY = 1.0f - taperY1; } else { - taperX = _pbs.PathTaperX * 0.01f; + taperX = BaseShape.PathTaperX * 0.01f; if (taperX < 0.0f) taperX = -taperX; taperX1 = 1.0f - taperX; - taperY = _pbs.PathTaperY * 0.01f; + taperY = BaseShape.PathTaperY * 0.01f; if (taperY < 0.0f) taperY = -taperY; taperY1 = 1.0f - taperY; @@ -1166,13 +1183,13 @@ public sealed class BSPrim : BSPhysObject volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); - pathBegin = (float)_pbs.PathBegin * 2.0e-5f; - pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f; + pathBegin = (float)BaseShape.PathBegin * 2.0e-5f; + pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f; volume *= (pathEnd - pathBegin); // this is crude aproximation - profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f; - profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f; + profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f; + profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; volume *= (profileEnd - profileBegin); returnMass = _density * volume; @@ -1207,7 +1224,8 @@ public sealed class BSPrim : BSPhysObject shape.Position = _position; shape.Rotation = _orientation; shape.Velocity = _velocity; - shape.Scale = _scale; + shape.Size = _size; + shape.Scale = Scale; shape.Mass = _isPhysical ? _mass : 0f; shape.Buoyancy = _buoyancy; shape.HullKey = 0; @@ -1217,7 +1235,6 @@ public sealed class BSPrim : BSPhysObject shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; shape.Solid = IsSolid ? ShapeData.numericFalse : ShapeData.numericTrue; - shape.Size = _size; } // Rebuild the geometry and object. // This is called when the shape changes so we need to recreate the mesh/hull. @@ -1234,7 +1251,7 @@ public sealed class BSPrim : BSPhysObject // Create the correct physical representation for this type of object. // Updates BSBody and BSShape with the new information. // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. - PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, _pbs, + PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape, null, delegate(BulletBody dBody) { // Called if the current prim body is about to be destroyed. @@ -1328,9 +1345,11 @@ public sealed class BSPrim : BSPhysObject _acceleration = entprop.Acceleration; _rotationalVelocity = entprop.RotationalVelocity; - PositionSanityCheck2(true); + // remember the current and last set values + LastEntityProperties = CurrentEntityProperties; + CurrentEntityProperties = entprop; - Linkset.UpdateProperties(this); + PositionSanityCheck(true); DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); @@ -1348,6 +1367,9 @@ public sealed class BSPrim : BSPhysObject entprop.Acceleration, entprop.RotationalVelocity); } */ + // The linkset implimentation might want to know about this. + + Linkset.UpdateProperties(this); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 2c3c481aea..6621d39f2c 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -256,14 +256,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); - WorldID = BulletSimAPI.Initialize(worldExtent, m_paramsHandle.AddrOfPinnedObject(), + World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(), m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(), - m_DebugLogCallbackHandle); - - // Initialization to support the transition to a new API which puts most of the logic - // into the C# code so it is easier to modify and add to. - World = new BulletSim(WorldID, this, BulletSimAPI.GetSimHandle2(WorldID)); + m_DebugLogCallbackHandle)); Constraints = new BSConstraintCollection(World); @@ -360,7 +356,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // Anything left in the unmanaged code should be cleaned out - BulletSimAPI.Shutdown(WorldID); + BulletSimAPI.Shutdown2(World.ptr); // Not logging any more PhysicsLogging.Close(); @@ -498,7 +494,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters { if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); - numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep, + numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); @@ -536,26 +532,26 @@ public class BSScene : PhysicsScene, IPhysicsParameters } } - // This is a kludge to get avatar movement updates. - // the simulator expects collisions for avatars even if there are have been no collisions. This updates - // avatar animations and stuff. - // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. - foreach (BSPhysObject bsp in m_avatars) - bsp.SendCollisions(); - // The above SendCollision's batch up the collisions on the objects. // Now push the collisions into the simulator. if (ObjectsWithCollisions.Count > 0) { foreach (BSPhysObject bsp in ObjectsWithCollisions) - if (!m_avatars.Contains(bsp)) // don't call avatars twice - if (!bsp.SendCollisions()) - { - // If the object is done colliding, see that it's removed from the colliding list - ObjectsWithNoMoreCollisions.Add(bsp); - } + if (!bsp.SendCollisions()) + { + // If the object is done colliding, see that it's removed from the colliding list + ObjectsWithNoMoreCollisions.Add(bsp); + } } + // This is a kludge to get avatar movement updates. + // The simulator expects collisions for avatars even if there are have been no collisions. + // The event updates avatar animations and stuff. + // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. + foreach (BSPhysObject bsp in m_avatars) + if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice + bsp.SendCollisions(); + // Objects that are done colliding are removed from the ObjectsWithCollisions list. // Not done above because it is inside an iteration of ObjectWithCollisions. if (ObjectsWithNoMoreCollisions.Count > 0) @@ -579,11 +575,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters } } + // 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. + // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG + // The physics engine returns the number of milliseconds it simulated this call. // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. - // We multiply by 45 to give a recognizable running rate (45 or less). - return numSubSteps * m_fixedTimeStep * 1000 * 45; - // return timeStep * 1000 * 45; + // We multiply by 55 to give a recognizable running rate (55 or less). + return numSubSteps * m_fixedTimeStep * 1000 * 55; + // return timeStep * 1000 * 55; } // Something has collided @@ -800,6 +800,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); delegate float ParamGet(BSScene scene); delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); + delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val); private struct ParameterDefn { @@ -809,6 +810,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters public ParamUser userParam; // get the value from the configuration file public ParamGet getter; // return the current value stored for this parameter public ParamSet setter; // set the current value for this parameter + public SetOnObject onObject; // set the value on an object in the physical domain public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) { name = n; @@ -817,6 +819,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters userParam = u; getter = g; setter = s; + onObject = null; + } + public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o) + { + name = n; + desc = d; + defaultValue = v; + userParam = u; + getter = g; + setter = s; + onObject = o; } } @@ -838,6 +851,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // // The single letter parameters for the delegates are: // s = BSScene + // o = BSPhysObject // p = string parameter name // l = localID of referenced object // v = float value @@ -947,70 +961,84 @@ public class BSScene : PhysicsScene, IPhysicsParameters -9.80665f, (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].gravity; }, - (s,p,l,v) => { s.m_params[0].gravity = v; s.TaintedUpdateParameter(p,l,v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].gravity, p, PhysParameterEntry.APPLY_TO_NONE, v); }, + (s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ), new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", 0f, (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].linearDamping; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearDamping, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ), new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", 0f, (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].angularDamping; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularDamping, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetDamping2(o.BSBody.ptr, v, v); } ), new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", 0.2f, (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].deactivationTime; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].deactivationTime, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.BSBody.ptr, v); } ), new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", 0.8f, (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].linearSleepingThreshold; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].linearSleepingThreshold, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ), new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", 1.0f, (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].angularSleepingThreshold; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].angularSleepingThreshold, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.BSBody.ptr, v, v); } ), new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , 0f, // set to zero to disable (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].ccdMotionThreshold; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdMotionThreshold, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.BSBody.ptr, v); } ), new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , 0f, (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].ccdSweptSphereRadius; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetCcdSweepSphereRadius2(o.BSBody.ptr, v); } ), new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , 0.1f, (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].contactProcessingThreshold; }, - (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ), + (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].contactProcessingThreshold, p, l, v); }, + (s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.BSBody.ptr, v); } ), new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 0.5f, (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].terrainFriction; }, - (s,p,l,v) => { s.m_params[0].terrainFriction = v; s.TaintedUpdateParameter(p,l,v); } ), + (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , 0.8f, (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].terrainHitFraction; }, - (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; s.TaintedUpdateParameter(p,l,v); } ), + (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; /* TODO: set on real terrain */ } ), new ParameterDefn("TerrainRestitution", "Bouncyness" , 0f, (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].terrainRestitution; }, - (s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ), + (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", 0.2f, (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].avatarFriction; }, (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), + new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", + 10f, + (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].avatarStandingFriction; }, + (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ), new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", 60f, (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, @@ -1227,52 +1255,54 @@ public class BSScene : PhysicsScene, IPhysicsParameters return ret; } - // check to see if we are updating a parameter for a particular or all of the prims - protected void UpdateParameterObject(ref float loc, string parm, uint localID, float val) - { - List operateOn; - lock (PhysObjects) operateOn = new List(PhysObjects.Keys); - UpdateParameterSet(operateOn, ref loc, parm, localID, val); - } - // update all the localIDs specified // If the local ID is APPLY_TO_NONE, just change the default value // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs // If the localID is a specific object, apply the parameter change to only that object - protected void UpdateParameterSet(List lIDs, ref float defaultLoc, string parm, uint localID, float val) + protected void UpdateParameterObject(ref float defaultLoc, string parm, uint localID, float val) { + List objectIDs = new List(); switch (localID) { case PhysParameterEntry.APPLY_TO_NONE: defaultLoc = val; // setting only the default value + // This will cause a call into the physical world if some operation is specified (SetOnObject). + objectIDs.Add(TERRAIN_ID); + TaintedUpdateParameter(parm, objectIDs, val); break; case PhysParameterEntry.APPLY_TO_ALL: defaultLoc = val; // setting ALL also sets the default value - List objectIDs = lIDs; - string xparm = parm.ToLower(); - float xval = val; - TaintedObject("BSScene.UpdateParameterSet", delegate() { - foreach (uint lID in objectIDs) - { - BulletSimAPI.UpdateParameter(WorldID, lID, xparm, xval); - } - }); + lock (PhysObjects) objectIDs = new List(PhysObjects.Keys); + TaintedUpdateParameter(parm, objectIDs, val); break; default: // setting only one localID - TaintedUpdateParameter(parm, localID, val); + objectIDs.Add(localID); + TaintedUpdateParameter(parm, objectIDs, val); break; } } // schedule the actual updating of the paramter to when the phys engine is not busy - protected void TaintedUpdateParameter(string parm, uint localID, float val) + protected void TaintedUpdateParameter(string parm, List lIDs, float val) { - uint xlocalID = localID; - string xparm = parm.ToLower(); float xval = val; - TaintedObject("BSScene.TaintedUpdateParameter", delegate() { - BulletSimAPI.UpdateParameter(WorldID, xlocalID, xparm, xval); + List xlIDs = lIDs; + string xparm = parm; + TaintedObject("BSScene.UpdateParameterSet", delegate() { + ParameterDefn thisParam; + if (TryGetParameter(xparm, out thisParam)) + { + if (thisParam.onObject != null) + { + foreach (uint lID in xlIDs) + { + BSPhysObject theObject = null; + PhysObjects.TryGetValue(lID, out theObject); + thisParam.onObject(this, theObject, xval); + } + } + } }); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index d189f1d287..d3ba2732a8 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -51,7 +51,7 @@ public class BSShapeCollection : IDisposable } // Description of a hull. - // Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects + // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations. private struct HullDesc { public IntPtr ptr; @@ -59,17 +59,9 @@ public class BSShapeCollection : IDisposable public DateTime lastReferenced; } - private struct BodyDesc - { - public IntPtr ptr; - // Bodies are only used once so reference count is always either one or zero - public int referenceCount; - public DateTime lastReferenced; - } - + // The sharable set of meshes and hulls. Indexed by their shape hash. private Dictionary Meshes = new Dictionary(); private Dictionary Hulls = new Dictionary(); - private Dictionary Bodies = new Dictionary(); public BSShapeCollection(BSScene physScene) { @@ -92,8 +84,12 @@ public class BSShapeCollection : IDisposable // First checks the shape and updates that if necessary then makes // sure the body is of the right type. // Return 'true' if either the body or the shape changed. + // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before + // the current shape or body is destroyed. This allows the caller to remove any + // higher level dependencies on the shape or body. Mostly used for LinkSets to + // remove the physical constraints before the body is destroyed. // Called at taint-time!! - public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, + public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) { @@ -103,7 +99,8 @@ public class BSShapeCollection : IDisposable lock (m_collectionActivityLock) { // Do we have the correct geometry for this type of object? - // Updates prim.BSShape with information/pointers to requested shape + // Updates prim.BSShape with information/pointers to shape. + // CreateGeom returns 'true' of BSShape as changed to a new shape. bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback); // If we had to select a new shape geometry for the object, // rebuild the body around it. @@ -120,40 +117,24 @@ public class BSShapeCollection : IDisposable // Track another user of a body // We presume the caller has allocated the body. - // Bodies only have one user so the reference count is either 1 or 0. + // 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) { lock (m_collectionActivityLock) { - BodyDesc bodyDesc; - if (Bodies.TryGetValue(body.ID, out bodyDesc)) + DetailLog("{0},BSShapeCollection.ReferenceBody,newBody", body.ID, body); + BSScene.TaintCallback createOperation = delegate() { - bodyDesc.referenceCount++; - DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount); - } - else - { - // New entry - bodyDesc.ptr = body.ptr; - bodyDesc.referenceCount = 1; - DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={2}", - body.ID, body, bodyDesc.referenceCount); - BSScene.TaintCallback createOperation = delegate() + if (!BulletSimAPI.IsInWorld2(body.ptr)) { - if (!BulletSimAPI.IsInWorld2(body.ptr)) - { - BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr); - DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", - body.ID, body); - } - }; - if (inTaintTime) - createOperation(); - else - PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation); - } - bodyDesc.lastReferenced = System.DateTime.Now; - Bodies[body.ID] = bodyDesc; + BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr); + DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); + } + }; + if (inTaintTime) + createOperation(); + else + PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation); } } @@ -166,43 +147,25 @@ public class BSShapeCollection : IDisposable lock (m_collectionActivityLock) { - BodyDesc bodyDesc; - if (Bodies.TryGetValue(body.ID, out bodyDesc)) + BSScene.TaintCallback removeOperation = delegate() { - bodyDesc.referenceCount--; - bodyDesc.lastReferenced = System.DateTime.Now; - Bodies[body.ID] = bodyDesc; - DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount); + DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}", + body.ID, body.ptr.ToString("X"), inTaintTime); + // If the caller needs to know the old body is going away, pass the event up. + if (bodyCallback != null) bodyCallback(body); - // If body is no longer being used, free it -- bodies can never be shared. - if (bodyDesc.referenceCount == 0) - { - Bodies.Remove(body.ID); - BSScene.TaintCallback removeOperation = delegate() - { - DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}", - body.ID, body.ptr.ToString("X"), inTaintTime); - // If the caller needs to know the old body is going away, pass the event up. - if (bodyCallback != null) bodyCallback(body); + // It may have already been removed from the world in which case the next is a NOOP. + BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); - // It may have already been removed from the world in which case the next is a NOOP. - BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); - - // 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.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); - } - } + // 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.DestroyObject2(PhysicsScene.World.ptr, body.ptr); + }; + // If already in taint-time, do the operations now. Otherwise queue for later. + if (inTaintTime) + removeOperation(); else - { - DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount); - } + PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation); } } @@ -271,7 +234,6 @@ public class BSShapeCollection : IDisposable } // Release the usage of a shape. - // The collisionObject is released since it is a copy of the real collision shape. public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback) { if (shape.ptr == IntPtr.Zero) @@ -279,26 +241,32 @@ public class BSShapeCollection : IDisposable BSScene.TaintCallback dereferenceOperation = delegate() { - switch (shape.type) + if (shape.ptr != IntPtr.Zero) { - case ShapeData.PhysicsShapeType.SHAPE_HULL: - DereferenceHull(shape, shapeCallback); - break; - case ShapeData.PhysicsShapeType.SHAPE_MESH: - DereferenceMesh(shape, shapeCallback); - break; - case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: - break; - default: + if (shape.isNativeShape) + { // Native shapes are not tracked and are released immediately - if (shape.ptr != IntPtr.Zero & shape.isNativeShape) + DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", + BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime); + if (shapeCallback != null) shapeCallback(shape); + BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); + } + else + { + switch (shape.type) { - DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", - BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime); - if (shapeCallback != null) shapeCallback(shape); - BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); + case ShapeData.PhysicsShapeType.SHAPE_HULL: + DereferenceHull(shape, shapeCallback); + break; + case ShapeData.PhysicsShapeType.SHAPE_MESH: + DereferenceMesh(shape, shapeCallback); + break; + case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: + break; + default: + break; } - break; + } } }; if (inTaintTime) @@ -351,19 +319,31 @@ public class BSShapeCollection : IDisposable // Create the geometry information in Bullet for later use. // The objects needs a hull if it's physical otherwise a mesh is enough. - // No locking here because this is done when we know physics is not simulating. - // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used. + // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls, + // shared geometries will be used. If the parameters of the existing shape are the same + // as this request, the shape is not rebuilt. + // Info in prim.BSShape is updated to the new shape. // Returns 'true' if the geometry was rebuilt. // Called at taint-time! - private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, + private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) { bool ret = false; bool haveShape = false; bool nativeShapePossible = true; + 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); + haveShape = true; + } // If the prim attributes are simple, this could be a simple Bullet native shape - if (nativeShapePossible + if (!haveShape + && pbs != null + && nativeShapePossible && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 && pbs.ProfileHollow == 0 @@ -406,7 +386,7 @@ public class BSShapeCollection : IDisposable // 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) + if (!haveShape && pbs != null) { if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects) { @@ -425,12 +405,12 @@ public class BSShapeCollection : IDisposable return ret; } - // Creates a native shape and assignes it to prim.BSShape - private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData, + // Creates a native shape and assignes it to prim.BSShape. + // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). + private bool GetReferenceToNativeShape(BSPhysObject prim, ShapeData shapeData, ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, ShapeDestructionCallback shapeCallback) { - BulletShape newShape; shapeData.Type = shapeType; // Bullet native objects are scaled by the Bullet engine so pass the size in @@ -440,23 +420,42 @@ public class BSShapeCollection : IDisposable // release any previous shape DereferenceShape(prim.BSShape, true, shapeCallback); - // Native shapes are always built independently. - newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType); - newShape.shapeKey = (System.UInt64)shapeKey; - newShape.isNativeShape = true; + BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey); - // Don't need to do a 'ReferenceShape()' here because native shapes are not tracked. - // DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape); + // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. + DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", + shapeData.ID, newShape, shapeData.Scale); prim.BSShape = newShape; return true; } + private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType, + ShapeData shapeData, ShapeData.FixedShapeKey shapeKey) + { + BulletShape newShape; + + if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) + { + newShape = new BulletShape( + BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale), + shapeType); + } + else + { + newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType); + } + newShape.shapeKey = (System.UInt64)shapeKey; + newShape.isNativeShape = true; + + return newShape; + } + // Builds a mesh shape in the physical world and updates prim.BSShape. // Dereferences previous shape in BSShape and adds a reference for this new shape. // Returns 'true' of a mesh was actually built. Otherwise . // Called at taint-time! - private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, + private bool GetReferenceToMesh(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) { BulletShape newShape = new BulletShape(IntPtr.Zero); @@ -475,6 +474,8 @@ public class BSShapeCollection : IDisposable DereferenceShape(prim.BSShape, true, shapeCallback); newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); + // Take evasive action if the mesh was not constructed. + newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs); ReferenceShape(newShape); @@ -488,7 +489,7 @@ public class BSShapeCollection : IDisposable private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { IMesh meshData = null; - IntPtr meshPtr; + IntPtr meshPtr = IntPtr.Zero; MeshDesc meshDesc; if (Meshes.TryGetValue(newMeshKey, out meshDesc)) { @@ -500,23 +501,26 @@ public class BSShapeCollection : IDisposable // Pass false for physicalness as this creates some sort of bounding box which we don't need meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); - int[] indices = meshData.getIndexListAsInt(); - List vertices = meshData.getVertexList(); - - float[] verticesAsFloats = new float[vertices.Count * 3]; - int vi = 0; - foreach (OMV.Vector3 vv in vertices) + if (meshData != null) { - verticesAsFloats[vi++] = vv.X; - verticesAsFloats[vi++] = vv.Y; - verticesAsFloats[vi++] = vv.Z; + int[] indices = meshData.getIndexListAsInt(); + List vertices = meshData.getVertexList(); + + float[] verticesAsFloats = new float[vertices.Count * 3]; + int vi = 0; + foreach (OMV.Vector3 vv in vertices) + { + verticesAsFloats[vi++] = vv.X; + verticesAsFloats[vi++] = vv.Y; + verticesAsFloats[vi++] = vv.Z; + } + + // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", + // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); + + meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, + indices.GetLength(0), indices, vertices.Count, verticesAsFloats); } - - // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", - // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); - - meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, - indices.GetLength(0), indices, vertices.Count, verticesAsFloats); } BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); newShape.shapeKey = newMeshKey; @@ -526,7 +530,7 @@ public class BSShapeCollection : IDisposable // See that hull shape exists in the physical world and update prim.BSShape. // We could be creating the hull because scale changed or whatever. - private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, + private bool GetReferenceToHull(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) { BulletShape newShape; @@ -545,6 +549,7 @@ public class BSShapeCollection : IDisposable DereferenceShape(prim.BSShape, true, shapeCallback); newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); + newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs); ReferenceShape(newShape); @@ -558,7 +563,7 @@ public class BSShapeCollection : IDisposable private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { - IntPtr hullPtr; + IntPtr hullPtr = IntPtr.Zero; HullDesc hullDesc; if (Hulls.TryGetValue(newHullKey, out hullDesc)) { @@ -570,86 +575,89 @@ public class BSShapeCollection : IDisposable // Build a new hull in the physical world // Pass false for physicalness as this creates some sort of bounding box which we don't need IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); - - int[] indices = meshData.getIndexListAsInt(); - List vertices = meshData.getVertexList(); - - //format conversion from IMesh format to DecompDesc format - List convIndices = new List(); - List convVertices = new List(); - for (int ii = 0; ii < indices.GetLength(0); ii++) + if (meshData != null) { - convIndices.Add(indices[ii]); - } - foreach (OMV.Vector3 vv in vertices) - { - convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); - } - // setup and do convex hull conversion - m_hulls = new List(); - DecompDesc dcomp = new DecompDesc(); - dcomp.mIndices = convIndices; - dcomp.mVertices = convVertices; - ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); - // create the hull into the _hulls variable - convexBuilder.process(dcomp); + int[] indices = meshData.getIndexListAsInt(); + List vertices = meshData.getVertexList(); - // Convert the vertices and indices for passing to unmanaged. - // The hull information is passed as a large floating point array. - // The format is: - // convHulls[0] = number of hulls - // convHulls[1] = number of vertices in first hull - // convHulls[2] = hull centroid X coordinate - // convHulls[3] = hull centroid Y coordinate - // convHulls[4] = hull centroid Z coordinate - // convHulls[5] = first hull vertex X - // convHulls[6] = first hull vertex Y - // convHulls[7] = first hull vertex Z - // convHulls[8] = second hull vertex X - // ... - // convHulls[n] = number of vertices in second hull - // convHulls[n+1] = second hull centroid X coordinate - // ... - // - // TODO: is is very inefficient. Someday change the convex hull generator to return - // data structures that do not need to be converted in order to pass to Bullet. - // And maybe put the values directly into pinned memory rather than marshaling. - int hullCount = m_hulls.Count; - int totalVertices = 1; // include one for the count of the hulls - foreach (ConvexResult cr in m_hulls) - { - totalVertices += 4; // add four for the vertex count and centroid - totalVertices += cr.HullIndices.Count * 3; // we pass just triangles - } - float[] convHulls = new float[totalVertices]; - - convHulls[0] = (float)hullCount; - int jj = 1; - foreach (ConvexResult cr in m_hulls) - { - // copy vertices for index access - float3[] verts = new float3[cr.HullVertices.Count]; - int kk = 0; - foreach (float3 ff in cr.HullVertices) + //format conversion from IMesh format to DecompDesc format + List convIndices = new List(); + List convVertices = new List(); + for (int ii = 0; ii < indices.GetLength(0); ii++) { - verts[kk++] = ff; + convIndices.Add(indices[ii]); + } + foreach (OMV.Vector3 vv in vertices) + { + convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); } - // add to the array one hull's worth of data - convHulls[jj++] = cr.HullIndices.Count; - convHulls[jj++] = 0f; // centroid x,y,z - convHulls[jj++] = 0f; - convHulls[jj++] = 0f; - foreach (int ind in cr.HullIndices) + // setup and do convex hull conversion + m_hulls = new List(); + DecompDesc dcomp = new DecompDesc(); + dcomp.mIndices = convIndices; + dcomp.mVertices = convVertices; + ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); + // create the hull into the _hulls variable + convexBuilder.process(dcomp); + + // Convert the vertices and indices for passing to unmanaged. + // The hull information is passed as a large floating point array. + // The format is: + // convHulls[0] = number of hulls + // convHulls[1] = number of vertices in first hull + // convHulls[2] = hull centroid X coordinate + // convHulls[3] = hull centroid Y coordinate + // convHulls[4] = hull centroid Z coordinate + // convHulls[5] = first hull vertex X + // convHulls[6] = first hull vertex Y + // convHulls[7] = first hull vertex Z + // convHulls[8] = second hull vertex X + // ... + // convHulls[n] = number of vertices in second hull + // convHulls[n+1] = second hull centroid X coordinate + // ... + // + // TODO: is is very inefficient. Someday change the convex hull generator to return + // data structures that do not need to be converted in order to pass to Bullet. + // And maybe put the values directly into pinned memory rather than marshaling. + int hullCount = m_hulls.Count; + int totalVertices = 1; // include one for the count of the hulls + foreach (ConvexResult cr in m_hulls) { - convHulls[jj++] = verts[ind].x; - convHulls[jj++] = verts[ind].y; - convHulls[jj++] = verts[ind].z; + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += cr.HullIndices.Count * 3; // we pass just triangles } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + foreach (ConvexResult cr in m_hulls) + { + // copy vertices for index access + float3[] verts = new float3[cr.HullVertices.Count]; + int kk = 0; + foreach (float3 ff in cr.HullVertices) + { + verts[kk++] = ff; + } + + // add to the array one hull's worth of data + convHulls[jj++] = cr.HullIndices.Count; + convHulls[jj++] = 0f; // centroid x,y,z + convHulls[jj++] = 0f; + convHulls[jj++] = 0f; + foreach (int ind in cr.HullIndices) + { + convHulls[jj++] = verts[ind].x; + convHulls[jj++] = verts[ind].y; + convHulls[jj++] = verts[ind].z; + } + } + // create the hull data structure in Bullet + hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls); } - // create the hull data structure in Bullet - hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls); } BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); @@ -690,11 +698,55 @@ public class BSShapeCollection : IDisposable return ComputeShapeKey(shapeData, pbs, out lod); } + private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs) + { + // If the shape was successfully created, nothing more to do + if (newShape.ptr != IntPtr.Zero) + return newShape; + + // The most common reason for failure is that an underlying asset is not available + + // 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) + { + prim.LastAssetBuildFailed = true; + BSPhysObject xprim = prim; + Util.FireAndForget(delegate + { + RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; + if (assetProvider != null) + { + BSPhysObject yprim = xprim; // probably not necessary, but, just in case. + assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) + { + if (!yprim.BaseShape.SculptEntry) + return; + if (yprim.BaseShape.SculptTexture.ToString() != asset.ID) + return; + + yprim.BaseShape.SculptData = new byte[asset.Data.Length]; + asset.Data.CopyTo(yprim.BaseShape.SculptData, 0); + // This will cause the prim to see that the filler shape is not the right + // one and try again to build the object. + yprim.ForceBodyShapeRebuild(false); + + }); + } + }); + } + + // While we figure out the real problem, stick a simple native shape on the object. + BulletShape fillinShape = + BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_SPHERE, shapeData, ShapeData.FixedShapeKey.KEY_SPHERE); + + return fillinShape; + } + // Create a body object in Bullet. // Updates prim.BSBody with the information about the new body if one is created. // Returns 'true' if an object was actually created. // Called at taint-time. - private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, + private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape, ShapeData shapeData, BodyDestructionCallback bodyCallback) { bool ret = false; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index caf411e695..41065344e2 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -114,6 +114,7 @@ public class BSTerrainManager BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity)); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr); + BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr); // Ground plane does not move BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION); // Everything collides with the ground plane. @@ -334,7 +335,8 @@ public class BSTerrainManager // Make sure the new shape is processed. // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true); - BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); + BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING); + // BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); m_terrainModified = true; }; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 276111c2b1..5ffd591264 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -223,6 +223,7 @@ public struct ShapeData KEY_SPHERE = 2, KEY_CONE = 3, KEY_CYLINDER = 4, + KEY_CAPSULE = 5, } } [StructLayout(LayoutKind.Sequential)] @@ -282,6 +283,7 @@ public struct ConfigurationParameters public float terrainHitFraction; public float terrainRestitution; public float avatarFriction; + public float avatarStandingFriction; public float avatarDensity; public float avatarRestitution; public float avatarCapsuleRadius; @@ -388,7 +390,7 @@ public enum CollisionFilterGroups : uint VolumeDetectMask = ~BSensorTrigger, TerrainFilter = BTerrainFilter, TerrainMask = BAllFilter & ~BStaticFilter, - GroundPlaneFilter = BAllFilter, + GroundPlaneFilter = BGroundPlaneFilter, GroundPlaneMask = BAllFilter }; @@ -428,6 +430,7 @@ public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg [return: MarshalAs(UnmanagedType.LPStr)] public static extern string GetVersion(); +/* Remove the linkage to the old api methods [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, int maxCollisions, IntPtr collisionArray, @@ -531,7 +534,7 @@ public static extern Vector3 RecoverFromPenetration(uint worldID, uint id); // =============================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void DumpBulletStatistics(); - +*/ // Log a debug message [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void SetDebugLogCallback(DebugLogCallback callback); @@ -562,7 +565,8 @@ public static extern IntPtr GetBodyHandle2(IntPtr world, uint id); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms, int maxCollisions, IntPtr collisionArray, - int maxUpdates, IntPtr updateArray); + int maxUpdates, IntPtr updateArray, + DebugLogCallback logRoutine); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value); @@ -603,6 +607,9 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData) [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool IsNativeShape2(IntPtr shape); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); + [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern IntPtr CreateCompoundShape2(IntPtr sim); diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll index 785f01f92d..f976efe72b 100755 Binary files a/bin/lib32/BulletSim.dll and b/bin/lib32/BulletSim.dll differ diff --git a/bin/lib32/libBulletSim.so b/bin/lib32/libBulletSim.so index 6af2fb4b28..a00bc3ab01 100755 Binary files a/bin/lib32/libBulletSim.so and b/bin/lib32/libBulletSim.so differ diff --git a/bin/lib64/BulletSim.dll b/bin/lib64/BulletSim.dll index e9e521a5e1..acaa9c875f 100755 Binary files a/bin/lib64/BulletSim.dll and b/bin/lib64/BulletSim.dll differ diff --git a/bin/lib64/libBulletSim.so b/bin/lib64/libBulletSim.so index cbf4f792e6..996ea21576 100755 Binary files a/bin/lib64/libBulletSim.so and b/bin/lib64/libBulletSim.so differ