diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs
index 1b35cdc35..a6d07fc2a 100644
--- a/Source/Editor/SceneGraph/Actors/SplineNode.cs
+++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs
@@ -7,6 +7,7 @@ using Real = System.Single;
#endif
using System;
+using System.Collections.Generic;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Modules;
using FlaxEngine;
@@ -287,6 +288,10 @@ namespace FlaxEditor.SceneGraph.Actors
private const Real PointNodeSize = 1.5f;
private const Real TangentNodeSize = 1.0f;
+ private const Real SnapIndicatorSize = 1.7f;
+ private const Real SnapPointIndicatorSize = 2f;
+
+ private static Spline _currentEditSpline;
///
public SplineNode(Actor actor)
@@ -298,6 +303,11 @@ namespace FlaxEditor.SceneGraph.Actors
private unsafe void OnUpdate()
{
+ if (Input.Keyboard.GetKey(KeyboardKeys.Shift))
+ {
+ EditSplineWithSnap();
+ }
+
// Sync spline points with gizmo handles
var actor = (Spline)Actor;
var dstCount = actor.SplinePointsCount;
@@ -328,6 +338,56 @@ namespace FlaxEditor.SceneGraph.Actors
}
}
+ private void EditSplineWithSnap()
+ {
+ if (_currentEditSpline == null || _currentEditSpline != Actor)
+ return;
+
+ var selectedNode = SelectedSplineNode();
+ if (selectedNode == null)
+ return;
+
+ var selectedNodeBounds = new BoundingSphere(selectedNode.Transform.Translation, 1f);
+ var allSplinesInView = GetSplinesOnView();
+ allSplinesInView.Remove(_currentEditSpline);
+
+ if (allSplinesInView.Count == 0 || selectedNode == null)
+ return;
+
+ var snappedOnSplinePoint = false;
+ for (int i = 0; i < allSplinesInView.Count; i++)
+ {
+ for (int x = 0; x < allSplinesInView[i].SplineKeyframes.Length; x++)
+ {
+ var keyframePosition = allSplinesInView[i].GetSplinePoint(x);
+ var pointIndicatorSize = NodeSizeByDistance(keyframePosition, SnapPointIndicatorSize);
+ var keyframeBounds = new BoundingSphere(keyframePosition, pointIndicatorSize);
+ DebugDraw.DrawSphere(keyframeBounds, Color.Red, 0, false);
+
+ if (keyframeBounds.Intersects(selectedNodeBounds))
+ {
+ _currentEditSpline.SetSplinePoint(selectedNode.Index, keyframeBounds.Center);
+ snappedOnSplinePoint = true;
+ break;
+ }
+ }
+ }
+
+ if (!snappedOnSplinePoint)
+ {
+ var nearSplineSnapPoint = GetNearSplineSnapPosition(selectedNode.Transform.Translation, allSplinesInView);
+ var snapIndicatorSize = NodeSizeByDistance(nearSplineSnapPoint, SnapIndicatorSize);
+ var snapBounds = new BoundingSphere(nearSplineSnapPoint, snapIndicatorSize);
+
+ if (snapBounds.Intersects(selectedNodeBounds))
+ {
+ _currentEditSpline.SetSplinePoint(selectedNode.Index, snapBounds.Center);
+ }
+
+ DebugDraw.DrawSphere(snapBounds, Color.Yellow, 0, true);
+ }
+ }
+
///
public override void PostSpawn()
{
@@ -396,6 +456,7 @@ namespace FlaxEditor.SceneGraph.Actors
internal static void OnSplineEdited(Spline spline)
{
+ _currentEditSpline = spline;
var collider = spline.GetChild();
if (collider && collider.Scene && collider.IsActiveInHierarchy && collider.HasStaticFlag(StaticFlags.Navigation) && !Editor.IsPlayMode)
{
@@ -407,6 +468,60 @@ namespace FlaxEditor.SceneGraph.Actors
}
}
+ private static SplinePointNode SelectedSplineNode()
+ {
+ var selection = Editor.Instance.SceneEditing.Selection;
+ if (selection.Count != 1)
+ return null;
+ if (selection[0] is not SplineNode.SplinePointNode)
+ return null;
+
+ return (SplinePointNode)selection[0];
+ }
+
+ private static List GetSplinesOnView()
+ {
+ var splines = Level.GetActors();
+ var splinesOnView = new List();
+
+ var viewTransform = Editor.Instance.Windows.EditWin.Viewport.ViewTransform;
+ var viewFov = Editor.Instance.Windows.EditWin.Viewport.FieldOfView;
+ var viewNear = Editor.Instance.Windows.EditWin.Viewport.NearPlane;
+ var viewFar = Editor.Instance.Windows.EditWin.Viewport.FarPlane;
+ var viewAspect = Editor.Instance.Windows.EditWin.Width / Editor.Instance.Windows.EditWin.Height;
+ var viewBounds = BoundingFrustum.FromCamera(viewTransform.Translation, viewTransform.Forward, viewTransform.Up, viewFov, viewNear, viewFar, viewAspect);
+
+ foreach (var s in splines)
+ {
+ var contains = viewBounds.Contains(s.EditorBox);
+ if (contains == ContainmentType.Contains || contains == ContainmentType.Intersects)
+ {
+ splinesOnView.Add(s);
+ }
+ }
+
+ return splinesOnView;
+ }
+
+ private static Vector3 GetNearSplineSnapPosition(Vector3 position, List splines)
+ {
+ var nearPoint = splines[0].GetSplinePointClosestToPoint(position);
+ var nearDistance = Vector3.Distance(nearPoint, position);
+
+ for (int i = 1; i < splines.Count; i++)
+ {
+ var point = splines[i].GetSplinePointClosestToPoint(position);
+ var distance = Vector3.Distance(point, position);
+ if (distance < nearDistance)
+ {
+ nearPoint = point;
+ nearDistance = distance;
+ }
+ }
+
+ return nearPoint;
+ }
+
internal static Real NodeSizeByDistance(Vector3 nodePosition, Real nodeSize)
{
var cameraTransform = Editor.Instance.Windows.EditWin.Viewport.ViewportCamera.Viewport.ViewTransform;