Welcome to Our Community

Welcome to our support forums. We hope you find the information you are looking for. Please note that some areas of these forums are not visible to guests, and require a forum account. Registration is free and easy, and we won't spam you or share your email address with anyone.

Sign Up
  1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Improved intersection tests for Unity

Discussion in 'Developer's Blog' started by StagPoint, Jan 8, 2015.

  1. StagPoint

    StagPoint Administrator
    Staff Member Verified Customer Beta Tester

    Joined:
    Dec 3, 2014
    Messages:
    20
    Likes Received:
    0
    I'm working on an additional example for the HTN Planner which will showcase an FPS-style combat AI in action, and as part of that effort I needed some intersection tests that provide more information than the intersection tests that Unity provides "out of the box".

    For example, I needed to be able to determine whether one Bounds value intersected with or even fully contained another. Unity's default Bounds.Intersects() method returns only whether the two Bounds values intersect, not whether one fully contains the other. I needed similar tests for spheres and plane-bounded volumes.

    Because of this, I had to resort to writing my own intersection tests. I figure that I must not be the only one who needs these kinds of functions, so I decided to share them here.

    I hope someone finds these useful.

    Code (C#):
    // Copyright (c) 2014-2015 StagPoint Consulting
     
    using System;
    using System.Collections;
    using System.Collections.Generic;

    using UnityEngine;

    /// <summary>
    /// Provides information about whether an intersection exists between two
    /// geometric primitives, as well as whether the objects simply intersect
    /// or whether the first object fully contains the second
    /// </summary>
    public enum IntersectionType
    {
       /// <summary>
       /// No intersection exists between the tested objects
       /// </summary>
       None,
       /// <summary>
       /// The first object intersects with, but does not fully contain, the second object
       /// </summary>
       Intersects,
       /// <summary>
       /// The first object fully contains the second object
       /// </summary>
       Contains
    }

    /// <summary>
    /// Provides a set of optimized functions for performing commong intersection
    /// tests between various typeof of geometric primitives.
    /// </summary>
    public class IntersectionTests
    {

       /// <summary>
       /// Returns TRUE if the axis-aligned bounding box and the sphere overlap
       /// </summary>
       public static bool Intersects( ref Bounds box, ref Vector3 center, float radius )
       {
     
         var closestPointInAabb = Vector3.Min( Vector3.Max( center, box.min ), box.max );
         var distanceSquared = ( closestPointInAabb - center ).sqrMagnitude;

         return distanceSquared <= radius * radius;

       }

       /// <summary>
       /// Returns the type of intersection (if any) between an axis-aligned bounding box and a sphere
       /// </summary>
       public static IntersectionType GetIntersectionType( ref Bounds box, ref Vector3 center, float radius )
       {

         var closestPointInBox = Vector3.Min( Vector3.Max( center, box.min ), box.max );
         var distanceSquared = ( closestPointInBox - center ).sqrMagnitude;

         if( distanceSquared <= ( radius * radius ) )
         {

           var c = closestPointInBox - box.center;
           var r = box.extents;
           var x = Mathf.Abs( c.x ) <= ( r.x - radius );
           var y = Mathf.Abs( c.y ) <= ( r.y - radius );
           var z = Mathf.Abs( c.z ) <= ( r.z - radius );

           if( x & y & z )
           {
             return IntersectionType.Contains;
           }

           return IntersectionType.Intersects;

         }

         return IntersectionType.None;

       }

       /// <summary>
       /// Returns the type of intersection (if any) between a sphere and an axis-aligned bounding box.
       /// </summary>
       public static IntersectionType GetIntersectionType( ref Vector3 center, float radius, ref Bounds box )
       {

         var closestPointInBox = Vector3.Min( Vector3.Max( center, box.min ), box.max );
         var distanceSquared = ( closestPointInBox - center ).sqrMagnitude;

         if( distanceSquared <= ( radius * radius ) )
         {

           var c = ( box.center - center ).magnitude;
           var r = ( box.max - box.center ).magnitude;

           if( c <= radius - r )
           {
             return IntersectionType.Contains;
           }

           return IntersectionType.Intersects;

         }

         return IntersectionType.None;

       }

       /// <summary>
       /// Returns TRUE if both axis-aligned bounding boxes overlap. This function
       /// is analogous to Bounds.Intersects(), but seems to perform better
       /// </summary>
       public static bool Intersects( ref Bounds lhs, ref Bounds rhs )
       {

         var c = lhs.center - rhs.center;
         var r = lhs.extents + rhs.extents;

         var x = Math.Abs( c.x ) <= r.x;
         var y = Math.Abs( c.y ) <= r.y;
         var z = Math.Abs( c.z ) <= r.z;

         return x & y & z;

       }

       /// <summary>
       /// Returns the type of intersection (if any) between two axis-aligned bounding boxes
       /// </summary>
       public static IntersectionType GetIntersectionType( ref Bounds lhs, ref Bounds rhs )
       {

         var c = lhs.center - rhs.center;
         var r = lhs.extents + rhs.extents;

         var x = Math.Abs( c.x ) <= r.x;
         var y = Math.Abs( c.y ) <= r.y;
         var z = Math.Abs( c.z ) <= r.z;

         if( x & y & z )
         {

           r = lhs.extents - rhs.extents;
           x = Math.Abs( c.x ) <= r.x;
           y = Math.Abs( c.y ) <= r.y;
           z = Math.Abs( c.z ) <= r.z;

           if( x & y & z )
           {
             return IntersectionType.Contains;
           }

           return IntersectionType.Intersects;

         }

         return IntersectionType.None;

       }

       /// <summary>
       /// Returns the type of intersection (if any) between an axis-aligned bounding box
       /// and a plane-bounded volume.
       /// </summary>
       public static IntersectionType GetIntersectionType( IList<Plane> planes, ref Bounds bounds )
       {

         if( planes == null || planes.Count == 0 )
         {
           throw new ArgumentNullException( "planes" );
         }

         var center = bounds.center;
         var extents = bounds.extents;

         var intersecting = false;

         var planeCount = planes.Count;
         for( int i = 0; i < planeCount; i++ )
         {

           var plane = planes[ i ];
           var planeNormal = plane.normal;

           // Compute the projection interval radius of box b onto L(t) = b.c + t * p.n
           var r =
             extents.x * Math.Abs( planeNormal.x ) +
             extents.y * Math.Abs( planeNormal.y ) +
             extents.z * Math.Abs( planeNormal.z );

           // Compute distance of box center from plane
           float distance = Vector3.Dot( planeNormal, center ) + plane.distance;

           if( distance < -r )
           {
             // If the AABB lies behind *any* of the planes, there
             // is no point in continuing with the rest of the test
             return IntersectionType.None;
           }

           // Intersection occurs when distance falls within [-r,+r] interval
           intersecting |= ( Math.Abs( distance ) <= r );

         }

         if( intersecting )
         {
           return IntersectionType.Intersects;
         }

         return IntersectionType.Contains;

       }

    }
     
     

    Attached Files:

    #1

Share This Page