Refactor Bezier splines drawing and editing to property evaluate value and match curve evaluation

#3051
This commit is contained in:
Wojtek Figat
2024-12-02 19:10:28 +01:00
parent 0a4a431f74
commit 57628c3d5f
12 changed files with 129 additions and 93 deletions

View File

@@ -66,27 +66,23 @@ namespace AnimationUtils
}
template<class T>
FORCE_INLINE static void GetTangent(const T& a, const T& b, float length, T& result)
FORCE_INLINE static void GetTangent(const T& value, const T& tangent, float tangentScale, T& result)
{
const float oneThird = 1.0f / 3.0f;
result = a + b * (length * oneThird);
result = value + tangent * tangentScale;
}
template<>
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& a, const Quaternion& b, float length, Quaternion& result)
FORCE_INLINE void GetTangent<Quaternion>(const Quaternion& value, const Quaternion& tangent, float tangentScale, Quaternion& result)
{
const float oneThird = 1.0f / 3.0f;
Quaternion::Slerp(a, b, oneThird, result);
Quaternion::Slerp(value, tangent, 1.0f / 3.0f, result);
}
template<>
FORCE_INLINE void GetTangent<Transform>(const Transform& a, const Transform& b, float length, Transform& result)
FORCE_INLINE void GetTangent<Transform>(const Transform& value, const Transform& tangent, float tangentScale, 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 - a.Scale) * oneThirdLength;
GetTangent(value.Translation, tangent.Translation, tangentScale, result.Translation);
GetTangent(value.Orientation, tangent.Orientation, tangentScale, result.Orientation);
GetTangent(value.Scale, tangent.Scale, tangentScale, result.Scale);
}
template<class T>

View File

@@ -24,9 +24,9 @@ namespace FlaxEngine
/// </summary>
/// <param name="value">The value.</param>
/// <param name="tangent">The tangent.</param>
/// <param name="lengthThird">The length divided by 3.</param>
/// <param name="tangentScale">The tangent scale factor.</param>
/// <param name="result">The result.</param>
void GetTangent(ref U value, ref U tangent, float lengthThird, out U result);
void GetTangent(ref U value, ref U tangent, float tangentScale, out U result);
/// <summary>
/// Calculates the linear interpolation at the specified alpha.
@@ -67,7 +67,7 @@ namespace FlaxEngine
IKeyframeAccess<Color32>,
IKeyframeAccess<Color>
{
public void GetTangent(ref bool value, ref bool tangent, float lengthThird, out bool result)
public void GetTangent(ref bool value, ref bool tangent, float tangentScale, out bool result)
{
result = value;
}
@@ -82,9 +82,9 @@ namespace FlaxEngine
result = p0;
}
public void GetTangent(ref int value, ref int tangent, float lengthThird, out int result)
public void GetTangent(ref int value, ref int tangent, float tangentScale, out int result)
{
result = value + (int)(tangent * lengthThird);
result = value + (int)(tangent * tangentScale);
}
public void Linear(ref int a, ref int b, float alpha, out int result)
@@ -102,9 +102,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref double value, ref double tangent, float lengthThird, out double result)
public void GetTangent(ref double value, ref double tangent, float tangentScale, out double result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref double a, ref double b, float alpha, out double result)
@@ -122,9 +122,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref float value, ref float tangent, float lengthThird, out float result)
public void GetTangent(ref float value, ref float tangent, float tangentScale, out float result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref float a, ref float b, float alpha, out float result)
@@ -142,9 +142,9 @@ namespace FlaxEngine
result = Mathf.Lerp(p012, p123, alpha);
}
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float lengthThird, out Vector2 result)
public void GetTangent(ref Vector2 value, ref Vector2 tangent, float tangentScale, out Vector2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector2 a, ref Vector2 b, float alpha, out Vector2 result)
@@ -162,9 +162,9 @@ namespace FlaxEngine
Vector2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float lengthThird, out Vector3 result)
public void GetTangent(ref Vector3 value, ref Vector3 tangent, float tangentScale, out Vector3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector3 a, ref Vector3 b, float alpha, out Vector3 result)
@@ -182,9 +182,9 @@ namespace FlaxEngine
Vector3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float lengthThird, out Vector4 result)
public void GetTangent(ref Vector4 value, ref Vector4 tangent, float tangentScale, out Vector4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Vector4 a, ref Vector4 b, float alpha, out Vector4 result)
@@ -202,9 +202,9 @@ namespace FlaxEngine
Vector4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float2 value, ref Float2 tangent, float lengthThird, out Float2 result)
public void GetTangent(ref Float2 value, ref Float2 tangent, float tangentScale, out Float2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float2 a, ref Float2 b, float alpha, out Float2 result)
@@ -222,9 +222,9 @@ namespace FlaxEngine
Float2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float3 value, ref Float3 tangent, float lengthThird, out Float3 result)
public void GetTangent(ref Float3 value, ref Float3 tangent, float tangentScale, out Float3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float3 a, ref Float3 b, float alpha, out Float3 result)
@@ -242,9 +242,9 @@ namespace FlaxEngine
Float3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Float4 value, ref Float4 tangent, float lengthThird, out Float4 result)
public void GetTangent(ref Float4 value, ref Float4 tangent, float tangentScale, out Float4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Float4 a, ref Float4 b, float alpha, out Float4 result)
@@ -262,9 +262,9 @@ namespace FlaxEngine
Float4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double2 value, ref Double2 tangent, float lengthThird, out Double2 result)
public void GetTangent(ref Double2 value, ref Double2 tangent, float tangentScale, out Double2 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double2 a, ref Double2 b, float alpha, out Double2 result)
@@ -282,9 +282,9 @@ namespace FlaxEngine
Double2.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double3 value, ref Double3 tangent, float lengthThird, out Double3 result)
public void GetTangent(ref Double3 value, ref Double3 tangent, float tangentScale, out Double3 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double3 a, ref Double3 b, float alpha, out Double3 result)
@@ -302,9 +302,9 @@ namespace FlaxEngine
Double3.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Double4 value, ref Double4 tangent, float lengthThird, out Double4 result)
public void GetTangent(ref Double4 value, ref Double4 tangent, float tangentScale, out Double4 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Double4 a, ref Double4 b, float alpha, out Double4 result)
@@ -322,7 +322,7 @@ namespace FlaxEngine
Double4.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float lengthThird, out Quaternion result)
public void GetTangent(ref Quaternion value, ref Quaternion tangent, float tangentScale, out Quaternion result)
{
Quaternion.Slerp(ref value, ref tangent, 1.0f / 3.0f, out result);
}
@@ -342,9 +342,9 @@ namespace FlaxEngine
Quaternion.Slerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Color32 value, ref Color32 tangent, float lengthThird, out Color32 result)
public void GetTangent(ref Color32 value, ref Color32 tangent, float tangentScale, out Color32 result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Color32 a, ref Color32 b, float alpha, out Color32 result)
@@ -362,9 +362,9 @@ namespace FlaxEngine
Color32.Lerp(ref p012, ref p123, alpha, out result);
}
public void GetTangent(ref Color value, ref Color tangent, float lengthThird, out Color result)
public void GetTangent(ref Color value, ref Color tangent, float tangentScale, out Color result)
{
result = value + tangent * lengthThird;
result = value + tangent * tangentScale;
}
public void Linear(ref Color a, ref Color b, float alpha, out Color result)
@@ -860,9 +860,9 @@ namespace FlaxEngine
// Evaluate the key at the curve
result.Time = leftKey.Time + length * t;
float lengthThird = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
float tangentScale = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result.Value);
result.TangentIn = leftKey.TangentOut;
result.TangentOut = rightKey.TangentIn;
@@ -895,9 +895,9 @@ namespace FlaxEngine
float t = Mathf.NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length;
// Evaluate the value at the curve
float lengthThird = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent);
float tangentScale = length / 3.0f;
_accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent);
_accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent);
_accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result);
}

View File

@@ -247,16 +247,18 @@ public:
static void Interpolate(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);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, 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);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result);
}
@@ -264,8 +266,9 @@ public:
{
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);
const float tangentScale = length / 3.0f;
AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent);
AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value);
result.TangentIn = a.TangentOut;
result.TangentOut = b.TangentIn;

View File

@@ -158,10 +158,10 @@ float Spline::GetSplineLength() const
const auto& b = Curve[i];
Vector3 prevPoint = a.Value.Translation * scale;
const float length = Math::Abs(b.Time - a.Time);
const float tangentScale = Math::Abs(b.Time - a.Time) / 3.0f;
Vector3 leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, tangentScale, rightTangent);
for (int32 slice = 1; slice < slices; slice++)
{
@@ -189,10 +189,10 @@ float Spline::GetSplineSegmentLength(int32 index) const
const Vector3 scale = _transform.Scale;
Vector3 prevPoint = a.Value.Translation * scale;
{
const float length = Math::Abs(b.Time - a.Time);
const float tangentScale = Math::Abs(b.Time - a.Time) / 3.0f;
Vector3 leftTangent, rightTangent;
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, tangentScale, leftTangent);
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, tangentScale, rightTangent);
for (int32 slice = 1; slice < slices; slice++)
{

View File

@@ -184,9 +184,9 @@ void SplineModel::OnSplineUpdated()
auto& instance = _instances[segment];
const auto& start = keyframes[segment];
const auto& end = keyframes[segment + 1];
const float length = end.Time - start.Time;
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
const float tangentScale = (end.Time - start.Time) / 3.0f;
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
// Find maximum scale over the segment spline and collect the segment positions for bounds
segmentPoints.Clear();
@@ -256,9 +256,9 @@ void SplineModel::UpdateDeformationBuffer()
auto& instance = _instances[segment];
const auto& start = keyframes[segment];
const auto& end = keyframes[segment + 1];
const float length = end.Time - start.Time;
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
const float tangentScale = (end.Time - start.Time) / 3.0f;
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
for (int32 chunk = 0; chunk < chunksPerSegment; chunk++)
{
const float alpha = (chunk == chunksPerSegment - 1) ? 1.0f : ((float)chunk * chunksPerSegmentInv);
@@ -291,10 +291,10 @@ void SplineModel::UpdateDeformationBuffer()
{
const auto& start = keyframes[segments - 1];
const auto& end = keyframes[segments];
const float length = end.Time - start.Time;
const float tangentScale = (end.Time - start.Time) / 3.0f;
const float alpha = 1.0f - ZeroTolerance; // Offset to prevent zero derivative at the end of the curve
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform);
Vector3 direction;
AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction);

View File

@@ -214,9 +214,9 @@ void SplineCollider::GetGeometry(CollisionShape& collision)
auto offsetIndices = segment * collisionIndices.Count();
const auto& start = keyframes[segment];
const auto& end = keyframes[segment + 1];
const float length = end.Time - start.Time;
AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent);
const float tangentScale = (end.Time - start.Time) / 3.0f;
AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent);
AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent);
// Vertex buffer is deformed along the spline
auto srcVertices = collisionVertices.Get();

View File

@@ -1882,19 +1882,46 @@ void Render2D::DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3,
const Float2 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;
// Draw segmented curve
Float2 p;
AnimationUtils::Bezier(p1, p2, p3, p4, 0, p);
Lines2.Clear();
Lines2.Add(p);
for (int32 i = 1; i <= segmentCount; i++)
Lines2.Add(p1);
for (int32 i = 1; i < segmentCount; i++)
{
const float t = i * segmentCountInv;
const float t = (float)i * segmentCountInv;
Float2 p;
AnimationUtils::Bezier(p1, p2, p3, p4, t, p);
Lines2.Add(p);
}
Lines2.Add(p4);
DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness);
}
void Render2D::DrawSpline(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness)
{
RENDER2D_CHECK_RENDERING_STATE;
// Find amount of segments to use
const Float2 d1 = p2 - p1;
const Float2 d2 = p3 - p2;
const Float2 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 / (float)segmentCount;
// Draw segmented curve
Lines2.Clear();
Lines2.Add(p1);
for (int32 i = 1; i < segmentCount; i++)
{
const float t = (float)i * segmentCountInv;
Float2 p;
p.X = Math::Lerp(p1.X, p4.X, t);
AnimationUtils::Bezier(p1.Y, p2.Y, p3.Y, p4.Y, t, p.Y);
Lines2.Add(p);
}
Lines2.Add(p4);
DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness);
}

View File

@@ -389,6 +389,17 @@ public:
/// <param name="thickness">The line thickness.</param>
API_FUNCTION() static void DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f);
/// <summary>
/// Draws a spline curve (Bezier but X axis represents uniform time).
/// </summary>
/// <param name="p1">The start point.</param>
/// <param name="p2">The first control point.</param>
/// <param name="p3">The second control point.</param>
/// <param name="p4">The end point.</param>
/// <param name="color">The line color</param>
/// <param name="thickness">The line thickness.</param>
API_FUNCTION() static void DrawSpline(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f);
/// <summary>
/// Draws the GUI material.
/// </summary>