VR4Medical/ICI/Library/PackageCache/com.unity.xr.interaction.toolkit@42ef3600567b/Editor/Utilities/PositionHandleUtility.cs
2025-07-29 13:45:50 +03:00

187 lines
7.4 KiB
C#

using System;
using UnityEngine;
namespace UnityEditor.XR.Interaction.Toolkit.Utilities.Internal
{
/// <summary>
/// Utility methods for Handles.
/// </summary>
/// <seealso cref="Handles"/>
static class PositionHandleUtility
{
// Most of the private methods (with the exception of the CapFunction) were directly copied from Handles source code.
// When axis is looking away from camera, fade it out along 25 -> 15 degrees range
// ReSharper disable InconsistentNaming -- Constant values from Handles
static readonly float k_CameraViewLerpStart1 = Mathf.Cos(Mathf.Deg2Rad * 25f);
static readonly float k_CameraViewLerpEnd1 = Mathf.Cos(Mathf.Deg2Rad * 15f);
// When axis is looking towards the camera, fade it out along 170 -> 175 degrees range
static readonly float k_CameraViewLerpStart2 = Mathf.Cos(Mathf.Deg2Rad * 170f);
static readonly float k_CameraViewLerpEnd2 = Mathf.Cos(Mathf.Deg2Rad * 175f);
// ReSharper restore InconsistentNaming
// Hide & disable axis if they have faded out more than 60%
const float k_CameraViewThreshold = 0.6f;
static readonly float[] s_CameraViewLerp = new float[3];
static readonly int[] s_AxisDrawOrder = { 0, 1, 2 };
/// <summary>
/// Draws a position handle without the arrow caps or plane squares.
/// </summary>
/// <param name="position">Center of the handle in 3D space.</param>
/// <param name="rotation">Orientation of the handle in 3D space.</param>
/// <param name="axisSize">Relative size of the handle to a regular position handle. Scales each axis of the constant screen-size handle.</param>
/// <remarks>
/// The handle looks like the built-in move tool in Unity but with only the axis lines and is not interactable.
/// </remarks>
public static void DrawLineOnlyPositionHandle(Vector3 position, Quaternion rotation, Vector3 axisSize)
{
var originalHandlesColor = Handles.color;
// Calculate the camera view vector in Handle draw space
// this handle the case where the matrix is skewed
var matrix = Handles.matrix;
var handlePosition = matrix.MultiplyPoint3x4(position);
var drawToWorldMatrix = matrix * Matrix4x4.TRS(position, rotation, Vector3.one);
var invDrawToWorldMatrix = drawToWorldMatrix.inverse;
var viewVectorDrawSpace = GetCameraViewFrom(handlePosition, invDrawToWorldMatrix);
var size = HandleUtility.GetHandleSize(position);
// Calculate per axis camera lerp
for (var i = 0; i < 3; ++i)
{
s_CameraViewLerp[i] = GetCameraViewLerpForWorldAxis(viewVectorDrawSpace, GetAxisVector(i));
}
// Calculate back-to-front order to draw the axes
CalcDrawOrder(viewVectorDrawSpace, s_AxisDrawOrder);
for (var ii = 0; ii < 3; ++ii)
{
var i = s_AxisDrawOrder[ii];
var cameraLerp = s_CameraViewLerp[i];
if (cameraLerp <= k_CameraViewThreshold)
{
Handles.color = GetColorByAxis(i);
Handles.color = GetFadedAxisColor(Handles.color, cameraLerp);
Handles.color = ToActiveColorSpace(Handles.color);
var axisVector = GetAxisVector(i);
var dir = rotation * axisVector;
position = Handles.Slider(position, dir, size * axisSize[i], LineOnlyCapFunction, EditorSnapSettings.move[i]);
}
}
Handles.color = originalHandlesColor;
}
static void LineOnlyCapFunction(int controlId, Vector3 position, Quaternion rotation, float size, EventType eventType)
{
switch (eventType)
{
case EventType.Layout:
case EventType.MouseMove:
{
var direction = rotation * Vector3.forward;
var linePos = position + direction * size;
HandleUtility.AddControl(controlId, HandleUtility.DistanceToLine(position, linePos));
break;
}
case EventType.Repaint:
{
var direction = rotation * Vector3.forward;
var linePos = position + direction * size;
#if UNITY_2020_3_OR_NEWER
Handles.DrawLine(position, linePos, Handles.lineThickness);
#else
Handles.DrawLine(position, linePos);
#endif
break;
}
}
}
static float GetCameraViewLerpForWorldAxis(Vector3 viewVector, Vector3 axis)
{
var dot = Vector3.Dot(viewVector, axis);
var l1 = Mathf.InverseLerp(k_CameraViewLerpStart1, k_CameraViewLerpEnd1, dot);
var l2 = Mathf.InverseLerp(k_CameraViewLerpStart2, k_CameraViewLerpEnd2, dot);
return Mathf.Max(l1, l2);
}
static Vector3 GetAxisVector(int axis)
{
switch (axis)
{
case 0:
return Vector3.right;
case 1:
return Vector3.up;
case 2:
return Vector3.forward;
default:
throw new ArgumentOutOfRangeException(nameof(axis));
}
}
static Color GetColorByAxis(int axis)
{
switch (axis)
{
case 0:
return Handles.xAxisColor;
case 1:
return Handles.yAxisColor;
case 2:
return Handles.zAxisColor;
default:
throw new ArgumentOutOfRangeException(nameof(axis));
}
}
static Color GetFadedAxisColor(Color col, float fade)
{
return Color.Lerp(col, Color.clear, fade);
}
static Color ToActiveColorSpace(Color color)
{
return QualitySettings.activeColorSpace == ColorSpace.Linear ? color.linear : color;
}
static Vector3 GetCameraViewFrom(Vector3 position, Matrix4x4 matrix)
{
var camera = Camera.current;
return camera.orthographic
? matrix.MultiplyVector(camera.transform.forward).normalized
: matrix.MultiplyVector(position - camera.transform.position).normalized;
}
static void Swap(ref Vector3 v, int[] indices, int a, int b)
{
(v[a], v[b]) = (v[b], v[a]);
(indices[a], indices[b]) = (indices[b], indices[a]);
}
// Given view direction in handle space, calculate
// back-to-front order in which handle axes should be drawn.
// The array should be [3] size, and will contain axis indices
// from (0,1,2) set.
static void CalcDrawOrder(Vector3 viewDir, int[] ordering)
{
ordering[0] = 0;
ordering[1] = 1;
ordering[2] = 2;
// Essentially an unrolled bubble sort for 3 elements
if (viewDir.y > viewDir.x) Swap(ref viewDir, ordering, 1, 0);
if (viewDir.z > viewDir.y) Swap(ref viewDir, ordering, 2, 1);
if (viewDir.y > viewDir.x) Swap(ref viewDir, ordering, 1, 0);
}
}
}