diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 2d02a82804..a16d17421d 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -1,5 +1,5 @@ <<<>>>>The following people have contributed to OpenSim (Thank you -for your effort!) +for your effort!) = Current OpenSim Developers (in very rough order of appearance) = These folks represent the current core team for OpenSim, and are the diff --git a/OpenSim/Region/Physics/BulletSPlugin/BS6DofConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BS6DofConstraint.cs index 683bc51163..3306a975fc 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BS6DofConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BS6DofConstraint.cs @@ -34,6 +34,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin public class BS6DofConstraint : BSConstraint { + private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]"; + + public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } } + // Create a btGeneric6DofConstraint public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, Vector3 frame1, Quaternion frame1rot, @@ -44,11 +48,14 @@ public class BS6DofConstraint : BSConstraint m_body1 = obj1; m_body2 = obj2; m_constraint = new BulletConstraint( - BulletSimAPI.Create6DofConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr, + BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr, frame1, frame1rot, frame2, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); m_enabled = true; + world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", + BSScene.DetailLogZero, world.worldID, + obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); } public BS6DofConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, @@ -58,11 +65,36 @@ public class BS6DofConstraint : BSConstraint m_world = world; m_body1 = obj1; m_body2 = obj2; - m_constraint = new BulletConstraint( - BulletSimAPI.Create6DofConstraintToPoint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr, - joinPoint, - useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); - m_enabled = true; + if (obj1.ptr == IntPtr.Zero || obj2.ptr == IntPtr.Zero) + { + world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", + BSScene.DetailLogZero, world.worldID, + 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}", + "[BULLETSIM 6DOF CONSTRAINT]", world.worldID, + obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); + m_enabled = false; + } + else + { + m_constraint = new BulletConstraint( + BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr, + joinPoint, + useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); + world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}", + BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"), + obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); + if (m_constraint.ptr == IntPtr.Zero) + { + world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}", + LogHeader, obj1.ID, obj2.ID); + m_enabled = false; + } + else + { + m_enabled = true; + } + } } public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot) @@ -70,7 +102,7 @@ public class BS6DofConstraint : BSConstraint bool ret = false; if (m_enabled) { - BulletSimAPI.SetFrames2(m_constraint.Ptr, frameA, frameArot, frameB, frameBrot); + BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot); ret = true; } return ret; @@ -81,9 +113,9 @@ public class BS6DofConstraint : BSConstraint bool ret = false; if (m_enabled) { - BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL); - BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL); - BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL); + BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL); + BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL); + BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL); ret = true; } return ret; @@ -94,7 +126,7 @@ public class BS6DofConstraint : BSConstraint bool ret = false; float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; if (m_enabled) - ret = BulletSimAPI.UseFrameOffset2(m_constraint.Ptr, onOff); + ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff); return ret; } @@ -103,7 +135,7 @@ public class BS6DofConstraint : BSConstraint bool ret = false; float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; if (m_enabled) - ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.Ptr, onOff, targetVelocity, maxMotorForce); + ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce); return ret; } @@ -111,7 +143,7 @@ public class BS6DofConstraint : BSConstraint { bool ret = false; if (m_enabled) - ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.Ptr, threshold); + ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold); return ret; } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 526dbadd97..b88ec3cf9d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -28,7 +28,7 @@ using System; using System.Collections.Generic; using System.Reflection; using log4net; -using OpenMetaverse; +using OMV = OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Physics.Manager; @@ -39,48 +39,35 @@ public class BSCharacter : BSPhysObject private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS CHAR]"; - public BSScene Scene { get; private set; } - private String _avName; // private bool _stopped; - private Vector3 _size; - private Vector3 _scale; + private OMV.Vector3 _size; + private OMV.Vector3 _scale; private PrimitiveBaseShape _pbs; - private uint _localID = 0; private bool _grabbed; private bool _selected; - private Vector3 _position; + private OMV.Vector3 _position; private float _mass; - public float _density; - public float _avatarVolume; - private Vector3 _force; - private Vector3 _velocity; - private Vector3 _torque; + private float _avatarDensity; + private float _avatarVolume; + private OMV.Vector3 _force; + private OMV.Vector3 _velocity; + private OMV.Vector3 _torque; private float _collisionScore; - private Vector3 _acceleration; - private Quaternion _orientation; + private OMV.Vector3 _acceleration; + private OMV.Quaternion _orientation; private int _physicsActorType; private bool _isPhysical; private bool _flying; private bool _setAlwaysRun; private bool _throttleUpdates; private bool _isColliding; - private long _collidingStep; - private bool _collidingGround; - private long _collidingGroundStep; private bool _collidingObj; private bool _floatOnWater; - private Vector3 _rotationalVelocity; + private OMV.Vector3 _rotationalVelocity; private bool _kinematic; private float _buoyancy; - public override BulletBody BSBody { get; set; } - public override BulletShape BSShape { get; set; } - public override BSLinkset Linkset { get; set; } - - private int _subscribedEventsMs = 0; - private int _nextCollisionOkTime = 0; - - private Vector3 _PIDTarget; + private OMV.Vector3 _PIDTarget; private bool _usePID; private float _PIDTau; private bool _useHoverPID; @@ -88,28 +75,26 @@ public class BSCharacter : BSPhysObject private PIDHoverType _PIDHoverType; private float _PIDHoverTao; - public BSCharacter(uint localID, String avName, BSScene parent_scene, Vector3 pos, Vector3 size, bool isFlying) + public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying) { - _localID = localID; - _avName = avName; - Scene = parent_scene; + base.BaseInitialize(parent_scene, localID, avName, "BSCharacter"); _physicsActorType = (int)ActorTypes.Agent; _position = pos; _size = size; _flying = isFlying; - _orientation = Quaternion.Identity; - _velocity = Vector3.Zero; + _orientation = OMV.Quaternion.Identity; + _velocity = OMV.Vector3.Zero; _buoyancy = ComputeBuoyancyFromFlying(isFlying); + // The dimensions of the avatar capsule are kept in the scale. // Physics creates a unit capsule which is scaled by the physics engine. - _scale = new Vector3(Scene.Params.avatarCapsuleRadius, Scene.Params.avatarCapsuleRadius, size.Z); - _density = Scene.Params.avatarDensity; - ComputeAvatarVolumeAndMass(); // set _avatarVolume and _mass based on capsule size, _density and _scale - - Linkset = new BSLinkset(Scene, this); + ComputeAvatarScale(_size); + _avatarDensity = PhysicsScene.Params.avatarDensity; + // set _avatarVolume and _mass based on capsule size, _density and _scale + ComputeAvatarVolumeAndMass(); ShapeData shapeData = new ShapeData(); - shapeData.ID = _localID; + shapeData.ID = LocalID; shapeData.Type = ShapeData.PhysicsShapeType.SHAPE_AVATAR; shapeData.Position = _position; shapeData.Rotation = _orientation; @@ -118,21 +103,25 @@ public class BSCharacter : BSPhysObject shapeData.Mass = _mass; shapeData.Buoyancy = _buoyancy; shapeData.Static = ShapeData.numericFalse; - shapeData.Friction = Scene.Params.avatarFriction; - shapeData.Restitution = Scene.Params.avatarRestitution; + shapeData.Friction = PhysicsScene.Params.avatarFriction; + shapeData.Restitution = PhysicsScene.Params.avatarRestitution; // do actual create at taint time - Scene.TaintedObject("BSCharacter.create", delegate() + PhysicsScene.TaintedObject("BSCharacter.create", delegate() { - DetailLog("{0},BSCharacter.create", _localID); - BulletSimAPI.CreateObject(Scene.WorldID, shapeData); + DetailLog("{0},BSCharacter.create,taint", LocalID); + BulletSimAPI.CreateObject(PhysicsScene.WorldID, shapeData); - // Set the buoyancy for flying. This will be refactored when all the settings happen in C# - BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy); + // 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(Scene.World.Ptr, LocalID)); + 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); }); - return; } @@ -140,9 +129,9 @@ public class BSCharacter : BSPhysObject public override void Destroy() { DetailLog("{0},BSCharacter.Destroy", LocalID); - Scene.TaintedObject("BSCharacter.destroy", delegate() + PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() { - BulletSimAPI.DestroyObject(Scene.WorldID, _localID); + BulletSimAPI.DestroyObject(PhysicsScene.WorldID, LocalID); }); } @@ -151,70 +140,83 @@ public class BSCharacter : BSPhysObject 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 Vector3 Size { + public override OMV.Vector3 Size { get { // Avatar capsule size is kept in the scale parameter. - return new Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z); + return new OMV.Vector3(_scale.X * 2, _scale.Y * 2, _scale.Z); } - set { + set { // When an avatar's size is set, only the height is changed // and that really only depends on the radius. _size = value; - _scale.Z = (_size.Z * 1.15f) - (_scale.X + _scale.Y); + ComputeAvatarScale(_size); // TODO: something has to be done with the avatar's vertical position ComputeAvatarVolumeAndMass(); - Scene.TaintedObject("BSCharacter.setSize", delegate() + PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() { - BulletSimAPI.SetObjectScaleMass(Scene.WorldID, LocalID, _scale, _mass, true); + BulletSimAPI.SetObjectScaleMass(PhysicsScene.WorldID, LocalID, _scale, _mass, true); }); - } - } - public override PrimitiveBaseShape Shape { - set { _pbs = value; - } - } - public override uint LocalID { - set { _localID = value; } - get { return _localID; } } - public override bool Grabbed { - set { _grabbed = value; - } + public override PrimitiveBaseShape Shape { + set { _pbs = value; + } } - public override bool Selected { - set { _selected = value; - } + public override bool Grabbed { + set { _grabbed = value; + } + } + public override bool Selected { + set { _selected = value; + } } public override void CrossingFailure() { return; } public override void link(PhysicsActor obj) { return; } public override void delink() { return; } - public override void LockAngularMotion(Vector3 axis) { return; } - public override Vector3 Position { + // Set motion values to zero. + // Do it to the properties so the values get set in the physics engine. + // Push the setting of the values to the viewer. + // Called at taint time! + public override void ZeroMotion() + { + _velocity = OMV.Vector3.Zero; + _acceleration = OMV.Vector3.Zero; + _rotationalVelocity = OMV.Vector3.Zero; + + // Zero some other properties directly into the physics engine + BulletSimAPI.SetLinearVelocity2(BSBody.ptr, OMV.Vector3.Zero); + BulletSimAPI.SetAngularVelocity2(BSBody.ptr, OMV.Vector3.Zero); + BulletSimAPI.SetInterpolationVelocity2(BSBody.ptr, OMV.Vector3.Zero, OMV.Vector3.Zero); + BulletSimAPI.ClearForces2(BSBody.ptr); + } + + public override void LockAngularMotion(OMV.Vector3 axis) { return; } + + public override OMV.Vector3 Position { get { // _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); - return _position; - } + return _position; + } set { _position = value; PositionSanityCheck(); - Scene.TaintedObject("BSCharacter.setPosition", delegate() + PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() { DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation); + BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); }); - } + } } // Check that the current position is sane and, if not, modify the position to make it so. @@ -223,9 +225,9 @@ public class BSCharacter : BSPhysObject private bool PositionSanityCheck() { bool ret = false; - + // If below the ground, move the avatar up - float terrainHeight = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); + float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); if (Position.Z < terrainHeight) { DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); @@ -247,200 +249,200 @@ public class BSCharacter : BSPhysObject { // The new position value must be pushed into the physics engine but we can't // just assign to "Position" because of potential call loops. - Scene.TaintedObject("BSCharacter.PositionSanityCheck", delegate() + PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", delegate() { DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation); + BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); }); ret = true; } 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; } } - public override Vector3 Force { - get { return _force; } + public override OMV.Vector3 Force { + get { return _force; } set { _force = value; // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); - Scene.TaintedObject("BSCharacter.SetForce", delegate() + PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() { DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); - BulletSimAPI.SetObjectForce(Scene.WorldID, LocalID, _force); + BulletSimAPI.SetObjectForce(PhysicsScene.WorldID, LocalID, _force); }); - } + } } - public override int VehicleType { - get { return 0; } - set { return; } + public override int VehicleType { + get { return 0; } + set { return; } } public override void VehicleFloatParam(int param, float value) { } - public override void VehicleVectorParam(int param, Vector3 value) {} - public override void VehicleRotationParam(int param, Quaternion rotation) { } + public override void VehicleVectorParam(int param, OMV.Vector3 value) {} + public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { } public override void VehicleFlags(int param, bool remove) { } // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more public override void SetVolumeDetect(int param) { return; } - public override Vector3 GeometricCenter { get { return Vector3.Zero; } } - public override Vector3 CenterOfMass { get { return Vector3.Zero; } } - public override Vector3 Velocity { - get { return _velocity; } + public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } + public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } + public override OMV.Vector3 Velocity { + get { return _velocity; } set { _velocity = value; // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); - Scene.TaintedObject("BSCharacter.setVelocity", delegate() + PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() { DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); - BulletSimAPI.SetObjectVelocity(Scene.WorldID, _localID, _velocity); + BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity); }); - } + } } - public override Vector3 Torque { - get { return _torque; } - set { _torque = value; - } + public override OMV.Vector3 Torque { + get { return _torque; } + set { _torque = value; + } } - public override float CollisionScore { - get { return _collisionScore; } - set { _collisionScore = value; - } + public override float CollisionScore { + get { return _collisionScore; } + set { _collisionScore = value; + } } - public override Vector3 Acceleration { + public override OMV.Vector3 Acceleration { get { return _acceleration; } set { _acceleration = value; } } - public override Quaternion Orientation { - get { return _orientation; } + public override OMV.Quaternion Orientation { + get { return _orientation; } set { _orientation = value; // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); - Scene.TaintedObject("BSCharacter.setOrientation", delegate() + PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() { // _position = BulletSimAPI.GetObjectPosition(Scene.WorldID, _localID); - BulletSimAPI.SetObjectTranslation(Scene.WorldID, _localID, _position, _orientation); + BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); }); - } + } } - public override int PhysicsActorType { - get { return _physicsActorType; } - set { _physicsActorType = value; - } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; + } } - public override bool IsPhysical { - get { return _isPhysical; } + public override bool IsPhysical { + get { return _isPhysical; } set { _isPhysical = value; - } + } } - public override bool Flying { - get { return _flying; } + public override bool Flying { + get { return _flying; } set { _flying = value; // simulate flying by changing the effect of gravity this.Buoyancy = ComputeBuoyancyFromFlying(_flying); - } + } } // Flying is implimented by changing the avatar's buoyancy. // Would this be done better with a vehicle type? private float ComputeBuoyancyFromFlying(bool ifFlying) { return ifFlying ? 1f : 0f; } - public override bool - SetAlwaysRun { - get { return _setAlwaysRun; } - set { _setAlwaysRun = value; } + public override bool + SetAlwaysRun { + get { return _setAlwaysRun; } + set { _setAlwaysRun = value; } } - public override bool ThrottleUpdates { - get { return _throttleUpdates; } - set { _throttleUpdates = value; } + public override bool ThrottleUpdates { + get { return _throttleUpdates; } + set { _throttleUpdates = value; } } public override bool IsColliding { - get { return (_collidingStep == Scene.SimulationStep); } - set { _isColliding = value; } + get { return (CollidingStep == PhysicsScene.SimulationStep); } + set { _isColliding = value; } } public override bool CollidingGround { - get { return (_collidingGroundStep == Scene.SimulationStep); } - set { _collidingGround = value; } + get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } + set { CollidingGround = value; } } - public override bool CollidingObj { - get { return _collidingObj; } - set { _collidingObj = value; } + public override bool CollidingObj { + get { return _collidingObj; } + set { _collidingObj = value; } } - public override bool FloatOnWater { - set { _floatOnWater = value; } + public override bool FloatOnWater { + set { _floatOnWater = value; } } - public override Vector3 RotationalVelocity { - get { return _rotationalVelocity; } - set { _rotationalVelocity = value; } + public override OMV.Vector3 RotationalVelocity { + get { return _rotationalVelocity; } + set { _rotationalVelocity = value; } } - public override bool Kinematic { - get { return _kinematic; } - set { _kinematic = value; } + public override bool Kinematic { + get { return _kinematic; } + set { _kinematic = value; } } // neg=fall quickly, 0=1g, 1=0g, pos=float up - public override float Buoyancy { - get { return _buoyancy; } - set { _buoyancy = value; - Scene.TaintedObject("BSCharacter.setBuoyancy", delegate() + public override float Buoyancy { + get { return _buoyancy; } + set { _buoyancy = value; + PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate() { DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); - BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy); + BulletSimAPI.SetObjectBuoyancy(PhysicsScene.WorldID, LocalID, _buoyancy); }); - } + } } // Used for MoveTo - public override Vector3 PIDTarget { - set { _PIDTarget = value; } + public override OMV.Vector3 PIDTarget { + set { _PIDTarget = value; } } - public override bool PIDActive { - set { _usePID = value; } + public override bool PIDActive { + set { _usePID = value; } } - public override float PIDTau { - set { _PIDTau = value; } + public override float PIDTau { + set { _PIDTau = value; } } // Used for llSetHoverHeight and maybe vehicle height // Hover Height will override MoveTo target's Z - public override bool PIDHoverActive { + public override bool PIDHoverActive { set { _useHoverPID = value; } } - public override float PIDHoverHeight { + public override float PIDHoverHeight { set { _PIDHoverHeight = value; } } - public override PIDHoverType PIDHoverType { + public override PIDHoverType PIDHoverType { set { _PIDHoverType = value; } } - public override float PIDHoverTau { + public override float PIDHoverTau { set { _PIDHoverTao = value; } } // For RotLookAt - public override Quaternion APIDTarget { set { return; } } + public override OMV.Quaternion APIDTarget { set { return; } } public override bool APIDActive { set { return; } } public override float APIDStrength { set { return; } } public override float APIDDamping { set { return; } } - public override void AddForce(Vector3 force, bool pushforce) { + public override void AddForce(OMV.Vector3 force, bool pushforce) { if (force.IsFinite()) { _force.X += force.X; _force.Y += force.Y; _force.Z += force.Z; // m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force); - Scene.TaintedObject("BSCharacter.AddForce", delegate() + PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate() { DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force); - BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force); + BulletSimAPI.SetObjectForce2(BSBody.ptr, _force); }); } else @@ -450,42 +452,19 @@ public class BSCharacter : BSPhysObject //m_lastUpdateSent = false; } - public override void AddAngularForce(Vector3 force, bool pushforce) { + public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { } - public override void SetMomentum(Vector3 momentum) { + public override void SetMomentum(OMV.Vector3 momentum) { } - // Turn on collision events at a rate no faster than one every the given milliseconds - public override void SubscribeEvents(int ms) { - _subscribedEventsMs = ms; - if (ms > 0) - { - // make sure first collision happens - _nextCollisionOkTime = Util.EnvironmentTickCount() - _subscribedEventsMs; - - Scene.TaintedObject("BSCharacter.SubscribeEvents", delegate() - { - BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); - }); - } - } - - public override void ZeroMotion() + private void ComputeAvatarScale(OMV.Vector3 size) { - return; - } + _scale.X = PhysicsScene.Params.avatarCapsuleRadius; + _scale.Y = PhysicsScene.Params.avatarCapsuleRadius; - // Stop collision events - public override void UnSubscribeEvents() { - _subscribedEventsMs = 0; - Scene.TaintedObject("BSCharacter.UnSubscribeEvents", delegate() - { - BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); - }); - } - // Return 'true' if someone has subscribed to events - public override bool SubscribedEvents() { - return (_subscribedEventsMs > 0); + // 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); } // set _avatarVolume and _mass based on capsule size, _density and _scale @@ -502,7 +481,7 @@ public class BSCharacter : BSPhysObject * Math.Min(_scale.X, _scale.Y) * _scale.Y // plus the volume of the capsule end caps ); - _mass = _density * _avatarVolume; + _mass = _avatarDensity * _avatarVolume; } // The physics engine says that properties have updated. Update same and inform @@ -520,67 +499,9 @@ public class BSCharacter : BSPhysObject // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. PositionSanityCheck2(); - float heightHere = Scene.TerrainManager.GetTerrainHeightAtXYZ(_position); // only for debug + float heightHere = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); // only for debug DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5},terrain={6}", LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity, heightHere); } - - // Called by the scene when a collision with this object is reported - // The collision, if it should be reported to the character, is placed in a collection - // that will later be sent to the simulator when SendCollisions() is called. - CollisionEventUpdate collisionCollection = null; - public override bool Collide(uint collidingWith, BSPhysObject collidee, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth) - { - bool ret = false; - - // The following makes IsColliding() and IsCollidingGround() work - _collidingStep = Scene.SimulationStep; - if (collidingWith <= Scene.TerrainManager.HighestTerrainID) - { - _collidingGroundStep = Scene.SimulationStep; - } - // DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith); - - // throttle collisions to the rate specified in the subscription - if (SubscribedEvents()) { - int nowTime = Scene.SimulationNowTime; - if (nowTime >= _nextCollisionOkTime) { - _nextCollisionOkTime = nowTime + _subscribedEventsMs; - - if (collisionCollection == null) - collisionCollection = new CollisionEventUpdate(); - collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); - ret = true; - } - } - return ret; - } - - public override void SendCollisions() - { - /* - if (collisionCollection != null && collisionCollection.Count > 0) - { - base.SendCollisionUpdate(collisionCollection); - collisionCollection = null; - } - */ - // Kludge to make a collision call even if there are no collisions. - // This causes the avatar animation to get updated. - if (collisionCollection == null) - collisionCollection = new CollisionEventUpdate(); - base.SendCollisionUpdate(collisionCollection); - // If there were any collisions in the collection, make sure we don't use the - // same instance next time. - if (collisionCollection.Count > 0) - collisionCollection = null; - // End kludge - } - - // Invoke the detailed logger and output something if it's enabled. - private void DetailLog(string msg, params Object[] args) - { - Scene.PhysicsLogging.Write(msg, args); - } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs index 1376a29bbf..63a4127075 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs @@ -49,20 +49,23 @@ public abstract class BSConstraint : IDisposable if (m_enabled) { m_enabled = false; - bool success = BulletSimAPI.DestroyConstraint2(m_world.Ptr, m_constraint.Ptr); - m_world.scene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success); - m_constraint.Ptr = System.IntPtr.Zero; + bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); + m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success); + m_constraint.ptr = System.IntPtr.Zero; } } 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) { bool ret = false; if (m_enabled) - ret = BulletSimAPI.SetLinearLimits2(m_constraint.Ptr, low, high); + ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high); return ret; } @@ -70,7 +73,7 @@ public abstract class BSConstraint : IDisposable { bool ret = false; if (m_enabled) - ret = BulletSimAPI.SetAngularLimits2(m_constraint.Ptr, low, high); + ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high); return ret; } @@ -79,7 +82,7 @@ public abstract class BSConstraint : IDisposable bool ret = false; if (m_enabled) { - BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.Ptr, cnt); + BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt); ret = true; } return ret; @@ -91,7 +94,7 @@ public abstract class BSConstraint : IDisposable if (m_enabled) { // Recompute the internal transforms - BulletSimAPI.CalculateTransforms2(m_constraint.Ptr); + BulletSimAPI.CalculateTransforms2(m_constraint.ptr); ret = true; } return ret; @@ -110,11 +113,11 @@ public abstract class BSConstraint : IDisposable // Setting an object's mass to zero (making it static like when it's selected) // automatically disables the constraints. // If the link is enabled, be sure to set the constraint itself to enabled. - BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true)); + BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, m_world.physicsScene.NumericBool(true)); } else { - m_world.scene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID); + m_world.physicsScene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID); } } return ret; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 61006f07d6..65b38d696a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -54,18 +54,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public class BSDynamics { - private int frcount = 0; // Used to limit dynamics debug output to - // every 100th frame - - private BSScene m_physicsScene; - private BSPrim m_prim; // the prim this dynamic controller belongs to + private BSScene PhysicsScene { get; set; } + // the prim this dynamic controller belongs to + private BSPrim Prim { get; set; } // Vehicle properties - private Vehicle m_type = Vehicle.TYPE_NONE; // If a 'VEHICLE', and what kind - public Vehicle Type - { - get { return m_type; } - } + public Vehicle Type { get; set; } + // private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings: // HOVER_TERRAIN_ONLY @@ -126,14 +121,20 @@ namespace OpenSim.Region.Physics.BulletSPlugin public BSDynamics(BSScene myScene, BSPrim myPrim) { - m_physicsScene = myScene; - m_prim = myPrim; - m_type = Vehicle.TYPE_NONE; + PhysicsScene = myScene; + Prim = myPrim; + Type = Vehicle.TYPE_NONE; + } + + // Return 'true' if this vehicle is doing vehicle things + public bool IsActive + { + get { return Type != Vehicle.TYPE_NONE; } } internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue) { - VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue); + VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue); switch (pParam) { case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY: @@ -232,7 +233,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue) { - VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue); + VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue); switch (pParam) { case Vehicle.ANGULAR_FRICTION_TIMESCALE: @@ -267,7 +268,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue) { - VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue); + VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", Prim.LocalID, pParam, pValue); switch (pParam) { case Vehicle.REFERENCE_FRAME: @@ -281,7 +282,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin internal void ProcessVehicleFlags(int pParam, bool remove) { - VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", m_prim.LocalID, pParam, remove); + VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", Prim.LocalID, pParam, remove); VehicleFlag parm = (VehicleFlag)pParam; if (remove) { @@ -301,9 +302,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin internal void ProcessTypeChange(Vehicle pType) { - VDetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType); + VDetailLog("{0},ProcessTypeChange,type={1}", Prim.LocalID, pType); // Set Defaults For Type - m_type = pType; + Type = pType; switch (pType) { case Vehicle.TYPE_NONE: @@ -465,26 +466,37 @@ namespace OpenSim.Region.Physics.BulletSPlugin } }//end SetDefaultsForType + // Some of the properties of this prim may have changed. + // Do any updating needed for a vehicle + public void Refresh() + { + if (Type == Vehicle.TYPE_NONE) return; + + // Set the prim's inertia to zero. The vehicle code handles that and this + // removes the torque action introduced by Bullet. + Vector3 inertia = Vector3.Zero; + BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia); + BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr); + } + // One step of the vehicle properties for the next 'pTimestep' seconds. internal void Step(float pTimestep) { - if (m_type == Vehicle.TYPE_NONE) return; - - frcount++; // used to limit debug comment output - if (frcount > 100) - frcount = 0; + if (!IsActive) return; MoveLinear(pTimestep); MoveAngular(pTimestep); LimitRotation(pTimestep); // remember the position so next step we can limit absolute movement effects - m_lastPositionVector = m_prim.Position; + m_lastPositionVector = Prim.Position; - VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", - m_prim.LocalID, m_prim.Position, m_prim.Force, m_prim.Velocity, m_prim.RotationalVelocity); + VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", + Prim.LocalID, Prim.Position, Prim.Force, Prim.Velocity, Prim.RotationalVelocity); }// end Step + // Apply the effect of the linear motor. + // Also does hover and float. private void MoveLinear(float pTimestep) { // m_linearMotorDirection is the direction we are moving relative to the vehicle coordinates @@ -499,8 +511,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale / pTimestep); // 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*10); + m_lastLinearVelocityVector += addAmount; // Limit the velocity vector to less than the last set linear motor direction if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X)) @@ -520,18 +532,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_linearMotorDirection *= keepfraction; VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},notDecay={4},dir={5},vel={6}", - m_prim.LocalID, origDir, origVel, addAmount, keepfraction, m_linearMotorDirection, m_lastLinearVelocityVector); + Prim.LocalID, origDir, origVel, addAmount, keepfraction, m_linearMotorDirection, m_lastLinearVelocityVector); } else { // if what remains of direction is very small, zero it. m_linearMotorDirection = Vector3.Zero; m_lastLinearVelocityVector = Vector3.Zero; - VDetailLog("{0},MoveLinear,zeroed", m_prim.LocalID); + VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); } // convert requested object velocity to object relative vector - Quaternion rotq = m_prim.Orientation; + Quaternion rotq = Prim.Orientation; m_newVelocity = m_lastLinearVelocityVector * rotq; // Add the various forces into m_dir which will be our new direction vector (velocity) @@ -539,7 +551,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // add Gravity and Buoyancy // There is some gravity, make a gravity force vector that is applied after object velocity. // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; - Vector3 grav = m_prim.Scene.DefaultGravity * (m_prim.Mass * (1f - m_VehicleBuoyancy)); + Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Mass * (1f - m_VehicleBuoyancy)); /* * RA: Not sure why one would do this @@ -548,11 +560,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity */ - Vector3 pos = m_prim.Position; + Vector3 pos = Prim.Position; // Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f); // If below the terrain, move us above the ground a little. - float terrainHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos); + float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset. // Need to add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass. // Vector3 rotatedSize = m_prim.Size * m_prim.Orientation; @@ -560,8 +572,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin if (pos.Z < terrainHeight) { pos.Z = terrainHeight + 2; - m_prim.Position = pos; - VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", m_prim.LocalID, terrainHeight, pos); + Prim.Position = pos; + VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos); } // Check if hovering @@ -570,7 +582,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // We should hover, get the target height if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) { - m_VhoverTargetHeight = m_prim.Scene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight; + m_VhoverTargetHeight = Prim.PhysicsScene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight; } if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) { @@ -590,7 +602,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2) { - m_prim.Position = pos; + Prim.Position = pos; } } else @@ -608,7 +620,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } } - VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight); + VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", Prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight); } Vector3 posChange = pos - m_lastPositionVector; @@ -642,9 +654,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin } if (changed) { - m_prim.Position = pos; + Prim.Position = pos; VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", - m_prim.LocalID, m_BlockingEndPoint, posChange, pos); + Prim.LocalID, m_BlockingEndPoint, posChange, pos); } } @@ -664,7 +676,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin float postemp = (pos.Z - terrainHeight); if (postemp > 2.5f) grav.Z = (float)(grav.Z * 1.037125); - VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", m_prim.LocalID, grav); + VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", Prim.LocalID, grav); } if ((m_flags & (VehicleFlag.NO_X)) != 0) m_newVelocity.X = 0; @@ -674,7 +686,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_newVelocity.Z = 0; // Apply velocity - m_prim.Velocity = m_newVelocity; + Prim.Velocity = m_newVelocity; // apply gravity force // Why is this set here? The physics engine already does gravity. // m_prim.AddForce(grav, false); @@ -683,11 +695,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep)); m_lastLinearVelocityVector *= keepFraction; - VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},grav={4},1Mdecay={5}", - m_prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, grav, 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() + // Apply the effect of the angular motor. private void MoveAngular(float pTimestep) { // m_angularMotorDirection // angular velocity requested by LSL motor @@ -699,7 +712,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_lastAngularVelocity // what was last applied to body // Get what the body is doing, this includes 'external' influences - Vector3 angularVelocity = m_prim.RotationalVelocity; + Vector3 angularVelocity = Prim.RotationalVelocity; if (m_angularMotorApply > 0) { @@ -715,8 +728,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep); m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep); - VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},dir={5},vel={6}", - m_prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity); + VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},dir={5},vel={6}", + Prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity); // This is done so that if script request rate is less than phys frame rate the expected // velocity may still be acheived. @@ -737,7 +750,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { float VAservo = 0.2f / (m_verticalAttractionTimescale / pTimestep); // get present body rotation - Quaternion rotq = m_prim.Orientation; + Quaternion rotq = Prim.Orientation; // make a vector pointing up Vector3 verterr = Vector3.Zero; verterr.Z = 1.0f; @@ -766,8 +779,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin vertattr.X += bounce * angularVelocity.X; vertattr.Y += bounce * angularVelocity.Y; - VDetailLog("{0},MoveAngular,verticalAttraction,verterr={1},bounce={2},vertattr={3}", - m_prim.LocalID, verterr, bounce, vertattr); + VDetailLog("{0},MoveAngular,verticalAttraction,verterr={1},bounce={2},vertattr={3}", + Prim.LocalID, verterr, bounce, vertattr); } // else vertical attractor is off @@ -779,18 +792,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Sum velocities m_lastAngularVelocity = m_angularMotorVelocity + vertattr; // + bank + deflection - + if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) { m_lastAngularVelocity.X = 0; m_lastAngularVelocity.Y = 0; - VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", m_prim.LocalID, m_lastAngularVelocity); + VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); } if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) { m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. - VDetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", m_prim.LocalID, m_lastAngularVelocity); + VDetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); } // apply friction @@ -798,14 +811,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_lastAngularVelocity -= m_lastAngularVelocity * decayamount; // Apply to the body - m_prim.RotationalVelocity = m_lastAngularVelocity; + Prim.RotationalVelocity = m_lastAngularVelocity; - VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", m_prim.LocalID, decayamount, m_lastAngularVelocity); + VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity); } //end MoveAngular internal void LimitRotation(float timestep) { - Quaternion rotq = m_prim.Orientation; + Quaternion rotq = Prim.Orientation; Quaternion m_rot = rotq; bool changed = false; if (m_RollreferenceFrame != Quaternion.Identity) @@ -840,8 +853,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin } if (changed) { - m_prim.Orientation = m_rot; - VDetailLog("{0},LimitRotation,done,orig={1},new={2}", m_prim.LocalID, rotq, m_rot); + Prim.Orientation = m_rot; + VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot); } } @@ -849,8 +862,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Invoke the detailed logger and output something if it's enabled. private void VDetailLog(string msg, params Object[] args) { - if (m_prim.Scene.VehicleLoggingEnabled) - m_prim.Scene.PhysicsLogging.Write(msg, args); + if (Prim.PhysicsScene.VehicleLoggingEnabled) + Prim.PhysicsScene.PhysicsLogging.Write(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSHingeConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSHingeConstraint.cs index d68048bd0a..7c8a21580d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSHingeConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSHingeConstraint.cs @@ -1,55 +1,57 @@ -/* - * 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 OpenMetaverse; - -namespace OpenSim.Region.Physics.BulletSPlugin -{ - -class BSHingeConstraint : BSConstraint -{ - public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, - Vector3 pivotInA, Vector3 pivotInB, - Vector3 axisInA, Vector3 axisInB, - bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) - { - m_world = world; - m_body1 = obj1; - m_body2 = obj2; - m_constraint = new BulletConstraint( - BulletSimAPI.CreateHingeConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr, - pivotInA, pivotInB, - axisInA, axisInB, - useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); - m_enabled = true; - } - -} - -} +/* + * 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 OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +class BSHingeConstraint : BSConstraint +{ + public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } } + + public BSHingeConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, + Vector3 pivotInA, Vector3 pivotInB, + Vector3 axisInA, Vector3 axisInB, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) + { + m_world = world; + m_body1 = obj1; + m_body2 = obj2; + m_constraint = new BulletConstraint( + BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr, + pivotInA, pivotInB, + axisInA, axisInB, + useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); + m_enabled = true; + } + +} + +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 7e784ebb83..4f225ae0f0 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -36,17 +36,24 @@ public class BSLinkset { private static string LogHeader = "[BULLETSIM LINKSET]"; - private BSPhysObject m_linksetRoot; - public BSPhysObject LinksetRoot { get { return m_linksetRoot; } } + public BSPhysObject LinksetRoot { get; protected set; } - private BSScene m_physicsScene; - public BSScene PhysicsScene { get { return m_physicsScene; } } + public BSScene PhysicsScene { get; private set; } static int m_nextLinksetID = 1; 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. private List m_children; + private List m_taintChildren; // We lock the diddling of linkset classes to prevent any badness. // This locks the modification of the instances of this class. Changes @@ -55,9 +62,9 @@ public class BSLinkset // We keep the prim's mass in the linkset structure since it could be dependent on other prims private float m_mass; - public float LinksetMass - { - get + public float LinksetMass + { + get { m_mass = ComputeLinksetMass(); return m_mass; @@ -79,22 +86,26 @@ public class BSLinkset // A simple linkset of one (no children) LinksetID = m_nextLinksetID++; // We create LOTS of linksets. - if (m_nextLinksetID < 0) + if (m_nextLinksetID <= 0) m_nextLinksetID = 1; - m_physicsScene = scene; - m_linksetRoot = parent; + PhysicsScene = scene; + LinksetRoot = parent; m_children = new List(); + m_taintChildren = new List(); m_mass = parent.MassRaw; } // Link to a linkset where the child knows the parent. // Parent changing should not happen so do some sanity checking. // We return the parent's linkset so the child can track its membership. + // Called at runtime. public BSLinkset AddMeToLinkset(BSPhysObject child) { lock (m_linksetActivityLock) { - AddChildToLinkset(child); + // Don't add the root to its own linkset + if (!IsRoot(child)) + AddChildToLinkset(child); } return this; } @@ -102,26 +113,18 @@ public class BSLinkset // Remove a child from a linkset. // Returns a new linkset for the child which is a linkset of one (just the // orphened child). + // Called at runtime. public BSLinkset RemoveMeFromLinkset(BSPhysObject child) { lock (m_linksetActivityLock) { if (IsRoot(child)) { - // if root of linkset, take the linkset apart - while (m_children.Count > 0) - { - // Note that we don't do a foreach because the remove routine - // takes it out of the list. - RemoveChildFromOtherLinkset(m_children[0]); - } - m_children.Clear(); // just to make sure - } - else - { - // Just removing a child from an existing linkset - RemoveChildFromLinkset(child); + // Cannot remove the root from a linkset. + return this; } + + RemoveChildFromLinkset(child); } // The child is down to a linkset of just itself @@ -131,7 +134,7 @@ public class BSLinkset // Return 'true' if the passed object is the root object of this linkset public bool IsRoot(BSPhysObject requestor) { - return (requestor.LocalID == m_linksetRoot.LocalID); + return (requestor.LocalID == LinksetRoot.LocalID); } public int NumberOfChildren { get { return m_children.Count; } } @@ -157,51 +160,6 @@ public class BSLinkset return ret; } - private float ComputeLinksetMass() - { - float mass = m_linksetRoot.MassRaw; - foreach (BSPhysObject bp in m_children) - { - mass += bp.MassRaw; - } - return mass; - } - - private OMV.Vector3 ComputeLinksetCenterOfMass() - { - OMV.Vector3 com = m_linksetRoot.Position * m_linksetRoot.MassRaw; - float totalMass = m_linksetRoot.MassRaw; - - lock (m_linksetActivityLock) - { - foreach (BSPhysObject bp in m_children) - { - com += bp.Position * bp.MassRaw; - totalMass += bp.MassRaw; - } - if (totalMass != 0f) - com /= totalMass; - } - - return com; - } - - private OMV.Vector3 ComputeLinksetGeometricCenter() - { - OMV.Vector3 com = m_linksetRoot.Position; - - lock (m_linksetActivityLock) - { - foreach (BSPhysObject bp in m_children) - { - com += bp.Position * bp.MassRaw; - } - com /= (m_children.Count + 1); - } - - return com; - } - // 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 @@ -210,8 +168,8 @@ public class BSLinkset // Called at taint-time! public bool MakeDynamic(BSPhysObject child) { - bool ret = false; - return ret; + // What is done for each object in BSPrim is what we want. + return false; } // The object is going static (non-physical). Do any setup necessary @@ -226,6 +184,7 @@ public class BSLinkset // When physical properties are changed the linkset needs to recalculate // its internal properties. + // Called at runtime. public void Refresh(BSPhysObject requestor) { // If there are no children, there can't be any constraints to recompute @@ -242,58 +201,125 @@ public class BSLinkset } } - // Call each of the constraints that make up this linkset and recompute the - // various transforms and variables. Used when objects are added or removed - // from a linkset to make sure the constraints know about the new mass and - // geometry. - // Must only be called at taint time!! - private void RecomputeLinksetConstraintVariables() + // Routine used when rebuilding the body of the root of the linkset + // Destroy all the constraints have have been made to root. + // This is called when the root body is changing. + // Returns 'true' of something eas actually removed and would need restoring + // Called at taint-time!! + public bool RemoveBodyDependencies(BSPrim child) { - float linksetMass = LinksetMass; + bool ret = false; + lock (m_linksetActivityLock) { - bool somethingMissing = false; - foreach (BSPhysObject child in m_children) + if (IsRoot(child)) { - BSConstraint constrain; - if (m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain)) + // If the one with the dependency is root, must undo all children + DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},numChild={2}", + child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); + foreach (BSPhysObject bpo in m_taintChildren) { - // DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}", - // LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID); - 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. - // Caused by the fact that m_children is built at run time but building constraints - // happens at taint time. - somethingMissing = true; - break; + PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody); + ret = true; } } - - // If the whole linkset is not here, doesn't make sense to recompute linkset wide values - if (!somethingMissing) + else { - // 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); - foreach (BSPhysObject child in m_children) + 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")); + // Remove the dependency on the body of this one + if (m_taintChildren.Contains(child)) { - BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity); + PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody); + ret = true; } - /* - // The root prim takes on the weight of the whole linkset - OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(LinksetRoot.BSShape.Ptr, linksetMass); - BulletSimAPI.SetMassProps2(LinksetRoot.BSBody.Ptr, linksetMass, inertia); - OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass(); - BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity); - BulletSimAPI.UpdateInertiaTensor2(LinksetRoot.BSBody.Ptr); - */ } } - return; + return ret; + } + + // Routine used when rebuilding the body of the root of the linkset + // This is called after RemoveAllLinksToRoot() to restore all the constraints. + // This is called when the root body has been changed. + // Called at taint-time!! + public void RestoreBodyDependencies(BSPrim child) + { + lock (m_linksetActivityLock) + { + 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, LinksetRoot.BSBody, bpo, bpo.BSBody); + } + } + 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, LinksetRoot.BSBody, child, child.BSBody); + } + } + } + + // ================================================================ + // Below this point is internal magic + + private float ComputeLinksetMass() + { + float mass; + lock (m_linksetActivityLock) + { + mass = LinksetRoot.MassRaw; + foreach (BSPhysObject bp in m_taintChildren) + { + mass += bp.MassRaw; + } + } + return mass; + } + + private OMV.Vector3 ComputeLinksetCenterOfMass() + { + OMV.Vector3 com; + lock (m_linksetActivityLock) + { + com = LinksetRoot.Position * LinksetRoot.MassRaw; + float totalMass = LinksetRoot.MassRaw; + + foreach (BSPhysObject bp in m_taintChildren) + { + com += bp.Position * bp.MassRaw; + totalMass += bp.MassRaw; + } + if (totalMass != 0f) + com /= totalMass; + } + + return com; + } + + private OMV.Vector3 ComputeLinksetGeometricCenter() + { + OMV.Vector3 com; + lock (m_linksetActivityLock) + { + com = LinksetRoot.Position; + + foreach (BSPhysObject bp in m_taintChildren) + { + com += bp.Position * bp.MassRaw; + } + com /= (m_taintChildren.Count + 1); + } + + return com; } // I am the root of a linkset and a new child is being added @@ -304,12 +330,22 @@ public class BSLinkset { m_children.Add(child); - BSPhysObject rootx = LinksetRoot; // capture the root as of now + BSPhysObject rootx = LinksetRoot; // capture the root and body as of now + BulletBody rootBodyx = LinksetRoot.BSBody; BSPhysObject childx = child; - m_physicsScene.TaintedObject("AddChildToLinkset", delegate() + BulletBody childBodyx = child.BSBody; + + DetailLog("{0},AddChildToLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", + rootx.LocalID, + rootx.LocalID, rootBodyx.ptr.ToString("X"), + childx.LocalID, childBodyx.ptr.ToString("X")); + + PhysicsScene.TaintedObject("AddChildToLinkset", delegate() { - DetailLog("{0},AddChildToLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID); - PhysicallyLinkAChildToRoot(rootx, childx); // build the physical binding between me and the child + DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID); + // build the physical binding between me and the child + m_taintChildren.Add(childx); + PhysicallyLinkAChildToRoot(rootx, rootBodyx, childx, childBodyx); }); } return; @@ -319,10 +355,10 @@ public class BSLinkset // 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 - // has to be updated also (like pointer to prim's parent). + // also has to be updated (like pointer to prim's parent). private void RemoveChildFromOtherLinkset(BSPhysObject pchild) { - pchild.Linkset = new BSLinkset(m_physicsScene, pchild); + pchild.Linkset = new BSLinkset(PhysicsScene, pchild); RemoveChildFromLinkset(pchild); } @@ -332,13 +368,22 @@ public class BSLinkset { if (m_children.Remove(child)) { - BSPhysObject rootx = LinksetRoot; // capture the root as of now + BSPhysObject rootx = LinksetRoot; // capture the root and body as of now + BulletBody rootBodyx = LinksetRoot.BSBody; BSPhysObject childx = child; - m_physicsScene.TaintedObject("RemoveChildFromLinkset", delegate() - { - DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID); + BulletBody childBodyx = child.BSBody; - PhysicallyUnlinkAChildFromRoot(rootx, childx); + DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", + childx.LocalID, + rootx.LocalID, rootBodyx.ptr.ToString("X"), + childx.LocalID, childBodyx.ptr.ToString("X")); + + PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() + { + if (m_taintChildren.Contains(childx)) + m_taintChildren.Remove(childx); + + PhysicallyUnlinkAChildFromRoot(rootx, rootBodyx, childx, childBodyx); RecomputeLinksetConstraintVariables(); }); @@ -353,7 +398,8 @@ public class BSLinkset // Create a constraint between me (root of linkset) and the passed prim (the child). // Called at taint time! - private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) + private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody, + BSPhysObject childPrim, BulletBody childBody) { // Zero motion for children so they don't interpolate childPrim.ZeroMotion(); @@ -365,18 +411,36 @@ public class BSLinkset // real world coordinate of midpoint between the two objects OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); + DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", + rootPrim.LocalID, + rootPrim.LocalID, rootBody.ptr.ToString("X"), + childPrim.LocalID, childBody.ptr.ToString("X"), + rootPrim.Position, childPrim.Position, midPoint); + // create a constraint that allows no freedom of movement between the two objects // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 - DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},child={2},rLoc={3},cLoc={4},midLoc={5}", - rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID, rootPrim.Position, childPrim.Position, midPoint); + + // There is great subtlty in these paramters. Notice the check for a ptr of zero. + // We pass the BulletBody structure into the taint in order to capture the pointer + // of the body at the time of constraint creation. This doesn't work for the very first + // construction because there is no body yet. The body + // is constructed later at taint time. Thus we use the body address at time of the + // taint creation but, if it is zero, use what's in the prim at the moment. + // There is a possible race condition since shape can change without a taint call + // (like changing to a mesh that is already constructed). The fix for that would be + // to only change BSShape at taint time thus syncronizing these operations at + // the cost of efficiency and lag. BS6DofConstraint constrain = new BS6DofConstraint( - m_physicsScene.World, rootPrim.BSBody, childPrim.BSBody, + PhysicsScene.World, + rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody, + childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody, midPoint, true, true ); + /* NOTE: below is an attempt to build constraint with full frame computation, etc. - * Using the midpoint is easier since it lets the Bullet code use the transforms + * Using the midpoint is easier since it lets the Bullet code manipulate the transforms * of the objects. * Code left as a warning to future programmers. // ================================================================================== @@ -408,7 +472,7 @@ public class BSLinkset // ================================================================================== */ - m_physicsScene.Constraints.AddConstraint(constrain); + PhysicsScene.Constraints.AddConstraint(constrain); // zero linear and angular limits makes the objects unable to move in relation to each other constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); @@ -429,31 +493,79 @@ public class BSLinkset } // Remove linkage between myself and a particular child + // The root and child bodies are passed in because we need to remove the constraint between + // the bodies that were at unlink time. // Called at taint time! - private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) + private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BulletBody rootBody, + BSPhysObject childPrim, BulletBody childBody) { - DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); + DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", + rootPrim.LocalID, + rootPrim.LocalID, rootBody.ptr.ToString("X"), + childPrim.LocalID, childBody.ptr.ToString("X")); // Find the constraint for this link and get rid of it from the overall collection and from my list - m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody); + PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootBody, childBody); // Make the child refresh its location - BulletSimAPI.PushUpdate2(childPrim.BSBody.Ptr); + BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); } - // Remove linkage between myself and any possible children I might have + /* + // Remove linkage between myself and any possible children I might have. // Called at taint time! private void PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) { DetailLog("{0},PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); - m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody); + PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody); } + */ + + // Call each of the constraints that make up this linkset and recompute the + // various transforms and variables. Used when objects are added or removed + // from a linkset to make sure the constraints know about the new mass and + // geometry. + // Must only be called at taint time!! + private void RecomputeLinksetConstraintVariables() + { + float linksetMass = LinksetMass; + foreach (BSPhysObject child in m_taintChildren) + { + BSConstraint constrain; + if (PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain)) + { + // DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}", + // LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID); + 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; + } + } + + // 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); + foreach (BSPhysObject child in m_taintChildren) + { + BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity); + } + } + return; + } + // Invoke the detailed logger and output something if it's enabled. private void DetailLog(string msg, params Object[] args) { - m_physicsScene.PhysicsLogging.Write(msg, args); + PhysicsScene.PhysicsLogging.Write(msg, args); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index 3fe71e1003..d9b738b865 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -39,26 +39,171 @@ namespace OpenSim.Region.Physics.BulletSPlugin // unless the difference is significant. public abstract class BSPhysObject : PhysicsActor { - public abstract BSLinkset Linkset { get; set; } + protected void BaseInitialize(BSScene parentScene, uint localID, string name, string typeName) + { + PhysicsScene = parentScene; + LocalID = localID; + PhysObjectName = name; + TypeName = typeName; - public abstract bool Collide(uint collidingWith, BSPhysObject collidee, - OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth); - public abstract void SendCollisions(); + Linkset = new BSLinkset(PhysicsScene, this); - // Return the object mass without calculating it or side effects + CollisionCollection = new CollisionEventUpdate(); + SubscribedEventsMs = 0; + CollidingStep = 0; + CollidingGroundStep = 0; + } + + public BSScene PhysicsScene { get; protected set; } + // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor + public string PhysObjectName { get; protected set; } + public string TypeName { get; protected set; } + + public BSLinkset Linkset { get; set; } + + // Return the object mass without calculating it or having side effects public abstract float MassRaw { get; } // Reference to the physical body (btCollisionObject) of this object - public abstract BulletBody BSBody { get; set; } + public BulletBody BSBody; // Reference to the physical shape (btCollisionShape) of this object - public abstract BulletShape BSShape { get; set; } + public BulletShape BSShape; + // Stop all physical motion. public abstract void ZeroMotion(); + // Step the vehicle simulation for this object. A NOOP if the vehicle was not configured. public virtual void StepVehicle(float timeStep) { } + // Update the physical location and motion of the object. Called with data from Bullet. public abstract void UpdateProperties(EntityProperties entprop); + // Tell the object to clean up. public abstract void Destroy(); + + #region Collisions + + // Requested number of milliseconds between collision events. Zero means disabled. + protected int SubscribedEventsMs { get; set; } + // Given subscription, the time that a collision may be passed up + protected int NextCollisionOkTime { get; set; } + // The simulation step that last had a collision + protected long CollidingStep { get; set; } + // The simulation step that last had a collision with the ground + protected long CollidingGroundStep { get; set; } + // The collision flags we think are set in Bullet + protected CollisionFlags CurrentCollisionFlags { get; set; } + + // The collisions that have been collected this tick + protected CollisionEventUpdate CollisionCollection; + + // The simulation step is telling this object about a collision. + // Return 'true' if a collision was processed and should be sent up. + // Called at taint time from within the Step() function + public virtual bool Collide(uint collidingWith, BSPhysObject collidee, + OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) + { + bool ret = false; + + // The following lines make IsColliding() and IsCollidingGround() work + CollidingStep = PhysicsScene.SimulationStep; + if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID) + { + CollidingGroundStep = PhysicsScene.SimulationStep; + } + + // prims in the same linkset cannot collide with each other + if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID)) + { + return ret; + } + + // 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); + + 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. + // 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; + + // throttle the collisions to the number of milliseconds specified in the subscription + int nowTime = PhysicsScene.SimulationNowTime; + if (nowTime >= NextCollisionOkTime) + { + NextCollisionOkTime = nowTime + 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. + if (CollisionCollection.Count == 0) + { + // If I have no collisions this time, remove me from the list of objects with collisions. + ret = false; + } + + // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count); + base.SendCollisionUpdate(CollisionCollection); + + // The collisionCollection structure is passed around in the simulator. + // Make sure we don't have a handle to that one and that a new one is used for next time. + CollisionCollection = new CollisionEventUpdate(); + } + return ret; + } + + // Subscribe for collision events. + // Parameter is the millisecond rate the caller wishes collision events to occur. + public override void SubscribeEvents(int ms) { + // DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms); + SubscribedEventsMs = ms; + if (ms > 0) + { + // make sure first collision happens + NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs); + + PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() + { + CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + }); + } + else + { + // Subscribing for zero or less is the same as unsubscribing + UnSubscribeEvents(); + } + } + public override void UnSubscribeEvents() { + // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); + SubscribedEventsMs = 0; + PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() + { + CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + }); + } + // Return 'true' if the simulator wants collision events + public override bool SubscribedEvents() { + return (SubscribedEventsMs > 0); + } + + #endregion // Collisions + + // High performance detailed logging routine used by the physical objects. + protected void DetailLog(string msg, params Object[] args) + { + PhysicsScene.PhysicsLogging.Write(msg, args); + } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs index 0f027b8d6a..20f5180551 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPlugin.cs @@ -33,7 +33,7 @@ using OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { /// - /// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim. + /// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim. /// This module interfaces to an unmanaged C++ library which makes the /// actual calls into the Bullet physics engine. /// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/. @@ -62,7 +62,7 @@ public class BSPlugin : IPhysicsPlugin if (Util.IsWindows()) Util.LoadArchSpecificWindowsDll("BulletSim.dll"); // If not Windows, loading is performed by the - // Mono loader as specified in + // Mono loader as specified in // "bin/Physics/OpenSim.Region.Physics.BulletSPlugin.dll.config". _mScene = new BSScene(sceneIdentifier); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 26a581fc77..a0e627ec66 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -24,6 +24,9 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +// Uncomment this it enable code to do all shape an body memory management +// in the C# code. using System; using System.Reflection; using System.Collections.Generic; @@ -36,6 +39,7 @@ using OpenSim.Region.Physics.ConvexDecompositionDotNet; namespace OpenSim.Region.Physics.BulletSPlugin { + [Serializable] public sealed class BSPrim : BSPhysObject { @@ -49,11 +53,6 @@ public sealed class BSPrim : BSPhysObject private ulong _hullKey; private List _hulls; - private BSScene _scene; - public BSScene Scene { get { return _scene; } } - private String _avName; - private uint _localID = 0; - // _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 @@ -87,18 +86,6 @@ public sealed class BSPrim : BSPhysObject private bool _kinematic; private float _buoyancy; - // Membership in a linkset is controlled by this class. - public override BSLinkset Linkset { get; set; } - - private int _subscribedEventsMs = 0; - private int _nextCollisionOkTime = 0; - long _collidingStep; - long _collidingGroundStep; - CollisionFlags m_currentCollisionFlags = 0; - - public override BulletBody BSBody { get; set; } - public override BulletShape BSShape { get; set; } - private BSDynamics _vehicle; private OMV.Vector3 _PIDTarget; @@ -113,10 +100,8 @@ public sealed class BSPrim : BSPhysObject OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) { // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); - _localID = localID; - _avName = primName; + base.BaseInitialize(parent_scene, localID, primName, "BSPrim"); _physicsActorType = (int)ActorTypes.Prim; - _scene = parent_scene; _position = pos; _size = size; _scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type @@ -129,25 +114,23 @@ public sealed class BSPrim : BSPhysObject _pbs = pbs; _isPhysical = pisPhysical; _isVolumeDetect = false; - _subscribedEventsMs = 0; - _friction = _scene.Params.defaultFriction; // TODO: compute based on object material - _density = _scene.Params.defaultDensity; // TODO: compute based on object material - _restitution = _scene.Params.defaultRestitution; - Linkset = new BSLinkset(Scene, this); // a linkset of one - _vehicle = new BSDynamics(Scene, this); // add vehicleness + _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material + _density = PhysicsScene.Params.defaultDensity; // TODO: compute based on object material + _restitution = PhysicsScene.Params.defaultRestitution; + _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness _mass = CalculateMass(); + + // No body or shape yet + BSBody = new BulletBody(LocalID, IntPtr.Zero); + BSShape = new BulletShape(IntPtr.Zero); + DetailLog("{0},BSPrim.constructor,call", LocalID); // do the actual object creation at taint time - _scene.TaintedObject("BSPrim.create", delegate() + PhysicsScene.TaintedObject("BSPrim.create", delegate() { CreateGeomAndObject(true); - // Get the pointer to the physical body for this object. - // At the moment, we're still letting BulletSim manage the creation and destruction - // of the object. Someday we'll move that into the C# code. - BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); - BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr)); - m_currentCollisionFlags = BulletSimAPI.GetCollisionFlags2(BSBody.Ptr); + CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(BSBody.ptr); }); } @@ -168,57 +151,62 @@ public sealed class BSPrim : BSPhysObject // Undo any vehicle properties this.VehicleType = (int)Vehicle.TYPE_NONE; - _scene.TaintedObject("BSPrim.destroy", delegate() + PhysicsScene.TaintedObject("BSPrim.destroy", delegate() { DetailLog("{0},BSPrim.Destroy,taint,", LocalID); - // everything in the C# world will get garbage collected. Tell the C++ world to free stuff. - BulletSimAPI.DestroyObject(_scene.WorldID, LocalID); + // If there are physical body and shape, release my use of same. + PhysicsScene.Shapes.DereferenceBody(BSBody, true, null); + PhysicsScene.Shapes.DereferenceShape(BSShape, true, null); }); } - - public override bool Stopped { - get { return _stopped; } + + public override bool Stopped { + get { return _stopped; } } - public override OMV.Vector3 Size { - get { return _size; } + public override OMV.Vector3 Size { + get { return _size; } set { _size = value; - _scene.TaintedObject("BSPrim.setSize", delegate() + 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); + // DetailLog("{0},BSPrim.setSize,size={1},scale={2},mass={3},physical={4}", LocalID, _size, _scale, _mass, IsPhysical); }); - } + } } - public override PrimitiveBaseShape Shape { + // 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 PrimitiveBaseShape Shape { set { _pbs = value; - _scene.TaintedObject("BSPrim.setShape", delegate() + PhysicsScene.TaintedObject("BSPrim.setShape", delegate() { _mass = CalculateMass(); // changing the shape changes the mass - CreateGeomAndObject(false); + CreateGeomAndObject(true); }); - } + } } - public override uint LocalID { - set { _localID = value; } - get { return _localID; } + public override bool Grabbed { + set { _grabbed = value; + } } - public override bool Grabbed { - set { _grabbed = value; - } - } - public override bool Selected { + public override bool Selected { set { _isSelected = value; - _scene.TaintedObject("BSPrim.setSelected", delegate() + PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() { - SetObjectDynamic(); + // DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); + SetObjectDynamic(false); }); - } + } } public override void CrossingFailure() { return; } @@ -232,10 +220,10 @@ public sealed class BSPrim : BSPhysObject Linkset = parent.Linkset.AddMeToLinkset(this); - DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}", + DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}", LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); } - return; + return; } // delink me from my linkset @@ -245,12 +233,12 @@ public sealed class BSPrim : BSPhysObject BSPhysObject parentBefore = Linkset.LinksetRoot; int childrenBefore = Linkset.NumberOfChildren; - + Linkset = Linkset.RemoveMeFromLinkset(this); - DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", + DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); - return; + return; } // Set motion values to zero. @@ -264,43 +252,40 @@ public sealed class BSPrim : BSPhysObject _rotationalVelocity = OMV.Vector3.Zero; // Zero some other properties directly into the physics engine - BulletSimAPI.SetLinearVelocity2(BSBody.Ptr, OMV.Vector3.Zero); - BulletSimAPI.SetAngularVelocity2(BSBody.Ptr, OMV.Vector3.Zero); - BulletSimAPI.SetInterpolationVelocity2(BSBody.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero); - BulletSimAPI.ClearForces2(BSBody.Ptr); + BulletSimAPI.ClearForces2(BSBody.ptr); } public override void LockAngularMotion(OMV.Vector3 axis) - { + { DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); return; } - public override OMV.Vector3 Position { - get { + public override OMV.Vector3 Position { + get { if (!Linkset.IsRoot(this)) // child prims move around based on their parent. Need to get the latest location - _position = BulletSimAPI.GetPosition2(BSBody.Ptr); + _position = BulletSimAPI.GetPosition2(BSBody.ptr); // don't do the GetObjectPosition for root elements because this function is called a zillion times - // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); - return _position; - } + // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); + return _position; + } set { _position = value; // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? - _scene.TaintedObject("BSPrim.setPosition", delegate() + PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() { - DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetTranslation2(BSBody.Ptr, _position, _orientation); + // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); }); - } + } } // Return the effective mass of the object. // If there are multiple items in the linkset, add them together for the root public override float Mass - { + { get { // return Linkset.LinksetMass; @@ -323,59 +308,59 @@ public sealed class BSPrim : BSPhysObject get { return Linkset.GeometricCenter; } } - public override OMV.Vector3 Force { - get { return _force; } + public override OMV.Vector3 Force { + get { return _force; } set { _force = value; - _scene.TaintedObject("BSPrim.setForce", delegate() + PhysicsScene.TaintedObject("BSPrim.setForce", delegate() { - DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); - BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force); + // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); + BulletSimAPI.SetObjectForce2(BSBody.ptr, _force); }); - } + } } - public override int VehicleType { + public override int VehicleType { get { return (int)_vehicle.Type; // if we are a vehicle, return that type - } + } set { Vehicle type = (Vehicle)value; BSPrim vehiclePrim = this; - _scene.TaintedObject("setVehicleType", delegate() + PhysicsScene.TaintedObject("setVehicleType", delegate() { // Done at taint time so we're sure the physics engine is not using the variables // Vehicle code changes the parameters for this vehicle type. _vehicle.ProcessTypeChange(type); // Tell the scene about the vehicle so it will get processing each frame. - _scene.VehicleInSceneTypeChanged(this, type); + PhysicsScene.VehicleInSceneTypeChanged(this, type); }); - } + } } - public override void VehicleFloatParam(int param, float value) + public override void VehicleFloatParam(int param, float value) { - _scene.TaintedObject("BSPrim.VehicleFloatParam", delegate() + PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() { _vehicle.ProcessFloatVehicleParam((Vehicle)param, value); }); } - public override void VehicleVectorParam(int param, OMV.Vector3 value) + public override void VehicleVectorParam(int param, OMV.Vector3 value) { - _scene.TaintedObject("BSPrim.VehicleVectorParam", delegate() + PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() { _vehicle.ProcessVectorVehicleParam((Vehicle)param, value); }); } - public override void VehicleRotationParam(int param, OMV.Quaternion rotation) + public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { - _scene.TaintedObject("BSPrim.VehicleRotationParam", delegate() + PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() { _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); }); } - public override void VehicleFlags(int param, bool remove) + public override void VehicleFlags(int param, bool remove) { - _scene.TaintedObject("BSPrim.VehicleFlags", delegate() + PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() { _vehicle.ProcessVehicleFlags(param, remove); }); @@ -392,73 +377,81 @@ public sealed class BSPrim : BSPhysObject // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more public override void SetVolumeDetect(int param) { bool newValue = (param != 0); - _isVolumeDetect = newValue; - _scene.TaintedObject("BSPrim.SetVolumeDetect", delegate() + if (_isVolumeDetect != newValue) { - SetObjectDynamic(); - }); - return; + _isVolumeDetect = newValue; + PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() + { + // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); + SetObjectDynamic(true); + }); + } + return; } - public override OMV.Vector3 Velocity { - get { return _velocity; } + public override OMV.Vector3 Velocity { + get { return _velocity; } set { _velocity = value; - _scene.TaintedObject("BSPrim.setVelocity", delegate() + PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() { - DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); - BulletSimAPI.SetLinearVelocity2(BSBody.Ptr, _velocity); + // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); + BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity); }); - } + } } - public override OMV.Vector3 Torque { - get { return _torque; } - set { _torque = value; - DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); - } + public override OMV.Vector3 Torque { + get { return _torque; } + set { _torque = value; + // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); + } } - public override float CollisionScore { - get { return _collisionScore; } - set { _collisionScore = value; - } + public override float CollisionScore { + get { return _collisionScore; } + set { _collisionScore = value; + } } - public override OMV.Vector3 Acceleration { + public override OMV.Vector3 Acceleration { get { return _acceleration; } set { _acceleration = value; } } - public override OMV.Quaternion Orientation { + public override OMV.Quaternion Orientation { get { if (!Linkset.IsRoot(this)) { // Children move around because tied to parent. Get a fresh value. - _orientation = BulletSimAPI.GetOrientation2(BSBody.Ptr); + _orientation = BulletSimAPI.GetOrientation2(BSBody.ptr); } return _orientation; - } + } set { _orientation = value; // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? - _scene.TaintedObject("BSPrim.setOrientation", delegate() + PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() { - // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); - DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); - BulletSimAPI.SetTranslation2(BSBody.Ptr, _position, _orientation); + // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); + // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); }); - } + } } - public override int PhysicsActorType { - get { return _physicsActorType; } - set { _physicsActorType = value; } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; } } - public override bool IsPhysical { - get { return _isPhysical; } + public override bool IsPhysical { + get { return _isPhysical; } set { - _isPhysical = value; - _scene.TaintedObject("BSPrim.setIsPhysical", delegate() + if (_isPhysical != value) { - SetObjectDynamic(); - }); - } + _isPhysical = value; + PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() + { + // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); + SetObjectDynamic(true); + }); + } + } } // An object is static (does not move) if selected or not physical @@ -468,53 +461,64 @@ public sealed class BSPrim : BSPhysObject } // An object is solid if it's not phantom and if it's not doing VolumeDetect - private bool IsSolid + public bool IsSolid { get { return !IsPhantom && !_isVolumeDetect; } } // Make gravity work if the object is physical and not selected - // No locking here because only called when it is safe + // Called at taint-time!! + private void SetObjectDynamic(bool forceRebuild) + { + // Recreate the physical object if necessary + CreateGeomAndObject(forceRebuild); + } + + // Convert the simulator's physical properties into settings on BulletSim objects. // There are four flags we're interested in: // IsStatic: Object does not move, otherwise the object has mass and moves // isSolid: other objects bounce off of this object // isVolumeDetect: other objects pass through but can generate collisions // collisionEvents: whether this object returns collision events - private void SetObjectDynamic() - { - // If it's becoming dynamic, it will need hullness - VerifyCorrectPhysicalShape(); - UpdatePhysicalParameters(); - } - private void UpdatePhysicalParameters() { - /* - // Bullet wants static objects to have a mass of zero - float mass = IsStatic ? 0f : _mass; + // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape); + + // 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). + BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, BSBody.ptr); - BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass); - */ - BulletSimAPI.RemoveObjectFromWorld2(Scene.World.Ptr, BSBody.Ptr); // Set up the object physicalness (does gravity and collisions move this object) MakeDynamic(IsStatic); - // Make solid or not (do things bounce off or pass through this object) - MakeSolid(IsSolid); + // Do any vehicle stuff + _vehicle.Refresh(); - // Arrange for collisions events if the simulator wants them + // Arrange for collision events if the simulator wants them EnableCollisions(SubscribedEvents()); - BulletSimAPI.AddObjectToWorld2(Scene.World.Ptr, BSBody.Ptr); + // Make solid or not (do things bounce off or pass through this object). + MakeSolid(IsSolid); + + BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, BSBody.ptr); + + // Rebuild its shape + BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, BSBody.ptr); + + // Collision filter can be set only when the object is in the world + if (BSBody.collisionFilter != 0 || BSBody.collisionMask != 0) + { + BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr, (uint)BSBody.collisionFilter, (uint)BSBody.collisionMask); + } // Recompute any linkset parameters. // When going from non-physical to physical, this re-enables the constraints that // had been automatically disabled when the mass was set to zero. Linkset.Refresh(this); - DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,static={1},solid={2},mass={3}, cf={4}", - LocalID, IsStatic, IsSolid, _mass, m_currentCollisionFlags); + DetailLog("{0},BSPrim.UpdatePhysicalParameters,exit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}", + LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, BSBody, BSShape); } // "Making dynamic" means changing to and from static. @@ -527,65 +531,86 @@ public sealed class BSPrim : BSPhysObject if (makeStatic) { // Become a Bullet 'static' object type - m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT); + CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_STATIC_OBJECT); // Stop all movement - BulletSimAPI.ClearAllForces2(BSBody.Ptr); + BulletSimAPI.ClearAllForces2(BSBody.ptr); // Center of mass is at the center of the object - BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.BSBody.Ptr, _position, _orientation); + BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.BSBody.ptr, _position, _orientation); // Mass is zero which disables a bunch of physics stuff in Bullet - BulletSimAPI.SetMassProps2(BSBody.Ptr, 0f, OMV.Vector3.Zero); + BulletSimAPI.SetMassProps2(BSBody.ptr, 0f, OMV.Vector3.Zero); // There is no inertia in a static object - BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr); + BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); // There can be special things needed for implementing linksets Linkset.MakeStatic(this); - // The activation state is 'sleeping' so Bullet will not try to act on it - BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.ISLAND_SLEEPING); + // The activation state is 'disabled' so Bullet will not try to act on it + BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_SIMULATION); + + BSBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; + BSBody.collisionMask = CollisionFilterGroups.StaticObjectMask; } else { // Not a Bullet static object - m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT); + CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.CF_STATIC_OBJECT); + + // Set various physical properties so internal dynamic properties will get computed correctly as they are set + BulletSimAPI.SetFriction2(BSBody.ptr, PhysicsScene.Params.defaultFriction); + BulletSimAPI.SetRestitution2(BSBody.ptr, PhysicsScene.Params.defaultRestitution); - // Set various physical properties so internal things will get computed correctly as they are set - BulletSimAPI.SetFriction2(BSBody.Ptr, Scene.Params.defaultFriction); - BulletSimAPI.SetRestitution2(BSBody.Ptr, Scene.Params.defaultRestitution); // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 - BulletSimAPI.SetInterpolationLinearVelocity2(BSBody.Ptr, OMV.Vector3.Zero); - BulletSimAPI.SetInterpolationAngularVelocity2(BSBody.Ptr, OMV.Vector3.Zero); - BulletSimAPI.SetInterpolationVelocity2(BSBody.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero); + BulletSimAPI.ClearAllForces2(BSBody.ptr); // A dynamic object has mass - IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.Ptr); - OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Linkset.LinksetMass); - BulletSimAPI.SetMassProps2(BSBody.Ptr, _mass, inertia); - // Inertia is based on our new mass - BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr); + IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.ptr); + OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Mass); + // OMV.Vector3 inertia = OMV.Vector3.Zero; + BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia); + BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); // Various values for simulation limits - BulletSimAPI.SetDamping2(BSBody.Ptr, Scene.Params.linearDamping, Scene.Params.angularDamping); - BulletSimAPI.SetDeactivationTime2(BSBody.Ptr, Scene.Params.deactivationTime); - BulletSimAPI.SetSleepingThresholds2(BSBody.Ptr, Scene.Params.linearSleepingThreshold, Scene.Params.angularSleepingThreshold); - BulletSimAPI.SetContactProcessingThreshold2(BSBody.Ptr, Scene.Params.contactProcessingThreshold); + BulletSimAPI.SetDamping2(BSBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping); + BulletSimAPI.SetDeactivationTime2(BSBody.ptr, PhysicsScene.Params.deactivationTime); + BulletSimAPI.SetSleepingThresholds2(BSBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); + BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold); - // There can be special things needed for implementing linksets + // There can be special things needed for implementing linksets. Linkset.MakeDynamic(this); // Force activation of the object so Bullet will act on it. - BulletSimAPI.Activate2(BSBody.Ptr, true); + // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. + BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); + BulletSimAPI.Activate2(BSBody.ptr, true); + + BSBody.collisionFilter = CollisionFilterGroups.ObjectFilter; + BSBody.collisionMask = CollisionFilterGroups.ObjectMask; } } // "Making solid" means that other object will not pass through this object. + // To make transparent, we create a Bullet ghost object. + // Note: This expects to be called from the UpdatePhysicalParameters() routine as + // the functions after this one set up the state of a possibly newly created collision body. private void MakeSolid(bool makeSolid) { + CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(BSBody.ptr); if (makeSolid) { - // Easy in Bullet -- just remove the object flag that controls collision response - m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); + // Verify the previous code created the correct shape for this type of thing. + if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0) + { + 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); } else { - m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); + if ((bodyType & CollisionObjectTypes.CO_GHOST_OBJECT) == 0) + { + 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); + BSBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter; + BSBody.collisionMask = CollisionFilterGroups.VolumeDetectMask; } } @@ -594,40 +619,40 @@ public sealed class BSPrim : BSPhysObject { if (wantsCollisionEvents) { - m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); } else { - m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); } } // prims don't fly - public override bool Flying { - get { return _flying; } + public override bool Flying { + get { return _flying; } set { _flying = value; - } + } } - public override bool SetAlwaysRun { - get { return _setAlwaysRun; } - set { _setAlwaysRun = value; } + public override bool SetAlwaysRun { + get { return _setAlwaysRun; } + set { _setAlwaysRun = value; } } - public override bool ThrottleUpdates { - get { return _throttleUpdates; } - set { _throttleUpdates = value; } + public override bool ThrottleUpdates { + get { return _throttleUpdates; } + set { _throttleUpdates = value; } } public override bool IsColliding { - get { return (_collidingStep == _scene.SimulationStep); } - set { _isColliding = value; } + get { return (CollidingStep == PhysicsScene.SimulationStep); } + set { _isColliding = value; } } public override bool CollidingGround { - get { return (_collidingGroundStep == _scene.SimulationStep); } - set { _collidingGround = value; } + get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } + set { _collidingGround = value; } } - public override bool CollidingObj { - get { return _collidingObj; } - set { _collidingObj = value; } + public override bool CollidingObj { + get { return _collidingObj; } + set { _collidingObj = value; } } public bool IsPhantom { get { @@ -637,10 +662,10 @@ public sealed class BSPrim : BSPhysObject return false; } } - public override bool FloatOnWater { - set { _floatOnWater = value; } + public override bool FloatOnWater { + set { _floatOnWater = value; } } - public override OMV.Vector3 RotationalVelocity { + public override OMV.Vector3 RotationalVelocity { get { /* OMV.Vector3 pv = OMV.Vector3.Zero; @@ -652,61 +677,60 @@ public sealed class BSPrim : BSPhysObject */ return _rotationalVelocity; - } + } set { _rotationalVelocity = value; // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); - _scene.TaintedObject("BSPrim.setRotationalVelocity", delegate() + PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() { - DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); - BulletSimAPI.SetAngularVelocity2(BSBody.Ptr, _rotationalVelocity); + // DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); + BulletSimAPI.SetAngularVelocity2(BSBody.ptr, _rotationalVelocity); }); - } + } } - public override bool Kinematic { - get { return _kinematic; } - set { _kinematic = value; + public override bool Kinematic { + get { return _kinematic; } + set { _kinematic = value; // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic); - } + } } - public override float Buoyancy { - get { return _buoyancy; } + public override float Buoyancy { + get { return _buoyancy; } set { _buoyancy = value; - _scene.TaintedObject("BSPrim.setBuoyancy", delegate() + PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() { - DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy); + // DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy); // Buoyancy is faked by changing the gravity applied to the object - float grav = Scene.Params.gravity * (1f - _buoyancy); - BulletSimAPI.SetGravity2(BSBody.Ptr, new OMV.Vector3(0f, 0f, grav)); - // BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy); + 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 { - set { _PIDTarget = value; } + public override OMV.Vector3 PIDTarget { + set { _PIDTarget = value; } } - public override bool PIDActive { - set { _usePID = value; } + public override bool PIDActive { + set { _usePID = value; } } - public override float PIDTau { - set { _PIDTau = value; } + public override float PIDTau { + set { _PIDTau = value; } } // Used for llSetHoverHeight and maybe vehicle height // Hover Height will override MoveTo target's Z - public override bool PIDHoverActive { + public override bool PIDHoverActive { set { _useHoverPID = value; } } - public override float PIDHoverHeight { + public override float PIDHoverHeight { set { _PIDHoverHeight = value; } } - public override PIDHoverType PIDHoverType { + public override PIDHoverType PIDHoverType { set { _PIDHoverType = value; } } - public override float PIDHoverTau { + public override float PIDHoverTau { set { _PIDHoverTao = value; } } @@ -730,7 +754,7 @@ public sealed class BSPrim : BSPhysObject m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); return; } - _scene.TaintedObject("BSPrim.AddForce", delegate() + PhysicsScene.TaintedObject("BSPrim.AddForce", delegate() { OMV.Vector3 fSum = OMV.Vector3.Zero; lock (m_accumulatedForces) @@ -741,43 +765,19 @@ public sealed class BSPrim : BSPhysObject } m_accumulatedForces.Clear(); } - DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum); + // DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum); // For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object. - BulletSimAPI.ApplyCentralForce2(BSBody.Ptr, fSum); + BulletSimAPI.ApplyCentralForce2(BSBody.ptr, fSum); }); } - public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { - DetailLog("{0},BSPrim.AddAngularForce,call,angForce={1},push={2}", LocalID, force, pushforce); + public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { + // DetailLog("{0},BSPrim.AddAngularForce,call,angForce={1},push={2}", LocalID, force, pushforce); // m_log.DebugFormat("{0}: AddAngularForce. f={1}, push={2}", LogHeader, force, pushforce); } - public override void SetMomentum(OMV.Vector3 momentum) { - DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum); + public override void SetMomentum(OMV.Vector3 momentum) { + // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum); } - public override void SubscribeEvents(int ms) { - _subscribedEventsMs = ms; - if (ms > 0) - { - // make sure first collision happens - _nextCollisionOkTime = Util.EnvironmentTickCount() - _subscribedEventsMs; - - Scene.TaintedObject("BSPrim.SubscribeEvents", delegate() - { - m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); - }); - } - } - public override void UnSubscribeEvents() { - _subscribedEventsMs = 0; - Scene.TaintedObject("BSPrim.UnSubscribeEvents", delegate() - { - m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); - }); - } - public override bool SubscribedEvents() { - return (_subscribedEventsMs > 0); - } - #region Mass Calculation private float CalculateMass() @@ -787,8 +787,8 @@ public sealed class BSPrim : BSPhysObject float returnMass = 0; float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; - float hollowVolume = hollowAmount * hollowAmount; - + float hollowVolume = hollowAmount * hollowAmount; + switch (_pbs.ProfileShape) { case ProfileShape.Square: @@ -824,16 +824,16 @@ public sealed class BSPrim : BSPhysObject else if (_pbs.PathCurve == (byte)Extrusion.Curve1) { - //a tube + //a tube volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); volume -= volume*tmp*tmp; - + if (hollowAmount > 0.0) { hollowVolume *= hollowAmount; - + switch (_pbs.HollowShape) { case HollowShape.Square: @@ -892,7 +892,7 @@ public sealed class BSPrim : BSPhysObject volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); volume *= (1.0f - tmp * tmp); - + if (hollowAmount > 0.0) { @@ -1071,302 +1071,17 @@ public sealed class BSPrim : BSPhysObject if (returnMass <= 0) returnMass = 0.0001f; - if (returnMass > _scene.MaximumObjectMass) - returnMass = _scene.MaximumObjectMass; + if (returnMass > PhysicsScene.MaximumObjectMass) + returnMass = PhysicsScene.MaximumObjectMass; return returnMass; }// end CalculateMass #endregion Mass Calculation - // 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. - // Returns 'true' if the geometry was rebuilt. - // Called at taint-time! - private bool CreateGeom(bool forceRebuild) - { - bool ret = false; - bool haveShape = false; - - // If the prim attributes are simple, this could be a simple Bullet native shape - if ((_pbs.SculptEntry && !Scene.ShouldMeshSculptedPrim) - || (_pbs.ProfileBegin == 0 && _pbs.ProfileEnd == 0 - && _pbs.ProfileHollow == 0 - && _pbs.PathTwist == 0 && _pbs.PathTwistBegin == 0 - && _pbs.PathBegin == 0 && _pbs.PathEnd == 0 - && _pbs.PathTaperX == 0 && _pbs.PathTaperY == 0 - && _pbs.PathScaleX == 100 && _pbs.PathScaleY == 100 - && _pbs.PathShearX == 0 && _pbs.PathShearY == 0) ) - { - if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1) - { - haveShape = true; - if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE)) - { - DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild); - _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; - // Bullet native objects are scaled by the Bullet engine so pass the size in - _scale = _size; - // TODO: do we need to check for and destroy a mesh or hull that might have been left from before? - ret = true; - } - } - else - { - // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size); - haveShape = true; - if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX)) - { - DetailLog("{0},BSPrim.CreateGeom,box (force={1})", LocalID, forceRebuild); - _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; - _scale = _size; - // TODO: do we need to check for and destroy a mesh or hull that might have been left from before? - ret = true; - } - } - } - // If a simple shape isn't happening, create a mesh and possibly a hull - if (!haveShape) - { - if (IsPhysical) - { - if (forceRebuild || _hullKey == 0) - { - // physical objects require a hull for interaction. - // This also creates the mesh if it doesn't already exist - ret = CreateGeomHull(); - } - } - else - { - if (forceRebuild || _meshKey == 0) - { - // Static (non-physical) objects only need a mesh for bumping into - ret = CreateGeomMesh(); - } - } - } - return ret; - } - - // No locking here because this is done when we know physics is not simulating - // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs). - // Called at taint-time! - private bool CreateGeomMesh() - { - // level of detail based on size and type of the object - float lod = _scene.MeshLOD; - if (_pbs.SculptEntry) - lod = _scene.SculptLOD; - float maxAxis = Math.Max(_size.X, Math.Max(_size.Y, _size.Z)); - if (maxAxis > _scene.MeshMegaPrimThreshold) - lod = _scene.MeshMegaPrimLOD; - - ulong newMeshKey = (ulong)_pbs.GetMeshKey(_size, lod); - // m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _meshKey, newMeshKey); - - // if this new shape is the same as last time, don't recreate the mesh - if (_meshKey == newMeshKey) return false; - - DetailLog("{0},BSPrim.CreateGeomMesh,create,key={1}", LocalID, newMeshKey); - // Since we're recreating new, get rid of any previously generated shape - if (_meshKey != 0) - { - // m_log.DebugFormat("{0}: CreateGeom: deleting old mesh. lID={1}, Key={2}", LogHeader, _localID, _meshKey); - DetailLog("{0},BSPrim.CreateGeomMesh,deleteOld,key={1}", LocalID, _meshKey); - BulletSimAPI.DestroyMesh(_scene.WorldID, _meshKey); - _mesh = null; - _meshKey = 0; - } - - _meshKey = newMeshKey; - // always pass false for physicalness as this creates some sort of bounding box which we don't need - _mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, lod, false); - - int[] indices = _mesh.getIndexListAsInt(); - List vertices = _mesh.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, _localID, _meshKey, indices.Length, vertices.Count); - BulletSimAPI.CreateMesh(_scene.WorldID, _meshKey, indices.GetLength(0), indices, - vertices.Count, verticesAsFloats); - - _shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH; - // meshes are already scaled by the meshmerizer - _scale = new OMV.Vector3(1f, 1f, 1f); - return true; - } - - // No locking here because this is done when we know physics is not simulating - // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs). - private bool CreateGeomHull() - { - float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD; - ulong newHullKey = (ulong)_pbs.GetMeshKey(_size, lod); - // m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _hullKey, newHullKey); - - // if the hull hasn't changed, don't rebuild it - if (newHullKey == _hullKey) return false; - - DetailLog("{0},BSPrim.CreateGeomHull,create,oldKey={1},newKey={2}", LocalID, _hullKey, newHullKey); - - // Since we're recreating new, get rid of any previously generated shape - if (_hullKey != 0) - { - // m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey); - DetailLog("{0},BSPrim.CreateGeomHull,deleteOldHull,key={1}", LocalID, _hullKey); - BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey); - _hullKey = 0; - } - - _hullKey = newHullKey; - - // Make sure the underlying mesh exists and is correct - CreateGeomMesh(); - - int[] indices = _mesh.getIndexListAsInt(); - List vertices = _mesh.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++) - { - 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 - _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 = _hulls.Count; - int totalVertices = 1; // include one for the count of the hulls - foreach (ConvexResult cr in _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 _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 definition in Bullet - // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid={1}, key={2}, hulls={3}", LogHeader, _localID, _hullKey, hullCount); - BulletSimAPI.CreateHull(_scene.WorldID, _hullKey, hullCount, convHulls); - _shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL; - // meshes are already scaled by the meshmerizer - _scale = new OMV.Vector3(1f, 1f, 1f); - DetailLog("{0},BSPrim.CreateGeomHull,done", LocalID); - return true; - } - - // Callback from convex hull creater with a newly created hull. - // Just add it to the collection of hulls for this shape. - private void HullReturn(ConvexResult result) - { - _hulls.Add(result); - return; - } - - private void VerifyCorrectPhysicalShape() - { - if (!IsStatic) - { - // if not static, it will need a hull to efficiently collide with things - if (_hullKey == 0) - { - CreateGeomAndObject(false); - } - - } - } - - // Create an object in Bullet if it has not already been created - // No locking here because this is done when the physics engine is not simulating - // Returns 'true' if an object was actually created. - private bool CreateObject() - { - // this routine is called when objects are rebuilt. - - // the mesh or hull must have already been created in Bullet - ShapeData shape; - FillShapeInfo(out shape); - // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type); - bool ret = BulletSimAPI.CreateObject(_scene.WorldID, shape); - - // the CreateObject() may have recreated the rigid body. Make sure we have the latest address. - BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); - BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr)); - - return ret; - } - // Copy prim's info into the BulletSim shape description structure public void FillShapeInfo(out ShapeData shape) { - shape.ID = _localID; + shape.ID = LocalID; shape.Type = _shapeType; shape.Position = _position; shape.Rotation = _orientation; @@ -1380,22 +1095,39 @@ public sealed class BSPrim : BSPhysObject shape.Restitution = _restitution; 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. - // No locking here because this is done when the physics engine is not simulating + // Called at taint-time!!! private void CreateGeomAndObject(bool forceRebuild) { - // m_log.DebugFormat("{0}: CreateGeomAndObject. lID={1}, force={2}", LogHeader, _localID, forceRebuild); - // Create the geometry that will make up the object - if (CreateGeom(forceRebuild)) + ShapeData shapeData; + FillShapeInfo(out shapeData); + + // If this prim is part of a linkset, we must remove and restore the physical + // links of the body is rebuilt. + bool needToRestoreLinkset = false; + + // Create the correct physical representation for this type of object. + // Updates BSBody and BSShape with the new information. + PhysicsScene.Shapes.GetBodyAndShape(forceRebuild, PhysicsScene.World, this, shapeData, _pbs, + null, delegate(BulletBody dBody) { - // Create the object and place it into the world - CreateObject(); - // Make sure the properties are set on the new object - UpdatePhysicalParameters(); + // Called if the current prim body is about to be destroyed. + // Remove all the physical dependencies on the old body. + needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); + }); + + if (needToRestoreLinkset) + { + // If physical body dependencies were removed, restore them + Linkset.RestoreBodyDependencies(this); } + + // Make sure the properties are set on the new object + UpdatePhysicalParameters(); return; } @@ -1485,66 +1217,10 @@ public sealed class BSPrim : BSPhysObject { // For debugging, we can also report the movement of children DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", - LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, + LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, entprop.Acceleration, entprop.RotationalVelocity); } */ } - - // I've collided with something - // Called at taint time from within the Step() function - CollisionEventUpdate collisionCollection; - public override bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) - { - bool ret = false; - - // The following lines make IsColliding() and IsCollidingGround() work - _collidingStep = Scene.SimulationStep; - if (collidingWith <= Scene.TerrainManager.HighestTerrainID) - { - _collidingGroundStep = Scene.SimulationStep; - } - - // DetailLog("{0},BSPrim.Collison,call,with={1}", LocalID, collidingWith); - - // prims in the same linkset cannot collide with each other - if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID)) - { - return ret; - } - - // if someone has subscribed for collision events.... - if (SubscribedEvents()) { - // throttle the collisions to the number of milliseconds specified in the subscription - int nowTime = Scene.SimulationNowTime; - if (nowTime >= _nextCollisionOkTime) { - _nextCollisionOkTime = nowTime + _subscribedEventsMs; - - if (collisionCollection == null) - collisionCollection = new CollisionEventUpdate(); - collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); - ret = true; - } - } - return ret; - } - - // The scene is telling us it's time to pass our collected collisions into the simulator - public override void SendCollisions() - { - if (collisionCollection != null && collisionCollection.Count > 0) - { - base.SendCollisionUpdate(collisionCollection); - // The collisionCollection structure is passed around in the simulator. - // Make sure we don't have a handle to that one and that a new one is used next time. - collisionCollection = null; - } - } - - // Invoke the detailed logger and output something if it's enabled. - private void DetailLog(string msg, params Object[] args) - { - Scene.PhysicsLogging.Write(msg, args); - } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 52997dd526..0cf8c91a2d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -39,20 +39,20 @@ using log4net; using OpenMetaverse; // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) -// Adjust character capsule size when height is adjusted (ScenePresence.SetHeight) -// Test sculpties +// Move all logic out of the C++ code and into the C# code for easier future modifications. +// Test sculpties (verified that they don't work) // Compute physics FPS reasonably // Based on material, set density and friction -// More efficient memory usage when passing hull information from BSPrim to BulletSim -// Move all logic out of the C++ code and into the C# code for easier future modifications. +// Don't use constraints in linksets of non-physical objects. Means having to move children manually. // Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? // In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) // At the moment, physical and phantom causes object to drop through the terrain // Physical phantom objects and related typing (collision options ) -// Use collision masks for collision with terrain and phantom objects // Check out llVolumeDetect. Must do something for that. +// Use collision masks for collision with terrain and phantom objects +// More efficient memory usage when passing hull information from BSPrim to BulletSim // Should prim.link() and prim.delink() membership checking happen at taint time? -// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once +// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once. // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect // Implement LockAngularMotion // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) @@ -60,7 +60,7 @@ using OpenMetaverse; // Add PID movement operations. What does ScenePresence.MoveToTarget do? // Check terrain size. 128 or 127? // Raycast -// +// namespace OpenSim.Region.Physics.BulletSPlugin { public class BSScene : PhysicsScene, IPhysicsParameters @@ -73,12 +73,15 @@ public class BSScene : PhysicsScene, IPhysicsParameters public string BulletSimVersion = "?"; - public Dictionary PhysObjects = new Dictionary(); + public Dictionary PhysObjects; + public BSShapeCollection Shapes; - private HashSet m_objectsWithCollisions = new HashSet(); - // Following is a kludge and can be removed when avatar animation updating is - // moved to a better place. - private HashSet m_avatarsWithCollisions = new HashSet(); + // Keeping track of the objects with collisions so we can report begin and end of a collision + public HashSet ObjectsWithCollisions = new HashSet(); + public HashSet ObjectsWithNoMoreCollisions = new HashSet(); + // Keep track of all the avatars so we can send them a collision event + // every tick so OpenSim will update its animation. + private HashSet m_avatars = new HashSet(); // List of all the objects that have vehicle properties and should be called // to update each physics step. @@ -202,6 +205,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters public override void Initialise(IMesher meshmerizer, IConfigSource config) { + mesher = meshmerizer; + _taintedObjects = new List(); + PhysObjects = new Dictionary(); + Shapes = new BSShapeCollection(this); + // Allocate pinned memory to pass parameters. m_params = new ConfigurationParameters[1]; m_paramsHandle = GCHandle.Alloc(m_params, GCHandleType.Pinned); @@ -215,12 +223,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned); - mesher = meshmerizer; - _taintedObjects = new List(); - // Enable very detailed logging. // By creating an empty logger when not logging, the log message invocation code - // can be left in and every call doesn't have to check for null. + // can be left in and every call doesn't have to check for null. if (m_physicsLoggingEnabled) { PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); @@ -251,7 +256,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // a child in a mega-region. // Turns out that Bullet really doesn't care about the extents of the simulated // area. It tracks active objects no matter where they are. - Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, 8192f); + 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(), @@ -322,7 +327,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters { m_log.Debug("[BULLETS UNMANAGED]:" + msg); } - + // Called directly from unmanaged code so don't do much private void BulletLoggerPhysLog(string msg) { @@ -351,6 +356,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters Constraints = null; } + if (Shapes != null) + { + Shapes.Dispose(); + Shapes = null; + } + // Anything left in the unmanaged code should be cleaned out BulletSimAPI.Shutdown(WorldID); @@ -379,7 +390,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // TODO: Remove kludge someday. // We must generate a collision for avatars whether they collide or not. // This is required by OpenSim to update avatar animations, etc. - lock (m_avatarsWithCollisions) m_avatarsWithCollisions.Add(actor); + lock (m_avatars) m_avatars.Add(actor); return actor; } @@ -397,7 +408,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters { lock (PhysObjects) PhysObjects.Remove(actor.LocalID); // Remove kludge someday - lock (m_avatarsWithCollisions) m_avatarsWithCollisions.Remove(bsactor); + lock (m_avatars) m_avatars.Remove(bsactor); } catch (Exception e) { @@ -449,7 +460,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // This is a call from the simulator saying that some physical property has been updated. - // The BulletSim driver senses the changing of relevant properties so this taint + // The BulletSim driver senses the changing of relevant properties so this taint // information call is not needed. public override void AddPhysicsActorTaint(PhysicsActor prim) { } @@ -464,6 +475,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters int collidersCount = 0; IntPtr collidersPtr; + int beforeTime = 0; + int simTime = 0; + // prevent simulation until we've been initialized if (!m_initialized) return 5.0f; @@ -481,16 +495,20 @@ public class BSScene : PhysicsScene, IPhysicsParameters int numSubSteps = 0; try { + if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); + numSubSteps = BulletSimAPI.PhysicsStep(WorldID, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); - DetailLog("{0},Simulate,call, nTaints= {1}, substeps={2}, updates={3}, colliders={4}", - DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); + + if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); + DetailLog("{0},Simulate,call, nTaints={1}, simTime={2}, substeps={3}, updates={4}, colliders={5}", + DetailLogZero, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); } catch (Exception e) { - m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", + m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); - DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", + DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); updatedEntityCount = 0; collidersCount = 0; @@ -502,12 +520,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters // Get a value for 'now' so all the collision and update routines don't have to get their own SimulationNowTime = Util.EnvironmentTickCount(); - // This is a kludge to get avatar movement updates. - // ODE sends 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. - m_objectsWithCollisions = new HashSet(m_avatarsWithCollisions); - // If there were collisions, process them by sending the event to the prim. // Collisions must be processed before updates. if (collidersCount > 0) @@ -523,11 +535,34 @@ public class BSScene : PhysicsScene, IPhysicsParameters } } + // This is a kludge to get avatar movement updates. + // ODE sends 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. - foreach (BSPhysObject bsp in m_objectsWithCollisions) - bsp.SendCollisions(); - m_objectsWithCollisions.Clear(); + 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); + } + } + + // Objects that are done colliding are removed from the ObjectsWithCollisions list. + // This can't be done by SendCollisions because it is inside an iteration of ObjectWithCollisions. + if (ObjectsWithNoMoreCollisions.Count > 0) + { + foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) + ObjectsWithCollisions.Remove(po); + ObjectsWithNoMoreCollisions.Clear(); + } // If any of the objects had updated properties, tell the object it has been changed by the physics engine if (updatedEntityCount > 0) @@ -555,7 +590,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // 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. // Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS. - return numSubSteps * m_fixedTimeStep; + return numSubSteps * m_fixedTimeStep * 1000; } // Something has collided @@ -570,20 +605,20 @@ public class BSScene : PhysicsScene, IPhysicsParameters if (!PhysObjects.TryGetValue(localID, out collider)) { // If the object that is colliding cannot be found, just ignore the collision. + DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith); return; } - // The terrain is not in the physical object list so 'collidee' - // can be null when Collide() is called. + // The terrain is not in the physical object list so 'collidee' can be null when Collide() is called. BSPhysObject collidee = null; PhysObjects.TryGetValue(collidingWith, out collidee); - // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); + DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) { // If a collision was posted, remember to send it to the simulator - m_objectsWithCollisions.Add(collider); + ObjectsWithCollisions.Add(collider); } return; @@ -599,7 +634,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters TerrainManager.SetTerrain(heightMap); } - public override void SetWaterLevel(float baseheight) + public override void SetWaterLevel(float baseheight) { m_waterLevel = baseheight; } @@ -609,7 +644,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters return m_waterLevel; } - public override void DeleteTerrain() + public override void DeleteTerrain() { // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); } @@ -771,7 +806,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // getters and setters. // It is easiest to find an existing definition and copy it. // Parameter values are floats. Booleans are converted to a floating value. - // + // // A ParameterDefn() takes the following parameters: // -- the text name of the parameter. This is used for console input and ini file. // -- a short text description of the parameter. This shows up in the console listing. @@ -998,7 +1033,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters (s) => { return s.m_params[0].shouldRandomizeSolverOrder; }, (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", - ConfigurationParameters.numericFalse, + ConfigurationParameters.numericTrue, (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, (s) => { return s.m_params[0].shouldSplitSimulationIslands; }, (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ), @@ -1193,7 +1228,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } }); break; - default: + default: // setting only one localID TaintedUpdateParameter(parm, localID, val); break; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs new file mode 100755 index 0000000000..dee6243b7a --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -0,0 +1,735 @@ +/* + * 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; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSShapeCollection : IDisposable +{ + private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; + + protected BSScene PhysicsScene { get; set; } + + private Object m_collectionActivityLock = new Object(); + + // Description of a Mesh + private struct MeshDesc + { + public IntPtr ptr; + public int referenceCount; + public DateTime lastReferenced; + } + + // Description of a hull. + // Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects + private struct HullDesc + { + public IntPtr ptr; + public int referenceCount; + 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; + } + + private Dictionary Meshes = new Dictionary(); + private Dictionary Hulls = new Dictionary(); + private Dictionary Bodies = new Dictionary(); + + public BSShapeCollection(BSScene physScene) + { + PhysicsScene = physScene; + } + + public void Dispose() + { + // TODO!!!!!!!!! + } + + // Callbacks called just before either the body or shape is destroyed. + // Mostly used for changing bodies out from under Linksets. + // Useful for other cases where parameters need saving. + // Passing 'null' says no callback. + public delegate void ShapeDestructionCallback(BulletShape shape); + public delegate void BodyDestructionCallback(BulletBody body); + + // Called to update/change the body and shape for an object. + // 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. + // Called at taint-time!! + public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, + ShapeData shapeData, PrimitiveBaseShape pbs, + ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) + { + bool ret = false; + + // This lock could probably be pushed down lower but building shouldn't take long + lock (m_collectionActivityLock) + { + // Do we have the correct geometry for this type of object? + // Updates prim.BSShape with information/pointers to requested 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. + // Updates prim.BSBody with information/pointers to requested body + bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, prim.BSShape, shapeData, bodyCallback); + ret = newGeom || newBody; + } + DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}", + prim.LocalID, forceRebuild, ret, prim.BSBody, prim.BSShape); + + return ret; + } + + // 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. + public void ReferenceBody(BulletBody body, bool atTaintTime) + { + lock (m_collectionActivityLock) + { + BodyDesc bodyDesc; + if (Bodies.TryGetValue(body.ID, out bodyDesc)) + { + 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={1}", body.ID, body, bodyDesc.referenceCount); + } + bodyDesc.lastReferenced = System.DateTime.Now; + Bodies[body.ID] = bodyDesc; + } +} + + // Release the usage of a body. + // Called when releasing use of a BSBody. BSShape is handled separately. + public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback ) + { + if (body.ptr == IntPtr.Zero) + return; + + lock (m_collectionActivityLock) + { + BodyDesc bodyDesc; + if (Bodies.TryGetValue(body.ID, out bodyDesc)) + { + bodyDesc.referenceCount--; + bodyDesc.lastReferenced = System.DateTime.Now; + Bodies[body.ID] = bodyDesc; + DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount); + + // If body is no longer being used, free it -- bodies are never shared. + if (bodyDesc.referenceCount == 0) + { + Bodies.Remove(body.ID); + BSScene.TaintCallback removeOperation = delegate() + { + DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}", + body.ID, body.ptr.ToString("X")); + // If the caller needs to know, pass the event up. + if (bodyCallback != null) bodyCallback(body); + + // 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); + // It may have already been removed from the world in which case the next is a NOOP. + BulletSimAPI.RemoveObjectFromWorld2(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); + } + } + else + { + DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount); + } + } + } + + // Track the datastructures and use count for a shape. + // When creating a hull, this is called first to reference the mesh + // and then again to reference the hull. + // 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. + // Returns 'true' if this is the initial reference to the shape. Otherwise reused. + private bool ReferenceShape(BulletShape shape) + { + bool ret = false; + switch (shape.type) + { + case ShapeData.PhysicsShapeType.SHAPE_MESH: + MeshDesc meshDesc; + if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) + { + // There is an existing instance of this mesh. + meshDesc.referenceCount++; + DetailLog("{0},BSShapeColliction.ReferenceShape,existingMesh,key={1},cnt={2}", + BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); + } + else + { + // This is a new reference to a mesh + meshDesc.ptr = shape.ptr; + // We keep a reference to the underlying IMesh data so a hull can be built + meshDesc.referenceCount = 1; + DetailLog("{0},BSShapeColliction.ReferenceShape,newMesh,key={1},cnt={2}", + BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); + ret = true; + } + meshDesc.lastReferenced = System.DateTime.Now; + Meshes[shape.shapeKey] = meshDesc; + break; + case ShapeData.PhysicsShapeType.SHAPE_HULL: + HullDesc hullDesc; + if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) + { + // There is an existing instance of this hull. + hullDesc.referenceCount++; + DetailLog("{0},BSShapeColliction.ReferenceShape,existingHull,key={1},cnt={2}", + BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); + } + else + { + // This is a new reference to a hull + hullDesc.ptr = shape.ptr; + hullDesc.referenceCount = 1; + DetailLog("{0},BSShapeColliction.ReferenceShape,newHull,key={1},cnt={2}", + BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); + ret = true; + + } + hullDesc.lastReferenced = System.DateTime.Now; + Hulls[shape.shapeKey] = hullDesc; + break; + case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN: + break; + default: + // Native shapes are not tracked and they don't go into any list + break; + } + return ret; + } + + // 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 atTaintTime, ShapeDestructionCallback shapeCallback) + { + if (shape.ptr == IntPtr.Zero) + return; + + BSScene.TaintCallback dereferenceOperation = delegate() + { + switch (shape.type) + { + 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: + // 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"), atTaintTime); + if (shapeCallback != null) shapeCallback(shape); + BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); + } + break; + } + }; + if (atTaintTime) + { + lock (m_collectionActivityLock) + { + dereferenceOperation(); + } + } + else + { + PhysicsScene.TaintedObject("BSShapeCollection.DereferenceShape", dereferenceOperation); + } + } + + // Count down the reference count for a mesh shape + // Called at taint-time. + private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback) + { + MeshDesc meshDesc; + if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) + { + meshDesc.referenceCount--; + // TODO: release the Bullet storage + if (shapeCallback != null) shapeCallback(shape); + meshDesc.lastReferenced = System.DateTime.Now; + Meshes[shape.shapeKey] = meshDesc; + DetailLog("{0},BSShapeCollection.DereferenceMesh,key={1},refCnt={2}", + BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); + + } + } + + // Count down the reference count for a hull shape + // Called at taint-time. + private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback) + { + HullDesc hullDesc; + if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) + { + hullDesc.referenceCount--; + // TODO: release the Bullet storage (aging old entries?) + if (shapeCallback != null) shapeCallback(shape); + hullDesc.lastReferenced = System.DateTime.Now; + Hulls[shape.shapeKey] = hullDesc; + DetailLog("{0},BSShapeCollection.DereferenceHull,key={1},refCnt={2}", + BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); + } + } + + // 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. + // Returns 'true' if the geometry was rebuilt. + // Called at taint-time! + private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, + PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) + { + bool ret = false; + bool haveShape = false; + bool nativeShapePossible = true; + + // If the prim attributes are simple, this could be a simple Bullet native shape + if (nativeShapePossible + && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) + || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 + && pbs.ProfileHollow == 0 + && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 + && pbs.PathBegin == 0 && pbs.PathEnd == 0 + && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 + && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 + && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) + { + if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) + { + haveShape = true; + if (forceRebuild + || prim.Scale != shapeData.Size + || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE + ) + { + ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE, + ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); + DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", + prim.LocalID, forceRebuild, prim.BSShape); + } + } + else + { + haveShape = true; + if (forceRebuild + || prim.Scale != shapeData.Size + || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX + ) + { + ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX, + ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); + DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", + prim.LocalID, forceRebuild, prim.BSShape); + } + } + } + // 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 (prim.IsPhysical) + { + // Update prim.BSShape to reference a hull of this shape. + ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback); + DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", + shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X")); + } + else + { + ret = GetReferenceToMesh(prim, shapeData, pbs, shapeCallback); + DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", + shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X")); + } + } + return ret; + } + + // Creates a native shape and assignes it to prim.BSShape + private bool GetReferenceToNativeShape( BSPrim 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 + prim.Scale = shapeData.Size; + shapeData.Scale = shapeData.Size; + + // 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 = (ulong)shapeKey; + newShape.isNativeShape = true; + + // Don't need to do a 'ReferenceShape()' here because native shapes are not tracked. + // DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape); + + prim.BSShape = newShape; + return true; + } + + // 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, + ShapeDestructionCallback shapeCallback) + { + BulletShape newShape = new BulletShape(IntPtr.Zero); + + float lod; + ulong newMeshKey = ComputeShapeKey(shapeData, pbs, out lod); + + // if this new shape is the same as last time, don't recreate the mesh + if (prim.BSShape.shapeKey == newMeshKey) return false; + + DetailLog("{0},BSShapeCollection.CreateGeomMesh,create,oldKey={1},newKey={2}", + prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newMeshKey.ToString("X")); + + // Since we're recreating new, get rid of the reference to the previous shape + DereferenceShape(prim.BSShape, true, shapeCallback); + + newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); + + ReferenceShape(newShape); + + // meshes are already scaled by the meshmerizer + prim.Scale = new OMV.Vector3(1f, 1f, 1f); + prim.BSShape = newShape; + + return true; // 'true' means a new shape has been added to this prim + } + + private BulletShape CreatePhysicalMesh(string objName, ulong newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + { + IMesh meshData = null; + IntPtr meshPtr; + MeshDesc meshDesc; + if (Meshes.TryGetValue(newMeshKey, out meshDesc)) + { + // If the mesh has already been built just use it. + meshPtr = meshDesc.ptr; + } + else + { + // 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) + { + 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); + } + BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); + newShape.shapeKey = newMeshKey; + + return newShape; + } + + // 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, + ShapeDestructionCallback shapeCallback) + { + BulletShape newShape; + + float lod; + ulong newHullKey = ComputeShapeKey(shapeData, pbs, out lod); + + // if the hull hasn't changed, don't rebuild it + if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) + return false; + + DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", + prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X")); + + // Remove usage of the previous shape. Also removes reference to underlying mesh if it is a hull. + DereferenceShape(prim.BSShape, true, shapeCallback); + + newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); + + ReferenceShape(newShape); + + // hulls are already scaled by the meshmerizer + prim.Scale = new OMV.Vector3(1f, 1f, 1f); + prim.BSShape = newShape; + return true; // 'true' means a new shape has been added to this prim + } + + List m_hulls; + private BulletShape CreatePhysicalHull(string objName, ulong newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + { + + IntPtr hullPtr; + HullDesc hullDesc; + if (Hulls.TryGetValue(newHullKey, out hullDesc)) + { + // If the hull shape already is created, just use it. + hullPtr = hullDesc.ptr; + } + else + { + // 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++) + { + 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); + + // 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) + { + 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); + } + + BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); + newShape.shapeKey = newHullKey; + + return newShape; // 'true' means a new shape has been added to this prim + } + + // Callback from convex hull creater with a newly created hull. + // Just add it to our collection of hulls for this shape. + private void HullReturn(ConvexResult result) + { + m_hulls.Add(result); + return; + } + + // Create a hash of all the shape parameters to be used as a key + // for this particular shape. + private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod) + { + // level of detail based on size and type of the object + float lod = PhysicsScene.MeshLOD; + if (pbs.SculptEntry) + lod = PhysicsScene.SculptLOD; + + float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); + if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) + lod = PhysicsScene.MeshMegaPrimLOD; + + retLod = lod; + return (ulong)pbs.GetMeshKey(shapeData.Size, lod); + } + // For those who don't want the LOD + private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs) + { + float lod; + return ComputeShapeKey(shapeData, pbs, out lod); + } + + // 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, + ShapeData shapeData, BodyDestructionCallback bodyCallback) + { + bool ret = false; + + // the mesh, hull or native shape must have already been created in Bullet + bool mustRebuild = (prim.BSBody.ptr == IntPtr.Zero); + + // 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 (!mustRebuild) + { + CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.BSBody.ptr); + if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY + || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) + { + // If the collisionObject is not the correct type for solidness, rebuild what's there + mustRebuild = true; + } + + } + + if (mustRebuild || forceRebuild) + { + DereferenceBody(prim.BSBody, true, bodyCallback); + + BulletBody aBody; + IntPtr bodyPtr = IntPtr.Zero; + if (prim.IsSolid) + { + bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, + shapeData.ID, shapeData.Position, shapeData.Rotation); + // DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); + } + else + { + bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, + shapeData.ID, shapeData.Position, shapeData.Rotation); + // DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); + } + aBody = new BulletBody(shapeData.ID, bodyPtr); + + ReferenceBody(aBody, true); + + prim.BSBody = aBody; + + ret = true; + } + + return ret; + } + + private void DetailLog(string msg, params Object[] args) + { + PhysicsScene.PhysicsLogging.Write(msg, args); + } +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index d48462e119..70aa429fc3 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -57,14 +57,14 @@ public class BSTerrainManager public const float TERRAIN_COLLISION_MARGIN = 0.0f; // Until the whole simulator is changed to pass us the region size, we rely on constants. - public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, 0f); + public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); // The scene that I am part of - private BSScene m_physicsScene; + private BSScene PhysicsScene { get; set; } // The ground plane created to keep thing from falling to infinity. private BulletBody m_groundPlane; - + // If doing mega-regions, if we're region zero we will be managing multiple // region terrains since region zero does the physics for the whole mega-region. private Dictionary m_heightMaps; @@ -84,18 +84,18 @@ public class BSTerrainManager // If the parent region (region 0), this is the extent of the combined regions // relative to the origin of region zero private Vector3 m_worldMax; - private PhysicsScene m_parentScene; + private PhysicsScene MegaRegionParentPhysicsScene { get; set; } public BSTerrainManager(BSScene physicsScene) { - m_physicsScene = physicsScene; + PhysicsScene = physicsScene; m_heightMaps = new Dictionary(); m_terrainModified = false; // Assume one region of default size m_worldOffset = Vector3.Zero; - m_worldMax = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, 4096f); - m_parentScene = null; + m_worldMax = new Vector3(DefaultRegionSize); + MegaRegionParentPhysicsScene = null; } // Create the initial instance of terrain and the underlying ground plane. @@ -107,10 +107,16 @@ public class BSTerrainManager public void CreateInitialGroundPlaneAndTerrain() { // The ground plane is here to catch things that are trying to drop to negative infinity - BulletShape groundPlaneShape = new BulletShape(BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN)); - m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, - BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.Ptr, Vector3.Zero, Quaternion.Identity)); - BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); + BulletShape groundPlaneShape = new BulletShape( + BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), + ShapeData.PhysicsShapeType.SHAPE_GROUNDPLANE); + m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, + BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, + Vector3.Zero, Quaternion.Identity)); + BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr); + // Everything collides with the ground plane. + BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, + (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); Vector3 minTerrainCoords = new Vector3(0f, 0f, HEIGHT_INITIALIZATION - HEIGHT_EQUAL_FUDGE); Vector3 maxTerrainCoords = new Vector3(DefaultRegionSize.X, DefaultRegionSize.Y, HEIGHT_INITIALIZATION); @@ -126,13 +132,13 @@ public class BSTerrainManager // Release all the terrain structures we might have allocated public void ReleaseGroundPlaneAndTerrain() { - if (m_groundPlane.Ptr != IntPtr.Zero) + if (m_groundPlane.ptr != IntPtr.Zero) { - if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, m_groundPlane.Ptr)) + if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr)) { - BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, m_groundPlane.Ptr); + BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr); } - m_groundPlane.Ptr = IntPtr.Zero; + m_groundPlane.ptr = IntPtr.Zero; } ReleaseTerrain(); @@ -143,9 +149,9 @@ public class BSTerrainManager { foreach (KeyValuePair kvp in m_heightMaps) { - if (BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, kvp.Value.terrainBody.Ptr)) + if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr)) { - BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, kvp.Value.terrainBody.Ptr); + BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, kvp.Value.terrainBody.ptr); BulletSimAPI.ReleaseHeightMapInfo2(kvp.Value.Ptr); } } @@ -155,19 +161,19 @@ public class BSTerrainManager // The simulator wants to set a new heightmap for the terrain. public void SetTerrain(float[] heightMap) { float[] localHeightMap = heightMap; - m_physicsScene.TaintedObject("TerrainManager.SetTerrain", delegate() + PhysicsScene.TaintedObject("TerrainManager.SetTerrain", delegate() { - if (m_worldOffset != Vector3.Zero && m_parentScene != null) + if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) { // If a child of a mega-region, we shouldn't have any terrain allocated for us ReleaseGroundPlaneAndTerrain(); // If doing the mega-prim stuff and we are the child of the zero region, // the terrain is added to our parent - if (m_parentScene is BSScene) + if (MegaRegionParentPhysicsScene is BSScene) { DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", BSScene.DetailLogZero, m_worldOffset, m_worldMax); - ((BSScene)m_parentScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID, + ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true); } } @@ -176,7 +182,8 @@ public class BSTerrainManager // If not doing the mega-prim thing, just change the terrain DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); - UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true); + UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, + m_worldOffset, m_worldOffset + DefaultRegionSize, true); } }); } @@ -194,10 +201,10 @@ public class BSTerrainManager // The 'doNow' boolean says whether to do all the unmanaged activities right now (like when // calling this routine from initialization or taint-time routines) or whether to delay // all the unmanaged activities to taint-time. - private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool doNow) + private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool atTaintTime) { - DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},doNow={3}", - BSScene.DetailLogZero, minCoords, maxCoords, doNow); + DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},atTaintTime={3}", + BSScene.DetailLogZero, minCoords, maxCoords, atTaintTime); float minZ = float.MaxValue; float maxZ = float.MinValue; @@ -227,12 +234,12 @@ public class BSTerrainManager mapInfo.maxZ = maxZ; mapInfo.sizeX = maxCoords.X - minCoords.X; mapInfo.sizeY = maxCoords.Y - minCoords.Y; - 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.TaintCallback rebuildOperation = delegate() { - if (m_parentScene != null) + if (MegaRegionParentPhysicsScene != null) { // It's possible that Combine() was called after this code was queued. // If we are a child of combined regions, we don't create any terrain for us. @@ -245,17 +252,17 @@ public class BSTerrainManager return; } - if (mapInfo.terrainBody.Ptr != IntPtr.Zero) + if (mapInfo.terrainBody.ptr != IntPtr.Zero) { // Updating an existing terrain. - DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}", + DetailLog("{0},UpdateOrCreateTerrain:UpdateExisting,taint,terrainBase={1},minC={2}, maxC={3}, szX={4}, szY={5}", BSScene.DetailLogZero, terrainRegionBase, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.sizeX, mapInfo.sizeY); // Remove from the dynamics world because we're going to mangle this object - BulletSimAPI.RemoveObjectFromWorld2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr); + BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr); // Get rid of the old terrain - BulletSimAPI.DestroyObject2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr); + BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr); BulletSimAPI.ReleaseHeightMapInfo2(mapInfo.Ptr); mapInfo.Ptr = IntPtr.Zero; @@ -282,11 +289,11 @@ public class BSTerrainManager // else { // Creating a new terrain. - DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}", + DetailLog("{0},UpdateOrCreateTerrain:CreateNewTerrain,taint,baseX={1},baseY={2},minZ={3},maxZ={4}", BSScene.DetailLogZero, mapInfo.minCoords.X, mapInfo.minCoords.Y, minZ, maxZ); mapInfo.ID = id; - mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(m_physicsScene.World.Ptr, mapInfo.ID, + mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); // The terrain object initial position is at the center of the object @@ -296,43 +303,49 @@ public class BSTerrainManager centerPos.Z = minZ + ((maxZ - minZ) / 2f); // Create the terrain shape from the mapInfo - mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr)); + mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr), + ShapeData.PhysicsShapeType.SHAPE_TERRAIN); - mapInfo.terrainBody = new BulletBody(mapInfo.ID, - BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.Ptr, - centerPos, Quaternion.Identity)); + mapInfo.terrainBody = new BulletBody(mapInfo.ID, + BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr, + id, centerPos, Quaternion.Identity)); } // Make sure the entry is in the heightmap table m_heightMaps[terrainRegionBase] = mapInfo; // Set current terrain attributes - BulletSimAPI.SetFriction2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainFriction); - BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainHitFraction); - BulletSimAPI.SetRestitution2(mapInfo.terrainBody.Ptr, m_physicsScene.Params.terrainRestitution); - BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.Ptr, CollisionFlags.CF_STATIC_OBJECT); + BulletSimAPI.SetFriction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainFriction); + BulletSimAPI.SetHitFraction2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction); + BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution); + BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT); - BulletSimAPI.SetMassProps2(mapInfo.terrainBody.Ptr, 0f, Vector3.Zero); - BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.Ptr); + BulletSimAPI.SetMassProps2(mapInfo.terrainBody.ptr, 0f, Vector3.Zero); + BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.ptr); // Return the new terrain to the world of physical objects - BulletSimAPI.AddObjectToWorld2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr); + BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr); // redo its bounding box now that it is in the world - BulletSimAPI.UpdateSingleAabb2(m_physicsScene.World.Ptr, mapInfo.terrainBody.Ptr); + BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr); + + BulletSimAPI.SetCollisionFilterMask2(mapInfo.terrainBody.ptr, + (uint)CollisionFilterGroups.TerrainFilter, + (uint)CollisionFilterGroups.TerrainMask); // Make sure the new shape is processed. - BulletSimAPI.Activate2(mapInfo.terrainBody.Ptr, true); + // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true); + BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); 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 (doNow) + if (atTaintTime) rebuildOperation(); else - m_physicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation); + PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation); } else { @@ -348,7 +361,7 @@ public class BSTerrainManager Vector3 minCoordsX = minCoords; Vector3 maxCoordsX = maxCoords; - DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", + DetailLog("{0},UpdateOrCreateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); // Code that must happen at taint-time @@ -357,7 +370,7 @@ public class BSTerrainManager 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 mapInfo = new BulletHeightMapInfo(id, heightMapX, - BulletSimAPI.CreateHeightMapInfo2(m_physicsScene.World.Ptr, newTerrainID, + BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, newTerrainID, minCoordsX, maxCoordsX, heightMapX, TERRAIN_COLLISION_MARGIN)); // Put the unfilled heightmap info into the collection of same m_heightMaps.Add(terrainRegionBase, mapInfo); @@ -368,10 +381,10 @@ public class BSTerrainManager }; // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time. - if (doNow) + if (atTaintTime) createOperation(); else - m_physicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation); + PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation); } } @@ -419,7 +432,7 @@ public class BSTerrainManager catch { // Sometimes they give us wonky values of X and Y. Give a warning and return something. - m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}", + PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}", LogHeader, terrainBaseXY, regionX, regionY); ret = HEIGHT_GETHEIGHT_RET; } @@ -428,8 +441,8 @@ public class BSTerrainManager } else { - m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", - LogHeader, m_physicsScene.RegionName, tX, tY); + PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", + LogHeader, PhysicsScene.RegionName, tX, tY); } m_terrainModified = false; lastHeight = ret; @@ -441,7 +454,7 @@ public class BSTerrainManager { return true; } - + // This routine is called two ways: // One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum // extent of the combined regions. This is to inform the parent of the size @@ -453,14 +466,14 @@ public class BSTerrainManager { m_worldOffset = offset; m_worldMax = extents; - m_parentScene = pScene; + MegaRegionParentPhysicsScene = pScene; if (pScene != null) { - // We are a child. + // We are a child. // We want m_worldMax to be the highest coordinate of our piece of terrain. m_worldMax = offset + DefaultRegionSize; } - DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}", + DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}", BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax); } @@ -474,7 +487,7 @@ public class BSTerrainManager private void DetailLog(string msg, params Object[] args) { - m_physicsScene.PhysicsLogging.Write(msg, args); + PhysicsScene.PhysicsLogging.Write(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 9221cdb30e..1125d7eb6a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -38,35 +38,112 @@ namespace OpenSim.Region.Physics.BulletSPlugin { // The physics engine controller class created at initialization public struct BulletSim { - public BulletSim(uint worldId, BSScene bss, IntPtr xx) { worldID = worldId; scene = bss; Ptr = xx; } + public BulletSim(uint worldId, BSScene bss, IntPtr xx) + { + ptr = xx; + worldID = worldId; + physicsScene = bss; + } + public IntPtr ptr; public uint worldID; // The scene is only in here so very low level routines have a handle to print debug/error messages - public BSScene scene; - public IntPtr Ptr; -} - -public struct BulletShape -{ - public BulletShape(IntPtr xx) { Ptr = xx; } - public IntPtr Ptr; + public BSScene physicsScene; } // An allocated Bullet btRigidBody public struct BulletBody { - public BulletBody(uint id, IntPtr xx) { ID = id; Ptr = xx; } - public IntPtr Ptr; + public BulletBody(uint id, IntPtr xx) + { + ID = id; + ptr = xx; + collisionFilter = 0; + collisionMask = 0; + } + public IntPtr ptr; public uint ID; + public CollisionFilterGroups collisionFilter; + public CollisionFilterGroups collisionMask; + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append(""); + return buff.ToString(); + } +} + +public struct BulletShape +{ + public BulletShape(IntPtr xx) + { + ptr = xx; + type=ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; + shapeKey = 0; + isNativeShape = false; + } + public BulletShape(IntPtr xx, ShapeData.PhysicsShapeType typ) + { + ptr = xx; + type = typ; + shapeKey = 0; + isNativeShape = false; + } + public IntPtr ptr; + public ShapeData.PhysicsShapeType type; + public ulong shapeKey; + public bool isNativeShape; + // Hulls have an underlying mesh. A pointer to it is hidden here. + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append(""); + return buff.ToString(); + } +} + + // Constraint type values as defined by Bullet +public enum ConstraintType : int +{ + POINT2POINT_CONSTRAINT_TYPE = 3, + HINGE_CONSTRAINT_TYPE, + CONETWIST_CONSTRAINT_TYPE, + D6_CONSTRAINT_TYPE, + SLIDER_CONSTRAINT_TYPE, + CONTACT_CONSTRAINT_TYPE, + D6_SPRING_CONSTRAINT_TYPE, + MAX_CONSTRAINT_TYPE } // An allocated Bullet btConstraint public struct BulletConstraint { - public BulletConstraint(IntPtr xx) { Ptr = xx; } - public IntPtr Ptr; + public BulletConstraint(IntPtr xx) + { + ptr = xx; + } + public IntPtr ptr; } -// An allocated HeightMapThing which hold various heightmap info +// An allocated HeightMapThing which holds various heightmap info. // Made a class rather than a struct so there would be only one // instance of this and C# will pass around pointers rather // than making copies. @@ -96,14 +173,14 @@ public class BulletHeightMapInfo // =============================================================================== [StructLayout(LayoutKind.Sequential)] -public struct ConvexHull +public struct ConvexHull { Vector3 Offset; int VertexCount; Vector3[] Vertices; } [StructLayout(LayoutKind.Sequential)] -public struct ShapeData +public struct ShapeData { public enum PhysicsShapeType { @@ -114,7 +191,9 @@ public struct ShapeData SHAPE_CYLINDER = 4, SHAPE_SPHERE = 5, SHAPE_MESH = 6, - SHAPE_HULL = 7 + SHAPE_HULL = 7, + SHAPE_GROUNDPLANE = 8, + SHAPE_TERRAIN = 9, }; public uint ID; public PhysicsShapeType Type; @@ -130,13 +209,24 @@ public struct ShapeData public float Restitution; public float Collidable; // true of things bump into this public float Static; // true if a static object. Otherwise gravity, etc. + public float Solid; // true if object cannot be passed through + public Vector3 Size; // note that bools are passed as floats since bool size changes by language and architecture public const float numericTrue = 1f; public const float numericFalse = 0f; + + // The native shapes have predefined shape hash keys + public enum FixedShapeKey : ulong + { + KEY_BOX = 1, + KEY_SPHERE = 2, + KEY_CONE = 3, + KEY_CYLINDER = 4, + } } [StructLayout(LayoutKind.Sequential)] -public struct SweepHit +public struct SweepHit { public uint ID; public float Fraction; @@ -227,7 +317,17 @@ public enum ActivationState : uint ISLAND_SLEEPING, WANTS_DEACTIVATION, DISABLE_DEACTIVATION, - DISABLE_SIMULATION + DISABLE_SIMULATION, +} + +public enum CollisionObjectTypes : int +{ + CO_COLLISION_OBJECT = 1 << 0, + CO_RIGID_BODY = 1 << 1, + CO_GHOST_OBJECT = 1 << 2, + CO_SOFT_BODY = 1 << 3, + CO_HF_FLUID = 1 << 4, + CO_USER_TYPE = 1 << 5, } // Values used by Bullet and BulletSim to control object properties. @@ -244,77 +344,60 @@ public enum CollisionFlags : uint CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, // Following used by BulletSim to control collisions BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, - BS_VOLUME_DETECT_OBJECT = 1 << 11, - BS_PHANTOM_OBJECT = 1 << 12, - BS_PHYSICAL_OBJECT = 1 << 13, - BS_TERRAIN_OBJECT = 1 << 14, + // BS_VOLUME_DETECT_OBJECT = 1 << 11, + // BS_PHANTOM_OBJECT = 1 << 12, + // BS_PHYSICAL_OBJECT = 1 << 13, + // BS_TERRAIN_OBJECT = 1 << 14, BS_NONE = 0, - BS_ALL = 0xFFFFFFFF + BS_ALL = 0xFFFFFFFF, + + // These are the collision flags switched depending on physical state. + // The other flags are used for other things and should not be fooled with. + BS_ACTIVE = CF_STATIC_OBJECT + | CF_KINEMATIC_OBJECT + | CF_NO_CONTACT_RESPONSE + // | BS_VOLUME_DETECT_OBJECT + // | BS_PHANTOM_OBJECT + // | BS_PHYSICAL_OBJECT, }; // Values for collisions groups and masks public enum CollisionFilterGroups : uint { - NoneFilter = 0, - DefaultFilter = 1 << 0, - StaticFilter = 1 << 1, - KinematicFilter = 1 << 2, - DebrisFilter = 1 << 3, - SensorTrigger = 1 << 4, - CharacterFilter = 1 << 5, - AllFilter = 0xFFFFFFFF, + // Don't use the bit definitions!! Define the use in a + // filter/mask definition below. This way collision interactions + // are more easily debugged. + BNoneFilter = 0, + BDefaultFilter = 1 << 0, + BStaticFilter = 1 << 1, + BKinematicFilter = 1 << 2, + BDebrisFilter = 1 << 3, + BSensorTrigger = 1 << 4, + BCharacterFilter = 1 << 5, + BAllFilter = 0xFFFFFFFF, // Filter groups defined by BulletSim - GroundPlaneFilter = 1 << 10, - TerrainFilter = 1 << 11, - RaycastFilter = 1 << 12, - SolidFilter = 1 << 13, + BGroundPlaneFilter = 1 << 10, + BTerrainFilter = 1 << 11, + BRaycastFilter = 1 << 12, + BSolidFilter = 1 << 13, + + // The collsion filters and masked are defined in one place -- don't want them scattered + AvatarFilter = BCharacterFilter, + AvatarMask = BAllFilter, + ObjectFilter = BSolidFilter, + ObjectMask = BAllFilter, + StaticObjectFilter = BStaticFilter, + StaticObjectMask = BAllFilter, + VolumeDetectFilter = BSensorTrigger, + VolumeDetectMask = ~BSensorTrigger, + TerrainFilter = BTerrainFilter, + TerrainMask = BAllFilter & ~BStaticFilter, + GroundPlaneFilter = BAllFilter, + GroundPlaneMask = BAllFilter + }; - // For each type, we first clear and then set the collision flags -public enum ClearCollisionFlag : uint -{ - Terrain = CollisionFlags.BS_ALL, - Phantom = CollisionFlags.BS_ALL, - VolumeDetect = CollisionFlags.BS_ALL, - PhysicalObject = CollisionFlags.BS_ALL, - StaticObject = CollisionFlags.BS_ALL -} -public enum SetCollisionFlag : uint -{ - Terrain = CollisionFlags.CF_STATIC_OBJECT - | CollisionFlags.BS_TERRAIN_OBJECT, - Phantom = CollisionFlags.CF_STATIC_OBJECT - | CollisionFlags.BS_PHANTOM_OBJECT - | CollisionFlags.CF_NO_CONTACT_RESPONSE, - VolumeDetect = CollisionFlags.CF_STATIC_OBJECT - | CollisionFlags.BS_VOLUME_DETECT_OBJECT - | CollisionFlags.CF_NO_CONTACT_RESPONSE, - PhysicalObject = CollisionFlags.BS_PHYSICAL_OBJECT, - StaticObject = CollisionFlags.CF_STATIC_OBJECT, -} - -// Collision filters used for different types of objects -public enum SetCollisionFilter : uint -{ - Terrain = CollisionFilterGroups.AllFilter, - Phantom = CollisionFilterGroups.GroundPlaneFilter - | CollisionFilterGroups.TerrainFilter, - VolumeDetect = CollisionFilterGroups.AllFilter, - PhysicalObject = CollisionFilterGroups.AllFilter, - StaticObject = CollisionFilterGroups.AllFilter, -} - -// Collision masks used for different types of objects -public enum SetCollisionMask : uint -{ - Terrain = CollisionFilterGroups.AllFilter, - Phantom = CollisionFilterGroups.GroundPlaneFilter - | CollisionFilterGroups.TerrainFilter, - VolumeDetect = CollisionFilterGroups.AllFilter, - PhysicalObject = CollisionFilterGroups.AllFilter, - StaticObject = CollisionFilterGroups.AllFilter -} // 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. @@ -350,8 +433,8 @@ public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg public static extern string GetVersion(); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, - int maxCollisions, IntPtr collisionArray, +public static extern uint Initialize(Vector3 maxPosition, IntPtr parms, + int maxCollisions, IntPtr collisionArray, int maxUpdates, IntPtr updateArray, DebugLogCallback logRoutine); @@ -370,19 +453,19 @@ public static extern bool UpdateParameter(uint worldID, uint localID, // =============================================================================== [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSteps, float fixedTimeStep, - out int updatedEntityCount, +public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSteps, float fixedTimeStep, + out int updatedEntityCount, out IntPtr updatedEntitiesPtr, out int collidersCount, out IntPtr collidersPtr); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool CreateHull(uint worldID, System.UInt64 meshKey, +public static extern bool CreateHull(uint worldID, System.UInt64 meshKey, int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls ); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool CreateMesh(uint worldID, System.UInt64 meshKey, +public static extern bool CreateMesh(uint worldID, System.UInt64 meshKey, int indexCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices, int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices ); @@ -496,7 +579,7 @@ public static extern void Shutdown2(IntPtr sim); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern int PhysicsStep2(IntPtr world, float timeStep, int maxSubSteps, float fixedTimeStep, - out int updatedEntityCount, + out int updatedEntityCount, out IntPtr updatedEntitiesPtr, out int collidersCount, out IntPtr collidersPtr); @@ -507,8 +590,8 @@ public static extern bool PushUpdate2(IntPtr obj); // ===================================================================================== // Mesh, hull, shape and body creation helper routines [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateMeshShape2(IntPtr world, - int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices, +public static extern IntPtr CreateMeshShape2(IntPtr world, + int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices, int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices ); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -516,11 +599,10 @@ public static extern IntPtr CreateHullShape2(IntPtr world, int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr BuildHullShape2(IntPtr world, IntPtr meshShape); +public static extern IntPtr BuildHullShapeFromMesh2(IntPtr world, IntPtr meshShape); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr BuildNativeShape2(IntPtr world, - float shapeType, float collisionMargin, Vector3 scale); +public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool IsNativeShape2(IntPtr shape); @@ -535,16 +617,25 @@ public static extern void AddChildToCompoundShape2(IntPtr cShape, IntPtr addShap public static extern void RemoveChildFromCompoundShape2(IntPtr cShape, IntPtr removeShape); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, IntPtr constructionInfo); +public static extern IntPtr DuplicateCollisionShape2(IntPtr sim, IntPtr srcShape, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, uint id, IntPtr constructionInfo); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vector3 pos, Quaternion rot); +public static extern int GetBodyType2(IntPtr obj); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot); +public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, uint id, Vector3 pos, Quaternion rot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, uint id, Vector3 pos, Quaternion rot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateGhostFromShape2(IntPtr sim, IntPtr shape, uint id, Vector3 pos, Quaternion rot); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern IntPtr AllocateBodyInfo2(IntPtr obj); @@ -558,11 +649,11 @@ public static extern void DestroyObject2(IntPtr sim, IntPtr obj); // ===================================================================================== // Terrain creation and helper routines [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords, +public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern IntPtr FillHeightMapInfo2(IntPtr sim, IntPtr mapInfo, uint id, Vector3 minCoords, Vector3 maxCoords, +public static extern IntPtr FillHeightMapInfo2(IntPtr sim, IntPtr mapInfo, uint id, Vector3 minCoords, Vector3 maxCoords, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -600,7 +691,7 @@ public static extern void SetConstraintEnable2(IntPtr constrain, float numericTr public static extern void SetConstraintNumSolverIterations2(IntPtr constrain, float iterations); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern bool SetFrames2(IntPtr constrain, +public static extern bool SetFrames2(IntPtr constrain, Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -963,6 +1054,9 @@ public static extern Vector3 GetPushVelocity2(IntPtr obj); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern Vector3 GetTurnVelocity2(IntPtr obj); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask); + // ===================================================================================== // btCollisionShape entries @@ -1014,9 +1108,6 @@ public static extern void SetMargin2(IntPtr shape, float val); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern float GetMargin2(IntPtr shape); -[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern void SetCollisionFilterMask(IntPtr shape, uint filter, uint mask); - // ===================================================================================== // Debugging [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index bd83bee789..6e6a089d7f 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -936,10 +936,10 @@ MaxPersistantManifoldPoolSize = 0 ShouldDisableContactPoolDynamicAllocation = False ShouldForceUpdateAllAabbs = False - ShouldRandomizeSolverOrder = False - ShouldSplitSimulationIslands = False + ShouldRandomizeSolverOrder = True + ShouldSplitSimulationIslands = True ShouldEnableFrictionCaching = False - NumberOfSolverIterations = 0; + NumberOfSolverIterations = 0 ; Linkset constraint parameters LinkConstraintUseFrameOffset = False diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll index 3b35a9804a..da6e31cdd2 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 65eba371a0..9503cdb188 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 f01655bd66..1782c8e741 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 5302e298ee..06e6ce27fb 100755 Binary files a/bin/lib64/libBulletSim.so and b/bin/lib64/libBulletSim.so differ