Best way to do a swept sphere test?

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
ecosky
Posts: 69
Joined: Fri Nov 04, 2011 7:13 am
Contact:

Best way to do a swept sphere test?

Post by ecosky »

Hi,

I'm trying to figure out the best way to use Bepu to prevent a chase camera's near clip plane from intersecting geometry. Typically, the games/engines I've worked on in the past had a swept sphere test API which I would use for this but so far I haven't seen anything in Bepu. The sphere would be sized to contain the near clip plane, and the length of the swept motion would be set to the desired distance from the follow target. The result of the test would tell me how far back from the target the camera should actually be. Using a single ray cast doesn't reliably detect the objects that the camera should be in front of, and I'd prefer not to do a grid of raycasts - it would be an improvement, but not 100% reliable like a swept sphere. I have a feeling it would not be a good idea to make an entity in the scene that is updated by the physics update because it would seem to require a (potentially expensive?) teleport of this entity to the target location and then a move to the goal location. Perhaps something along these lines would work well enough though? Another option I suppose is that I could query the broadphase and do these tests myself, but I'm hoping there is perhaps something in Bepu that might save me some time here. If there isn't, I'd like to suggest a set of APIs parallel to the Space.RayCast which would provide a few swept shape tests (sphere at least, oriented boxes would probably be ideal for clip planes) that would be useful in situations like this.

Thanks
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Best way to do a swept sphere test?

Post by Norbo »

All BroadPhaseEntry objects support a ConvexCast method. ConvexCast allows any convex shape to be used as a casting object.

If there is not a set of known BroadPhaseEntry objects which are candidates for the convex cast, a set can be found by using the BroadPhase.QueryAccelerator.GetEntries method. A bounding box which contains the convex cast can be computed using the Toolbox.GetExpandedBoundingBox method.

Here's an example implementation of what a Space.ConvexCast would look like if it existed:

Code: Select all

        /// <summary>
        /// <para>Casts a convex shape against the space.</para>
        /// <para>Convex casts are sensitive to length; avoid extremely long convex casts for better stability and performance.</para>
        /// </summary>
        /// <param name="castShape">Shape to cast.</param>
        /// <param name="startingTransform">Initial transform of the shape.</param>
        /// <param name="sweep">Sweep to apply to the shape.</param>
        /// <param name="result">Hit data, if any.</param>
        /// <returns>Whether or not the cast hit anything.</returns>
        public bool ConvexCast(ConvexShape castShape, ref RigidTransform startingTransform, ref Vector3 sweep, out RayCastResult result)
        {
            var overlappedElements = Resources.GetBroadPhaseEntryList();
            BoundingBox boundingBox;
            Toolbox.GetExpandedBoundingBox(ref castShape, ref startingTransform, ref sweep, out boundingBox);

            BroadPhase.QueryAccelerator.GetEntries(boundingBox, overlappedElements);
            result = new RayCastResult();
            result.HitData.T = float.MaxValue;
            for (int i = 0; i < overlappedElements.count; ++i)
            {
                RayHit hit;
                if (overlappedElements.Elements[i].ConvexCast(castShape, ref startingTransform, ref sweep, out hit))
                {
                    if (hit.T < result.HitData.T)
                    {
                        result.HitData = hit;
                        result.HitObject = overlappedElements.Elements[i];
                    }
                }
            }
            Resources.GiveBack(overlappedElements);
            return result.HitData.T < float.MaxValue;
        }
I've hesitated to add such a thing in the past due to the relative rarity of space-wide convex casts and to ensure that users knew they were gathering entries from a bounding box as opposed to a regular ray traversal due to the performance implications. It's not a particularly strong argument against adding it, so I'll probably just add a space method documented with plenty of usage warnings.
ecosky
Posts: 69
Joined: Fri Nov 04, 2011 7:13 am
Contact:

Re: Best way to do a swept sphere test?

Post by ecosky »

Thanks Norbo! I had already started down the path you suggested but had overlooked the GetExpandedBoundingBox part so I'm glad you had time to respond so quickly, you have probably saved me a bunch of time here (again :) )

I can see why you'd hesitate to add these given the special cases they are best suited for, but when those special cases pop up they will sure come in handy! I'll take a shot using your example and see how it goes.

Thanks again,
ecosky
Posts: 69
Joined: Fri Nov 04, 2011 7:13 am
Contact:

Re: Best way to do a swept sphere test?

Post by ecosky »

That worked great. I made a few variations that allowed for the use of filters and returning a list of all intersections which are of course similar to the RayCast overloads in case you are interested,

Code: Select all

		/// <summary>
		/// <para>Casts a convex shape against the space.</para>
		/// <para>Convex casts are sensitive to length; avoid extremely long convex casts for better stability and performance.</para>
		/// </summary>
		/// <param name="castShape">Shape to cast.</param>
		/// <param name="startingTransform">Initial transform of the shape.</param>
		/// <param name="sweep">Sweep to apply to the shape.</param>
		/// <param name="filter">Callback to filter out entries based on application requirements.</param>
		/// <param name="result">Hit data, if any, for the first object hit.</param>
		/// <returns>Whether or not the cast hit anything.</returns>
		public bool ConvexCast(ConvexShape castShape, ref RigidTransform startingTransform, ref Vector3 sweep, Func<BroadPhaseEntry, bool> filter, out RayCastResult result)
		{
			var overlappedElements = Resources.GetBroadPhaseEntryList();
			BoundingBox boundingBox;
			Toolbox.GetExpandedBoundingBox(ref castShape, ref startingTransform, ref sweep, out boundingBox);

			BroadPhase.QueryAccelerator.GetEntries(boundingBox, overlappedElements);
			result = new RayCastResult();
			result.HitData.T = float.MaxValue;
			for (int i = 0; i < overlappedElements.count; ++i)
			{
				var element = overlappedElements.Elements[i];
				if (!filter(element))
					continue;

				RayHit hit;
				if (element.ConvexCast(castShape, ref startingTransform, ref sweep, out hit))
				{
					if (hit.T < result.HitData.T)
					{
						result.HitData = hit;
						result.HitObject = overlappedElements.Elements[i];
					}
				}
			}
			Resources.GiveBack(overlappedElements);
			return result.HitData.T < float.MaxValue;
		}

		/// <summary>
		/// <para>Casts a convex shape against the space.</para>
		/// <para>Convex casts are sensitive to length; avoid extremely long convex casts for better stability and performance.</para>
		/// </summary>
		/// <param name="castShape">Shape to cast.</param>
		/// <param name="startingTransform">Initial transform of the shape.</param>
		/// <param name="sweep">Sweep to apply to the shape.</param>
		/// <param name="filter">Callback to filter out entries based on application requirements.</param>
		/// <param name="results">Collection of RayCastResult objects which includes all items this swept shape hits.</param>
		/// <returns>Whether or not the cast hit anything.</returns>
		public void ConvexCast(ConvexShape castShape, ref RigidTransform startingTransform, ref Vector3 sweep, Func<BroadPhaseEntry, bool> filter, IList<RayCastResult> results)
		{
			var overlappedElements = Resources.GetBroadPhaseEntryList();
			BoundingBox boundingBox;
			Toolbox.GetExpandedBoundingBox(ref castShape, ref startingTransform, ref sweep, out boundingBox);

			BroadPhase.QueryAccelerator.GetEntries(boundingBox, overlappedElements);
			for (int i = 0; i < overlappedElements.count; ++i)
			{
				var element = overlappedElements.Elements[i];
				if (!filter(element))
					continue;

				RayHit hit;
				if (element.ConvexCast(castShape, ref startingTransform, ref sweep, out hit))
				{
					results.Add(new RayCastResult(hit, element));
				}
			}
			Resources.GiveBack(overlappedElements);
		}
Thanks again!
Post Reply