mixedreality/com.microsoft.mixedreality..../SDK/Features/UX/Scripts/BoundingBox/BoundingBoxHelper.cs

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);
}
}
}