From f348f7fa90cf4784e0e173c122594ecc145d3bb8 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sun, 3 Sep 2017 17:15:27 -0700 Subject: BulletSim: first version of raycast. Only single contact point and no filtering. --- .../Region/PhysicsModules/BulletS/BSApiTemplate.cs | 14 ++ .../Region/PhysicsModules/BulletS/BSCharacter.cs | 3 - OpenSim/Region/PhysicsModules/BulletS/BSParam.cs | 6 +- OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs | 2 - .../PhysicsModules/BulletS/BSPrimDisplaced.cs | 1 - OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 182 ++++++++++----------- .../PhysicsModules/BulletS/BSShapeCollection.cs | 4 - .../Region/PhysicsModules/BulletS/Tests/Raycast.cs | 53 +++--- 8 files changed, 137 insertions(+), 128 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs b/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs index afb0ba2..a288048 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs @@ -121,6 +121,14 @@ public struct SweepHit public float Fraction; public Vector3 Normal; public Vector3 Point; + + public bool hasHit() + { + float sum = Fraction + + Normal.X + Normal.Y + Normal.Z + + Point.X + Point.Y + Point.Z; + return (sum != 0) || (ID != 0); + } } [StructLayout(LayoutKind.Sequential)] public struct RaycastHit @@ -129,6 +137,12 @@ public struct RaycastHit public float Fraction; public Vector3 Normal; public Vector3 Point; + + public bool hasHit() + { + float sum = Normal.X + Normal.Y + Normal.Z + Point.X + Point.Y + Point.Z; + return (sum != 0); + } } [StructLayout(LayoutKind.Sequential)] public struct CollisionDesc diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs index 04ac659..7faee70 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs @@ -496,7 +496,6 @@ public sealed class BSCharacter : BSPhysObject public override OMV.Vector3 ForceVelocity { get { return RawVelocity; } set { - PhysScene.AssertNotInSimulationTime("BSCharacter.ForceVelocity"); DetailLog("{0},BSCharacter.ForceVelocity.set={1}", LocalID, value); RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity); @@ -638,8 +637,6 @@ public sealed class BSCharacter : BSPhysObject public override float ForceBuoyancy { get { return _buoyancy; } set { - PhysScene.AssertNotInSimulationTime("BSCharacter.ForceBuoyancy"); - _buoyancy = value; DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); // Buoyancy is faked by changing the gravity applied to the object diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs index 0792f5d..fcda92c 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs @@ -838,7 +838,7 @@ public static class BSParam new ParameterDefn("ResetBroadphasePool", "Setting this is any value resets the broadphase collision pool", 0f, (s) => { return 0f; }, - (s,v) => { BSParam.ResetBroadphasePoolTainted(s, v, false /* inTaintTime */); } ), + (s,v) => { BSParam.ResetBroadphasePoolTainted(s, v); } ), new ParameterDefn("ResetConstraintSolver", "Setting this is any value resets the constraint solver", 0f, (s) => { return 0f; }, @@ -924,10 +924,10 @@ public static class BSParam // ===================================================================== // There are parameters that, when set, cause things to happen in the physics engine. // This causes the broadphase collision cache to be cleared. - private static void ResetBroadphasePoolTainted(BSScene pPhysScene, float v, bool inTaintTime) + private static void ResetBroadphasePoolTainted(BSScene pPhysScene, float v) { BSScene physScene = pPhysScene; - physScene.TaintedObject(inTaintTime, "BSParam.ResetBroadphasePoolTainted", delegate() + physScene.TaintedObject(BSScene.DetailLogZero, "BSParam.ResetBroadphasePoolTainted", delegate() { physScene.PE.ResetBroadphasePool(physScene.World); }); diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs index e1990ee..f085d70 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs @@ -790,8 +790,6 @@ public class BSPrim : BSPhysObject public override OMV.Vector3 ForceVelocity { get { return RawVelocity; } set { - PhysScene.AssertNotInSimulationTime("BSPrim.ForceVelocity"); - RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity); if (PhysBody.HasPhysicalBody) { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs index d8ed56b..3f90fc5 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs @@ -81,7 +81,6 @@ public class BSPrimDisplaced : BSPrim // Called at taint time. public virtual Vector3 SetEffectiveCenterOfMassDisplacement(Vector3 centerOfMassDisplacement) { - PhysScene.AssertInTaintTime("BSPrimDisplaced.SetEffectiveCenterOfMassDisplacement"); Vector3 comDisp; if (UserSetCenterOfMassDisplacement.HasValue) comDisp = (OMV.Vector3)UserSetCenterOfMassDisplacement; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs index 52aea87..ca54721 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -124,10 +124,8 @@ namespace OpenSim.Region.PhysicsModule.BulletS // True if initialized and ready to do simulation steps private bool m_initialized = false; - // Flag which is true when processing taints. - // Not guaranteed to be correct all the time (don't depend on this) but good for debugging. - public bool InTaintTime { get; private set; } - + // Object locked whenever execution is inside the physics engine + public Object PhysicsEngineLock = new object(); // Flag that is true when the simulator is active and shouldn't be touched public bool InSimulationTime { get; private set; } @@ -348,7 +346,6 @@ namespace OpenSim.Region.PhysicsModule.BulletS m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); InSimulationTime = false; - InTaintTime = false; m_initialized = true; // If the physics engine runs on its own thread, start same. @@ -661,60 +658,57 @@ namespace OpenSim.Region.PhysicsModule.BulletS int beforeTime = Util.EnvironmentTickCount(); int simTime = 0; + int numTaints = 0; + int numSubSteps = 0; - InTaintTime = true; - // update the prim states while we know the physics engine is not busy - int numTaints = ProcessTaints(); - - // Some of the physical objects requre individual, pre-step calls - // (vehicles and avatar movement, in particular) - TriggerPreStepEvent(timeStep); - - // the prestep actions might have added taints - numTaints += ProcessTaints(); - - lock (_taintLock) + lock (PhysicsEngineLock) { InSimulationTime = true; - } + // update the prim states while we know the physics engine is not busy + numTaints += ProcessTaints(); - // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. - // Only enable this in a limited test world with few objects. - if (m_physicsPhysicalDumpEnabled) - PE.DumpAllInfo(World); + // Some of the physical objects requre individual, pre-step calls + // (vehicles and avatar movement, in particular) + TriggerPreStepEvent(timeStep); - // step the physical world one interval - m_simulationStep++; - int numSubSteps = 0; - try - { - numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); - } - catch (Exception e) - { - 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}", - DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); - updatedEntityCount = 0; - collidersCount = 0; - } + // the prestep actions might have added taints + numTaints += ProcessTaints(); - // Make the physics engine dump useful statistics periodically - if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) - PE.DumpPhysicsStatistics(World); + // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. + // Only enable this in a limited test world with few objects. + if (m_physicsPhysicalDumpEnabled) + PE.DumpAllInfo(World); + + // step the physical world one interval + m_simulationStep++; + try + { + numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); + } + catch (Exception e) + { + 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}", + DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); + updatedEntityCount = 0; + collidersCount = 0; + } + + // Make the physics engine dump useful statistics periodically + if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) + PE.DumpPhysicsStatistics(World); - lock (_taintLock) - { - InTaintTime = false; InSimulationTime = false; - } - // Some actors want to know when the simulation step is complete. - TriggerPostStepEvent(timeStep); + // Some actors want to know when the simulation step is complete. + TriggerPostStepEvent(timeStep); - // In case there were any parameter updates that happened during the simulation step - numTaints += ProcessTaints(); + // In case there were any parameter updates that happened during the simulation step + numTaints += ProcessTaints(); + + InSimulationTime = false; + } // Get a value for 'now' so all the collision and update routines don't have to get their own. SimulationNowTime = Util.EnvironmentTickCount(); @@ -1040,20 +1034,39 @@ namespace OpenSim.Region.PhysicsModule.BulletS } } - public override List RaycastWorld(Vector3 position, Vector3 direction, float length, int Count) + public override List RaycastWorld(Vector3 position, Vector3 direction, float length, int count) { - List ret = new List(); - if (BSParam.UseBulletRaycast) - { - } - return ret; + return (List)RaycastWorld(position, direction, length, count, RayFilterFlags.All); } - public override object RaycastWorld(Vector3 position, Vector3 direction, float length, int Count, RayFilterFlags filter) + public override object RaycastWorld(Vector3 position, Vector3 direction, float length, int count, RayFilterFlags filter) { - object ret = null; + List ret = new List(); if (BSParam.UseBulletRaycast) { + DetailLog("{0},RaycastWorld,pos={1},dir={2},len={3},count={4},filter={5}", + DetailLogZero, position, direction, length, count, filter); + // NOTE: locking ensures the physics engine is not executing. + // The caller might have to wait for the physics engine to finish. + lock (PhysicsEngineLock) + { + Vector3 posFrom = position; + Vector3 posTo = Vector3.Normalize(direction) * length + position; + DetailLog("{0},RaycastWorld,RayTest2,from={1},to={2}", + DetailLogZero, posFrom, posTo); + RaycastHit hitInfo = PE.RayTest2(World, posFrom, posTo, 0xffff, 0xffff); + if (hitInfo.hasHit()) + { + ContactResult result = new ContactResult(); + result.Pos = hitInfo.Point; + result.Normal = hitInfo.Normal; + result.ConsumerID = hitInfo.ID; + result.Depth = hitInfo.Fraction; + ret.Add(result); + DetailLog("{0},RaycastWorld,hit,pos={1},norm={2},depth={3},id={4}", + DetailLogZero, result.Pos, result.Normal, result.Depth, result.ConsumerID); + } + } } return ret; } @@ -1173,47 +1186,40 @@ namespace OpenSim.Region.PhysicsModule.BulletS // Calls to the PhysicsActors can't directly call into the physics engine // because it might be busy. We delay changes to a known time. // We rely on C#'s closure to save and restore the context for the delegate. - public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback) + // NOTE: 'inTaintTime' is no longer used. This entry exists so all the calls don't have to be changed. + // public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback) + // { + // TaintedObject(BSScene.DetailLogZero, pIdent, pCallback); + // } + // NOTE: 'inTaintTime' is no longer used. This entry exists so all the calls don't have to be changed. + public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback) { - TaintedObject(false /*inTaintTime*/, pOriginator, pIdent, pCallback); + TaintedObject(m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); } public void TaintedObject(uint pOriginator, String pIdent, TaintCallback pCallback) { - TaintedObject(false /*inTaintTime*/, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); - } - public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback) - { - TaintedObject(inTaintTime, BSScene.DetailLogZero, pIdent, pCallback); - } - public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback) - { - TaintedObject(inTaintTime, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); + TaintedObject(m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); } // Sometimes a potentially tainted operation can be used in and out of taint time. // This routine executes the command immediately if in taint-time otherwise it is queued. - public void TaintedObject(bool inTaintTime, string pOriginator, string pIdent, TaintCallback pCallback) + public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback) { if (!m_initialized) return; - lock (_taintLock) { - if (inTaintTime || !InSimulationTime) { - pCallback(); - } - else { - _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback)); - } - } - /* - if (inTaintTime) + if (Monitor.TryEnter(PhysicsEngineLock)) + { + // If we can get exclusive access to the physics engine, just do the operation pCallback(); + Monitor.Exit(PhysicsEngineLock); + } else { + // The physics engine is busy, queue the operation lock (_taintLock) { _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback)); } } - */ } private void TriggerPreStepEvent(float timeStep) @@ -1236,6 +1242,7 @@ namespace OpenSim.Region.PhysicsModule.BulletS // a callback into itself to do the actual property change. That callback is called // here just before the physics engine is called to step the simulation. // Returns the number of taints processed + // NOTE: Called while PhysicsEngineLock is locked public int ProcessTaints() { int ret = 0; @@ -1245,6 +1252,7 @@ namespace OpenSim.Region.PhysicsModule.BulletS } // Returns the number of taints processed + // NOTE: Called while PhysicsEngineLock is locked private int ProcessRegularTaints() { int ret = 0; @@ -1293,6 +1301,7 @@ namespace OpenSim.Region.PhysicsModule.BulletS // Taints that happen after the normal taint processing but before the simulation step. // Returns the number of taints processed + // NOTE: Called while PhysicsEngineLock is locked private int ProcessPostTaintTaints() { int ret = 0; @@ -1322,19 +1331,6 @@ namespace OpenSim.Region.PhysicsModule.BulletS } return ret; } - - // Verify that things are being diddled when the physics engine is not running. - public bool AssertNotInSimulationTime(string whereFrom) - { - if (InSimulationTime) - { - DetailLog("{0},BSScene.AssertInTaintTime,IN SIMULATION TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); - m_log.ErrorFormat("{0} IN SIMULATION TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); - // Util.PrintCallStack(DetailLog); - } - return InSimulationTime; - } - #endregion // Taints #region IPhysicsParameters diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs index cda33e4..86bf23f 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs @@ -75,8 +75,6 @@ public sealed class BSShapeCollection : IDisposable // Called at taint-time. public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback) { - m_physicsScene.AssertNotInSimulationTime("BSShapeCollection.GetBodyAndShape"); - bool ret = false; // This lock could probably be pushed down lower but building shouldn't take long @@ -346,8 +344,6 @@ public sealed class BSShapeCollection : IDisposable if (!body.HasPhysicalBody) return; - m_physicsScene.AssertNotInSimulationTime("BSShapeCollection.DereferenceBody"); - lock (m_collectionActivityLock) { if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body); diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/Raycast.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/Raycast.cs index 046df56..bfa95c1 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/Raycast.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/Raycast.cs @@ -48,67 +48,76 @@ namespace OpenSim.Region.PhysicsModule.BulletS.Tests // Documentation on attributes: http://www.nunit.org/index.php?p=attributes&r=2.6.1 // Documentation on assertions: http://www.nunit.org/index.php?p=assertions&r=2.6.1 - BSScene PhysicsScene { get; set; } - BSPrim TargetSphere { get; set; } - Vector3 TargetSpherePosition { get; set; } - float simulationTimeStep = 0.089f; + BSScene _physicsScene { get; set; } + BSPrim _targetSphere { get; set; } + Vector3 _targetSpherePosition { get; set; } + float _simulationTimeStep = 0.089f; + + uint _targetLocalID = 123; [TestFixtureSetUp] public void Init() { Dictionary engineParams = new Dictionary(); engineParams.Add("UseBulletRaycast", "true"); - PhysicsScene = BulletSimTestsUtil.CreateBasicPhysicsEngine(engineParams); + _physicsScene = BulletSimTestsUtil.CreateBasicPhysicsEngine(engineParams); PrimitiveBaseShape pbs = PrimitiveBaseShape.CreateSphere(); Vector3 pos = new Vector3(100.0f, 100.0f, 50f); - pos.Z = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2f; - TargetSpherePosition = pos; + _targetSpherePosition = pos; Vector3 size = new Vector3(10f, 10f, 10f); pbs.Scale = size; Quaternion rot = Quaternion.Identity; bool isPhys = false; - uint localID = 123; - PhysicsScene.AddPrimShape("TargetSphere", pbs, pos, size, rot, isPhys, localID); - TargetSphere = (BSPrim)PhysicsScene.PhysObjects[localID]; + _physicsScene.AddPrimShape("TargetSphere", pbs, pos, size, rot, isPhys, _targetLocalID); + _targetSphere = (BSPrim)_physicsScene.PhysObjects[_targetLocalID]; // The actual prim shape creation happens at taint time - PhysicsScene.ProcessTaints(); + _physicsScene.ProcessTaints(); } [TestFixtureTearDown] public void TearDown() { - if (PhysicsScene != null) + if (_physicsScene != null) { // The Dispose() will also free any physical objects in the scene - PhysicsScene.Dispose(); - PhysicsScene = null; + _physicsScene.Dispose(); + _physicsScene = null; } } // There is a 10x10x10 sphere at <100,100,50> // Shoot rays around the sphere and verify it hits and doesn't hit // TestCase parameters are of start and of end and expected result - [TestCase(20f, 20f, 50f, 50f, 50f, 50f, true)] // in front to sphere - [TestCase(20f, 20f, 100f, 50f, 50f, 50f, true)] // from above to sphere - [TestCase(50f, 50f, 50f, 150f, 150f, 50f, true)] // through sphere - [TestCase(50f, 50f, 65f, 150f, 150f, 65f, false)] // pass over sphere - public void RaycastAroundObject(float fromX, float fromY, float fromZ, float toX, float toY, float toZ, bool expected) { + [TestCase(100f, 50f, 50f, 100f, 150f, 50f, true, "Pass through sphere from front")] + [TestCase(50f, 100f, 50f, 150f, 100f, 50f, true, "Pass through sphere from side")] + [TestCase(50f, 50f, 50f, 150f, 150f, 50f, true, "Pass through sphere diaginally")] + [TestCase(100f, 100f, 100f, 100f, 100f, 20f, true, "Pass through sphere from above")] + [TestCase(20f, 20f, 50f, 80f, 80f, 50f, false, "Not reach sphere")] + [TestCase(50f, 50f, 65f, 150f, 150f, 65f, false, "Passed over sphere")] + public void RaycastAroundObject(float fromX, float fromY, float fromZ, float toX, float toY, float toZ, bool expected, string msg) { Vector3 fromPos = new Vector3(fromX, fromY, fromZ); Vector3 toPos = new Vector3(toX, toY, toZ); Vector3 direction = toPos - fromPos; float len = Vector3.Distance(fromPos, toPos); - List results = PhysicsScene.RaycastWorld(fromPos, direction, len, 1); + List results = _physicsScene.RaycastWorld(fromPos, direction, len, 1); if (expected) { - Assert.True(results.Count > 0); + // The test coordinates should generate a hit + Assert.True(results.Count != 0, msg + ": Did not return a hit but expected to."); + Assert.True(results.Count == 1, msg + ": Raycast returned not just one hit result."); + Assert.True(results[0].ConsumerID == _targetLocalID, msg + ": Raycast returned a collision object other than the target"); } else { - Assert.False(results.Count > 0); + // The test coordinates should not generate a hit + if (results.Count > 0) + { + Assert.False(results.Count > 0, msg + ": Returned a hit at " + results[0].Pos.ToString()); + } } } } -- cgit v1.1