#if BURST_PRESENT using Unity.Burst; #else using System.Runtime.CompilerServices; #endif using Unity.Mathematics; namespace UnityEngine.XR.Interaction.Toolkit.Utilities { /// /// Provides utility functions related to vector and quaternion calculations, /// optimized for use with the Burst compiler when available. /// #if BURST_PRESENT [BurstCompile] #endif public static class BurstMathUtility { /// /// Calculates the orthogonal up vector for a given forward vector and a reference up vector. /// /// The forward vector. /// The reference up vector. /// The calculated orthogonal up vector. /// /// Convenience method signature to cast output from to . /// #if BURST_PRESENT [BurstCompile] #endif public static void OrthogonalUpVector(in Vector3 forward, in Vector3 referenceUp, out Vector3 orthogonalUp) { OrthogonalUpVector(forward, referenceUp, out float3 float3OrthogonalUp); orthogonalUp = float3OrthogonalUp; } /// /// Calculates the orthogonal up vector for a given forward vector and a reference up vector. /// /// The forward vector. /// The reference up vector. /// The calculated orthogonal up vector. #if BURST_PRESENT [BurstCompile] #endif public static void OrthogonalUpVector(in float3 forward, in float3 referenceUp, out float3 orthogonalUp) { var right = -math.cross(forward, referenceUp); orthogonalUp = math.cross(forward, right); } /// /// Calculates a look rotation quaternion given a forward vector and a reference up vector. /// /// The forward vector. /// The reference up vector. /// The calculated look rotation quaternion. /// /// Convenience method signature to cast output from to . /// #if BURST_PRESENT [BurstCompile] #endif public static void OrthogonalLookRotation(in Vector3 forward, in Vector3 referenceUp, out Quaternion lookRotation) { OrthogonalLookRotation(forward, referenceUp, out quaternion lookRot); lookRotation = lookRot; } /// /// Calculates a look rotation quaternion given a forward vector and a reference up vector. /// /// The forward vector. /// The reference up vector. /// The calculated look rotation quaternion. #if BURST_PRESENT [BurstCompile] #endif public static void OrthogonalLookRotation(in float3 forward, in float3 referenceUp, out quaternion lookRotation) { OrthogonalUpVector(forward, referenceUp, out float3 orthogonalUp); lookRotation = quaternion.LookRotation(forward, orthogonalUp); } /// /// Projects a vector onto a plane defined by a normal orthogonal to the plane. /// /// The vector to be projected. /// The normal vector orthogonal to the plane. /// The projected vector on the plane. #if BURST_PRESENT [BurstCompile] #else [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif public static void ProjectOnPlane(in float3 vector, in float3 planeNormal, out float3 projectedVector) { var sqrMag = math.dot(planeNormal, planeNormal); if (sqrMag < math.EPSILON) { projectedVector = vector; return; } var dot = math.dot(vector, planeNormal); projectedVector = new float3(vector.x - planeNormal.x * dot / sqrMag, vector.y - planeNormal.y * dot / sqrMag, vector.z - planeNormal.z * dot / sqrMag); } /// /// Projects a vector onto a plane defined by a normal orthogonal to the plane. /// /// The vector to be projected. /// The normal vector orthogonal to the plane. /// The projected vector on the plane. /// /// Convenience method signature to cast output from to . /// #if BURST_PRESENT [BurstCompile] #endif public static void ProjectOnPlane(in Vector3 vector, in Vector3 planeNormal, out Vector3 projectedVector) { ProjectOnPlane(vector, planeNormal, out float3 float3ProjectedVector); projectedVector = float3ProjectedVector; } /// /// Computes the look rotation with the forward vector projected on a plane defined by a normal orthogonal to the plane. /// /// The forward vector to be projected onto the plane. /// The normal vector orthogonal to the plane. /// The resulting look rotation with the projected forward vector and plane normal as up direction. #if BURST_PRESENT [BurstCompile] #endif public static void LookRotationWithForwardProjectedOnPlane(in float3 forward, in float3 planeNormal, out quaternion lookRotation) { ProjectOnPlane(forward, planeNormal, out float3 projectedForward); lookRotation = quaternion.LookRotation(projectedForward, planeNormal); } /// /// Computes the look rotation with the forward vector projected on a plane defined by a normal orthogonal to the plane. /// /// The forward vector to be projected onto the plane. /// The normal vector orthogonal to the plane. /// The resulting look rotation with the projected forward vector and plane normal as up direction. /// /// Convenience method signature to cast output from to . /// #if BURST_PRESENT [BurstCompile] #endif public static void LookRotationWithForwardProjectedOnPlane(in Vector3 forward, in Vector3 planeNormal, out Quaternion lookRotation) { LookRotationWithForwardProjectedOnPlane(forward, planeNormal, out quaternion lookRot); lookRotation = lookRot; } /// /// Returns the angle in degrees between two rotations and . /// Equivalent to . /// /// The first rotation in the quaternion set. /// The second rotation in the quaternion set. /// The angle in degrees between a and b. #if BURST_PRESENT [BurstCompile] #endif public static void Angle(in quaternion a, in quaternion b, out float angle) { // See Quaternion.cs in Unity source code // 0.999999f = 1f - Quaternion.kEpsilon // 57.29578f = Mathf.Rad2Deg var dot = math.min(math.abs(math.dot(a, b)), 1f); angle = (dot > 0.999999f) ? 0f : (math.acos(dot) * 2f * 57.29578f); } /// /// Returns the angle in degrees between two vectors and . /// Equivalent to . /// /// The first vector. /// The second vector. /// The angle in degrees between a and b. #if BURST_PRESENT [BurstCompile] #endif public static void Angle(in Vector3 a, in Vector3 b, out float angle) { // See Vector3.cs in Unity source code // 1e-15f = Vector3.kEpsilonNormalSqrt // 57.29578f = Mathf.Rad2Deg var denominator = math.sqrt(a.sqrMagnitude * b.sqrMagnitude); if (denominator < 1e-15f) { angle = 0f; return; } var dot = math.clamp(math.dot(a, b) / denominator, -1f, 1f); angle = math.acos(dot) * 57.29578f; } /// /// Compares two float3s for equality with a specified level of tolerance. /// /// The first float3 to compare. /// The second float3 to compare. /// The level of tolerance for the equality check. Defaults to 0.0001f. /// Returns if the difference between the corresponding components of the float3s is less than the specified tolerance; otherwise, . #if BURST_PRESENT [BurstCompile] #endif public static bool FastVectorEquals(in float3 a, in float3 b, float tolerance = 0.0001f) { return math.abs(a.x - b.x) < tolerance && math.abs(a.y - b.y) < tolerance && math.abs(a.z - b.z) < tolerance; } /// /// Compares two Vector3s for equality with a specified level of tolerance. /// /// The first Vector3 to compare. /// The second Vector3 to compare. /// The level of tolerance for the equality check. Defaults to 0.0001f. /// Returns if the difference between the corresponding components of the Vector3s is less than the specified tolerance; otherwise, . #if BURST_PRESENT [BurstCompile] #endif public static bool FastVectorEquals(in Vector3 a, in Vector3 b, float tolerance = 0.0001f) { return math.abs(a.x - b.x) < tolerance && math.abs(a.y - b.y) < tolerance && math.abs(a.z - b.z) < tolerance; } /// /// Performs a safe division of two Vector3s. If the difference between any corresponding pair of components in the vectors exceeds a specified tolerance, the division is carried out for that component. /// /// The dividend Vector3. /// The divisor Vector3. /// The resulting Vector3 after division. If the difference between the corresponding components of the dividend and divisor is less than the tolerance, the respective component in the result vector remains zero. /// The tolerance for the component-wise division operation. Defaults to 0.000001f. #if BURST_PRESENT [BurstCompile] #endif public static void FastSafeDivide(in Vector3 a, in Vector3 b, out Vector3 result, float tolerance = 0.000001f) { FastSafeDivide(a, b, out float3 float3Result, tolerance); result = float3Result; } /// /// Performs a safe division of two float3 vectors. If the difference between any corresponding pair of components in the vectors exceeds a specified tolerance, the division is carried out for that component. /// /// The dividend float3 vector. /// The divisor float3 vector. /// The resulting float3 vector after division. If the difference between the corresponding components of the dividend and divisor is less than the tolerance, the respective component in the result vector remains zero. /// The tolerance for the component-wise division operation. Defaults to 0.000001f. #if BURST_PRESENT [BurstCompile] #endif public static void FastSafeDivide(in float3 a, in float3 b, out float3 result, float tolerance = 0.000001f) { result = new float3(); if (math.abs(a.x - b.x) > tolerance) result.x = a.x / b.x; if (math.abs(a.y - b.y) > tolerance) result.y = a.y / b.y; if (math.abs(a.z - b.z) > tolerance) result.z = a.z / b.z; } /// /// Multiplies the corresponding elements of two float3 vectors in a fast, non-matrix multiplication. /// /// The first float3 vector. /// The second float3 vector. /// The resulting float3 vector after element-wise multiplication. #if BURST_PRESENT [BurstCompile] #endif public static void Scale(in float3 a, in float3 b, out float3 result) { result = new float3(a.x * b.x, a.y * b.y, a.z * b.z); } /// /// Multiplies the corresponding elements of two Vector3 in a fast, non-matrix multiplication. /// /// The first Vector3. /// The second Vector3. /// The resulting Vector3 after element-wise multiplication. #if BURST_PRESENT [BurstCompile] #endif public static void Scale(in Vector3 a, in Vector3 b, out Vector3 result) { result = new Vector3(a.x * b.x, a.y * b.y, a.z * b.z); } /// /// Calculates an orthogonal vector to the given vector. /// The method finds the smallest component of the input vector and crosses it with the corresponding basis vector. /// /// The input vector. /// The resulting orthogonal vector. public static Vector3 Orthogonal(Vector3 input) { Orthogonal(input, out float3 resultFloat3); return resultFloat3; } /// /// Calculates an orthogonal vector to the given vector. /// The method finds the smallest component of the input vector and crosses it with the corresponding basis vector. /// /// The input vector. /// The resulting orthogonal vector. #if BURST_PRESENT [BurstCompile] #endif public static void Orthogonal(in float3 input, out float3 result) { // Find the smallest component of v and cross it with the corresponding basis vector if (math.abs(input.x) < math.abs(input.y) && math.abs(input.x) < math.abs(input.z)) result = math.cross(input, new float3(1, 0, 0)); // equivalent to Vector3.right else if (math.abs(input.y) < math.abs(input.z)) result = math.cross(input, new float3(0, 1, 0)); // equivalent to Vector3.up else result = math.cross(input, new float3(0, 0, 1)); // equivalent to Vector3.forward } } }