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(-)

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<float>("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<float>("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<ContactResult> RaycastWorld(Vector3 position, Vector3 direction, float length, int Count)
+        public override List<ContactResult> RaycastWorld(Vector3 position, Vector3 direction, float length, int count)
         {
-            List<ContactResult> ret = new List<ContactResult>();
-            if (BSParam.UseBulletRaycast)
-            {
-            }
-            return ret;
+            return (List<ContactResult>)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<ContactResult> ret = new List<ContactResult>();
             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<string, string> engineParams = new Dictionary<string, string>();
             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 <x,y,z> of start and <x,y,z> 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<ContactResult> results = PhysicsScene.RaycastWorld(fromPos, direction, len, 1);
+            List<ContactResult> 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