255 lines
10 KiB
C#
255 lines
10 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.UI
|
|
{
|
|
/// <summary>
|
|
/// The BoundingBoxHelper class contains functions for getting geometric info from the non-axis-aligned
|
|
/// bounding box of a GameObject. These functions can be used to align another object to the center of
|
|
/// a certain face or the center of an edge of a face... etc.
|
|
/// The BoundingBoxHelper static function can be used for a one time calculation.
|
|
/// The dynamic functions can be used to obtain boundingcube info on an object's Update loop. Operations
|
|
/// are minimized in the dynamic use scenario.
|
|
/// </summary>
|
|
public class BoundingBoxHelper
|
|
{
|
|
readonly int[] face0 = { 0, 1, 3, 2 };
|
|
readonly int[] face1 = { 1, 5, 7, 3 };
|
|
readonly int[] face2 = { 5, 4, 6, 7 };
|
|
readonly int[] face3 = { 4, 0, 2, 6 };
|
|
readonly int[] face4 = { 6, 2, 3, 7 };
|
|
readonly int[] face5 = { 1, 0, 4, 5 };
|
|
readonly int[] noFaceIndices = { };
|
|
readonly Vector3[] noFaceVertices = { };
|
|
|
|
private Vector3[] face = new Vector3[4];
|
|
private Vector3[] midpoints = new Vector3[4];
|
|
private List<Vector3> rawBoundingCorners = new List<Vector3>();
|
|
private List<Vector3> worldBoundingCorners = new List<Vector3>();
|
|
private BoxCollider targetBounds;
|
|
private bool rawBoundingCornersObtained = false;
|
|
|
|
|
|
/// <summary>
|
|
/// Objects that align to an target's bounding box can call this function in the object's UpdateLoop
|
|
/// to get current bound points;
|
|
/// </summary>
|
|
[Obsolete("Use UpdateNonAABoundsCornerPositions and pass in TargetBounds")]
|
|
public void UpdateNonAABoundingBoxCornerPositions(BoundingBox boundingBox, List<Vector3> boundsPoints)
|
|
{
|
|
UpdateNonAABoundsCornerPositions(boundingBox.TargetBounds, boundsPoints);
|
|
}
|
|
/// <summary>
|
|
/// Returns the corner points of the given collider bounds
|
|
/// </summary>
|
|
/// <param name="colliderBounds">The collider bounds the corner points are calculated from</param>
|
|
/// <param name="boundsPoints">The corner points calculated from the collider points</param>
|
|
public void UpdateNonAABoundsCornerPositions(BoxCollider colliderBounds, List<Vector3> boundsPoints)
|
|
{
|
|
if (colliderBounds != targetBounds || rawBoundingCornersObtained == false)
|
|
{
|
|
GetRawBoundsCorners(colliderBounds);
|
|
}
|
|
|
|
if (colliderBounds == targetBounds && rawBoundingCornersObtained)
|
|
{
|
|
boundsPoints.Clear();
|
|
for (int i = 0; i < rawBoundingCorners.Count; ++i)
|
|
{
|
|
boundsPoints.Add(colliderBounds.transform.localToWorldMatrix.MultiplyPoint(rawBoundingCorners[i]));
|
|
}
|
|
|
|
worldBoundingCorners.Clear();
|
|
worldBoundingCorners.AddRange(boundsPoints);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function calculates the untransformed bounding box corner points of a GameObject.
|
|
/// </summary>
|
|
[Obsolete("Use GetRawBoundsCorners and pass in boundingBox.TargetBounds")]
|
|
public void GetRawBBCorners(BoundingBox boundingBox)
|
|
{
|
|
GetRawBoundsCorners(boundingBox.TargetBounds);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates the untransformed corner points of the given collider bounds
|
|
/// </summary>
|
|
/// <param name="colliderBounds">The collider bounds the corner points are calculated from.</param>
|
|
public void GetRawBoundsCorners(BoxCollider colliderBounds)
|
|
{
|
|
targetBounds = colliderBounds;
|
|
rawBoundingCorners.Clear();
|
|
rawBoundingCornersObtained = false;
|
|
|
|
GetUntransformedCornersFromObject(colliderBounds, rawBoundingCorners);
|
|
|
|
if (rawBoundingCorners != null && rawBoundingCorners.Count >= 4)
|
|
{
|
|
rawBoundingCornersObtained = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// this function gets the indices of the bounding cube corners that make up a face.
|
|
/// </summary>
|
|
/// <param name="index">the face index of the bounding cube 0-5</param>
|
|
/// <returns>an array of four integer indices</returns>
|
|
public int[] GetFaceIndices(int index)
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
return face0;
|
|
case 1:
|
|
return face1;
|
|
case 2:
|
|
return face2;
|
|
case 3:
|
|
return face3;
|
|
case 4:
|
|
return face4;
|
|
case 5:
|
|
return face5;
|
|
}
|
|
|
|
return noFaceIndices;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function returns the midpoints of each of the edges of the face of the bounding box
|
|
/// </summary>
|
|
/// <param name="index">the index of the face of the bounding cube- 0-5</param>
|
|
/// <returns>four Vector3 points</returns>
|
|
public Vector3[] GetFaceEdgeMidpoints(int index)
|
|
{
|
|
Vector3[] corners = GetFaceCorners(index);
|
|
midpoints[0] = (corners[0] + corners[1]) * 0.5f;
|
|
midpoints[1] = (corners[1] + corners[2]) * 0.5f;
|
|
midpoints[2] = (corners[2] + corners[3]) * 0.5f;
|
|
midpoints[3] = (corners[3] + corners[0]) * 0.5f;
|
|
|
|
return midpoints;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the normal of the face of the bounding cube specified by index
|
|
/// </summary>
|
|
/// <param name="index">the index of the face of the bounding cube 0-5</param>
|
|
/// <returns>a vector3 representing the face normal</returns>
|
|
public Vector3 GetFaceNormal(int index)
|
|
{
|
|
int[] face = GetFaceIndices(index);
|
|
|
|
if (face.Length == 4)
|
|
{
|
|
Vector3 ab = (worldBoundingCorners[face[1]] - worldBoundingCorners[face[0]]).normalized;
|
|
Vector3 ac = (worldBoundingCorners[face[2]] - worldBoundingCorners[face[0]]).normalized;
|
|
return Vector3.Cross(ab, ac).normalized;
|
|
}
|
|
|
|
return Vector3.zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function returns the centroid of a face of the bounding cube of an object specified
|
|
/// by the index parameter;
|
|
/// </summary>
|
|
/// <param name="index">an index into the list of faces of a boundingcube. 0-5</param>
|
|
public Vector3 GetFaceCentroid(int index)
|
|
{
|
|
int[] faceIndices = GetFaceIndices(index);
|
|
|
|
if (faceIndices.Length == 4)
|
|
{
|
|
return (worldBoundingCorners[faceIndices[0]] +
|
|
worldBoundingCorners[faceIndices[1]] +
|
|
worldBoundingCorners[faceIndices[2]] +
|
|
worldBoundingCorners[faceIndices[3]]) * 0.25f;
|
|
}
|
|
|
|
return Vector3.zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the center of the bottom edge of a face of the bounding box determined by index
|
|
/// </summary>
|
|
/// <param name="index">parameter indicating which face is used. 0-5</param>
|
|
/// <returns>a vector representing the bottom most edge center of the face</returns>
|
|
public Vector3 GetFaceBottomCentroid(int index)
|
|
{
|
|
Vector3[] edgeCentroids = GetFaceEdgeMidpoints(index);
|
|
|
|
Vector3 leastYPoint = edgeCentroids[0];
|
|
for (int i = 1; i < 4; ++i)
|
|
{
|
|
leastYPoint = edgeCentroids[i].y < leastYPoint.y ? edgeCentroids[i] : leastYPoint;
|
|
}
|
|
return leastYPoint;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function returns the four corners of a face of a bounding cube specified by index.
|
|
/// </summary>
|
|
/// <param name="index">the index of the face of the bounding cube. 0-5</param>
|
|
/// <returns>an array of 4 vectors</returns>
|
|
public Vector3[] GetFaceCorners(int index)
|
|
{
|
|
int[] faceIndices = GetFaceIndices(index);
|
|
|
|
if (faceIndices.Length == 4)
|
|
{
|
|
face[0] = worldBoundingCorners[faceIndices[0]];
|
|
face[1] = worldBoundingCorners[faceIndices[1]];
|
|
face[2] = worldBoundingCorners[faceIndices[2]];
|
|
face[3] = worldBoundingCorners[faceIndices[3]];
|
|
return face;
|
|
}
|
|
|
|
return noFaceVertices;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function gets the index of the face of the bounding cube that is most facing the lookAtPoint.
|
|
/// This could be the headPosition or camera position if the face that was facing the view is desired.
|
|
/// </summary>
|
|
/// <param name="lookAtPoint">the world coordinate to test which face is desired</param>
|
|
/// <returns>an integer representing the index of the bounding box faces</returns>
|
|
public int GetIndexOfForwardFace(Vector3 lookAtPoint)
|
|
{
|
|
int highestDotIndex = -1;
|
|
float hightestDotValue = float.MinValue;
|
|
for (int i = 0; i < 6; ++i)
|
|
{
|
|
Vector3 a = (lookAtPoint - GetFaceCentroid(i)).normalized;
|
|
Vector3 b = GetFaceNormal(i);
|
|
float dot = Vector3.Dot(a, b);
|
|
if (hightestDotValue < dot)
|
|
{
|
|
hightestDotValue = dot;
|
|
highestDotIndex = i;
|
|
}
|
|
}
|
|
return highestDotIndex;
|
|
}
|
|
|
|
/// <summary>
|
|
/// static function that performs one-time non-persistent calculation of corner points of given bounds
|
|
/// without taking world transform into account.
|
|
/// </summary>
|
|
/// <param name="targetBounds">the bounds the corner points are to be calculated from</param>
|
|
/// <param name="boundsPoints">the array of 8 corner points that will be filled</param>
|
|
public static void GetUntransformedCornersFromObject(BoxCollider targetBounds, List<Vector3> boundsPoints)
|
|
{
|
|
Bounds cloneBounds = new Bounds(targetBounds.center, targetBounds.size);
|
|
Vector3[] corners = null;
|
|
cloneBounds.GetCornerPositions(ref corners);
|
|
boundsPoints.AddRange(corners);
|
|
}
|
|
}
|
|
}
|