mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 18:23:04 +08:00
Fix bulbs on Catmull sliders
This commit is contained in:
parent
6ef54321c4
commit
600098d845
@ -42,6 +42,17 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
private readonly List<double> cumulativeLength = new List<double>();
|
private readonly List<double> cumulativeLength = new List<double>();
|
||||||
private readonly Cached pathCache = new Cached();
|
private readonly Cached pathCache = new Cached();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Any additional length of the path which was optimised out during piecewise approximation, but should still be considered as part of <see cref="calculatedLength"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is a hack for Catmull paths.
|
||||||
|
/// </remarks>
|
||||||
|
private double optimisedLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The final calculated length of the path.
|
||||||
|
/// </summary>
|
||||||
private double calculatedLength;
|
private double calculatedLength;
|
||||||
|
|
||||||
private readonly List<int> segmentEnds = new List<int>();
|
private readonly List<int> segmentEnds = new List<int>();
|
||||||
@ -244,6 +255,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
{
|
{
|
||||||
calculatedPath.Clear();
|
calculatedPath.Clear();
|
||||||
segmentEnds.Clear();
|
segmentEnds.Clear();
|
||||||
|
optimisedLength = 0;
|
||||||
|
|
||||||
if (ControlPoints.Count == 0)
|
if (ControlPoints.Count == 0)
|
||||||
return;
|
return;
|
||||||
@ -268,7 +280,8 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
calculatedPath.Add(segmentVertices[0]);
|
calculatedPath.Add(segmentVertices[0]);
|
||||||
else if (segmentVertices.Length > 1)
|
else if (segmentVertices.Length > 1)
|
||||||
{
|
{
|
||||||
List<Vector2> subPath = calculateSubPath(segmentVertices, segmentType);
|
List<Vector2> subPath = calculateSubPath(segmentVertices, segmentType, ref optimisedLength);
|
||||||
|
|
||||||
// Skip the first vertex if it is the same as the last vertex from the previous segment
|
// Skip the first vertex if it is the same as the last vertex from the previous segment
|
||||||
bool skipFirst = calculatedPath.Count > 0 && subPath.Count > 0 && calculatedPath.Last() == subPath[0];
|
bool skipFirst = calculatedPath.Count > 0 && subPath.Count > 0 && calculatedPath.Last() == subPath[0];
|
||||||
|
|
||||||
@ -287,7 +300,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Vector2> calculateSubPath(ReadOnlySpan<Vector2> subControlPoints, PathType type)
|
private static List<Vector2> calculateSubPath(ReadOnlySpan<Vector2> subControlPoints, PathType type, ref double optimisedLength)
|
||||||
{
|
{
|
||||||
switch (type.Type)
|
switch (type.Type)
|
||||||
{
|
{
|
||||||
@ -295,6 +308,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
return PathApproximator.LinearToPiecewiseLinear(subControlPoints);
|
return PathApproximator.LinearToPiecewiseLinear(subControlPoints);
|
||||||
|
|
||||||
case SplineType.PerfectCurve:
|
case SplineType.PerfectCurve:
|
||||||
|
{
|
||||||
if (subControlPoints.Length != 3)
|
if (subControlPoints.Length != 3)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -305,9 +319,55 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
return subPath;
|
return subPath;
|
||||||
|
}
|
||||||
|
|
||||||
case SplineType.Catmull:
|
case SplineType.Catmull:
|
||||||
return PathApproximator.CatmullToPiecewiseLinear(subControlPoints);
|
{
|
||||||
|
List<Vector2> subPath = PathApproximator.CatmullToPiecewiseLinear(subControlPoints);
|
||||||
|
|
||||||
|
// At draw time, osu!stable optimises paths by only keeping piecewise segments that are 6px apart.
|
||||||
|
// For the most part we don't care about this optimisation, and its additional heuristics are hard to reproduce in every implementation.
|
||||||
|
//
|
||||||
|
// However, it matters for Catmull paths which form "bulbs" around sequential knots with identical positions,
|
||||||
|
// so we'll apply a very basic form of the optimisation here and return a length representing the optimised portion.
|
||||||
|
// The returned length is important so that the optimisation doesn't cause the path to get extended to match the value of ExpectedDistance.
|
||||||
|
|
||||||
|
List<Vector2> optimisedPath = new List<Vector2>(subPath.Count);
|
||||||
|
|
||||||
|
Vector2? lastStart = null;
|
||||||
|
double lengthRemovedSinceStart = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < subPath.Count; i++)
|
||||||
|
{
|
||||||
|
if (lastStart == null)
|
||||||
|
{
|
||||||
|
optimisedPath.Add(subPath[i]);
|
||||||
|
lastStart = subPath[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(i > 0);
|
||||||
|
|
||||||
|
double distFromStart = Vector2.Distance(lastStart.Value, subPath[i]);
|
||||||
|
lengthRemovedSinceStart += Vector2.Distance(subPath[i - 1], subPath[i]);
|
||||||
|
|
||||||
|
// See PathApproximator.catmull_detail.
|
||||||
|
const int catmull_detail = 50;
|
||||||
|
const int catmull_segment_length = catmull_detail * 2;
|
||||||
|
|
||||||
|
// Either 6px from the start, the last vertex at every knot, or the end of the path.
|
||||||
|
if (distFromStart > 6 || (i + 1) % catmull_segment_length == 0 || i == subPath.Count - 1)
|
||||||
|
{
|
||||||
|
optimisedPath.Add(subPath[i]);
|
||||||
|
optimisedLength += lengthRemovedSinceStart - distFromStart;
|
||||||
|
|
||||||
|
lastStart = null;
|
||||||
|
lengthRemovedSinceStart = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return optimisedPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PathApproximator.BSplineToPiecewiseLinear(subControlPoints, type.Degree ?? subControlPoints.Length);
|
return PathApproximator.BSplineToPiecewiseLinear(subControlPoints, type.Degree ?? subControlPoints.Length);
|
||||||
@ -315,7 +375,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
|
|
||||||
private void calculateLength()
|
private void calculateLength()
|
||||||
{
|
{
|
||||||
calculatedLength = 0;
|
calculatedLength = optimisedLength;
|
||||||
cumulativeLength.Clear();
|
cumulativeLength.Clear();
|
||||||
cumulativeLength.Add(0);
|
cumulativeLength.Add(0);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user