Merge branch '1.1' of https://github.com/FlaxEngine/FlaxEngine into network
This commit is contained in:
@@ -36,6 +36,7 @@ ArchitectureType LinuxPlatformTools::GetArchitecture() const
|
||||
|
||||
bool LinuxPlatformTools::OnDeployBinaries(CookingData& data)
|
||||
{
|
||||
const auto gameSettings = GameSettings::Get();
|
||||
const auto platformSettings = LinuxPlatformSettings::Get();
|
||||
const auto outputPath = data.OutputPath;
|
||||
|
||||
@@ -64,7 +65,7 @@ bool LinuxPlatformTools::OnDeployBinaries(CookingData& data)
|
||||
// Apply game executable file name
|
||||
#if !BUILD_DEBUG
|
||||
const String outputExePath = outputPath / TEXT("FlaxGame");
|
||||
const String gameExePath = outputPath / GameSettings::ProductName;
|
||||
const String gameExePath = outputPath / gameSettings->ProductName;
|
||||
if (FileSystem::FileExists(outputExePath) && gameExePath.Compare(outputExePath, StringSearchCase::IgnoreCase) == 0)
|
||||
{
|
||||
if (FileSystem::MoveFile(gameExePath, outputExePath, true))
|
||||
|
||||
64
Source/Editor/CustomEditors/Dedicated/SplineEditor.cs
Normal file
64
Source/Editor/CustomEditors/Dedicated/SplineEditor.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Actions;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for <see cref="Spline"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ActorEditor" />
|
||||
[CustomEditor(typeof(Spline)), DefaultEditor]
|
||||
public class SplineEditor : ActorEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
layout.Space(10);
|
||||
var grid = layout.CustomContainer<UniformGridPanel>();
|
||||
grid.CustomControl.SlotsHorizontally = 2;
|
||||
grid.CustomControl.SlotsVertically = 1;
|
||||
grid.Button("Set Linear Tangents").Button.Clicked += OnSetTangentsLinear;
|
||||
grid.Button("Set Smooth Tangents").Button.Clicked += OnSetTangentsSmooth;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSetTangentsLinear()
|
||||
{
|
||||
var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
if (Values[i] is Spline spline)
|
||||
{
|
||||
var before = enableUndo ? (BezierCurve<Transform>.Keyframe[])spline.SplineKeyframes.Clone() : null;
|
||||
spline.SetTangentsLinear();
|
||||
if (enableUndo)
|
||||
Presenter.Undo.AddAction(new EditSplineAction(spline, before));
|
||||
Editor.Instance.Scene.MarkSceneEdited(spline.Scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSetTangentsSmooth()
|
||||
{
|
||||
var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
if (Values[i] is Spline spline)
|
||||
{
|
||||
var before = enableUndo ? (BezierCurve<Transform>.Keyframe[])spline.SplineKeyframes.Clone() : null;
|
||||
spline.SetTangentsSmooth();
|
||||
if (enableUndo)
|
||||
Presenter.Undo.AddAction(new EditSplineAction(spline, before));
|
||||
Editor.Instance.Scene.MarkSceneEdited(spline.Scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Assertions;
|
||||
using FlaxEngine.GUI;
|
||||
@@ -553,6 +554,8 @@ namespace FlaxEditor.CustomEditors
|
||||
var group = Group(name, true);
|
||||
group.Panel.Close(false);
|
||||
group.Panel.TooltipText = tooltip;
|
||||
group.Panel.Tag = editor;
|
||||
group.Panel.MouseButtonRightClicked += OnGroupPanelMouseButtonRightClicked;
|
||||
return group.Object(values, editor);
|
||||
}
|
||||
|
||||
@@ -560,6 +563,23 @@ namespace FlaxEditor.CustomEditors
|
||||
return property.Object(values, editor);
|
||||
}
|
||||
|
||||
private void OnGroupPanelMouseButtonRightClicked(DropPanel groupPanel, Vector2 location)
|
||||
{
|
||||
var linkedEditor = (CustomEditor)groupPanel.Tag;
|
||||
var menu = new ContextMenu();
|
||||
|
||||
var revertToPrefab = menu.AddButton("Revert to Prefab", linkedEditor.RevertToReferenceValue);
|
||||
revertToPrefab.Enabled = linkedEditor.CanRevertReferenceValue;
|
||||
var resetToDefault = menu.AddButton("Reset to default", linkedEditor.RevertToDefaultValue);
|
||||
resetToDefault.Enabled = linkedEditor.CanRevertDefaultValue;
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste;
|
||||
|
||||
menu.Show(groupPanel, location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds object property editor. Selects proper <see cref="CustomEditor"/> based on overrides.
|
||||
/// </summary>
|
||||
|
||||
19
Source/Editor/SceneGraph/Actors/SplineNode.cs
Normal file
19
Source/Editor/SceneGraph/Actors/SplineNode.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="Spline"/> actor type.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public sealed class SplineNode : ActorNode
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public SplineNode(Actor actor)
|
||||
: base(actor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ namespace FlaxEditor.SceneGraph
|
||||
CustomNodesTypes.Add(typeof(NavModifierVolume), typeof(NavModifierVolumeNode));
|
||||
CustomNodesTypes.Add(typeof(ParticleEffect), typeof(ParticleEffectNode));
|
||||
CustomNodesTypes.Add(typeof(SceneAnimationPlayer), typeof(SceneAnimationPlayerNode));
|
||||
CustomNodesTypes.Add(typeof(Spline), typeof(SplineNode));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
73
Source/Editor/Undo/Actions/EditSplineAction.cs
Normal file
73
Source/Editor/Undo/Actions/EditSplineAction.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Modules;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Change <see cref="Spline"/> keyframes undo action.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.IUndoAction" />
|
||||
/// <seealso cref="FlaxEditor.ISceneEditAction" />
|
||||
[Serializable]
|
||||
public class EditSplineAction : IUndoAction, ISceneEditAction
|
||||
{
|
||||
[Serialize]
|
||||
private Guid _splineId;
|
||||
|
||||
[Serialize]
|
||||
private BezierCurve<Transform>.Keyframe[] _before;
|
||||
|
||||
[Serialize]
|
||||
private BezierCurve<Transform>.Keyframe[] _after;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EditSplineAction"/> class.
|
||||
/// </summary>
|
||||
/// <param name="spline">The spline.</param>
|
||||
/// <param name="before">The spline keyframes state before editing it.</param>
|
||||
public EditSplineAction(Spline spline, BezierCurve<Transform>.Keyframe[] before)
|
||||
{
|
||||
_splineId = spline.ID;
|
||||
_before = before;
|
||||
_after = (BezierCurve<Transform>.Keyframe[])spline.SplineKeyframes.Clone();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ActionString => "Edit spline keyframes";
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Do()
|
||||
{
|
||||
var spline = FlaxEngine.Object.Find<Spline>(ref _splineId);
|
||||
if (spline == null)
|
||||
return;
|
||||
spline.SplineKeyframes = _after;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Undo()
|
||||
{
|
||||
var spline = FlaxEngine.Object.Find<Spline>(ref _splineId);
|
||||
if (spline == null)
|
||||
return;
|
||||
spline.SplineKeyframes = _before;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_before = _after = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void MarkSceneEdited(SceneModule sceneModule)
|
||||
{
|
||||
var spline = FlaxEngine.Object.Find<Spline>(ref _splineId);
|
||||
if (spline != null)
|
||||
sceneModule.MarkSceneEdited(spline.Scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,7 @@ namespace FlaxEditor.Windows
|
||||
new KeyValuePair<string, Type>("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)),
|
||||
new KeyValuePair<string, Type>("Nav Link", typeof(NavLink)),
|
||||
new KeyValuePair<string, Type>("Nav Modifier Volume", typeof(NavModifierVolume)),
|
||||
new KeyValuePair<string, Type>("Spline", typeof(Spline)),
|
||||
}
|
||||
},
|
||||
new ActorsGroup
|
||||
|
||||
@@ -167,6 +167,7 @@ namespace FlaxEditor.Windows
|
||||
groupOther.AddChild(CreateActorItem("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)));
|
||||
groupOther.AddChild(CreateActorItem("Nav Link", typeof(NavLink)));
|
||||
groupOther.AddChild(CreateActorItem("Nav Modifier Volume", typeof(NavModifierVolume)));
|
||||
groupOther.AddChild(CreateActorItem("Spline", typeof(Spline)));
|
||||
|
||||
var groupGui = CreateGroupWithList(actorGroups, "GUI");
|
||||
groupGui.AddChild(CreateActorItem("UI Control", typeof(UIControl)));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Math/Quaternion.h"
|
||||
#include "Engine/Core/Math/Transform.h"
|
||||
|
||||
namespace AnimationUtils
|
||||
{
|
||||
@@ -40,6 +41,12 @@ namespace AnimationUtils
|
||||
return Quaternion::Identity;
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE Transform GetZero<Transform>()
|
||||
{
|
||||
return Transform::Identity;
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE Color GetZero<Color>()
|
||||
{
|
||||
@@ -66,22 +73,32 @@ namespace AnimationUtils
|
||||
Quaternion::Slerp(a, b, oneThird, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE void GetTangent<Transform>(const Transform& a, const Transform& b, float length, Transform& result)
|
||||
{
|
||||
const float oneThird = 1.0f / 3.0f;
|
||||
const float oneThirdLength = length * oneThird;
|
||||
result.Translation = a.Translation + b.Translation * oneThirdLength;
|
||||
Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation);
|
||||
result.Scale = a.Scale + b.Scale * oneThirdLength;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
FORCE_INLINE static void Interpolate(const T& a, const T& b, float alpha, T& result)
|
||||
FORCE_INLINE static void Interpolate(const T& a, const T& b, float t, T& result)
|
||||
{
|
||||
result = (T)(a + alpha * (b - a));
|
||||
result = (T)(a + t * (b - a));
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE void Interpolate<Vector3>(const Vector3& a, const Vector3& b, float alpha, Vector3& result)
|
||||
FORCE_INLINE void Interpolate<Vector3>(const Vector3& a, const Vector3& b, float t, Vector3& result)
|
||||
{
|
||||
Vector3::Lerp(a, b, alpha, result);
|
||||
Vector3::Lerp(a, b, t, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
FORCE_INLINE void Interpolate<Quaternion>(const Quaternion& a, const Quaternion& b, float alpha, Quaternion& result)
|
||||
FORCE_INLINE void Interpolate<Quaternion>(const Quaternion& a, const Quaternion& b, float t, Quaternion& result)
|
||||
{
|
||||
Quaternion::Slerp(a, b, alpha, result);
|
||||
Quaternion::Slerp(a, b, t, result);
|
||||
}
|
||||
|
||||
static void WrapTime(float& time, float start, float end, bool loop)
|
||||
@@ -117,90 +134,111 @@ namespace AnimationUtils
|
||||
/// Evaluates a cubic Hermite curve at a specific point.
|
||||
/// </summary>
|
||||
/// <param name="t">The time parameter that at which to evaluate the curve, in range [0, 1].</param>
|
||||
/// <param name="pointA">The starting point (at t=0).</param>
|
||||
/// <param name="pointB">The ending point (at t=1).</param>
|
||||
/// <param name="tangentA">The starting tangent (at t=0).</param>
|
||||
/// <param name="tangentB">The ending tangent (at t = 1).</param>
|
||||
/// <param name="p0">The starting point (at t=0).</param>
|
||||
/// <param name="p1">The ending point (at t=1).</param>
|
||||
/// <param name="t0">The starting tangent (at t=0).</param>
|
||||
/// <param name="t1">The ending tangent (at t = 1).</param>
|
||||
/// <param name="result">The evaluated value.</param>
|
||||
template<class T>
|
||||
static void CubicHermite(const float t, const T& pointA, const T& pointB, const T& tangentA, const T& tangentB, T* result)
|
||||
static void CubicHermite(const T& p0, const T& p1, const T& t0, const T& t1, float t, T& result)
|
||||
{
|
||||
const float t2 = t * t;
|
||||
const float t3 = t2 * t;
|
||||
|
||||
float a = 2 * t3 - 3 * t2 + 1;
|
||||
float b = t3 - 2 * t2 + t;
|
||||
float c = -2 * t3 + 3 * t2;
|
||||
float d = t3 - t2;
|
||||
|
||||
*result = a * pointA + b * tangentA + c * pointB + d * tangentB;
|
||||
const float tt = t * t;
|
||||
const float ttt = tt * t;
|
||||
result = (2 * ttt - 3 * tt + 1) * p0 + (ttt - 2 * tt + t) * t0 + (-2 * ttt + 3 * tt) * p1 + (ttt - tt) * t1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the first derivative of a cubic Hermite curve at a specific point.
|
||||
/// </summary>
|
||||
/// <param name="t">The time parameter that at which to evaluate the curve, in range [0, 1].</param>
|
||||
/// <param name="pointA">The starting point (at t=0).</param>
|
||||
/// <param name="pointB">The ending point (at t=1).</param>
|
||||
/// <param name="tangentA">The starting tangent (at t=0).</param>
|
||||
/// <param name="tangentB">The ending tangent (at t = 1).</param>
|
||||
/// <param name="p0">The starting point (at t=0).</param>
|
||||
/// <param name="p1">The ending point (at t=1).</param>
|
||||
/// <param name="t0">The starting tangent (at t=0).</param>
|
||||
/// <param name="t1">The ending tangent (at t=1).</param>
|
||||
/// <param name="result">The evaluated value.</param>
|
||||
template<class T>
|
||||
static void CubicHermiteD1(const float t, const T& pointA, const T& pointB, const T& tangentA, const T& tangentB, T* result)
|
||||
static void CubicHermiteFirstDerivative(const T& p0, const T& p1, const T& t0, const T& t1, float t, T& result)
|
||||
{
|
||||
const float t2 = t * t;
|
||||
|
||||
float a = 6 * t2 - 6 * t;
|
||||
float b = 3 * t2 - 4 * t + 1;
|
||||
float c = -6 * t2 + 6 * t;
|
||||
float d = 3 * t2 - 2 * t;
|
||||
|
||||
*result = a * pointA + b * tangentA + c * pointB + d * tangentB;
|
||||
const float tt = t * t;
|
||||
result = (6 * tt - 6 * t) * p0 + (3 * tt - 4 * t + 1) * t0 + (-6 * tt + 6 * t) * p1 + (3 * tt - 2 * t) * t1;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void Bezier(const T& p0, const T& p1, const T& p2, const T& p3, float alpha, T& result)
|
||||
static void Bezier(const T& p0, const T& p1, const T& p2, const T& p3, float t, T& result)
|
||||
{
|
||||
T p01, p12, p23, p012, p123;
|
||||
Interpolate(p0, p1, alpha, p01);
|
||||
Interpolate(p1, p2, alpha, p12);
|
||||
Interpolate(p2, p3, alpha, p23);
|
||||
Interpolate(p01, p12, alpha, p012);
|
||||
Interpolate(p12, p23, alpha, p123);
|
||||
Interpolate(p012, p123, alpha, result);
|
||||
Interpolate(p0, p1, t, p01);
|
||||
Interpolate(p1, p2, t, p12);
|
||||
Interpolate(p2, p3, t, p23);
|
||||
Interpolate(p01, p12, t, p012);
|
||||
Interpolate(p12, p23, t, p123);
|
||||
Interpolate(p012, p123, t, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Bezier<Vector2>(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3, float alpha, Vector2& result)
|
||||
void Bezier<Vector2>(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3, float t, Vector2& result)
|
||||
{
|
||||
const float u = 1.0f - alpha;
|
||||
const float tt = alpha * alpha;
|
||||
const float u = 1.0f - t;
|
||||
const float tt = t * t;
|
||||
const float uu = u * u;
|
||||
const float uuu = uu * u;
|
||||
const float ttt = tt * alpha;
|
||||
result = uuu * p0 + 3 * uu * alpha * p1 + 3 * u * tt * p2 + ttt * p3;
|
||||
const float ttt = tt * t;
|
||||
result = uuu * p0 + 3 * uu * t * p1 + 3 * u * tt * p2 + ttt * p3;
|
||||
}
|
||||
|
||||
template<>
|
||||
void Bezier<Vector3>(const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3, float alpha, Vector3& result)
|
||||
void Bezier<Vector3>(const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3, float t, Vector3& result)
|
||||
{
|
||||
const float u = 1.0f - alpha;
|
||||
const float tt = alpha * alpha;
|
||||
const float u = 1.0f - t;
|
||||
const float tt = t * t;
|
||||
const float uu = u * u;
|
||||
const float uuu = uu * u;
|
||||
const float ttt = tt * alpha;
|
||||
result = uuu * p0 + 3 * uu * alpha * p1 + 3 * u * tt * p2 + ttt * p3;
|
||||
const float ttt = tt * t;
|
||||
result = uuu * p0 + 3 * uu * t * p1 + 3 * u * tt * p2 + ttt * p3;
|
||||
}
|
||||
|
||||
template<>
|
||||
void Bezier<Quaternion>(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float alpha, Quaternion& result)
|
||||
void Bezier<Quaternion>(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float t, Quaternion& result)
|
||||
{
|
||||
Quaternion p01, p12, p23, p012, p123;
|
||||
Quaternion::Slerp(p0, p1, alpha, p01);
|
||||
Quaternion::Slerp(p1, p2, alpha, p12);
|
||||
Quaternion::Slerp(p2, p3, alpha, p23);
|
||||
Quaternion::Slerp(p01, p12, alpha, p012);
|
||||
Quaternion::Slerp(p12, p23, alpha, p123);
|
||||
Quaternion::Slerp(p012, p123, alpha, result);
|
||||
Quaternion::Slerp(p0, p1, t, p01);
|
||||
Quaternion::Slerp(p1, p2, t, p12);
|
||||
Quaternion::Slerp(p2, p3, t, p23);
|
||||
Quaternion::Slerp(p01, p12, t, p012);
|
||||
Quaternion::Slerp(p12, p23, t, p123);
|
||||
Quaternion::Slerp(p012, p123, t, result);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Bezier<Transform>(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float t, Transform& result)
|
||||
{
|
||||
Bezier<Vector3>(p0.Translation, p1.Translation, p2.Translation, p3.Translation, t, result.Translation);
|
||||
Bezier<Quaternion>(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, t, result.Orientation);
|
||||
Bezier<Vector3>(p0.Scale, p1.Scale, p2.Scale, p3.Scale, t, result.Scale);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void BezierFirstDerivative(const T& p0, const T& p1, const T& p2, const T& p3, float t, T& result)
|
||||
{
|
||||
const float u = 1.0f - t;
|
||||
const float tt = t * t;
|
||||
const float uu = u * u;
|
||||
result = 3.0f * uu * (p1 - p0) + 6.0f * u * t * (p2 - p1) + 3.0f * tt * (p3 - p2);
|
||||
}
|
||||
|
||||
template<>
|
||||
static void BezierFirstDerivative<Quaternion>(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float t, Quaternion& result)
|
||||
{
|
||||
Vector3 euler;
|
||||
BezierFirstDerivative<Vector3>(p0.GetEuler(), p1.GetEuler(), p2.GetEuler(), p3.GetEuler(), t, euler);
|
||||
result = Quaternion::Euler(euler);
|
||||
}
|
||||
|
||||
template<>
|
||||
static void BezierFirstDerivative<Transform>(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float t, Transform& result)
|
||||
{
|
||||
BezierFirstDerivative<Vector3>(p0.Translation, p1.Translation, p2.Translation, p3.Translation, t, result.Translation);
|
||||
BezierFirstDerivative<Quaternion>(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, t, result.Orientation);
|
||||
BezierFirstDerivative<Vector3>(p0.Scale, p1.Scale, p2.Scale, p3.Scale, t, result.Scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
@@ -338,6 +339,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// A single keyframe that can be injected into linear curve.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Keyframe : IComparable, IComparable<Keyframe>
|
||||
{
|
||||
/// <summary>
|
||||
@@ -586,6 +588,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// A single keyframe that can be injected into Bezier curve.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Keyframe : IComparable, IComparable<Keyframe>
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -48,10 +48,20 @@ public:
|
||||
result = a.Value;
|
||||
}
|
||||
|
||||
FORCE_INLINE static void InterpolateFirstDerivative(const StepCurveKeyframe& a, const StepCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
result = AnimationUtils::GetZero<T>();
|
||||
}
|
||||
|
||||
FORCE_INLINE static void InterpolateKey(const StepCurveKeyframe& a, const StepCurveKeyframe& b, float alpha, float length, StepCurveKeyframe& result)
|
||||
{
|
||||
result = a;
|
||||
}
|
||||
|
||||
bool operator==(const StepCurveKeyframe& other) const
|
||||
{
|
||||
return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value);
|
||||
}
|
||||
} PACK_END();
|
||||
|
||||
/// <summary>
|
||||
@@ -92,11 +102,21 @@ public:
|
||||
AnimationUtils::Interpolate(a.Value, b.Value, alpha, result);
|
||||
}
|
||||
|
||||
FORCE_INLINE static void InterpolateFirstDerivative(const LinearCurveKeyframe& a, const LinearCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
result = b.Value - a.Value;
|
||||
}
|
||||
|
||||
FORCE_INLINE static void InterpolateKey(const LinearCurveKeyframe& a, const LinearCurveKeyframe& b, float alpha, float length, LinearCurveKeyframe& result)
|
||||
{
|
||||
result.Time = a.Time + (b.Time - a.Time) * alpha;
|
||||
AnimationUtils::Interpolate(a.Value, b.Value, alpha, result.Value);
|
||||
}
|
||||
|
||||
bool operator==(const LinearCurveKeyframe& other) const
|
||||
{
|
||||
return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value);
|
||||
}
|
||||
} PACK_END();
|
||||
|
||||
/// <summary>
|
||||
@@ -147,23 +167,31 @@ public:
|
||||
{
|
||||
T leftTangent = a.Value + a.TangentOut * length;
|
||||
T rightTangent = b.Value + b.TangentIn * length;
|
||||
AnimationUtils::CubicHermite( a.Value, b.Value, leftTangent, rightTangent, alpha, result);
|
||||
}
|
||||
|
||||
AnimationUtils::CubicHermite(alpha, a.Value, b.Value, leftTangent, rightTangent, result);
|
||||
static void InterpolateFirstDerivative(const HermiteCurveKeyframe& a, const HermiteCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
T leftTangent = a.Value + a.TangentOut * length;
|
||||
T rightTangent = b.Value + b.TangentIn * length;
|
||||
AnimationUtils::CubicHermiteFirstDerivative( a.Value, b.Value, leftTangent, rightTangent, alpha, result);
|
||||
}
|
||||
|
||||
static void InterpolateKey(const HermiteCurveKeyframe& a, const HermiteCurveKeyframe& b, float alpha, float length, HermiteCurveKeyframe& result)
|
||||
{
|
||||
result.Time = a.Time + length * alpha;
|
||||
|
||||
T leftTangent = a.Value + a.TangentOut * length;
|
||||
T rightTangent = b.Value + b.TangentIn * length;
|
||||
|
||||
AnimationUtils::CubicHermite(alpha, a.Value, b.Value, leftTangent, rightTangent, result.Value);
|
||||
AnimationUtils::CubicHermiteD1(alpha, a.Value, b.Value, leftTangent, rightTangent, result.TangentIn);
|
||||
|
||||
AnimationUtils::CubicHermite(a.Value, b.Value, leftTangent, rightTangent, alpha, result.Value);
|
||||
AnimationUtils::CubicHermiteFirstDerivative(a.Value, b.Value, leftTangent, rightTangent, alpha, result.TangentIn);
|
||||
result.TangentIn /= length;
|
||||
result.TangentOut = result.TangentIn;
|
||||
}
|
||||
|
||||
bool operator==(const HermiteCurveKeyframe& other) const
|
||||
{
|
||||
return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value) && Math::NearEqual(TangentIn, other.TangentIn) && Math::NearEqual(TangentOut, other.TangentOut);
|
||||
}
|
||||
} PACK_END();
|
||||
|
||||
/// <summary>
|
||||
@@ -223,23 +251,32 @@ public:
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
|
||||
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
||||
}
|
||||
|
||||
static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result)
|
||||
{
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
|
||||
}
|
||||
|
||||
static void InterpolateKey(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, BezierCurveKeyframe& result)
|
||||
{
|
||||
result.Time = a.Time + length * alpha;
|
||||
|
||||
T leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent);
|
||||
|
||||
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value);
|
||||
|
||||
result.TangentIn = a.TangentOut;
|
||||
result.TangentOut = b.TangentIn;
|
||||
}
|
||||
|
||||
bool operator==(const BezierCurveKeyframe& other) const
|
||||
{
|
||||
return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value) && Math::NearEqual(TangentIn, other.TangentIn) && Math::NearEqual(TangentOut, other.TangentOut);
|
||||
}
|
||||
} PACK_END();
|
||||
|
||||
// @formatter:on
|
||||
@@ -346,6 +383,48 @@ public:
|
||||
KeyFrame::Interpolate(leftKey, rightKey, t, length, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the first derivative of the animation curve at the specified time (aka velocity).
|
||||
/// </summary>
|
||||
/// <param name="data">The keyframes data container.</param>
|
||||
/// <param name="result">The calculated first derivative from the curve at provided time.</param>
|
||||
/// <param name="time">The time to evaluate the curve at.</param>
|
||||
/// <param name="loop">If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped.</param>
|
||||
void EvaluateFirstDerivative(const KeyFrameData& data, T& result, float time, bool loop = true) const
|
||||
{
|
||||
const int32 count = data.Length();
|
||||
if (count == 0)
|
||||
{
|
||||
result = _default;
|
||||
return;
|
||||
}
|
||||
|
||||
const float start = 0;
|
||||
const float end = data[count - 1].Time;
|
||||
AnimationUtils::WrapTime(time, start, end, loop);
|
||||
|
||||
int32 leftKeyIdx;
|
||||
int32 rightKeyIdx;
|
||||
FindKeys(data, time, leftKeyIdx, rightKeyIdx);
|
||||
|
||||
const KeyFrame& leftKey = data[leftKeyIdx];
|
||||
const KeyFrame& rightKey = data[rightKeyIdx];
|
||||
|
||||
if (leftKeyIdx == rightKeyIdx)
|
||||
{
|
||||
result = leftKey.Value;
|
||||
return;
|
||||
}
|
||||
|
||||
const float length = rightKey.Time - leftKey.Time;
|
||||
|
||||
// Scale from arbitrary range to [0, 1]
|
||||
float t = Math::NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length;
|
||||
|
||||
// Evaluate the derivative at the curve
|
||||
KeyFrame::InterpolateFirstDerivative(leftKey, rightKey, t, length, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the animation curve key at the specified time.
|
||||
/// </summary>
|
||||
@@ -566,6 +645,18 @@ public:
|
||||
Base::Evaluate(data, result, time, loop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the first derivative of the animation curve at the specified time (aka velocity).
|
||||
/// </summary>
|
||||
/// <param name="result">The calculated first derivative from the curve at provided time.</param>
|
||||
/// <param name="time">The time to evaluate the curve at.</param>
|
||||
/// <param name="loop">If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped.</param>
|
||||
void EvaluateFirstDerivative(T& result, float time, bool loop = true) const
|
||||
{
|
||||
typename Base::KeyFrameData data(_keyframes);
|
||||
Base::EvaluateFirstDerivative(data, result, time, loop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the animation curve key at the specified time.
|
||||
/// </summary>
|
||||
@@ -708,6 +799,30 @@ public:
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
FORCE_INLINE KeyFrame& operator[](int32 index)
|
||||
{
|
||||
return _keyframes[index];
|
||||
}
|
||||
|
||||
FORCE_INLINE const KeyFrame& operator[](int32 index) const
|
||||
{
|
||||
return _keyframes[index];
|
||||
}
|
||||
|
||||
bool operator==(const Curve& other) const
|
||||
{
|
||||
if (_keyframes.Count() != other._keyframes.Count())
|
||||
return false;
|
||||
for (int32 i = 0; i < _keyframes.Count(); i++)
|
||||
{
|
||||
if (!(_keyframes[i] == other._keyframes[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
171
Source/Engine/Animations/CurveSerialization.h
Normal file
171
Source/Engine/Animations/CurveSerialization.h
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Curve.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
|
||||
// @formatter:off
|
||||
|
||||
namespace Serialization
|
||||
{
|
||||
// StepCurveKeyframe
|
||||
|
||||
template<class T>
|
||||
inline bool ShouldSerialize(const StepCurveKeyframe<T>& v, const void* otherObj)
|
||||
{
|
||||
if (!otherObj)
|
||||
return true;
|
||||
const auto other = (const StepCurveKeyframe<T>*)otherObj;
|
||||
return !(v == *other);
|
||||
}
|
||||
template<class T>
|
||||
inline void Serialize(ISerializable::SerializeStream& stream, const StepCurveKeyframe<T>& v, const void* otherObj)
|
||||
{
|
||||
stream.StartObject();
|
||||
stream.JKEY("Time");
|
||||
Serialize(stream, v.Time, nullptr);
|
||||
stream.JKEY("Value");
|
||||
Serialize(stream, v.Value, nullptr);
|
||||
stream.EndObject();
|
||||
}
|
||||
template<class T>
|
||||
inline void Deserialize(ISerializable::DeserializeStream& stream, StepCurveKeyframe<T>& v, ISerializeModifier* modifier)
|
||||
{
|
||||
DESERIALIZE_MEMBER(Time, v.Time);
|
||||
DESERIALIZE_MEMBER(Value, v.Value);
|
||||
}
|
||||
|
||||
// LinearCurveKeyframe
|
||||
|
||||
template<class T>
|
||||
inline bool ShouldSerialize(const LinearCurveKeyframe<T>& v, const void* otherObj)
|
||||
{
|
||||
if (!otherObj)
|
||||
return true;
|
||||
const auto other = (const LinearCurveKeyframe<T>*)otherObj;
|
||||
return !(v == *other);
|
||||
}
|
||||
template<class T>
|
||||
inline void Serialize(ISerializable::SerializeStream& stream, const LinearCurveKeyframe<T>& v, const void* otherObj)
|
||||
{
|
||||
stream.StartObject();
|
||||
stream.JKEY("Time");
|
||||
Serialize(stream, v.Time, nullptr);
|
||||
stream.JKEY("Value");
|
||||
Serialize(stream, v.Value, nullptr);
|
||||
stream.EndObject();
|
||||
}
|
||||
template<class T>
|
||||
inline void Deserialize(ISerializable::DeserializeStream& stream, LinearCurveKeyframe<T>& v, ISerializeModifier* modifier)
|
||||
{
|
||||
DESERIALIZE_MEMBER(Time, v.Time);
|
||||
DESERIALIZE_MEMBER(Value, v.Value);
|
||||
}
|
||||
|
||||
// HermiteCurveKeyframe
|
||||
|
||||
template<class T>
|
||||
inline bool ShouldSerialize(const HermiteCurveKeyframe<T>& v, const void* otherObj)
|
||||
{
|
||||
if (!otherObj)
|
||||
return true;
|
||||
const auto other = (const HermiteCurveKeyframe<T>*)otherObj;
|
||||
return !(v == *other);
|
||||
}
|
||||
template<class T>
|
||||
inline void Serialize(ISerializable::SerializeStream& stream, const HermiteCurveKeyframe<T>& v, const void* otherObj)
|
||||
{
|
||||
stream.StartObject();
|
||||
stream.JKEY("Time");
|
||||
Serialize(stream, v.Time, nullptr);
|
||||
stream.JKEY("Value");
|
||||
Serialize(stream, v.Value, nullptr);
|
||||
stream.JKEY("TangentIn");
|
||||
Serialize(stream, v.TangentIn, nullptr);
|
||||
stream.JKEY("TangentOut");
|
||||
Serialize(stream, v.TangentOut, nullptr);
|
||||
stream.EndObject();
|
||||
}
|
||||
template<class T>
|
||||
inline void Deserialize(ISerializable::DeserializeStream& stream, HermiteCurveKeyframe<T>& v, ISerializeModifier* modifier)
|
||||
{
|
||||
DESERIALIZE_MEMBER(Time, v.Time);
|
||||
DESERIALIZE_MEMBER(Value, v.Value);
|
||||
DESERIALIZE_MEMBER(TangentIn, v.TangentIn);
|
||||
DESERIALIZE_MEMBER(TangentOut, v.TangentOut);
|
||||
}
|
||||
|
||||
// BezierCurveKeyframe
|
||||
|
||||
template<class T>
|
||||
inline bool ShouldSerialize(const BezierCurveKeyframe<T>& v, const void* otherObj)
|
||||
{
|
||||
if (!otherObj)
|
||||
return true;
|
||||
const auto other = (const BezierCurveKeyframe<T>*)otherObj;
|
||||
return !(v == *other);
|
||||
}
|
||||
template<class T>
|
||||
inline void Serialize(ISerializable::SerializeStream& stream, const BezierCurveKeyframe<T>& v, const void* otherObj)
|
||||
{
|
||||
stream.StartObject();
|
||||
stream.JKEY("Time");
|
||||
Serialize(stream, v.Time, nullptr);
|
||||
stream.JKEY("Value");
|
||||
Serialize(stream, v.Value, nullptr);
|
||||
stream.JKEY("TangentIn");
|
||||
Serialize(stream, v.TangentIn, nullptr);
|
||||
stream.JKEY("TangentOut");
|
||||
Serialize(stream, v.TangentOut, nullptr);
|
||||
stream.EndObject();
|
||||
}
|
||||
template<class T>
|
||||
inline void Deserialize(ISerializable::DeserializeStream& stream, BezierCurveKeyframe<T>& v, ISerializeModifier* modifier)
|
||||
{
|
||||
DESERIALIZE_MEMBER(Time, v.Time);
|
||||
DESERIALIZE_MEMBER(Value, v.Value);
|
||||
DESERIALIZE_MEMBER(TangentIn, v.TangentIn);
|
||||
DESERIALIZE_MEMBER(TangentOut, v.TangentOut);
|
||||
}
|
||||
|
||||
// Curve
|
||||
|
||||
template<class T, typename KeyFrame>
|
||||
inline bool ShouldSerialize(const Curve<T, KeyFrame>& v, const void* otherObj)
|
||||
{
|
||||
if (!otherObj)
|
||||
return true;
|
||||
const auto other = (const Curve<T, KeyFrame>*)otherObj;
|
||||
return !(v == *other);
|
||||
}
|
||||
template<class T, typename KeyFrame>
|
||||
inline void Serialize(ISerializable::SerializeStream& stream, const Curve<T, KeyFrame>& v, const void* otherObj)
|
||||
{
|
||||
stream.StartObject();
|
||||
stream.JKEY("Keyframes");
|
||||
stream.StartArray();
|
||||
for (auto& k : v.GetKeyframes())
|
||||
Serialize(stream, k, nullptr);
|
||||
stream.EndArray();
|
||||
stream.EndObject();
|
||||
}
|
||||
template<class T, typename KeyFrame>
|
||||
inline void Deserialize(ISerializable::DeserializeStream& stream, Curve<T, KeyFrame>& v, ISerializeModifier* modifier)
|
||||
{
|
||||
if (!stream.IsObject())
|
||||
return;
|
||||
const auto mKeyframes = SERIALIZE_FIND_MEMBER(stream, "Keyframes");
|
||||
if (mKeyframes != stream.MemberEnd())
|
||||
{
|
||||
const auto& keyframesArray = mKeyframes->value.GetArray();
|
||||
auto& keyframes = v.GetKeyframes();
|
||||
keyframes.Resize(keyframesArray.Size());
|
||||
for (rapidjson::SizeType i = 0; i < keyframesArray.Size(); i++)
|
||||
Deserialize(keyframesArray[i], keyframes[i], modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @formatter:on
|
||||
@@ -506,6 +506,14 @@ inline Color operator*(float a, const Color& b)
|
||||
return b * a;
|
||||
}
|
||||
|
||||
namespace Math
|
||||
{
|
||||
FORCE_INLINE static bool NearEqual(const Color& a, const Color& b)
|
||||
{
|
||||
return Color::NearEqual(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
struct TIsPODType<Color>
|
||||
{
|
||||
|
||||
@@ -231,6 +231,14 @@ inline Color32 operator*(float a, const Color32& b)
|
||||
return b * a;
|
||||
}
|
||||
|
||||
namespace Math
|
||||
{
|
||||
FORCE_INLINE static bool NearEqual(const Color32& a, const Color32& b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
struct TIsPODType<Color32>
|
||||
{
|
||||
|
||||
@@ -94,6 +94,13 @@ Vector3 Transform::LocalToWorld(const Vector3& point) const
|
||||
return result + Translation;
|
||||
}
|
||||
|
||||
Vector3 Transform::LocalToWorldVector(const Vector3& vector) const
|
||||
{
|
||||
Vector3 result = vector * Scale;
|
||||
Vector3::Transform(result, Orientation, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Transform::LocalToWorld(const Vector3& point, Vector3& result) const
|
||||
{
|
||||
Vector3 tmp = point * Scale;
|
||||
@@ -171,6 +178,24 @@ Vector3 Transform::WorldToLocal(const Vector3& point) const
|
||||
return result * invScale;
|
||||
}
|
||||
|
||||
Vector3 Transform::WorldToLocalVector(const Vector3& vector) const
|
||||
{
|
||||
Vector3 invScale = Scale;
|
||||
if (invScale.X != 0.0f)
|
||||
invScale.X = 1.0f / invScale.X;
|
||||
if (invScale.Y != 0.0f)
|
||||
invScale.Y = 1.0f / invScale.Y;
|
||||
if (invScale.Z != 0.0f)
|
||||
invScale.Z = 1.0f / invScale.Z;
|
||||
|
||||
const Quaternion invRotation = Orientation.Conjugated();
|
||||
|
||||
Vector3 result;
|
||||
Vector3::Transform(vector, invRotation, result);
|
||||
|
||||
return result * invScale;
|
||||
}
|
||||
|
||||
void Transform::WorldToLocal(const Vector3* points, int32 pointsCount, Vector3* result) const
|
||||
{
|
||||
Vector3 invScale = Scale;
|
||||
|
||||
@@ -197,6 +197,18 @@ namespace FlaxEngine
|
||||
return point + Translation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs transformation of the given vector in local space to the world space of this transform.
|
||||
/// </summary>
|
||||
/// <param name="vector">The local space vector.</param>
|
||||
/// <returns>The world space vector.</returns>
|
||||
public Vector3 LocalToWorldVector(Vector3 vector)
|
||||
{
|
||||
vector *= Scale;
|
||||
Vector3.Transform(ref vector, ref Orientation, out vector);
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform transformation of the given points in local space
|
||||
/// </summary>
|
||||
@@ -259,6 +271,29 @@ namespace FlaxEngine
|
||||
return result * invScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform transformation of the given vector in world space
|
||||
/// </summary>
|
||||
/// <param name="vector">World space vector</param>
|
||||
/// <returns>Local space vector</returns>
|
||||
public Vector3 WorldToLocalVector(Vector3 vector)
|
||||
{
|
||||
Vector3 invScale = Scale;
|
||||
if (invScale.X != 0.0f)
|
||||
invScale.X = 1.0f / invScale.X;
|
||||
if (invScale.Y != 0.0f)
|
||||
invScale.Y = 1.0f / invScale.Y;
|
||||
if (invScale.Z != 0.0f)
|
||||
invScale.Z = 1.0f / invScale.Z;
|
||||
|
||||
Quaternion invRotation = Orientation;
|
||||
invRotation.Invert();
|
||||
|
||||
Vector3.Transform(ref vector, ref invRotation, out var result);
|
||||
|
||||
return result * invScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform transformation of the given points in world space
|
||||
/// </summary>
|
||||
|
||||
@@ -184,6 +184,13 @@ public:
|
||||
/// <returns>The world space point.</returns>
|
||||
Vector3 LocalToWorld(const Vector3& point) const;
|
||||
|
||||
/// <summary>
|
||||
/// Performs transformation of the given vector in local space to the world space of this transform.
|
||||
/// </summary>
|
||||
/// <param name="vector">The local space vector.</param>
|
||||
/// <returns>The world space vector.</returns>
|
||||
Vector3 LocalToWorldVector(const Vector3& vector) const;
|
||||
|
||||
/// <summary>
|
||||
/// Performs transformation of the given point in local space to the world space of this transform.
|
||||
/// </summary>
|
||||
@@ -220,6 +227,13 @@ public:
|
||||
/// <returns>The local space point.</returns>
|
||||
Vector3 WorldToLocal(const Vector3& point) const;
|
||||
|
||||
/// <summary>
|
||||
/// Performs transformation of the given vector in world space to the local space of this transform.
|
||||
/// </summary>
|
||||
/// <param name="vector">The world space vector.</param>
|
||||
/// <returns>The local space vector.</returns>
|
||||
Vector3 WorldToLocalVector(const Vector3& vector) const;
|
||||
|
||||
/// <summary>
|
||||
/// Performs transformation of the given points in world space to the local space of this transform.
|
||||
/// </summary>
|
||||
@@ -293,6 +307,14 @@ public:
|
||||
static void Lerp(const Transform& t1, const Transform& t2, float amount, Transform& result);
|
||||
};
|
||||
|
||||
namespace Math
|
||||
{
|
||||
FORCE_INLINE static bool NearEqual(const Transform& a, const Transform& b)
|
||||
{
|
||||
return Transform::NearEqual(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
struct TIsPODType<Transform>
|
||||
{
|
||||
|
||||
@@ -379,7 +379,7 @@ public:
|
||||
|
||||
static bool NearEqual(const Vector4& a, const Vector4& b)
|
||||
{
|
||||
return Math::NearEqual(a.X, b.X) && Math::NearEqual(a.Y, b.Y) & Math::NearEqual(a.Z, b.Z) && Math::NearEqual(a.W, b.W);
|
||||
return Math::NearEqual(a.X, b.X) && Math::NearEqual(a.Y, b.Y) && Math::NearEqual(a.Z, b.Z) && Math::NearEqual(a.W, b.W);
|
||||
}
|
||||
|
||||
static bool NearEqual(const Vector4& a, const Vector4& b, float epsilon)
|
||||
|
||||
@@ -650,16 +650,6 @@ void DebugDraw::DrawLines(const Span<Vector3>& lines, const Matrix& transform, c
|
||||
}
|
||||
}
|
||||
|
||||
static Vector3 GetPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
|
||||
{
|
||||
const float oneMinusT = 1.0f - t;
|
||||
return
|
||||
oneMinusT * oneMinusT * oneMinusT * p0 +
|
||||
3.0f * oneMinusT * oneMinusT * t * p1 +
|
||||
3.0f * oneMinusT * t * t * p2 +
|
||||
t * t * t * p3;
|
||||
}
|
||||
|
||||
void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Color& color, float duration, bool depthTest)
|
||||
{
|
||||
// Create draw call entry
|
||||
@@ -676,13 +666,13 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3&
|
||||
const Vector3 d3 = p4 - p3;
|
||||
const float len = d1.Length() + d2.Length() + d3.Length();
|
||||
const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100);
|
||||
const float segmentCountInv = 1.0f / segmentCount;
|
||||
const float segmentCountInv = 1.0f / (float)segmentCount;
|
||||
list->EnsureCapacity(list->Count() + segmentCount + 2);
|
||||
|
||||
// Draw segmented curve
|
||||
for (int32 i = 0; i <= segmentCount; i++)
|
||||
{
|
||||
const float t = i * segmentCountInv;
|
||||
const float t = (float)i * segmentCountInv;
|
||||
AnimationUtils::Bezier(p1, p2, p3, p4, t, l.End);
|
||||
list->Add(l);
|
||||
l.Start = l.End;
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
/// Sets the material to the entry slot. Can be used to override the material of the meshes using this slot.
|
||||
/// </summary>
|
||||
/// <param name="entryIndex">The material slot entry index.</param>
|
||||
/// <param name="material">The material to set..</param>
|
||||
/// <param name="material">The material to set.</param>
|
||||
API_FUNCTION() void SetMaterial(int32 entryIndex, MaterialBase* material);
|
||||
|
||||
/// <summary>
|
||||
|
||||
421
Source/Engine/Level/Actors/Spline.cpp
Normal file
421
Source/Engine/Level/Actors/Spline.cpp
Normal file
@@ -0,0 +1,421 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Spline.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Animations/CurveSerialization.h"
|
||||
#include <ThirdParty/mono-2.0/mono/metadata/object.h>
|
||||
|
||||
Spline::Spline(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
{
|
||||
}
|
||||
|
||||
void Spline::SetIsLoop(bool value)
|
||||
{
|
||||
if (_loop != value)
|
||||
{
|
||||
_loop = value;
|
||||
UpdateSpline();
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplinePoint(float time) const
|
||||
{
|
||||
Transform t;
|
||||
Curve.Evaluate(t, time, _loop);
|
||||
return _transform.LocalToWorld(t.Translation);
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplineLocalPoint(float time) const
|
||||
{
|
||||
Transform t;
|
||||
Curve.Evaluate(t, time, _loop);
|
||||
return t.Translation;
|
||||
}
|
||||
|
||||
Quaternion Spline::GetSplineOrientation(float time) const
|
||||
{
|
||||
Transform t;
|
||||
Curve.Evaluate(t, time, _loop);
|
||||
Quaternion::Multiply(_transform.Orientation, t.Orientation, t.Orientation);
|
||||
t.Orientation.Normalize();
|
||||
return t.Orientation;
|
||||
}
|
||||
|
||||
Quaternion Spline::GetSplineLocalOrientation(float time) const
|
||||
{
|
||||
Transform t;
|
||||
Curve.Evaluate(t, time, _loop);
|
||||
return t.Orientation;
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplineScale(float time) const
|
||||
{
|
||||
Transform t;
|
||||
Curve.Evaluate(t, time, _loop);
|
||||
return _transform.Scale * t.Scale;
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplineLocalScale(float time) const
|
||||
{
|
||||
Transform t;
|
||||
Curve.Evaluate(t, time, _loop);
|
||||
return t.Scale;
|
||||
}
|
||||
|
||||
Transform Spline::GetSplineTransform(float time) const
|
||||
{
|
||||
Transform t;
|
||||
Curve.Evaluate(t, time, _loop);
|
||||
return _transform.LocalToWorld(t);
|
||||
}
|
||||
|
||||
Transform Spline::GetSplineLocalTransform(float time) const
|
||||
{
|
||||
Transform t;
|
||||
Curve.Evaluate(t, time, _loop);
|
||||
return t;
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplineDirection(float time) const
|
||||
{
|
||||
return _transform.LocalToWorldVector(GetSplineLocalDirection(time));
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplineLocalDirection(float time) const
|
||||
{
|
||||
if (Curve.GetKeyframes().Count() == 0)
|
||||
return Vector3::Forward;
|
||||
Transform t;
|
||||
Curve.EvaluateFirstDerivative(t, time, _loop);
|
||||
t.Translation.Normalize();
|
||||
return t.Translation;
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplinePoint(int32 index) const
|
||||
{
|
||||
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero)
|
||||
return _transform.LocalToWorld(Curve[index].Value.Translation);
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplineLocalPoint(int32 index) const
|
||||
{
|
||||
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero)
|
||||
return Curve[index].Value.Translation;
|
||||
}
|
||||
|
||||
Transform Spline::GetSplineTransform(int32 index) const
|
||||
{
|
||||
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero)
|
||||
return _transform.LocalToWorld(Curve[index].Value);
|
||||
}
|
||||
|
||||
Transform Spline::GetSplineLocalTransform(int32 index) const
|
||||
{
|
||||
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero)
|
||||
return Curve[index].Value;
|
||||
}
|
||||
|
||||
int32 Spline::GetSplinePointsCount() const
|
||||
{
|
||||
return Curve.GetKeyframes().Count();
|
||||
}
|
||||
|
||||
float Spline::GetSplineDuration() const
|
||||
{
|
||||
return Curve.GetLength();
|
||||
}
|
||||
|
||||
float Spline::GetSplineLength() const
|
||||
{
|
||||
float sum = 0.0f;
|
||||
const int32 slices = 20;
|
||||
const float step = 1.0f / (float)slices;
|
||||
Vector3 prevPoint = Vector3::Zero;
|
||||
for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++)
|
||||
{
|
||||
const auto& a = Curve[i = 1];
|
||||
const auto& b = Curve[i];
|
||||
|
||||
const float length = Math::Abs(b.Time - a.Time);
|
||||
Vector3 leftTangent, rightTangent;
|
||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
||||
|
||||
// TODO: implement sth more analytical than brute-force solution
|
||||
for (int32 slice = 0; slice < slices; slice++)
|
||||
{
|
||||
const float t = (float)slice * step;
|
||||
Vector3 pos;
|
||||
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
||||
pos *= _transform.Scale;
|
||||
sum += Vector3::DistanceSquared(pos, prevPoint);
|
||||
prevPoint = pos;
|
||||
}
|
||||
}
|
||||
return Math::Sqrt(sum);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void FindTimeClosestToPoint(const Vector3& point, const Spline::Keyframe& start, const Spline::Keyframe& end, float& bestDistanceSquared, float& bestTime)
|
||||
{
|
||||
// TODO: implement sth more analytical than brute-force solution
|
||||
const int32 slices = 100;
|
||||
const float step = 1.0f / (float)slices;
|
||||
const float length = Math::Abs(end.Time - start.Time);
|
||||
for (int32 i = 0; i <= slices; i++)
|
||||
{
|
||||
const float t = (float)i * step;
|
||||
Transform result;
|
||||
Spline::Keyframe::Interpolate(start, end, t, length, result);
|
||||
const float distanceSquared = Vector3::DistanceSquared(point, result.Translation);
|
||||
if (distanceSquared < bestDistanceSquared)
|
||||
{
|
||||
bestDistanceSquared = distanceSquared;
|
||||
bestTime = start.Time + t * length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Spline::GetSplineTimeClosestToPoint(const Vector3& point) const
|
||||
{
|
||||
const int32 pointsCount = Curve.GetKeyframes().Count();
|
||||
if (pointsCount == 0)
|
||||
return 0.0f;
|
||||
if (pointsCount == 1)
|
||||
return Curve[0].Time;
|
||||
const Vector3 localPoint = _transform.WorldToLocal(point);
|
||||
float bestDistanceSquared = MAX_float;
|
||||
float bestTime = 0.0f;
|
||||
for (int32 i = 1; i < pointsCount; i++)
|
||||
FindTimeClosestToPoint(localPoint, Curve[i - 1], Curve[i], bestDistanceSquared, bestTime);
|
||||
return bestTime;
|
||||
}
|
||||
|
||||
Vector3 Spline::GetSplinePointClosestToPoint(const Vector3& point) const
|
||||
{
|
||||
return GetSplinePoint(GetSplineTimeClosestToPoint(point));
|
||||
}
|
||||
|
||||
void Spline::GetSplinePoints(Array<Vector3>& points) const
|
||||
{
|
||||
for (auto& e : Curve.GetKeyframes())
|
||||
points.Add(_transform.LocalToWorld(e.Value.Translation));
|
||||
}
|
||||
|
||||
void Spline::GetSplineLocalPoints(Array<Vector3>& points) const
|
||||
{
|
||||
for (auto& e : Curve.GetKeyframes())
|
||||
points.Add(e.Value.Translation);
|
||||
}
|
||||
|
||||
void Spline::GetSplinePoints(Array<Transform>& points) const
|
||||
{
|
||||
for (auto& e : Curve.GetKeyframes())
|
||||
points.Add(_transform.LocalToWorld(e.Value));
|
||||
}
|
||||
|
||||
void Spline::GetSplineLocalPoints(Array<Transform>& points) const
|
||||
{
|
||||
for (auto& e : Curve.GetKeyframes())
|
||||
points.Add(e.Value);
|
||||
}
|
||||
|
||||
void Spline::ClearSpline()
|
||||
{
|
||||
if (Curve.IsEmpty())
|
||||
return;
|
||||
Curve.Clear();
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
void Spline::AddSplinePoint(const Vector3& point, bool updateSpline)
|
||||
{
|
||||
const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(point));
|
||||
Curve.GetKeyframes().Add(k);
|
||||
if (updateSpline)
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
void Spline::AddSplineLocalPoint(const Vector3& point, bool updateSpline)
|
||||
{
|
||||
const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(_transform.WorldToLocal(point)));
|
||||
Curve.GetKeyframes().Add(k);
|
||||
if (updateSpline)
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
void Spline::AddSplinePoint(const Transform& point, bool updateSpline)
|
||||
{
|
||||
const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, _transform.WorldToLocal(point));
|
||||
Curve.GetKeyframes().Add(k);
|
||||
if (updateSpline)
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
void Spline::AddSplineLocalPoint(const Transform& point, bool updateSpline)
|
||||
{
|
||||
const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, point);
|
||||
Curve.GetKeyframes().Add(k);
|
||||
if (updateSpline)
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
void Spline::SetTangentsLinear()
|
||||
{
|
||||
const int32 count = Curve.GetKeyframes().Count();
|
||||
if (count < 2)
|
||||
return;
|
||||
|
||||
if (_loop)
|
||||
Curve[count - 1].Value = Curve[0].Value;
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
auto& k = Curve[i];
|
||||
k.TangentIn = k.TangentOut = Transform::Identity;
|
||||
}
|
||||
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
void Spline::SetTangentsSmooth()
|
||||
{
|
||||
const int32 count = Curve.GetKeyframes().Count();
|
||||
if (count < 2)
|
||||
return;
|
||||
|
||||
auto& keys = Curve.GetKeyframes();
|
||||
const int32 last = count - 2;
|
||||
if (_loop)
|
||||
Curve[count - 1].Value = Curve[0].Value;
|
||||
for (int32 i = 0; i <= last; i++)
|
||||
{
|
||||
auto& key = keys[i];
|
||||
const auto& prevKey = keys[i == 0 ? (_loop ? last : 0) : i - 1];
|
||||
const auto& nextKey = keys[i == last ? (_loop ? 0 : last) : i + 1];
|
||||
const float prevTime = _loop && i == 0 ? key.Time : prevKey.Time;
|
||||
const float nextTime = _loop && i == last ? key.Time : nextKey.Time;
|
||||
const Vector3 slope = key.Value.Translation - prevKey.Value.Translation + nextKey.Value.Translation - key.Value.Translation;
|
||||
const Vector3 tangent = slope / Math::Max(nextTime - prevTime, ZeroTolerance);
|
||||
key.TangentIn.Translation = -tangent;
|
||||
key.TangentOut.Translation = tangent;
|
||||
}
|
||||
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
void Spline::UpdateSpline()
|
||||
{
|
||||
// Always keep last point in the loop
|
||||
const int32 count = Curve.GetKeyframes().Count();
|
||||
if (_loop && count > 1)
|
||||
{
|
||||
auto& first = Curve[0];
|
||||
auto& last = Curve[count - 1];
|
||||
last.Value = first.Value;
|
||||
last.TangentIn = first.TangentIn;
|
||||
last.TangentOut = first.TangentOut;
|
||||
}
|
||||
}
|
||||
|
||||
void Spline::GetKeyframes(MonoArray* data)
|
||||
{
|
||||
Platform::MemoryCopy(mono_array_addr_with_size(data, sizeof(Keyframe), 0), Curve.GetKeyframes().Get(), sizeof(Keyframe) * Curve.GetKeyframes().Count());
|
||||
}
|
||||
|
||||
void Spline::SetKeyframes(MonoArray* data)
|
||||
{
|
||||
const auto count = (int32)mono_array_length(data);
|
||||
Curve.GetKeyframes().Resize(count, false);
|
||||
Platform::MemoryCopy(Curve.GetKeyframes().Get(), mono_array_addr_with_size(data, sizeof(Keyframe), 0), sizeof(Keyframe) * count);
|
||||
UpdateSpline();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
void DrawSegment(Spline* spline, int32 start, int32 end, const Color& color, const Transform& transform, bool depthTest)
|
||||
{
|
||||
const auto& startKey = spline->Curve[start];
|
||||
const auto& endKey = spline->Curve[end];
|
||||
const Vector3 startPos = transform.LocalToWorld(startKey.Value.Translation);
|
||||
const Vector3& startTangent = startKey.TangentOut.Translation;
|
||||
const Vector3 endPos = transform.LocalToWorld(endKey.Value.Translation);
|
||||
const Vector3& endTangent = endKey.TangentIn.Translation;
|
||||
const float d = (endKey.Time - startKey.Time) / 3.0f;
|
||||
DEBUG_DRAW_BEZIER(startPos, startPos + startTangent * d, endPos + endTangent * d, endPos, color, 0.0f, depthTest);
|
||||
}
|
||||
|
||||
void DrawSpline(Spline* spline, const Color& color, const Transform& transform, bool depthTest)
|
||||
{
|
||||
const int32 count = spline->Curve.GetKeyframes().Count();
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 p = transform.LocalToWorld(spline->Curve[i].Value.Translation);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(p, 5.0f), color, 0.0f, true);
|
||||
if (i != 0)
|
||||
DrawSegment(spline, i - 1, i, color, transform, depthTest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Spline::OnDebugDraw()
|
||||
{
|
||||
const Color color = GetSplineColor();
|
||||
DrawSpline(this, color.AlphaMultiplied(0.7f), _transform, true);
|
||||
|
||||
// Base
|
||||
Actor::OnDebugDraw();
|
||||
}
|
||||
|
||||
void Spline::OnDebugDrawSelected()
|
||||
{
|
||||
const Color color = GetSplineColor();
|
||||
DrawSpline(this, color.AlphaMultiplied(0.3f), _transform, false);
|
||||
DrawSpline(this, color, _transform, true);
|
||||
|
||||
// Base
|
||||
Actor::OnDebugDrawSelected();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Spline::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
// Base
|
||||
Actor::Serialize(stream, otherObj);
|
||||
|
||||
SERIALIZE_GET_OTHER_OBJ(Spline);
|
||||
|
||||
SERIALIZE_MEMBER(IsLoop, _loop);
|
||||
SERIALIZE(Curve);
|
||||
}
|
||||
|
||||
void Spline::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
{
|
||||
// Base
|
||||
Actor::Deserialize(stream, modifier);
|
||||
|
||||
DESERIALIZE_MEMBER(IsLoop, _loop);
|
||||
DESERIALIZE(Curve);
|
||||
|
||||
// Initialize spline when loading data during gameplay
|
||||
if (IsDuringPlay())
|
||||
{
|
||||
UpdateSpline();
|
||||
}
|
||||
}
|
||||
|
||||
void Spline::OnEnable()
|
||||
{
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
|
||||
// Initialize spline
|
||||
UpdateSpline();
|
||||
}
|
||||
271
Source/Engine/Level/Actors/Spline.h
Normal file
271
Source/Engine/Level/Actors/Spline.h
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Actor.h"
|
||||
#include "Engine/Animations/Curve.h"
|
||||
|
||||
/// <summary>
|
||||
/// Spline shape actor that defines spatial curve with utility functions for general purpose usage.
|
||||
/// </summary>
|
||||
API_CLASS() class FLAXENGINE_API Spline : public Actor
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(Spline);
|
||||
typedef BezierCurveKeyframe<Transform> Keyframe;
|
||||
private:
|
||||
|
||||
bool _loop = false;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The spline bezier curve points represented as series of transformations in 3D space (with tangents). Points are stored in local-space of the actor.
|
||||
/// </summary>
|
||||
/// <remarks>Ensure to call UpdateSpline() after editing curve to reflect the changes.</remarks>
|
||||
BezierCurve<Transform> Curve;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use spline as closed loop. In that case, ensure to place start and end at the same location.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(0), EditorDisplay(\"Spline\")")
|
||||
FORCE_INLINE bool GetIsLoop() const
|
||||
{
|
||||
return _loop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use spline as closed loop. In that case, ensure to place start and end at the same location.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetIsLoop(bool value);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given time and calculates the point location in 3D (world-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve point location (world-space).</returns>
|
||||
API_FUNCTION() Vector3 GetSplinePoint(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given time and calculates the point location in 3D (local-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve point location (local-space).</returns>
|
||||
API_FUNCTION() Vector3 GetSplineLocalPoint(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given time and calculates the point rotation in 3D (world-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve point rotation (world-space).</returns>
|
||||
API_FUNCTION() Quaternion GetSplineOrientation(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given time and calculates the point rotation in 3D (local-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve point rotation (local-space).</returns>
|
||||
API_FUNCTION() Quaternion GetSplineLocalOrientation(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given time and calculates the point scale in 3D (world-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve point scale (world-space).</returns>
|
||||
API_FUNCTION() Vector3 GetSplineScale(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given time and calculates the point scale in 3D (local-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve point scale (local-space).</returns>
|
||||
API_FUNCTION() Vector3 GetSplineLocalScale(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given time and calculates the transformation in 3D (world-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve point transformation (world-space).</returns>
|
||||
API_FUNCTION() Transform GetSplineTransform(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given time and calculates the transformation in 3D (local-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve point transformation (local-space).</returns>
|
||||
API_FUNCTION() Transform GetSplineLocalTransform(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve direction (forward vector, aka position 1st derivative) at the given time in 3D (world-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve direction (world-space).</returns>
|
||||
API_FUNCTION() Vector3 GetSplineDirection(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve direction (forward vector, aka position 1st derivative) at the given time in 3D (local-space).
|
||||
/// </summary>
|
||||
/// <param name="time">The time value. Can be negative or larger than curve length (curve will loop or clamp).</param>
|
||||
/// <returns>The calculated curve direction (local-space).</returns>
|
||||
API_FUNCTION() Vector3 GetSplineLocalDirection(float time) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given index (world-space).
|
||||
/// </summary>
|
||||
/// <param name="index">The curve keyframe index. Zero-based, smaller than GetSplinePointsCount().</param>
|
||||
/// <returns>The curve point location (world-space).</returns>
|
||||
API_FUNCTION() Vector3 GetSplinePoint(int32 index) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given index (local-space).
|
||||
/// </summary>
|
||||
/// <param name="index">The curve keyframe index. Zero-based, smaller than GetSplinePointsCount().</param>
|
||||
/// <returns>The curve point location (local-space).</returns>
|
||||
API_FUNCTION() Vector3 GetSplineLocalPoint(int32 index) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given index (world-space).
|
||||
/// </summary>
|
||||
/// <param name="index">The curve keyframe index. Zero-based, smaller than GetSplinePointsCount().</param>
|
||||
/// <returns>The curve point transformation (world-space).</returns>
|
||||
API_FUNCTION() Transform GetSplineTransform(int32 index) const;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the spline curve at the given index (local-space).
|
||||
/// </summary>
|
||||
/// <param name="index">The curve keyframe index. Zero-based, smaller than GetSplinePointsCount().</param>
|
||||
/// <returns>The curve point transformation (local-space).</returns>
|
||||
API_FUNCTION() Transform GetSplineLocalTransform(int32 index) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of points in the spline.
|
||||
/// </summary>
|
||||
API_PROPERTY() int32 GetSplinePointsCount() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total duration of the spline curve (time of the last point).
|
||||
/// </summary>
|
||||
API_PROPERTY() float GetSplineDuration() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total length of the spline curve (distance between all the points).
|
||||
/// </summary>
|
||||
API_PROPERTY() float GetSplineLength() const;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the closest point to the given location and returns the spline time at that point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point in world-space to find the spline point that is closest to it.</param>
|
||||
/// <returns>The spline time.</returns>
|
||||
API_FUNCTION() float GetSplineTimeClosestToPoint(const Vector3& point) const;
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the closest point to the given location.
|
||||
/// </summary>
|
||||
/// <param name="point">The point in world-space to find the spline point that is closest to it.</param>
|
||||
/// <returns>The spline position.</returns>
|
||||
API_FUNCTION() Vector3 GetSplinePointClosestToPoint(const Vector3& point) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the spline curve points list (world-space).
|
||||
/// </summary>
|
||||
/// <param name="points">The result points collection.</param>
|
||||
API_FUNCTION() void GetSplinePoints(API_PARAM(Out) Array<Vector3>& points) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the spline curve points list (local-space).
|
||||
/// </summary>
|
||||
/// <param name="points">The result points collection.</param>
|
||||
API_FUNCTION() void GetSplineLocalPoints(API_PARAM(Out) Array<Vector3>& points) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the spline curve points list (world-space).
|
||||
/// </summary>
|
||||
/// <param name="points">The result points collection.</param>
|
||||
API_FUNCTION() void GetSplinePoints(API_PARAM(Out) Array<Transform>& points) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the spline curve points list (local-space).
|
||||
/// </summary>
|
||||
/// <param name="points">The result points collection.</param>
|
||||
API_FUNCTION() void GetSplineLocalPoints(API_PARAM(Out) Array<Transform>& points) const;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Clears the spline to be empty.
|
||||
/// </summary>
|
||||
API_FUNCTION() void ClearSpline();
|
||||
|
||||
/// <summary>
|
||||
/// Adds the point to the spline curve (at the end).
|
||||
/// </summary>
|
||||
/// <param name="point">The location of the point to add to the curve (world-space).</param>
|
||||
/// <param name="updateSpline">True if update spline after adding the point, otherwise false.</param>
|
||||
API_FUNCTION() void AddSplinePoint(const Vector3& point, bool updateSpline = true);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the point to the spline curve (at the end).
|
||||
/// </summary>
|
||||
/// <param name="point">The location of the point to add to the curve (local-space).</param>
|
||||
/// <param name="updateSpline">True if update spline after adding the point, otherwise false.</param>
|
||||
API_FUNCTION() void AddSplineLocalPoint(const Vector3& point, bool updateSpline = true);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the point to the spline curve (at the end).
|
||||
/// </summary>
|
||||
/// <param name="point">The transformation of the point to add to the curve (world-space).</param>
|
||||
/// <param name="updateSpline">True if update spline after adding the point, otherwise false.</param>
|
||||
API_FUNCTION() void AddSplinePoint(const Transform& point, bool updateSpline = true);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the point to the spline curve (at the end).
|
||||
/// </summary>
|
||||
/// <param name="point">The transformation of the point to add to the curve (local-space).</param>
|
||||
/// <param name="updateSpline">True if update spline after adding the point, otherwise false.</param>
|
||||
API_FUNCTION() void AddSplineLocalPoint(const Transform& point, bool updateSpline = true);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the curve tangent points to make curve linear.
|
||||
/// </summary>
|
||||
API_FUNCTION() void SetTangentsLinear();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the curve tangent points to make curve smooth.
|
||||
/// </summary>
|
||||
API_FUNCTION() void SetTangentsSmooth();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Updates the spline after it was modified. Recreates the collision and/or any cached state that depends on the spline type.
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void UpdateSpline();
|
||||
|
||||
protected:
|
||||
|
||||
#if USE_EDITOR
|
||||
virtual Color GetSplineColor()
|
||||
{
|
||||
return Color::White;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// Internal bindings
|
||||
API_FUNCTION(NoProxy) void GetKeyframes(MonoArray* data);
|
||||
API_FUNCTION(NoProxy) void SetKeyframes(MonoArray* data);
|
||||
|
||||
public:
|
||||
|
||||
// [Actor]
|
||||
#if USE_EDITOR
|
||||
void OnDebugDraw() override;
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
void OnEnable() override;
|
||||
};
|
||||
41
Source/Engine/Level/Spline.cs
Normal file
41
Source/Engine/Level/Spline.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FlaxEngine
|
||||
{
|
||||
partial class Spline
|
||||
{
|
||||
private BezierCurve<Transform>.Keyframe[] _keyframes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the spline keyframes collection.
|
||||
/// </summary>
|
||||
[Unmanaged]
|
||||
[Tooltip("Spline keyframes collection.")]
|
||||
[EditorOrder(10), EditorDisplay("Spline"), Collection(CanReorderItems = false)]
|
||||
public BezierCurve<Transform>.Keyframe[] SplineKeyframes
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = SplinePointsCount;
|
||||
if (_keyframes == null || _keyframes.Length != count)
|
||||
_keyframes = new BezierCurve<Transform>.Keyframe[count];
|
||||
#if !BUILD_RELEASE
|
||||
if (Marshal.SizeOf(typeof(BezierCurve<Transform>.Keyframe)) != Transform.SizeInBytes * 3 + sizeof(float))
|
||||
throw new Exception("Invalid size of BezierCurve keyframe " + Marshal.SizeOf(typeof(BezierCurve<Transform>.Keyframe)) + " bytes.");
|
||||
#endif
|
||||
Internal_GetKeyframes(__unmanagedPtr, _keyframes);
|
||||
return _keyframes;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
value = Utils.GetEmptyArray<BezierCurve<Transform>.Keyframe>();
|
||||
_keyframes = null;
|
||||
Internal_SetKeyframes(__unmanagedPtr, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,14 @@ namespace FlaxEngine.GUI
|
||||
protected bool _mouseOverHeader;
|
||||
|
||||
/// <summary>
|
||||
/// The 'mouse down' flag (over header).
|
||||
/// The 'mouse down' flag (over header) for the left mouse button.
|
||||
/// </summary>
|
||||
protected bool _mouseDown;
|
||||
protected bool _mouseButtonLeftDown;
|
||||
|
||||
/// <summary>
|
||||
/// The 'mouse down' flag (over header) for the right mouse button.
|
||||
/// </summary>
|
||||
protected bool _mouseButtonRightDown;
|
||||
|
||||
/// <summary>
|
||||
/// The animation progress (normalized).
|
||||
@@ -126,6 +131,11 @@ namespace FlaxEngine.GUI
|
||||
[EditorDisplay("Style"), EditorOrder(2000)]
|
||||
public bool EnableDropDownIcon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when mouse right-clicks over the header.
|
||||
/// </summary>
|
||||
public event Action<DropPanel, Vector2> MouseButtonRightClicked;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when drop panel is opened or closed.
|
||||
/// </summary>
|
||||
@@ -430,10 +440,14 @@ namespace FlaxEngine.GUI
|
||||
return true;
|
||||
|
||||
_mouseOverHeader = HeaderRectangle.Contains(location);
|
||||
|
||||
if (button == MouseButton.Left && _mouseOverHeader)
|
||||
{
|
||||
_mouseDown = true;
|
||||
_mouseButtonLeftDown = true;
|
||||
return true;
|
||||
}
|
||||
if (button == MouseButton.Right && _mouseOverHeader)
|
||||
{
|
||||
_mouseButtonRightDown = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -455,16 +469,17 @@ namespace FlaxEngine.GUI
|
||||
return true;
|
||||
|
||||
_mouseOverHeader = HeaderRectangle.Contains(location);
|
||||
|
||||
if (button == MouseButton.Left && _mouseDown)
|
||||
if (button == MouseButton.Left && _mouseButtonLeftDown)
|
||||
{
|
||||
_mouseDown = false;
|
||||
|
||||
_mouseButtonLeftDown = false;
|
||||
if (_mouseOverHeader)
|
||||
{
|
||||
Toggle();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (button == MouseButton.Right && _mouseButtonRightDown)
|
||||
{
|
||||
_mouseButtonRightDown = false;
|
||||
MouseButtonRightClicked?.Invoke(this, location);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -474,7 +489,8 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
_mouseDown = false;
|
||||
_mouseButtonLeftDown = false;
|
||||
_mouseButtonRightDown = false;
|
||||
_mouseOverHeader = false;
|
||||
|
||||
base.OnMouseLeave();
|
||||
|
||||
Reference in New Issue
Block a user