diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 7b7e98239..179f3267e 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.Json; using Object = FlaxEngine.Object; @@ -27,6 +28,8 @@ namespace FlaxEditor.SceneGraph.Actors public override bool CanBeSelectedDirectly => true; + public override bool CanDuplicate => true; + public override bool CanDelete => true; public override bool CanUseState => true; @@ -45,7 +48,7 @@ namespace FlaxEditor.SceneGraph.Actors } } - private struct Data + struct Data { public Guid Spline; public int Index; @@ -78,6 +81,69 @@ namespace FlaxEditor.SceneGraph.Actors actor.RemoveSplinePoint(Index); } + class DuplicateUndoAction : IUndoAction, ISceneEditAction + { + public Guid SplineId; + public int Index; + public float Time; + public Transform Value; + + public string ActionString => "Duplicate spline point"; + + public void Dispose() + { + } + + public void Do() + { + var spline = Object.Find(ref SplineId); + spline.InsertSplineLocalPoint(Index, Time, Value); + } + + public void Undo() + { + var spline = Object.Find(ref SplineId); + spline.RemoveSplinePoint(Index); + } + + public void MarkSceneEdited(SceneModule sceneModule) + { + var spline = Object.Find(ref SplineId); + sceneModule.MarkSceneEdited(spline.Scene); + } + } + + public override SceneGraphNode Duplicate(out IUndoAction undoAction) + { + var actor = (Spline)_node.Actor; + int newIndex; + float newTime; + if (Index == actor.SplinePointsCount - 1) + { + // Append to the end + newIndex = actor.SplinePointsCount; + newTime = actor.GetSplineTime(newIndex - 1) + 1.0f; + } + else + { + // Insert between this and next point + newIndex = Index + 1; + newTime = (actor.GetSplineTime(Index) + actor.GetSplineTime(Index + 1)) * 0.5f; + } + var action = new DuplicateUndoAction + { + SplineId = actor.ID, + Index = newIndex, + Time = newTime, + Value = actor.GetSplineLocalTransform(Index), + }; + actor.InsertSplineLocalPoint(newIndex, newTime, action.Value); + undoAction = action; + var splineNode = (SplineNode)SceneGraphFactory.FindNode(action.SplineId); + splineNode.OnUpdate(); + return splineNode.ActorChildNodes[newIndex]; + } + public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal) { var actor = (Spline)_node.Actor; diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index f522a8770..e16ff915e 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -169,6 +169,12 @@ float Spline::GetSplineLength() const return Math::Sqrt(sum); } +float Spline::GetSplineTime(int32 index) const +{ + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), 0.0f) + return Curve[index].Time; +} + namespace { void FindTimeClosestToPoint(const Vector3& point, const Spline::Keyframe& start, const Spline::Keyframe& end, float& bestDistanceSquared, float& bestTime) diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index 7f8076eac..c712695d3 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -171,6 +171,13 @@ public: /// API_PROPERTY() float GetSplineLength() const; + /// + /// Gets the time of the spline keyframe. + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The spline time. + API_FUNCTION() float GetSplineTime(int32 index) const; + /// /// Calculates the closest point to the given location and returns the spline time at that point. ///