WuhuIslandTesting/Library/PackageCache/com.unity.splines@1.0.1/Editor/Utilities/SplineHandleUtility.cs

233 lines
8.3 KiB
C#
Raw Permalink Normal View History

2025-01-07 02:06:59 +01:00
using System;
using UnityEngine;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
namespace UnityEditor.Splines
{
struct ColorScope : IDisposable
{
readonly Color m_PrevColor;
public ColorScope(Color color)
{
m_PrevColor = Handles.color;
Handles.color = color;
}
public void Dispose()
{
Handles.color = m_PrevColor;
}
}
struct ZTestScope : IDisposable
{
readonly CompareFunction m_Original;
public ZTestScope(CompareFunction function)
{
m_Original = Handles.zTest;
Handles.zTest = function;
}
public void Dispose()
{
Handles.zTest = m_Original;
}
}
static class SplineHandleUtility
{
const int k_MaxDecimals = 15;
const int k_SegmentsPointCount = 30;
static readonly Vector3[] s_ClosestPointArray = new Vector3[k_SegmentsPointCount];
const float k_KnotPickingDistance = 18f;
static readonly Vector3[] s_LineBuffer = new Vector3[2];
internal static Ray TransformRay(Ray ray, Matrix4x4 matrix)
{
return new Ray(matrix.MultiplyPoint3x4(ray.origin), matrix.MultiplyVector(ray.direction));
}
internal static Vector3 DoIncrementSnap(Vector3 position, Vector3 previousPosition)
{
var delta = position - previousPosition;
var right = Tools.handleRotation * Vector3.right;
var up = Tools.handleRotation * Vector3.up;
var forward = Tools.handleRotation * Vector3.forward;
var snappedDelta =
Snapping.Snap(Vector3.Dot(delta, right), EditorSnapSettings.move[0]) * right +
Snapping.Snap(Vector3.Dot(delta, up), EditorSnapSettings.move[1]) * up +
Snapping.Snap(Vector3.Dot(delta, forward), EditorSnapSettings.move[2]) * forward;
return previousPosition + snappedDelta;
}
static Vector3 SnapToGrid(Vector3 position)
{
//todo Temporary version, waiting for a trunk PR to land to move to the commented version:
//#if UNITY_2022_2_OR_NEWER
// if(EditorSnapSettings.gridSnapActive)
// return Snapping.Snap(position, EditorSnapSettings.gridSize, SnapAxis.All);
//#else
GameObject tmp = new GameObject();
tmp.hideFlags = HideFlags.HideAndDontSave;
var trs = tmp.transform;
trs.position = position;
Handles.SnapToGrid(new []{trs});
var snapped = trs.position;
Object.DestroyImmediate(tmp);
return snapped;
//#endif
}
internal static bool GetPointOnSurfaces(Vector2 mousePosition, out Vector3 point, out Vector3 normal)
{
#if UNITY_2020_1_OR_NEWER
if(HandleUtility.PlaceObject(mousePosition, out point, out normal))
{
if(EditorSnapSettings.gridSnapEnabled)
point = SnapToGrid(point);
return true;
}
#endif
var ray = HandleUtility.GUIPointToWorldRay(mousePosition);
#if !UNITY_2020_1_OR_NEWER
if (Physics.Raycast(ray, out RaycastHit hit))
{
point = hit.point;
normal = hit.normal;
return true;
}
#endif
//Backup if couldn't find a surface
var constraint = new Plane(Vector3.up, Vector3.zero); //This should be in the direction of the current grid
if (constraint.Raycast(ray, out float distance))
{
normal = constraint.normal;
point = ray.origin + ray.direction * distance;
if(EditorSnapSettings.gridSnapEnabled)
point = SnapToGrid(point);
return true;
}
point = normal = Vector3.zero;
return false;
}
internal static void DrawLineWithWidth(Vector3 a, Vector3 b, float width, Texture2D lineAATex = null)
{
s_LineBuffer[0] = a;
s_LineBuffer[1] = b;
Handles.DrawAAPolyLine(lineAATex, width, s_LineBuffer);
}
public static float DistanceToKnot(Vector3 position)
{
return DistanceToCircle(position, k_KnotPickingDistance);
}
public static float DistanceToCircle(Vector3 point, float radius)
{
Vector3 screenPos = HandleUtility.WorldToGUIPointWithDepth(point);
if (screenPos.z < 0)
return float.MaxValue;
return Mathf.Max(0, Vector2.Distance(screenPos, Event.current.mousePosition) - radius);
}
internal static Vector3 RoundBasedOnMinimumDifference(Vector3 position)
{
var minDiff = GetMinDifference(position);
position.x = RoundBasedOnMinimumDifference(position.x, minDiff.x);
position.y = RoundBasedOnMinimumDifference(position.y, minDiff.y);
position.z = RoundBasedOnMinimumDifference(position.z, minDiff.z);
return position;
}
internal static Vector3 GetMinDifference(Vector3 position)
{
return Vector3.one * (HandleUtility.GetHandleSize(position) / 80f);
}
internal static float RoundBasedOnMinimumDifference(float valueToRound, float minDifference)
{
var numberOfDecimals = Mathf.Clamp(-Mathf.FloorToInt(Mathf.Log10(Mathf.Abs(minDifference))), 0, k_MaxDecimals);
return (float)Math.Round(valueToRound, numberOfDecimals, MidpointRounding.AwayFromZero);
}
public static void GetNearestPointOnCurve(CurveData curve, out Vector3 position, out float t)
{
Vector3 closestA = Vector3.zero;
Vector3 closestB = Vector3.zero;
float closestDist = float.MaxValue;
int closestSegmentFirstPoint = -1;
GetCurveSegments(curve, s_ClosestPointArray);
for (int j = 0; j < s_ClosestPointArray.Length - 1; ++j)
{
Vector3 a = s_ClosestPointArray[j];
Vector3 b = s_ClosestPointArray[j + 1];
float dist = HandleUtility.DistanceToLine(a, b);
if (dist < closestDist)
{
closestA = a;
closestB = b;
closestDist = dist;
closestSegmentFirstPoint = j;
}
}
//Calculate position
Vector2 screenPosA = HandleUtility.WorldToGUIPoint(closestA);
Vector2 screenPosB = HandleUtility.WorldToGUIPoint(closestB);
Vector2 relativePoint = Event.current.mousePosition - screenPosA;
Vector2 lineDirection = screenPosB - screenPosA;
float length = lineDirection.magnitude;
float dot = Vector3.Dot(lineDirection, relativePoint);
if (length > .000001f)
dot /= length * length;
dot = Mathf.Clamp01(dot);
position = Vector3.Lerp(closestA, closestB, dot);
//Calculate percent on curve's segment
float percentPerSegment = 1.0f / (k_SegmentsPointCount - 1);
float percentA = closestSegmentFirstPoint * percentPerSegment;
float lengthAB = (closestB - closestA).magnitude;
float lengthAToClosest = (position - closestA).magnitude;
t = percentA + percentPerSegment * (lengthAToClosest / lengthAB);
}
internal static void GetCurveSegments(CurveData curve, Vector3[] results)
{
if (!curve.IsValid())
throw new ArgumentException(nameof(curve));
if (results == null)
throw new ArgumentNullException(nameof(results));
if (results.Length < 2)
throw new ArgumentException("Get curve segments requires a results array of at least two points", nameof(results));
var segmentCount = results.Length - 1;
float segmentPercentage = 1f / segmentCount;
var path = curve.a.spline;
for (int i = 0; i <= segmentCount; ++i)
{
results[i] = path.GetPointOnCurve(curve, i * segmentPercentage);
}
}
}
}