feat: improve camera motion and shot transitions

This commit is contained in:
KINDNICK
2026-08-03 06:27:17 +09:00
parent 02d76bc87c
commit 80ff929a1c
22 changed files with 2563 additions and 129 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
fileFormatVersion: 2
guid: f8bdd831f8794ea18ca288f3745462b1
guid: 572fff6d310f4e669243b957117466c1
@@ -1,2 +1,2 @@
fileFormatVersion: 2
guid: 3ef4feff8a1c42a0be6affcf5d6429db
guid: c0bd869f0b114cca884580f0d6229b19
@@ -41,6 +41,15 @@ namespace Streamingle.Editor
private const string PreviewGenerationProvenanceName =
"AI Camera Preview Generation Provenance";
private const double CutBoundaryGuardSeconds = 0.000001;
private const int DirectionChangeDetectionFlankFrames = 5;
// Matches the worker's 0.35-second braking radius at 60 fps.
private const int DirectionChangePreserveRadiusFrames = 21;
private const float DirectionChangeMinimumSpeedMetersPerSecond = 0.04f;
// Preserve the generated braking envelope for every concentrated turn
// covered by the worker policy (40 degrees and above), not only an
// almost complete reversal. Linear curve simplification may otherwise
// collapse a mild 40--55 or clear 60/90-degree slowdown into a kink.
private const float DirectionChangeMaximumCosine = 0.76604444f;
public const CurveSimplificationPreset DefaultCurveSimplificationPreset =
CurveSimplificationPreset.Balanced;
@@ -2451,6 +2460,7 @@ namespace Streamingle.Editor
AddScalarExtrema(values.Count, index => values[index].x, required);
AddScalarExtrema(values.Count, index => values[index].y, required);
AddScalarExtrema(values.Count, index => values[index].z, required);
AddDirectionChangeNeighborhoods(times, values, required);
return SimplifyIndices(
times,
maximumError,
@@ -2593,6 +2603,61 @@ namespace Streamingle.Editor
indices.Add(maximumIndex);
}
private static void AddDirectionChangeNeighborhoods(
IReadOnlyList<double> times,
IReadOnlyList<Vector3> values,
ISet<int> indices)
{
var flank = DirectionChangeDetectionFlankFrames;
if (values.Count < flank * 2 + 1)
{
return;
}
for (var center = flank; center + flank < values.Count; center++)
{
var beforeDuration = times[center] - times[center - flank];
var afterDuration = times[center + flank] - times[center];
if (beforeDuration <= double.Epsilon ||
afterDuration <= double.Epsilon)
{
continue;
}
var beforeVelocity =
(values[center] - values[center - flank]) /
(float)beforeDuration;
var afterVelocity =
(values[center + flank] - values[center]) /
(float)afterDuration;
var beforeSpeed = beforeVelocity.magnitude;
var afterSpeed = afterVelocity.magnitude;
if (Mathf.Min(beforeSpeed, afterSpeed) <
DirectionChangeMinimumSpeedMetersPerSecond)
{
continue;
}
var cosine = Vector3.Dot(beforeVelocity, afterVelocity) /
(beforeSpeed * afterSpeed);
if (cosine > DirectionChangeMaximumCosine)
{
continue;
}
var start = Mathf.Max(
0,
center - DirectionChangePreserveRadiusFrames);
var end = Mathf.Min(
values.Count - 1,
center + DirectionChangePreserveRadiusFrames);
for (var index = start; index <= end; index++)
{
indices.Add(index);
}
}
}
private static Quaternion[] MakeQuaternionSequenceContinuous(
IReadOnlyList<Quaternion> rotations)
{