Commit 29de0c28 authored by BlackAngle233's avatar BlackAngle233
Browse files

10.19 learned

parent 912976bb
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Microsoft.MixedReality.Toolkit
{
public static class AssemblyExtensions
{
/// <summary>
/// Assembly.GetTypes() can throw in some cases. This extension will catch that exception and return only the types which were successfully loaded from the assembly.
/// </summary>
public static IEnumerable<Type> GetLoadableTypes(this Assembly @this)
{
try
{
return @this.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's Bounds struct
/// </summary>
public static class BoundsExtensions
{
// Corners
public const int LBF = 0;
public const int LBB = 1;
public const int LTF = 2;
public const int LTB = 3;
public const int RBF = 4;
public const int RBB = 5;
public const int RTF = 6;
public const int RTB = 7;
// X axis
public const int LTF_RTF = 8;
public const int LBF_RBF = 9;
public const int RTB_LTB = 10;
public const int RBB_LBB = 11;
// Y axis
public const int LTF_LBF = 12;
public const int RTB_RBB = 13;
public const int LTB_LBB = 14;
public const int RTF_RBF = 15;
// Z axis
public const int RBF_RBB = 16;
public const int RTF_RTB = 17;
public const int LBF_LBB = 18;
public const int LTF_LTB = 19;
// 2D corners
public const int LT = 0;
public const int LB = 1;
public const int RT = 2;
public const int RB = 3;
// 2D midpoints
public const int LT_RT = 4;
public const int RT_RB = 5;
public const int RB_LB = 6;
public const int LB_LT = 7;
// Face points
public const int TOP = 0;
public const int BOT = 1;
public const int LFT = 2;
public const int RHT = 3;
public const int FWD = 4;
public const int BCK = 5;
// Axis of the capsule’s lengthwise orientation in the object’s local space
private const int CAPSULE_X_AXIS = 0;
private const int CAPSULE_Y_AXIS = 1;
private const int CAPSULE_Z_AXIS = 2;
// Edges used to render the bounds.
private static readonly int[] boundsEdges = new int[]
{
LBF, LBB,
LBB, LTB,
LTB, LTF,
LTF, LBF,
LBF, RTB,
RTB, RTF,
RTF, RBF,
RBF, RBB,
RBB, RTB,
RTF, LBB,
RBF, LTB,
RBB, LTF
};
public enum Axis
{
X,
Y,
Z
}
private static Vector3[] corners = null;
private static Vector3[] rectTransformCorners = new Vector3[4];
#region Public Static Functions
/// <summary>
/// Returns an instance of the 'Bounds' class which is invalid. An invalid 'Bounds' instance
/// is one which has its size vector set to 'float.MaxValue' for all 3 components. The center
/// of an invalid bounds instance is the zero vector.
/// </summary>
public static Bounds GetInvalidBoundsInstance()
{
return new Bounds(Vector3.zero, GetInvalidBoundsSize());
}
/// <summary>
/// Checks if the specified bounds instance is valid. A valid 'Bounds' instance is
/// one whose size vector does not have all 3 components set to 'float.MaxValue'.
/// </summary>
public static bool IsValid(this Bounds bounds)
{
return bounds.size != GetInvalidBoundsSize();
}
/// <summary>
/// Gets all the corner points of the bounds in world space by transforming input bounds using the given transform
/// </summary>
/// <param name="transform">Local to world transform</param>
/// <param name="positions">Output corner positions</param>
/// <param name="bounds">Input bounds, in local space</param>
/// <remarks>
/// Use BoxColliderExtensions.{Left|Right}{Bottom|Top}{Front|Back} consts to index into the output
/// corners array.
/// </remarks>
public static void GetCornerPositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
{
// Calculate the local points to transform.
Vector3 center = bounds.center;
Vector3 extents = bounds.extents;
float leftEdge = center.x - extents.x;
float rightEdge = center.x + extents.x;
float bottomEdge = center.y - extents.y;
float topEdge = center.y + extents.y;
float frontEdge = center.z - extents.z;
float backEdge = center.z + extents.z;
// Allocate the array if needed.
const int numPoints = 8;
if (positions == null || positions.Length != numPoints)
{
positions = new Vector3[numPoints];
}
// Transform all the local points to world space.
positions[LBF] = transform.TransformPoint(leftEdge, bottomEdge, frontEdge);
positions[LBB] = transform.TransformPoint(leftEdge, bottomEdge, backEdge);
positions[LTF] = transform.TransformPoint(leftEdge, topEdge, frontEdge);
positions[LTB] = transform.TransformPoint(leftEdge, topEdge, backEdge);
positions[RBF] = transform.TransformPoint(rightEdge, bottomEdge, frontEdge);
positions[RBB] = transform.TransformPoint(rightEdge, bottomEdge, backEdge);
positions[RTF] = transform.TransformPoint(rightEdge, topEdge, frontEdge);
positions[RTB] = transform.TransformPoint(rightEdge, topEdge, backEdge);
}
/// <summary>
/// Gets all the corner points of the bounds
/// </summary>
/// <remarks>
/// Use BoxColliderExtensions.{Left|Right}{Bottom|Top}{Front|Back} consts to index into the output
/// corners array.
/// </remarks>
public static void GetCornerPositions(this Bounds bounds, ref Vector3[] positions)
{
// Calculate the local points to transform.
Vector3 center = bounds.center;
Vector3 extents = bounds.extents;
float leftEdge = center.x - extents.x;
float rightEdge = center.x + extents.x;
float bottomEdge = center.y - extents.y;
float topEdge = center.y + extents.y;
float frontEdge = center.z - extents.z;
float backEdge = center.z + extents.z;
// Allocate the array if needed.
const int numPoints = 8;
if (positions == null || positions.Length != numPoints)
{
positions = new Vector3[numPoints];
}
// Transform all the local points to world space.
positions[LBF] = new Vector3(leftEdge, bottomEdge, frontEdge);
positions[LBB] = new Vector3(leftEdge, bottomEdge, backEdge);
positions[LTF] = new Vector3(leftEdge, topEdge, frontEdge);
positions[LTB] = new Vector3(leftEdge, topEdge, backEdge);
positions[RBF] = new Vector3(rightEdge, bottomEdge, frontEdge);
positions[RBB] = new Vector3(rightEdge, bottomEdge, backEdge);
positions[RTF] = new Vector3(rightEdge, topEdge, frontEdge);
positions[RTB] = new Vector3(rightEdge, topEdge, backEdge);
}
/// <summary>
/// Gets all the corner points from Renderer's Bounds
/// </summary>
public static void GetCornerPositionsFromRendererBounds(this Bounds bounds, ref Vector3[] positions)
{
Vector3 center = bounds.center;
Vector3 extents = bounds.extents;
float leftEdge = center.x - extents.x;
float rightEdge = center.x + extents.x;
float bottomEdge = center.y - extents.y;
float topEdge = center.y + extents.y;
float frontEdge = center.z - extents.z;
float backEdge = center.z + extents.z;
const int numPoints = 8;
if (positions == null || positions.Length != numPoints)
{
positions = new Vector3[numPoints];
}
positions[LBF] = new Vector3(leftEdge, bottomEdge, frontEdge);
positions[LBB] = new Vector3(leftEdge, bottomEdge, backEdge);
positions[LTF] = new Vector3(leftEdge, topEdge, frontEdge);
positions[LTB] = new Vector3(leftEdge, topEdge, backEdge);
positions[RBF] = new Vector3(rightEdge, bottomEdge, frontEdge);
positions[RBB] = new Vector3(rightEdge, bottomEdge, backEdge);
positions[RTF] = new Vector3(rightEdge, topEdge, frontEdge);
positions[RTB] = new Vector3(rightEdge, topEdge, backEdge);
}
public static void GetFacePositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
{
Vector3 center = bounds.center;
Vector3 extents = bounds.extents;
const int numPoints = 6;
if (positions == null || positions.Length != numPoints)
{
positions = new Vector3[numPoints];
}
positions[TOP] = transform.TransformPoint(center + Vector3.up * extents.y);
positions[BOT] = transform.TransformPoint(center + Vector3.down * extents.y);
positions[LFT] = transform.TransformPoint(center + Vector3.left * extents.x);
positions[RHT] = transform.TransformPoint(center + Vector3.right * extents.x);
positions[FWD] = transform.TransformPoint(center + Vector3.forward * extents.z);
positions[BCK] = transform.TransformPoint(center + Vector3.back * extents.z);
}
/// <summary>
/// Gets all the corner points and mid points from Renderer's Bounds
/// </summary>
public static void GetCornerAndMidPointPositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
{
// Calculate the local points to transform.
Vector3 center = bounds.center;
Vector3 extents = bounds.extents;
float leftEdge = center.x - extents.x;
float rightEdge = center.x + extents.x;
float bottomEdge = center.y - extents.y;
float topEdge = center.y + extents.y;
float frontEdge = center.z - extents.z;
float backEdge = center.z + extents.z;
// Allocate the array if needed.
const int numPoints = LTF_LTB + 1;
if (positions == null || positions.Length != numPoints)
{
positions = new Vector3[numPoints];
}
// Transform all the local points to world space.
positions[LBF] = transform.TransformPoint(leftEdge, bottomEdge, frontEdge);
positions[LBB] = transform.TransformPoint(leftEdge, bottomEdge, backEdge);
positions[LTF] = transform.TransformPoint(leftEdge, topEdge, frontEdge);
positions[LTB] = transform.TransformPoint(leftEdge, topEdge, backEdge);
positions[RBF] = transform.TransformPoint(rightEdge, bottomEdge, frontEdge);
positions[RBB] = transform.TransformPoint(rightEdge, bottomEdge, backEdge);
positions[RTF] = transform.TransformPoint(rightEdge, topEdge, frontEdge);
positions[RTB] = transform.TransformPoint(rightEdge, topEdge, backEdge);
positions[LTF_RTF] = Vector3.Lerp(positions[LTF], positions[RTF], 0.5f);
positions[LBF_RBF] = Vector3.Lerp(positions[LBF], positions[RBF], 0.5f);
positions[RTB_LTB] = Vector3.Lerp(positions[RTB], positions[LTB], 0.5f);
positions[RBB_LBB] = Vector3.Lerp(positions[RBB], positions[LBB], 0.5f);
positions[LTF_LBF] = Vector3.Lerp(positions[LTF], positions[LBF], 0.5f);
positions[RTB_RBB] = Vector3.Lerp(positions[RTB], positions[RBB], 0.5f);
positions[LTB_LBB] = Vector3.Lerp(positions[LTB], positions[LBB], 0.5f);
positions[RTF_RBF] = Vector3.Lerp(positions[RTF], positions[RBF], 0.5f);
positions[RBF_RBB] = Vector3.Lerp(positions[RBF], positions[RBB], 0.5f);
positions[RTF_RTB] = Vector3.Lerp(positions[RTF], positions[RTB], 0.5f);
positions[LBF_LBB] = Vector3.Lerp(positions[LBF], positions[LBB], 0.5f);
positions[LTF_LTB] = Vector3.Lerp(positions[LTF], positions[LTB], 0.5f);
}
/// <summary>
/// Gets all the corner points and mid points from Renderer's Bounds, ignoring the z axis
/// </summary>
public static void GetCornerAndMidPointPositions2D(this Bounds bounds, Transform transform, ref Vector3[] positions, Axis flattenAxis)
{
// Calculate the local points to transform.
Vector3 center = bounds.center;
Vector3 extents = bounds.extents;
float leftEdge = 0;
float rightEdge = 0;
float bottomEdge = 0;
float topEdge = 0;
// Allocate the array if needed.
const int numPoints = LB_LT + 1;
if (positions == null || positions.Length != numPoints)
{
positions = new Vector3[numPoints];
}
switch (flattenAxis)
{
case Axis.X:
default:
leftEdge = center.z - extents.z;
rightEdge = center.z + extents.z;
bottomEdge = center.y - extents.y;
topEdge = center.y + extents.y;
// Transform all the local points to world space.
positions[LT] = transform.TransformPoint(0, topEdge, leftEdge);
positions[LB] = transform.TransformPoint(0, bottomEdge, leftEdge);
positions[RT] = transform.TransformPoint(0, topEdge, rightEdge);
positions[RB] = transform.TransformPoint(0, bottomEdge, rightEdge);
break;
case Axis.Y:
leftEdge = center.z - extents.z;
rightEdge = center.z + extents.z;
bottomEdge = center.x - extents.x;
topEdge = center.x + extents.x;
// Transform all the local points to world space.
positions[LT] = transform.TransformPoint(topEdge, 0, leftEdge);
positions[LB] = transform.TransformPoint(bottomEdge, 0, leftEdge);
positions[RT] = transform.TransformPoint(topEdge, 0, rightEdge);
positions[RB] = transform.TransformPoint(bottomEdge, 0, rightEdge);
break;
case Axis.Z:
leftEdge = center.x - extents.x;
rightEdge = center.x + extents.x;
bottomEdge = center.y - extents.y;
topEdge = center.y + extents.y;
// Transform all the local points to world space.
positions[LT] = transform.TransformPoint(leftEdge, topEdge, 0);
positions[LB] = transform.TransformPoint(leftEdge, bottomEdge, 0);
positions[RT] = transform.TransformPoint(rightEdge, topEdge, 0);
positions[RB] = transform.TransformPoint(rightEdge, bottomEdge, 0);
break;
}
positions[LT_RT] = Vector3.Lerp(positions[LT], positions[RT], 0.5f);
positions[RT_RB] = Vector3.Lerp(positions[RT], positions[RB], 0.5f);
positions[RB_LB] = Vector3.Lerp(positions[RB], positions[LB], 0.5f);
positions[LB_LT] = Vector3.Lerp(positions[LB], positions[LT], 0.5f);
}
/// <summary>
/// Method to get bounds from a collection of points.
/// </summary>
/// <param name="points">The points to construct a bounds around.</param>
/// <param name="bounds">An AABB in world space around all the points.</param>
/// <returns>True if bounds were calculated, if zero points are present bounds will not be calculated.</returns>
public static bool GetPointsBounds(List<Vector3> points, out Bounds bounds)
{
if (points.Count != 0)
{
bounds = new Bounds(points[0], Vector3.zero);
for (var i = 1; i < points.Count; ++i)
{
bounds.Encapsulate(points[i]);
}
return true;
}
bounds = new Bounds();
return false;
}
/// <summary>
/// Method to get bounds using collider method.
/// </summary>
/// <param name="target">GameObject to generate the bounds around.</param>
/// <param name="bounds">An AABB in world space around all the colliders in a gameObject hierarchy.</param>
/// <param name="ignoreLayers">A LayerMask to restrict the colliders selected.</param>
/// <returns>True if bounds were calculated, if zero colliders are present bounds will not be calculated.</returns>
public static bool GetColliderBounds(GameObject target, out Bounds bounds, LayerMask ignoreLayers)
{
var boundsPoints = new List<Vector3>();
GetColliderBoundsPoints(target, boundsPoints, ignoreLayers);
return GetPointsBounds(boundsPoints, out bounds);
}
/// <summary>
/// Calculates how much scale is required for this Bounds to match another Bounds.
/// </summary>
/// <param name="otherBounds">Object representation to be scaled to</param>
/// <param name="padding">padding multiplied into another bounds</param>
/// <returns>Scale represented as a Vector3 </returns>
public static Vector3 GetScaleToMatchBounds(this Bounds bounds, Bounds otherBounds, Vector3 padding = default(Vector3))
{
Vector3 szA = otherBounds.size + new Vector3(otherBounds.size.x * padding.x, otherBounds.size.y * padding.y, otherBounds.size.z * padding.z);
Vector3 szB = bounds.size;
Assert.IsTrue(szB.x != 0 && szB.y != 0 && szB.z != 0, "The bounds of the object must not be zero.");
return new Vector3(szA.x / szB.x, szA.y / szB.y, szA.z / szB.z);
}
/// <summary>
/// Calculates how much scale is required for this Bounds to fit inside another bounds without stretching.
/// </summary>
/// <param name="containerBounds">The bounds of the container we're trying to fit this object.</param>
/// <returns>A single scale factor that can be applied to this object to fit inside the container.</returns>
public static float GetScaleToFitInside(this Bounds bounds, Bounds containerBounds)
{
var objectSize = bounds.size;
var containerSize = containerBounds.size;
Assert.IsTrue(objectSize.x != 0 && objectSize.y != 0 && objectSize.z != 0, "The bounds of the container must not be zero.");
return Mathf.Min(containerSize.x / objectSize.x, containerSize.y / objectSize.y, containerSize.z / objectSize.z);
}
/// <summary>
/// Method to get bounding box points using Collider method.
/// </summary>
/// <param name="target">gameObject that boundingBox bounds.</param>
/// <param name="boundsPoints">array reference that gets filled with points</param>
/// <param name="ignoreLayers">layerMask to simplify search</param>
/// <param name="relativeTo">compute bounds relative to this transform</param>
public static void GetColliderBoundsPoints(GameObject target, List<Vector3> boundsPoints, LayerMask ignoreLayers, Transform relativeTo = null)
{
Collider[] colliders = target.GetComponentsInChildren<Collider>();
for (int i = 0; i < colliders.Length; i++)
{
GetColliderBoundsPoints(colliders[i], boundsPoints, ignoreLayers, relativeTo);
}
}
private static void InverseTransformPoints(ref Vector3[] positions, Transform relativeTo)
{
if (relativeTo)
{
for (var i = 0; i < positions.Length; ++i)
{
positions[i] = relativeTo.InverseTransformPoint(positions[i]);
}
}
}
/// <summary>
/// Method to get bounds from a single Collider
/// </summary>
/// <param name="collider">Target collider</param>
/// <param name="boundsPoints">array reference that gets filled with points</param>
/// <param name="ignoreLayers">layerMask to simplify search</param>
public static void GetColliderBoundsPoints(Collider collider, List<Vector3> boundsPoints, LayerMask ignoreLayers, Transform relativeTo = null)
{
if (ignoreLayers == (1 << collider.gameObject.layer | ignoreLayers)) { return; }
if (collider is SphereCollider)
{
SphereCollider sc = collider as SphereCollider;
Bounds sphereBounds = new Bounds(sc.center, Vector3.one * sc.radius * 2);
sphereBounds.GetFacePositions(sc.transform, ref corners);
InverseTransformPoints(ref corners, relativeTo);
boundsPoints.AddRange(corners);
}
else if (collider is BoxCollider)
{
BoxCollider bc = collider as BoxCollider;
Bounds boxBounds = new Bounds(bc.center, bc.size);
boxBounds.GetCornerPositions(bc.transform, ref corners);
InverseTransformPoints(ref corners, relativeTo);
boundsPoints.AddRange(corners);
}
else if (collider is MeshCollider)
{
MeshCollider mc = collider as MeshCollider;
Bounds meshBounds = mc.sharedMesh.bounds;
meshBounds.GetCornerPositions(mc.transform, ref corners);
InverseTransformPoints(ref corners, relativeTo);
boundsPoints.AddRange(corners);
}
else if (collider is CapsuleCollider)
{
CapsuleCollider cc = collider as CapsuleCollider;
Bounds capsuleBounds = new Bounds(cc.center, Vector3.zero);
switch (cc.direction)
{
case CAPSULE_X_AXIS:
capsuleBounds.size = new Vector3(cc.height, cc.radius * 2, cc.radius * 2);
break;
case CAPSULE_Y_AXIS:
capsuleBounds.size = new Vector3(cc.radius * 2, cc.height, cc.radius * 2);
break;
case CAPSULE_Z_AXIS:
capsuleBounds.size = new Vector3(cc.radius * 2, cc.radius * 2, cc.height);
break;
}
capsuleBounds.GetFacePositions(cc.transform, ref corners);
InverseTransformPoints(ref corners, relativeTo);
boundsPoints.AddRange(corners);
}
}
/// <summary>
/// Method to get bounds using renderer method.
/// </summary>
/// <param name="target">GameObject to generate the bounds around.</param>
/// <param name="bounds">An AABB in world space around all the renderers in a gameObject hierarchy.</param>
/// <param name="ignoreLayers">A LayerMask to restrict the colliders selected.</param>
/// <returns>True if bounds were calculated, if zero renderers are present bounds will not be calculated.</returns>
public static bool GetRenderBounds(GameObject target, out Bounds bounds, LayerMask ignoreLayers)
{
var boundsPoints = new List<Vector3>();
GetRenderBoundsPoints(target, boundsPoints, ignoreLayers);
return GetPointsBounds(boundsPoints, out bounds);
}
/// <summary>
/// GetRenderBoundsPoints gets bounding box points using Render method.
/// </summary>
/// <param name="target">gameObject that boundingbox bounds</param>
/// <param name="boundsPoints">array reference that gets filled with points</param>
/// <param name="ignoreLayers">layerMask to simplify search</param>
public static void GetRenderBoundsPoints(GameObject target, List<Vector3> boundsPoints, LayerMask ignoreLayers)
{
Renderer[] renderers = target.GetComponentsInChildren<Renderer>();
for (int i = 0; i < renderers.Length; ++i)
{
Renderer rendererObj = renderers[i];
if (ignoreLayers == (1 << rendererObj.gameObject.layer | ignoreLayers))
{
continue;
}
rendererObj.bounds.GetCornerPositionsFromRendererBounds(ref corners);
boundsPoints.AddRange(corners);
}
}
/// <summary>
/// Method to get bounds using mesh filters method.
/// </summary>
/// <param name="target">GameObject to generate the bounds around.</param>
/// <param name="bounds">An AABB in world space around all the mesh filters in a GameObject hierarchy.</param>
/// <param name="ignoreLayers">A LayerMask to restrict the colliders selected.</param>
/// <returns>True if bounds were calculated, if zero mesh filters are present bounds will not be calculated.</returns>
public static bool GetMeshFilterBounds(GameObject target, out Bounds bounds, LayerMask ignoreLayers)
{
var boundsPoints = new List<Vector3>();
GetMeshFilterBoundsPoints(target, boundsPoints, ignoreLayers);
return GetPointsBounds(boundsPoints, out bounds);
}
/// <summary>
/// GetMeshFilterBoundsPoints - gets bounding box points using MeshFilter method.
/// </summary>
/// <param name="target">gameObject that boundingbox bounds</param>
/// <param name="boundsPoints">array reference that gets filled with points</param>
/// <param name="ignoreLayers">layerMask to simplify search</param>
public static void GetMeshFilterBoundsPoints(GameObject target, List<Vector3> boundsPoints, LayerMask ignoreLayers)
{
MeshFilter[] meshFilters = target.GetComponentsInChildren<MeshFilter>();
for (int i = 0; i < meshFilters.Length; i++)
{
MeshFilter meshFilterObj = meshFilters[i];
if (ignoreLayers == (1 << meshFilterObj.gameObject.layer | ignoreLayers))
{
continue;
}
Bounds meshBounds = meshFilterObj.sharedMesh.bounds;
meshBounds.GetCornerPositions(meshFilterObj.transform, ref corners);
boundsPoints.AddRange(corners);
}
RectTransform[] rectTransforms = target.GetComponentsInChildren<RectTransform>();
for (int i = 0; i < rectTransforms.Length; i++)
{
rectTransforms[i].GetWorldCorners(rectTransformCorners);
boundsPoints.AddRange(rectTransformCorners);
}
}
/// <summary>
/// Transforms 'bounds' using the specified transform matrix.
/// </summary>
/// <remarks>
/// Transforming a 'Bounds' instance means that the function will construct a new 'Bounds'
/// instance which has its center translated using the translation information stored in
/// the specified matrix and its size adjusted to account for rotation and scale. The size
/// of the new 'Bounds' instance will be calculated in such a way that it will contain the
/// old 'Bounds'.
/// </remarks>
/// <param name="bounds">
/// The 'Bounds' instance which must be transformed.
/// </param>
/// <param name="transformMatrix">
/// The specified 'Bounds' instance will be transformed using this transform matrix. The function
/// assumes that the matrix doesn't contain any projection or skew transformation.
/// </param>
/// <returns>
/// The transformed 'Bounds' instance.
/// </returns>
public static Bounds Transform(this Bounds bounds, Matrix4x4 transformMatrix)
{
// We will need access to the right, up and look vector which are encoded inside the transform matrix
Vector3 rightAxis = transformMatrix.GetColumn(0);
Vector3 upAxis = transformMatrix.GetColumn(1);
Vector3 lookAxis = transformMatrix.GetColumn(2);
// We will 'imagine' that we want to rotate the bounds' extents vector using the rotation information
// stored inside the specified transform matrix. We will need these when calculating the new size if
// the transformed bounds.
Vector3 rotatedExtentsRight = rightAxis * bounds.extents.x;
Vector3 rotatedExtentsUp = upAxis * bounds.extents.y;
Vector3 rotatedExtentsLook = lookAxis * bounds.extents.z;
// Calculate the new bounds size along each axis. The size on each axis is calculated by summing up the
// corresponding vector component values of the rotated extents vectors. We multiply by 2 because we want
// to get a size and currently we are working with extents which represent half the size.
float newSizeX = (Mathf.Abs(rotatedExtentsRight.x) + Mathf.Abs(rotatedExtentsUp.x) + Mathf.Abs(rotatedExtentsLook.x)) * 2.0f;
float newSizeY = (Mathf.Abs(rotatedExtentsRight.y) + Mathf.Abs(rotatedExtentsUp.y) + Mathf.Abs(rotatedExtentsLook.y)) * 2.0f;
float newSizeZ = (Mathf.Abs(rotatedExtentsRight.z) + Mathf.Abs(rotatedExtentsUp.z) + Mathf.Abs(rotatedExtentsLook.z)) * 2.0f;
// Construct the transformed 'Bounds' instance
var transformedBounds = new Bounds();
transformedBounds.center = transformMatrix.MultiplyPoint(bounds.center);
transformedBounds.size = new Vector3(newSizeX, newSizeY, newSizeZ);
// Return the instance to the caller
return transformedBounds;
}
/// <summary>
/// Returns the screen space corner points of the specified 'Bounds' instance.
/// </summary>
/// <param name="camera">
/// The camera used for rendering to the screen. This is needed to perform the
/// transformation to screen space.
/// </param>
public static Vector2[] GetScreenSpaceCornerPoints(this Bounds bounds, Camera camera)
{
Vector3 aabbCenter = bounds.center;
Vector3 aabbExtents = bounds.extents;
// Return the screen space point array
return new Vector2[]
{
camera.WorldToScreenPoint(new Vector3(aabbCenter.x - aabbExtents.x, aabbCenter.y - aabbExtents.y, aabbCenter.z - aabbExtents.z)),
camera.WorldToScreenPoint(new Vector3(aabbCenter.x + aabbExtents.x, aabbCenter.y - aabbExtents.y, aabbCenter.z - aabbExtents.z)),
camera.WorldToScreenPoint(new Vector3(aabbCenter.x + aabbExtents.x, aabbCenter.y + aabbExtents.y, aabbCenter.z - aabbExtents.z)),
camera.WorldToScreenPoint(new Vector3(aabbCenter.x - aabbExtents.x, aabbCenter.y + aabbExtents.y, aabbCenter.z - aabbExtents.z)),
camera.WorldToScreenPoint(new Vector3(aabbCenter.x - aabbExtents.x, aabbCenter.y - aabbExtents.y, aabbCenter.z + aabbExtents.z)),
camera.WorldToScreenPoint(new Vector3(aabbCenter.x + aabbExtents.x, aabbCenter.y - aabbExtents.y, aabbCenter.z + aabbExtents.z)),
camera.WorldToScreenPoint(new Vector3(aabbCenter.x + aabbExtents.x, aabbCenter.y + aabbExtents.y, aabbCenter.z + aabbExtents.z)),
camera.WorldToScreenPoint(new Vector3(aabbCenter.x - aabbExtents.x, aabbCenter.y + aabbExtents.y, aabbCenter.z + aabbExtents.z))
};
}
/// <summary>
/// Returns the rectangle which encloses the specifies 'Bounds' instance in screen space.
/// </summary>
public static Rect GetScreenRectangle(this Bounds bounds, Camera camera)
{
// Retrieve the bounds' corner points in screen space
Vector2[] screenSpaceCornerPoints = bounds.GetScreenSpaceCornerPoints(camera);
// Identify the minimum and maximum points in the array
Vector3 minScreenPoint = screenSpaceCornerPoints[0], maxScreenPoint = screenSpaceCornerPoints[0];
for (int screenPointIndex = 1; screenPointIndex < screenSpaceCornerPoints.Length; ++screenPointIndex)
{
minScreenPoint = Vector3.Min(minScreenPoint, screenSpaceCornerPoints[screenPointIndex]);
maxScreenPoint = Vector3.Max(maxScreenPoint, screenSpaceCornerPoints[screenPointIndex]);
}
// Return the screen space rectangle
return new Rect(minScreenPoint.x, minScreenPoint.y, maxScreenPoint.x - minScreenPoint.x, maxScreenPoint.y - minScreenPoint.y);
}
/// <summary>
/// Returns the volume of the bounds.
/// </summary>
public static float Volume(this Bounds bounds)
{
return bounds.size.x * bounds.size.y * bounds.size.z;
}
/// <summary>
/// Returns bounds that contain both this bounds and the bounds passed in.
/// </summary>
public static Bounds ExpandToContain(this Bounds originalBounds, Bounds otherBounds)
{
Bounds tmpBounds = originalBounds;
tmpBounds.Encapsulate(otherBounds);
return tmpBounds;
}
/// <summary>
/// Checks to see if bounds contains the other bounds completely.
/// </summary>
public static bool ContainsBounds(this Bounds bounds, Bounds otherBounds)
{
return bounds.Contains(otherBounds.min) && bounds.Contains(otherBounds.max);
}
/// <summary>
/// Checks to see whether point is closer to bounds or otherBounds
/// </summary>
public static bool CloserToPoint(this Bounds bounds, Vector3 point, Bounds otherBounds)
{
Vector3 distToClosestPoint1 = bounds.ClosestPoint(point) - point;
Vector3 distToClosestPoint2 = otherBounds.ClosestPoint(point) - point;
if (distToClosestPoint1.magnitude == distToClosestPoint2.magnitude)
{
Vector3 toCenter1 = point - bounds.center;
Vector3 toCenter2 = point - otherBounds.center;
return (toCenter1.magnitude <= toCenter2.magnitude);
}
return (distToClosestPoint1.magnitude <= distToClosestPoint2.magnitude);
}
/// <summary>
/// Draws a wire frame <see href="https://docs.unity3d.com/ScriptReference/Bounds.html">Bounds</see> object using <see href="https://docs.unity3d.com/ScriptReference/Debug.DrawLine.html">Debug.DrawLine</see>.
/// </summary>
/// <param name="bounds">The <see href="https://docs.unity3d.com/ScriptReference/Bounds.html">Bounds</see> to draw.</param>
/// <param name="color">Color of the line.</param>
/// <param name="duration">How long the line should be visible for in seconds.</param>
/// <param name="depthTest">Should the line be obscured by objects closer to the camera?</param>
public static void DebugDraw(this Bounds bounds, Color color, float duration = 0.0f, bool depthTest = true)
{
var center = bounds.center;
var x = bounds.extents.x;
var y = bounds.extents.y;
var z = bounds.extents.z;
var a = new Vector3(-x, y, -z);
var b = new Vector3(x, -y, -z);
var c = new Vector3(x, y, -z);
var verticies = new Vector3[]
{
bounds.min, center + a, center + b, center + c,
bounds.max, center - a, center - b, center - c
};
for (var i = 0; i < boundsEdges.Length; i += 2)
{
Debug.DrawLine(verticies[boundsEdges[i]], verticies[boundsEdges[i + 1]], color, duration, depthTest);
}
}
#endregion
#region Private Static Functions
/// <summary>
/// Returns the vector which is used to represent and invalid bounds size.
/// </summary>
private static Vector3 GetInvalidBoundsSize()
{
return new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
}
#endregion
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for the Unity's Camera class
/// </summary>
public static class CameraExtensions
{
/// <summary>
/// Get the horizontal FOV from the stereo camera in radians
/// </summary>
public static float GetHorizontalFieldOfViewRadians(this Camera camera)
{
return 2f * Mathf.Atan(Mathf.Tan(camera.fieldOfView * Mathf.Deg2Rad * 0.5f) * camera.aspect);
}
/// <summary>
/// Get the horizontal FOV from the stereo camera in degrees
/// </summary>
public static float GetHorizontalFieldOfViewDegrees(this Camera camera)
{
return camera.GetHorizontalFieldOfViewRadians() * Mathf.Rad2Deg;
}
/// <summary>
/// Returns if a point will be rendered on the screen in either eye
/// </summary>
/// <param name="camera">The camera to check the point against</param>
public static bool IsInFOV(this Camera camera, Vector3 position)
{
Vector3 screenPoint = camera.WorldToViewportPoint(position);
return screenPoint.z >= camera.nearClipPlane && screenPoint.z <= camera.farClipPlane
&& screenPoint.x >= 0 && screenPoint.x <= 1
&& screenPoint.y >= 0 && screenPoint.y <= 1;
}
/// <summary>
/// Returns true if a point is in the a cone inscribed into the Camera's frustum, false otherwise
/// The cone is inscribed to a radius equal to the vertical height of the camera's FOV.
/// By default, the cone's tip is "chopped off" by an amount defined by the camera's
/// far and near clip planes.
/// </summary>
/// <param name="point">Point to test</param>
/// <param name="coneAngleBufferDegrees">Degrees to expand the cone radius by.</param>
public static bool IsInFOVCone(this Camera camera,
Vector3 point,
float coneAngleBufferDegrees = 0)
{
return MathUtilities.IsInFOVCone(camera.transform,
point,
camera.fieldOfView + coneAngleBufferDegrees,
camera.nearClipPlane,
camera.farClipPlane
);
}
/// <summary>
/// Gets the frustum size at a given distance from the camera.
/// </summary>
/// <param name="camera">The camera to get the frustum size for</param>
/// <param name="distanceFromCamera">The distance from the camera to get the frustum size at</param>
public static Vector2 GetFrustumSizeForDistance(this Camera camera, float distanceFromCamera)
{
Vector2 frustumSize = new Vector2
{
y = 2.0f * distanceFromCamera * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad)
};
frustumSize.x = frustumSize.y * camera.aspect;
return frustumSize;
}
/// <summary>
/// Gets the distance to the camera that a specific frustum height would be at.
/// </summary>
/// <param name="camera">The camera to get the distance from</param>
/// <param name="frustumHeight">The frustum height</param>
public static float GetDistanceForFrustumHeight(this Camera camera, float frustumHeight)
{
return frustumHeight * 0.5f / Mathf.Max(0.00001f, Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad));
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEngine;
using UnityEngine.UI;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extensions for the Canvas class.
/// </summary>
public static class CanvasExtensions
{
/// <summary>
/// Convenience method for getting a plane for this canvas in world coordinates.
/// </summary>
/// <param name="canvas">The canvas to get the plane from.</param>
/// <returns>A Plane for this canvas.</returns>
public static Plane GetPlane(this Canvas canvas)
{
Vector3[] corners = canvas.GetWorldCorners();
// Now set a plane from any of the 3 corners (clockwise) so that we can compute our gaze intersection
Plane plane = new Plane(corners[0], corners[1], corners[2]);
return plane;
}
/// <summary>
/// Convenience method for getting the corners of the canvas in world coordinates. Ordered clockwise from bottom-left.
/// </summary>
/// <param name="canvas">The canvas to get the world corners from.</param>
/// <returns>An array of Vector3s that represent the corners of the canvas in world coordinates.</returns>
public static Vector3[] GetWorldCorners(this Canvas canvas)
{
Vector3[] worldCorners = new Vector3[4];
RectTransform rect = canvas.GetComponent<RectTransform>();
rect.GetWorldCorners(worldCorners);
return worldCorners;
}
/// <summary>
/// Convenience method for getting the corners of the canvas in local coordinates. Ordered clockwise from bottom-left.
/// </summary>
/// <param name="canvas">The canvas to get the local corners from.</param>
/// <returns>An array of Vector3s that represent the corners of the canvas in local coordinates.</returns>
public static Vector3[] GetLocalCorners(this Canvas canvas)
{
Vector3[] localCorners = new Vector3[4];
RectTransform rect = canvas.GetComponent<RectTransform>();
rect.GetLocalCorners(localCorners);
return localCorners;
}
/// <summary>
/// Convenience method for getting the corners of the canvas in viewport coordinates. Note
/// that the points have the same ordering as the array returned in GetWorldCorners()
/// </summary>
/// <param name="canvas">The canvas to get the viewport corners from</param>
/// <returns>An array of Vector3s that represent the corners of the canvas in viewport coordinates</returns>
public static Vector3[] GetViewportCorners(this Canvas canvas)
{
Vector3[] viewportCorners = new Vector3[4];
Vector3[] worldCorners = canvas.GetWorldCorners();
for (int i = 0; i < 4; i++)
{
viewportCorners[i] = CameraCache.Main.WorldToViewportPoint(worldCorners[i]);
}
return viewportCorners;
}
/// <summary>
/// Gets the position of the corners for a canvas in screen space.
/// 1 -- 2
/// | |
/// 0 -- 3
/// </summary>
/// <param name="canvas">The canvas to get the screen corners for.</param>
public static Vector3[] GetScreenCorners(this Canvas canvas)
{
Vector3[] screenCorners = new Vector3[4];
Vector3[] worldCorners = canvas.GetWorldCorners();
for (int i = 0; i < 4; i++)
{
screenCorners[i] = CameraCache.Main.WorldToScreenPoint(worldCorners[i]);
}
return screenCorners;
}
/// <summary>
/// Returns a rectangle in screen coordinates that encompasses the bounds of the target canvas.
/// </summary>
/// <param name="canvas">The canvas the get the screen rect for</param>
public static Rect GetScreenRect(this Canvas canvas)
{
Vector3[] screenCorners = canvas.GetScreenCorners();
float x = Mathf.Min(screenCorners[0].x, screenCorners[1].x);
float y = Mathf.Min(screenCorners[0].y, screenCorners[3].y);
float xMax = Mathf.Max(screenCorners[2].x, screenCorners[3].x);
float yMax = Mathf.Max(screenCorners[1].y, screenCorners[2].y);
return new Rect(x, y, xMax - x, yMax - y);
}
/// <summary>
/// Raycast against a canvas using a ray.
/// </summary>
/// <param name="canvas">The canvas to raycast against</param>
/// <param name="rayOrigin">The origin of the ray</param>
/// <param name="rayDirection">The direction of the ray</param>
/// <param name="distance">The distance of the ray</param>
/// <param name="hitPoint">The hitpoint of the ray</param>
/// <param name="hitChildObject">The child object that was hit or the canvas itself if it has no active children that were within the hit range.</param>
public static bool Raycast(this Canvas canvas, Vector3 rayOrigin, Vector3 rayDirection, out float distance, out Vector3 hitPoint, out GameObject hitChildObject)
{
hitChildObject = null;
Plane plane = canvas.GetPlane();
Ray ray = new Ray(rayOrigin, rayDirection);
if (plane.Raycast(ray, out distance))
{
// See if the point lies within the local canvas rect of the plane
Vector3[] corners = canvas.GetLocalCorners();
hitPoint = rayOrigin + (rayDirection.normalized * distance);
Vector3 localHitPoint = canvas.transform.InverseTransformPoint(hitPoint);
if (localHitPoint.x >= corners[0].x
&& localHitPoint.x <= corners[3].x
&& localHitPoint.y <= corners[2].y
&& localHitPoint.y >= corners[3].y)
{
hitChildObject = canvas.gameObject;
// look for the child object that was hit
RectTransform rectTransform = GetChildRectTransformAtPoint(canvas.GetComponent<RectTransform>(), hitPoint, true, true, true);
if (rectTransform != null)
{
hitChildObject = rectTransform.gameObject;
}
else
{
hitChildObject = canvas.gameObject;
}
return true;
}
}
hitPoint = Vector3.zero;
return false;
}
/// <summary>
/// Gets a child rect transform for the given point and parameters.
/// </summary>
/// <param name="rectTransformParent">The rect transform to look for children that may contain the projected (orthogonal to the child's normal) world point</param>
/// <param name="worldPoint">The world point</param>
/// <param name="recursive">Indicates if the check should be done recursively</param>
/// <param name="shouldReturnActive">If true, will only check children that are active, otherwise it will check all children.</param>
/// <param name="shouldReturnRaycastable">If true, will only check children that if they have a graphic and have its member raycastTarget set to true, otherwise will ignore the raycastTarget value. Will still allow children to be checked that do not have a graphic component.</param>
public static RectTransform GetChildRectTransformAtPoint(this RectTransform rectTransformParent, Vector3 worldPoint, bool recursive, bool shouldReturnActive, bool shouldReturnRaycastable)
{
Vector3[] localCorners = new Vector3[4];
Vector3 childLocalPoint;
RectTransform rectTransform;
bool shouldRaycast = false;
for (int i = rectTransformParent.childCount - 1; i >= 0; i--)
{
rectTransform = rectTransformParent.GetChild(i).GetComponent<RectTransform>();
Graphic graphic = rectTransform.GetComponent<Graphic>();
shouldRaycast = ((shouldReturnRaycastable && graphic != null && graphic.raycastTarget) || graphic == null || !shouldReturnRaycastable);
if (((shouldReturnActive && rectTransform.gameObject.activeSelf) || !shouldReturnActive))
{
rectTransform.GetLocalCorners(localCorners);
childLocalPoint = rectTransform.InverseTransformPoint(worldPoint);
if (recursive)
{
RectTransform childRect = GetChildRectTransformAtPoint(rectTransform, worldPoint, recursive, shouldReturnActive, shouldReturnRaycastable);
if (childRect != null)
{
return childRect;
}
}
if (shouldRaycast
&& childLocalPoint.x >= localCorners[0].x
&& childLocalPoint.x <= localCorners[3].x
&& childLocalPoint.y <= localCorners[2].y
&& childLocalPoint.y >= localCorners[3].y)
{
return rectTransform;
}
}
}
return null;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.MixedReality.Toolkit.Input;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for .Net Collection objects, e.g. Lists, Dictionaries, Arrays
/// </summary>
public static class CollectionsExtensions
{
/// <summary>
/// Creates a read-only wrapper around an existing collection.
/// </summary>
/// <typeparam name="TElement">The type of element in the collection.</typeparam>
/// <param name="elements">The collection to be wrapped.</param>
/// <returns>The new, read-only wrapper around <paramref name="elements"/>.</returns>
public static ReadOnlyCollection<TElement> AsReadOnly<TElement>(this IList<TElement> elements)
{
return new ReadOnlyCollection<TElement>(elements);
}
/// <summary>
/// Creates a read-only copy of an existing collection.
/// </summary>
/// <typeparam name="TElement">The type of element in the collection.</typeparam>
/// <param name="elements">The collection to be copied.</param>
/// <returns>The new, read-only copy of <paramref name="elements"/>.</returns>
public static ReadOnlyCollection<TElement> ToReadOnlyCollection<TElement>(this IEnumerable<TElement> elements)
{
return elements.ToArray().AsReadOnly();
}
/// <summary>
/// Inserts an item in its sorted position into an already sorted collection. This is useful if you need to consume the
/// collection in between insertions and need it to stay correctly sorted the whole time. If you just need to insert a
/// bunch of items and then consume the sorted collection at the end, it's faster to add all the elements and then use
/// <see cref="System.Collections.Generic.List{T}.Sort"/> at the end.
/// </summary>
/// <typeparam name="TElement">The type of element in the collection.</typeparam>
/// <param name="elements">The collection of sorted elements to be inserted into.</param>
/// <param name="toInsert">The element to insert.</param>
/// <param name="comparer">The comparer to use when sorting or null to use <see cref="System.Collections.Generic.Comparer{T}.Default"/>.</param>
public static int SortedInsert<TElement>(this List<TElement> elements, TElement toInsert, IComparer<TElement> comparer = null)
{
var effectiveComparer = comparer ?? Comparer<TElement>.Default;
if (Application.isEditor)
{
for (int iElement = 0; iElement < elements.Count - 1; iElement++)
{
var element = elements[iElement];
var nextElement = elements[iElement + 1];
if (effectiveComparer.Compare(element, nextElement) > 0)
{
Debug.LogWarning("Elements must already be sorted to call this method.");
break;
}
}
}
int searchResult = elements.BinarySearch(toInsert, effectiveComparer);
int insertionIndex = searchResult >= 0
? searchResult
: ~searchResult;
elements.Insert(insertionIndex, toInsert);
return insertionIndex;
}
/// <summary>
/// Disposes of all non-null elements in a collection.
/// </summary>
/// <typeparam name="TElement">The type of element in the collection.</typeparam>
/// <param name="elements">The collection of elements to be disposed.</param>
public static void DisposeElements<TElement>(this IEnumerable<TElement> elements)
where TElement : IDisposable
{
foreach (var element in elements)
{
if (element != null)
{
element.Dispose();
}
}
}
/// <summary>
/// Disposes of all non-null elements in a collection.
/// </summary>
/// <typeparam name="TElement">The type of element in the collection.</typeparam>
/// <param name="elements">The collection of elements to be disposed.</param>
public static void DisposeElements<TElement>(this IList<TElement> elements)
where TElement : IDisposable
{
for (int iElement = 0; iElement < elements.Count; iElement++)
{
var element = elements[iElement];
if (element != null)
{
element.Dispose();
}
}
}
/// <summary>
/// Exports the values of a uint indexed Dictionary as an Array
/// </summary>
/// <typeparam name="T">Type of data stored in the values of the Dictionary</typeparam>
/// <param name="input">Dictionary to be exported</param>
/// <returns>array in the type of data stored in the Dictionary</returns>
public static T[] ExportDictionaryValuesAsArray<T>(this Dictionary<uint, T> input)
{
T[] output = new T[input.Count];
input.Values.CopyTo(output, 0);
return output;
}
/// <summary>
/// Overload extension to enable getting of an InteractionDefinition of a specific type
/// </summary>
/// <param name="input">The InteractionDefinition array reference</param>
/// <param name="key">The specific DeviceInputType value to query</param>
public static MixedRealityInteractionMapping GetInteractionByType(this MixedRealityInteractionMapping[] input, DeviceInputType key)
{
for (int i = 0; i < input?.Length; i++)
{
if (input[i].InputType == key)
{
return input[i];
}
}
return default(MixedRealityInteractionMapping);
}
/// <summary>
/// Overload extension to enable getting of an InteractionDefinition of a specific type
/// </summary>
/// <param name="input">The InteractionDefinition array reference</param>
/// <param name="key">The specific DeviceInputType value to query</param>
public static bool SupportsInputType(this MixedRealityInteractionMapping[] input, DeviceInputType key)
{
for (int i = 0; i < input.Length; i++)
{
if (input[i].InputType == key)
{
return true;
}
}
return false;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Globalization;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's Color32 struct
/// </summary>
public static class Color32Extensions
{
public static Color PremultiplyAlpha(Color col)
{
col.r *= col.a;
col.g *= col.a;
col.b *= col.a;
return col;
}
public static Color32 PremultiplyAlpha(Color32 col)
{
Color floatCol = col;
return (Color32)PremultiplyAlpha(floatCol);
}
/// <summary>
/// Creates a Color from a hexcode string
/// </summary>
public static Color ParseHexcode(string hexstring)
{
if (hexstring.StartsWith("#"))
{
hexstring = hexstring.Substring(1);
}
if (hexstring.StartsWith("0x"))
{
hexstring = hexstring.Substring(2);
}
if (hexstring.Length == 6)
{
hexstring += "FF";
}
if (hexstring.Length != 8)
{
throw new ArgumentException(string.Format("{0} is not a valid color string.", hexstring));
}
byte r = byte.Parse(hexstring.Substring(0, 2), NumberStyles.HexNumber);
byte g = byte.Parse(hexstring.Substring(2, 2), NumberStyles.HexNumber);
byte b = byte.Parse(hexstring.Substring(4, 2), NumberStyles.HexNumber);
byte a = byte.Parse(hexstring.Substring(6, 2), NumberStyles.HexNumber);
const float maxRgbValue = 255;
Color c = new Color(r / maxRgbValue, g / maxRgbValue, b / maxRgbValue, a / maxRgbValue);
return c;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for .Net Comparer's
/// </summary>
public static class ComparerExtensions
{
/// <summary>
/// Gets a comparer that sorts elements in the opposite order of the original comparer.
/// </summary>
/// <typeparam name="TElement">The type of element the comparer compares.</typeparam>
/// <param name="originalComparer">The comparer whose order should be reversed.</param>
/// <returns>A comparer that sorts elements in the opposite order of <paramref name="originalComparer"/>.</returns>
public static IComparer<TElement> GetReversed<TElement>(this IComparer<TElement> originalComparer)
{
return new ReverseComparer<TElement>(originalComparer);
}
private class ReverseComparer<TElement> : IComparer<TElement>
{
private readonly IComparer<TElement> originalComparer;
public ReverseComparer(IComparer<TElement> originalComparer)
{
Debug.Assert(originalComparer != null, "originalComparer cannot be null.");
this.originalComparer = originalComparer;
}
public int Compare(TElement left, TElement right)
{
return originalComparer.Compare(right, left);
}
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extensions methods for the Unity Component class.
/// This also includes some component-related extensions for the GameObject class.
/// </summary>
public static class ComponentExtensions
{
/// <summary>
/// Ensure that a component of type <typeparamref name="T"/> exists on the game object.
/// If it doesn't exist, creates it.
/// </summary>
/// <typeparam name="T">Type of the component.</typeparam>
/// <param name="component">A component on the game object for which a component of type <typeparamref name="T"/> should exist.</param>
/// <returns>The component that was retrieved or created.</returns>
public static T EnsureComponent<T>(this Component component) where T : Component
{
return EnsureComponent<T>(component.gameObject);
}
/// <summary>
/// Find the first component of type <typeparamref name="T"/> in the ancestors of the game object of the specified component.
/// </summary>
/// <typeparam name="T">Type of component to find.</typeparam>
/// <param name="component">Component for which its game object's ancestors must be considered.</param>
/// <param name="includeSelf">Indicates whether the specified game object should be included.</param>
/// <returns>The component of type <typeparamref name="T"/>. Null if it none was found.</returns>
public static T FindAncestorComponent<T>(this Component component, bool includeSelf = true) where T : Component
{
return component.transform.FindAncestorComponent<T>(includeSelf);
}
/// <summary>
/// Ensure that a component of type <typeparamref name="T"/> exists on the game object.
/// If it doesn't exist, creates it.
/// </summary>
/// <typeparam name="T">Type of the component.</typeparam>
/// <param name="gameObject">Game object on which component should be.</param>
/// <returns>The component that was retrieved or created.</returns>
/// <remarks>
/// This extension has to remain in this class as it is required by the <see cref="EnsureComponent{T}(Component)"/> method
/// </remarks>
public static T EnsureComponent<T>(this GameObject gameObject) where T : Component
{
T foundComponent = gameObject.GetComponent<T>();
return foundComponent == null ? gameObject.AddComponent<T>() : foundComponent;
}
/// <summary>
/// Ensure that a component of type exists on the game object.
/// If it doesn't exist, creates it.
/// </summary>
/// <param name="component">A component on the game object for which a component of type should exist.</param>
/// <returns>The component that was retrieved or created.</returns>
public static Component EnsureComponent(this GameObject gameObject, Type component)
{
var foundComponent = gameObject.GetComponent(component);
return foundComponent == null ? gameObject.AddComponent(component) : foundComponent;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// <see cref="System.DateTime"/> Extensions.
/// </summary>
public static class DateTimeExtensions
{
/// <summary>
/// Gets string literal for relative time from now since the DateTime provided. String output is in most appropriate "x time units ago"
/// Example: If DateTime provided is 30 seconds before now, then result will be "30 seconds ago"
/// </summary>
/// <param name="time">DateTime in UTC to compare against DateTime.UtcNow</param>
/// <returns>Encoded string.</returns>
public static string GetRelativeTime(this DateTime time)
{
var delta = new TimeSpan(DateTime.UtcNow.Ticks - time.Ticks);
if (Math.Abs(delta.TotalDays) > 1.0)
{
return (int)Math.Abs(delta.TotalDays) + " days ago";
}
else if (Math.Abs(delta.TotalHours) > 1.0)
{
return (int)Math.Abs(delta.TotalHours) + " hours ago";
}
else if (Math.Abs(delta.TotalMinutes) > 1.0)
{
return (int)Math.Abs(delta.TotalMinutes) + " minutes ago";
}
else
{
return (int)Math.Abs(delta.TotalSeconds) + " seconds ago";
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for the .Net Double struct
/// </summary>
public static class DoubleExtensions
{
/// <summary>
/// Checks if two numbers are approximately equal. Similar to <see href="https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html">Mathf.Approximately(float, float)</see>, but the tolerance
/// can be specified.
/// </summary>
/// <param name="number">One of the numbers to compare.</param>
/// <param name="other">The other number to compare.</param>
/// <param name="tolerance">The amount of tolerance to allow while still considering the numbers approximately equal.</param>
/// <returns>True if the difference between the numbers is less than or equal to the tolerance, false otherwise.</returns>
public static bool Approximately(this double number, double other, double tolerance)
{
return Math.Abs(number - other) <= tolerance;
}
}
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
[assembly: System.Reflection.AssemblyVersion("2.5.0.0")]
[assembly: System.Reflection.AssemblyFileVersion("2.5.0.0")]
[assembly: System.Reflection.AssemblyProduct("Microsoft® Mixed Reality Toolkit aipmragent_work")]
[assembly: System.Reflection.AssemblyCopyright("Copyright © Microsoft Corporation")]
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEditor;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Editor
{
public static class EditorLayerExtensions
{
private static SerializedProperty tagManagerLayers = null;
/// <summary>
/// The current layers defined in the Tag Manager.
/// </summary>
public static UnityEditor.SerializedProperty TagManagerLayers
{
get
{
if (tagManagerLayers == null)
{
InitializeTagManager();
}
return tagManagerLayers;
}
}
private static void InitializeTagManager()
{
Object[] tagAssets = UnityEditor.AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset");
if ((tagAssets == null) || (tagAssets.Length == 0))
{
Debug.LogError("Failed to load TagManager!");
return;
}
var tagsManager = new UnityEditor.SerializedObject(tagAssets);
tagManagerLayers = tagsManager.FindProperty("layers");
Debug.Assert(tagManagerLayers != null);
}
/// <summary>
/// Attempts to set the layer in Project Settings Tag Manager.
/// </summary>
/// <param name="layerId">The layer Id to attempt to set the layer on.</param>
/// <param name="layerName">The layer name to attempt to set the layer on.</param>
/// <returns>
/// True if the specified layerId was newly configured, false otherwise.
/// </returns>
public static bool SetupLayer(int layerId, string layerName)
{
SerializedProperty layer = TagManagerLayers.GetArrayElementAtIndex(layerId);
if (!string.IsNullOrEmpty(layer.stringValue))
{
// layer already set.
return false;
}
layer.stringValue = layerName;
layer.serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
return true;
}
/// <summary>
/// Attempts to remove the layer from the Project Settings Tag Manager.
/// </summary>
public static void RemoveLayer(string layerName)
{
for (int i = 0; i < TagManagerLayers.arraySize; i++)
{
var layer = TagManagerLayers.GetArrayElementAtIndex(i);
if (layer.stringValue == layerName)
{
layer.stringValue = string.Empty;
layer.serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
break;
}
}
}
}
}
\ No newline at end of file
{
"name": "Microsoft.MixedReality.Toolkit.Editor.ClassExtensions",
"references": [],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Editor
{
/// <summary>
/// Extensions for <see href="https://docs.unity3d.com/ScriptReference/ScriptableObject.html">ScriptableObject</see>s
/// </summary>
public static class ScriptableObjectExtensions
{
/// <summary>
/// Creates, saves, and then opens a new asset for the target <see href="https://docs.unity3d.com/ScriptReference/ScriptableObject.html">ScriptableObject</see>.
/// </summary>
/// <param name="scriptableObject"><see href="https://docs.unity3d.com/ScriptReference/ScriptableObject.html">ScriptableObject</see> you want to create an asset file for.</param>
/// <param name="path">Optional path for the new asset.</param>
/// <param name="fileName">Optional filename for the new asset.</param>
public static ScriptableObject CreateAsset(this ScriptableObject scriptableObject, string path = null, string fileName = null)
{
var name = string.IsNullOrEmpty(fileName) ? $"{scriptableObject.GetType().Name}" : fileName;
if (string.IsNullOrEmpty(path))
{
path = "Assets";
}
if (Path.GetExtension(path) != string.Empty)
{
var subtractedPath = path.Substring(path.LastIndexOf("/", StringComparison.Ordinal));
path = path.Replace(subtractedPath, string.Empty);
}
if (!Directory.Exists(Path.GetFullPath(path)))
{
Directory.CreateDirectory(Path.GetFullPath(path));
}
string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath($"{path}/{name}.asset");
AssetDatabase.CreateAsset(scriptableObject, assetPathAndName);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorGUIUtility.PingObject(scriptableObject);
return scriptableObject;
}
/// <summary>
/// Gets all the scriptable object instances in the project.
/// </summary>
/// <typeparam name="T">The Type of <see href="https://docs.unity3d.com/ScriptReference/ScriptableObject.html">ScriptableObject</see> you're wanting to find instances of.</typeparam>
/// <returns>An Array of instances for the type.</returns>
public static T[] GetAllInstances<T>() where T : ScriptableObject
{
// FindAssets uses tags check documentation for more info
string[] guids = AssetDatabase.FindAssets($"t:{typeof(T).Name}");
var instances = new T[guids.Length];
for (int i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
instances[i] = AssetDatabase.LoadAssetAtPath<T>(path);
}
return instances;
}
/// <summary>
/// Gets all the scriptable object instances in the project.
/// </summary>
/// <param name="assetType">The Type of <see href="https://docs.unity3d.com/ScriptReference/ScriptableObject.html">ScriptableObject</see> you're wanting to find instances of.</param>
/// <returns>An Array of instances for the type.</returns>
public static ScriptableObject[] GetAllInstances(Type assetType)
{
// FindAssets uses tags check documentation for more info
string[] guids = AssetDatabase.FindAssets($"t:{assetType.Name}");
var instances = new ScriptableObject[guids.Length];
for (int i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
instances[i] = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
}
return instances;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for the .Net IEnumerable class
/// </summary>
public static class EnumerableExtensions
{
/// <summary>
/// Returns the max element based on the provided comparer or the default value when the list is empty
/// </summary>
/// <returns>Max or default value of T</returns>
public static T MaxOrDefault<T>(this IEnumerable<T> items, IComparer<T> comparer = null)
{
if (items == null) { throw new ArgumentNullException("items"); }
comparer = comparer ?? Comparer<T>.Default;
using (var enumerator = items.GetEnumerator())
{
if (!enumerator.MoveNext())
{
return default(T);
}
var max = enumerator.Current;
while (enumerator.MoveNext())
{
if (comparer.Compare(max, enumerator.Current) < 0)
{
max = enumerator.Current;
}
}
return max;
}
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Microsoft.MixedReality.Toolkit.Physics;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's EventSystem
/// </summary>
public static class EventSystemExtensions
{
private static readonly List<RaycastResult> RaycastResults = new List<RaycastResult>();
private static readonly RaycastResultComparer RaycastResultComparer = new RaycastResultComparer();
private static readonly ProfilerMarker RaycastPerfMarker = new ProfilerMarker("[MRTK] EventSystemExtensions.Raycast");
/// <summary>
/// Executes a raycast all and returns the closest element.
/// Fixes the current issue with Unity's raycast sorting which does not consider separate canvases.
/// </summary>
/// <remarks>
/// Takes an optional RaycastResultComparer, which will be used to select the highest priority
/// raycast result.
/// </remarks>
/// <returns>RaycastResult if hit, or an empty RaycastResult if nothing was hit</returns>
public static RaycastResult Raycast(this EventSystem eventSystem, PointerEventData pointerEventData, LayerMask[] layerMasks, RaycastResultComparer raycastResultComparer = null)
{
using (RaycastPerfMarker.Auto())
{
eventSystem.RaycastAll(pointerEventData, RaycastResults);
return PrioritizeRaycastResult(layerMasks, raycastResultComparer);
}
}
private static readonly ProfilerMarker PrioritizeRaycastResultPerfMarker = new ProfilerMarker("[MRTK] EventSystemExtensions.PrioritizeRaycastResult");
/// <summary>
/// Sorts the available Raycasts in to a priority order for query.
/// </summary>
/// <param name="priority">The layer mask priority.</param>
/// <returns><see cref="RaycastResult"/></returns>
private static RaycastResult PrioritizeRaycastResult(LayerMask[] priority, RaycastResultComparer raycastResultComparer)
{
using (PrioritizeRaycastResultPerfMarker.Auto())
{
// If not specified, default to the in-box RaycastResultComparer.
if (raycastResultComparer == null)
{
raycastResultComparer = RaycastResultComparer;
}
ComparableRaycastResult maxResult = default(ComparableRaycastResult);
for (var i = 0; i < RaycastResults.Count; i++)
{
if (RaycastResults[i].gameObject == null) { continue; }
var layerMaskIndex = RaycastResults[i].gameObject.layer.FindLayerListIndex(priority);
if (layerMaskIndex == -1) { continue; }
var result = new ComparableRaycastResult(RaycastResults[i], layerMaskIndex);
if (maxResult.RaycastResult.module == null || raycastResultComparer.Compare(maxResult, result) < 0)
{
maxResult = result;
}
}
return maxResult.RaycastResult;
}
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for the .Net Float struct
/// </summary>
public static class FloatExtensions
{
/// <summary>
/// Checks if two numbers are approximately equal. Similar to <see href="https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html">Mathf.Approximately(float, float)</see>, but the tolerance
/// can be specified.
/// </summary>
/// <param name="number">One of the numbers to compare.</param>
/// <param name="other">The other number to compare.</param>
/// <param name="tolerance">The amount of tolerance to allow while still considering the numbers approximately equal.</param>
/// <returns>True if the difference between the numbers is less than or equal to the tolerance, false otherwise.</returns>
public static bool Approximately(this float number, float other, float tolerance)
{
return Mathf.Abs(number - other) <= tolerance;
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's GameObject class
/// </summary>
public static class GameObjectExtensions
{
/// <summary>
/// Export mesh data of current GameObject, and children if enabled, to file provided in OBJ format
/// </summary>
public static async Task ExportOBJAsync(this GameObject root, string filePath, bool includeChildren = true)
{
await OBJWriterUtility.ExportOBJAsync(root, filePath, includeChildren);
}
/// <summary>
/// Set all GameObject children active or inactive based on argument
/// </summary>
/// <param name="root">GameObject parent to traverse from</param>
/// <param name="isActive">Indicates whether children GameObjects should be active or not</param>
/// <remarks>
/// Does not call SetActive on the top level GameObject, only its children
/// </remarks>
public static void SetChildrenActive(this GameObject root, bool isActive)
{
for (int i = 0; i < root.transform.childCount; i++)
{
root.transform.GetChild(i).gameObject.SetActive(isActive);
}
}
/// <summary>
/// Set the layer to the given object and the full hierarchy below it.
/// </summary>
/// <param name="root">Start point of the traverse</param>
/// <param name="layer">The layer to apply</param>
public static void SetLayerRecursively(this GameObject root, int layer)
{
if (root == null)
{
throw new ArgumentNullException(nameof(root), "Root transform can't be null.");
}
foreach (var child in root.transform.EnumerateHierarchy())
{
child.gameObject.layer = layer;
}
}
/// <summary>
/// Set the layer to the given object and the full hierarchy below it and cache the previous layers in the out parameter.
/// </summary>
/// <param name="root">Start point of the traverse</param>
/// <param name="layer">The layer to apply</param>
/// <param name="cache">The previously set layer for each object</param>
public static void SetLayerRecursively(this GameObject root, int layer, out Dictionary<GameObject, int> cache)
{
if (root == null) { throw new ArgumentNullException(nameof(root)); }
cache = new Dictionary<GameObject, int>();
foreach (var child in root.transform.EnumerateHierarchy())
{
cache[child.gameObject] = child.gameObject.layer;
child.gameObject.layer = layer;
}
}
/// <summary>
/// Reapplies previously cached hierarchy layers
/// </summary>
/// <param name="root">Start point of the traverse</param>
/// <param name="cache">The previously set layer for each object</param>
public static void ApplyLayerCacheRecursively(this GameObject root, Dictionary<GameObject, int> cache)
{
if (root == null) { throw new ArgumentNullException(nameof(root)); }
if (cache == null) { throw new ArgumentNullException(nameof(cache)); }
foreach (var child in root.transform.EnumerateHierarchy())
{
int layer;
if (!cache.TryGetValue(child.gameObject, out layer)) { continue; }
child.gameObject.layer = layer;
cache.Remove(child.gameObject);
}
}
/// <summary>
/// Determines whether or not a game object's layer is included in the specified layer mask.
/// </summary>
/// <param name="gameObject">The game object whose layer to test.</param>
/// <param name="layerMask">The layer mask to test against.</param>
/// <returns>True if <paramref name="gameObject"/>'s layer is included in <paramref name="layerMask"/>, false otherwise.</returns>
public static bool IsInLayerMask(this GameObject gameObject, LayerMask layerMask)
{
LayerMask gameObjectMask = 1 << gameObject.layer;
return (gameObjectMask & layerMask) == gameObjectMask;
}
/// <summary>
/// Apply the specified delegate to all objects in the hierarchy under a specified game object.
/// </summary>
/// <param name="root">Root game object of the hierarchy.</param>
/// <param name="action">Delegate to apply.</param>
public static void ApplyToHierarchy(this GameObject root, Action<GameObject> action)
{
action(root);
Transform[] items = root.GetComponentsInChildren<Transform>();
for (var i = 0; i < items.Length; i++)
{
action(items[i].gameObject);
}
}
/// <summary>
/// Find the first component of type <typeparamref name="T"/> in the ancestors of the specified game object.
/// </summary>
/// <typeparam name="T">Type of component to find.</typeparam>
/// <param name="gameObject">Game object for which ancestors must be considered.</param>
/// <param name="includeSelf">Indicates whether the specified game object should be included.</param>
/// <returns>The component of type <typeparamref name="T"/>. Null if it none was found.</returns>
public static T FindAncestorComponent<T>(this GameObject gameObject, bool includeSelf = true) where T : Component
{
return gameObject.transform.FindAncestorComponent<T>(includeSelf);
}
/// <summary>
/// Perform an action on every component of type T that is on this GameObject
/// </summary>
/// <typeparam name="T">Component Type</typeparam>
/// <param name="gameObject">this gameObject</param>
/// <param name="action">Action to perform.</param>
public static void ForEachComponent<T>(this GameObject gameObject, Action<T> action)
{
foreach (T i in gameObject.GetComponents<T>())
{
action(i);
}
}
/// <summary>
/// Destroys GameObject appropriately depending if in edit or playmode
/// </summary>
/// <param name="gameObject">GameObject to destroy</param>
/// <param name="t">time in seconds at which to destroy GameObject if applicable</param>
public static void DestroyGameObject(GameObject gameObject, float t = 0.0f)
{
UnityObjectExtensions.DestroyObject(gameObject, t);
}
/// <summary>
/// Checks if any MonoBehaviour on the given GameObject is using the RequireComponentAttribute requiring type T
/// </summary>
/// <remarks>Only functions when called within a UNITY_EDITOR context. Outside of UNITY_EDITOR, always returns false</remarks>
/// <typeparam name="T">The potentially required component</typeparam>
/// <param name="gameObject">the GameObject requiring the component</param>
/// <param name="requiringTypes">A list of types that do require the component in question</param>
/// <returns>true if <typeparamref name="T"/> appears in any RequireComponentAttribute, otherwise false </returns>
public static bool IsComponentRequired<T>(this GameObject gameObject, out List<Type> requiringTypes) where T : Component
{
var genericType = typeof(T);
requiringTypes = null;
#if UNITY_EDITOR
foreach (var monoBehaviour in gameObject.GetComponents<MonoBehaviour>())
{
if (monoBehaviour == null)
{
continue;
}
var monoBehaviourType = monoBehaviour.GetType();
var attributes = Attribute.GetCustomAttributes(monoBehaviourType);
foreach (var attribute in attributes)
{
var requireComponentAttribute = attribute as RequireComponent;
if (requireComponentAttribute != null)
{
if (requireComponentAttribute.m_Type0 == genericType ||
requireComponentAttribute.m_Type1 == genericType ||
requireComponentAttribute.m_Type2 == genericType)
{
if (requiringTypes == null)
{
requiringTypes = new List<Type>();
}
requiringTypes.Add(monoBehaviourType);
}
}
}
}
#endif // UNITY_EDITOR
return requiringTypes != null;
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// <see cref=" Microsoft.MixedReality.Toolkit.Utilities.Handedness"/> type method extensions.
/// </summary>
public static class HandednessExtensions
{
/// <summary>
/// Gets the opposite "hand" flag for the current Handedness value.
/// </summary>
/// <remarks>
/// If current = Left, returns Right.
/// If current = Right, returns Left.
/// Otherwise, returns None
/// </remarks>
public static Handedness GetOppositeHandedness(this Handedness current)
{
if (current == Handedness.Left)
{
return Handedness.Right;
}
else if (current == Handedness.Right)
{
return Handedness.Left;
}
else
{
return Handedness.None;
}
}
/// <summary>
/// Returns true if the current Handedness is the Right (i.e == Handedness.Right), false otherwise
/// </summary>
public static bool IsRight(this Handedness current)
{
return current == Handedness.Right;
}
/// <summary>
/// Returns true if the current Handedness is the Right (i.e == Handedness.Right), false otherwise
/// </summary>
public static bool IsLeft(this Handedness current)
{
return current == Handedness.Left;
}
/// <summary>
/// Returns true if the current Handedness is the Right (i.e == Handedness.Right), false otherwise
/// </summary>
public static bool IsNone(this Handedness current)
{
return current == Handedness.None;
}
/// <summary>
/// Returns true if the current Handedness flags are a match with the comparison Handedness flags, false otherwise
/// </summary>
public static bool IsMatch(this Handedness current, Handedness compare)
{
return (current & compare) != 0;
}
}
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Extension methods for Unity's LayerMask struct
/// </summary>
public static class LayerExtensions
{
/// <summary>
/// The Invalid Layer Id.
/// </summary>
public const int InvalidLayerId = -1;
/// <summary>
/// Look through the layerMaskList and find the index in that list for which the supplied layer is part of
/// </summary>
/// <param name="layer">Layer to search for</param>
/// <param name="layerMasks">List of LayerMasks to search</param>
/// <returns>LayerMaskList index, or -1 for not found</returns>
public static int FindLayerListIndex(this int layer, LayerMask[] layerMasks)
{
for (int j = 0; j < layerMasks.Length; j++)
{
if (layer.IsInLayerMask(layerMasks[j]))
{
return j;
}
}
return -1;
}
/// <summary>
/// Checks whether a layer is in a layer mask
/// </summary>
/// <returns>True if the layer mask contains the layer</returns>
public static bool IsInLayerMask(this int layer, int layerMask)
{
return ((1 << layer) & layerMask) != 0;
}
/// <summary>
/// Combines provided layers into a single layer mask.
/// </summary>
/// <returns>The combined layer mask</returns>
public static int Combine(this LayerMask[] layerMaskList)
{
int combinedLayerMask = 0;
for (int i = 0; i < layerMaskList.Length; i++)
{
combinedLayerMask = combinedLayerMask | layerMaskList[i].value;
}
return combinedLayerMask;
}
/// <summary>
/// Transform layer id to <see href="https://docs.unity3d.com/ScriptReference/LayerMask.html">LayerMask</see>
/// </summary>
public static LayerMask ToMask(int layerId)
{
return 1 << layerId;
}
/// <summary>
/// Gets a valid layer id using the layer name.
/// </summary>
/// <param name="cache">The cached layer id.</param>
/// <param name="layerName">The name of the layer to look for if the cache is unset.</param>
/// <returns>The layer id.</returns>
public static int GetLayerId(ref int cache, string layerName)
{
if (cache == InvalidLayerId)
{
cache = LayerMask.NameToLayer(layerName);
}
return cache;
}
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment