galaxer och motion blur
This commit is contained in:
@@ -6,7 +6,7 @@ using Universe.Core.World;
|
||||
namespace Universe.App.Runtime;
|
||||
|
||||
/// <summary>Repeatable camera path and frame-time capture for a selected planetary terrain scene.</summary>
|
||||
internal sealed class BenchmarkRunner
|
||||
internal sealed class BenchmarkRunner : IBenchmarkSession
|
||||
{
|
||||
private const double SpaceSeconds = 8;
|
||||
private const double DescentSeconds = 40;
|
||||
@@ -77,7 +77,7 @@ internal sealed class BenchmarkRunner
|
||||
_orbitBitangent = DVector3.Cross(_targetRadial, _orbitTangent).Normalized();
|
||||
|
||||
var initialRadius = _planet.RadiusMeters * 4.8;
|
||||
camera.SetPlayerPose(_body.Add(_spaceRadial * initialRadius, cellSizeMeters), -_spaceRadial, _spaceUp);
|
||||
camera.SetPlayerPose(_body.Add(_spaceRadial * initialRadius, cellSizeMeters), -_spaceRadial, _spaceUp, resetMotionHistory: true);
|
||||
}
|
||||
|
||||
public bool IsRunning => !_stopped && _elapsed < TotalSeconds;
|
||||
@@ -169,7 +169,7 @@ internal sealed class BenchmarkRunner
|
||||
_cellSize, cameraRadiusMeters: 1.5, samples: 32);
|
||||
if (_elapsed >= TotalSeconds)
|
||||
{
|
||||
camera.SetPlayerPose(_returnPosition, _returnForward, _returnUp);
|
||||
camera.SetPlayerPose(_returnPosition, _returnForward, _returnUp, resetMotionHistory: true);
|
||||
return;
|
||||
}
|
||||
var finalPosition = proposed;
|
||||
|
||||
@@ -18,11 +18,15 @@ public sealed class FlyCamera
|
||||
{
|
||||
Position = initialPosition;
|
||||
CellSizeMeters = cellSizeMeters;
|
||||
TravelLimitMeters = cellSizeMeters * .45;
|
||||
SpeedMetersPerSecond = 250_000;
|
||||
}
|
||||
public UniversePosition Position { get; private set; }
|
||||
public double CellSizeMeters { get; }
|
||||
public double SpeedMetersPerSecond { get; private set; }
|
||||
public double TravelLimitMeters { get; set; }
|
||||
public long MotionHistoryVersion { get; private set; }
|
||||
public void RestoreDefaultFieldOfView() => FieldOfViewRadians = _defaultFieldOfView;
|
||||
public bool IsFocusing => _flight is not null;
|
||||
public DVector3 Forward => _forward;
|
||||
public DVector3 Right => DVector3.Cross(_forward, _up).Normalized();
|
||||
@@ -37,16 +41,18 @@ public sealed class FlyCamera
|
||||
_forward = RotateAround(_forward, axis, -mouseDeltaY * 0.0022).Normalized();
|
||||
_up = RotateAround(beforePitchUp, axis, -mouseDeltaY * 0.0022).Normalized();
|
||||
}
|
||||
public void AdjustSpeed(double delta) => SpeedMetersPerSecond = System.Math.Clamp(SpeedMetersPerSecond * System.Math.Pow(1.7, delta), 0.1, UniverseScale.LightSpeedMetersPerSecond * 1_000_000);
|
||||
public void AdjustSpeed(double delta) => SpeedMetersPerSecond = System.Math.Clamp(SpeedMetersPerSecond * System.Math.Pow(1.7, delta), 0.1, UniverseScale.LightSpeedMetersPerSecond * 1e15);
|
||||
|
||||
public void SetPlayerPose(UniversePosition position, DVector3 forward, DVector3 up)
|
||||
public void SetPlayerPose(UniversePosition position, DVector3 forward, DVector3 up, bool resetMotionHistory = false)
|
||||
{
|
||||
if (resetMotionHistory) MotionHistoryVersion++;
|
||||
_flight = null;
|
||||
Position = position; _forward = forward.Normalized(); _up = up.Normalized();
|
||||
}
|
||||
|
||||
public void SetInitialView(StarSystemData system)
|
||||
{
|
||||
MotionHistoryVersion++;
|
||||
var planet = system.Planets[0];
|
||||
var center = UniversePosition.AtCellCenter(system.Coordinate, CellSizeMeters);
|
||||
var sun = (system.Stars[0].PositionMeters - planet.PositionMeters).Normalized();
|
||||
@@ -59,6 +65,7 @@ public sealed class FlyCamera
|
||||
|
||||
public void SetInitialView(StarSystemData system, StartupConfiguration startup)
|
||||
{
|
||||
MotionHistoryVersion++;
|
||||
var planet = system.Planets.FirstOrDefault(p => p.Id == startup.PlanetId)
|
||||
?? throw new InvalidDataException($"Startup planet '{startup.PlanetId}' does not exist in system '{system.Id}'.");
|
||||
var longitude = startup.LongitudeDegrees * System.Math.PI / 180;
|
||||
@@ -302,6 +309,22 @@ public sealed class FlyCamera
|
||||
star.RadiusMeters * .08, duration: 9, fieldOfView: 32 * System.Math.PI / 180, departure: departure);
|
||||
}
|
||||
|
||||
public void RequestCosmicView(UniversePosition center,double radius,PlanetOrientation orientation,bool nebula,IEnumerable<StarSystemData> departureSystems)
|
||||
{
|
||||
var radial=orientation.ToWorld((nebula?new DVector3(.1,.05,1):new DVector3(.1,1,.3)).Normalized());
|
||||
var up=orientation.ToWorld(nebula?new(0,1,0):new DVector3(0,0,-1));
|
||||
Departure? departure=null;
|
||||
var escape=departureSystems.SelectMany(s=>s.Planets.Where(p=>!p.IsGasPlanet).Select(p=>(System:s,Planet:p)))
|
||||
.MinBy(p=>World(p.System,p.Planet.PositionMeters).RelativeTo(Position,CellSizeMeters).Length/p.Planet.RadiusMeters);
|
||||
if(escape.Planet is {} solid)
|
||||
{
|
||||
var origin=World(escape.System,solid.PositionMeters);var start=Position.RelativeTo(origin,CellSizeMeters);
|
||||
if(start.Length<solid.MaximumTerrainRadiusMeters*1.1)
|
||||
departure=new(origin,start.Normalized(),center.RelativeTo(origin,CellSizeMeters).Normalized(),start.Length,solid.MaximumTerrainRadiusMeters*1.12);
|
||||
}
|
||||
BeginFlight(center,radial,radius*2.5,radius*3.5,-radial,up,radius*.2,duration:8,departure:departure);
|
||||
}
|
||||
|
||||
private UniversePosition World(StarSystemData system, DVector3 offset) => UniversePosition.AtCellCenter(system.Coordinate, CellSizeMeters).Add(offset, CellSizeMeters);
|
||||
|
||||
private void BeginFlight(UniversePosition body, DVector3 radial, double finalRadius, double approachRadius, DVector3 forward, DVector3 up, double speed,
|
||||
@@ -361,7 +384,8 @@ public sealed class FlyCamera
|
||||
return;
|
||||
}
|
||||
if (movement.LengthSquared < 1e-12) return;
|
||||
var distance = System.Math.Min(SpeedMetersPerSecond * System.Math.Min(deltaSeconds, 0.1), CellSizeMeters * 0.45);
|
||||
var nearSurface = systems.Any(s => s.Planets.Any(p => Position.RelativeTo(World(s, p.PositionMeters), CellSizeMeters).Length < p.RadiusMeters * 10));
|
||||
var distance = System.Math.Min(SpeedMetersPerSecond * System.Math.Min(deltaSeconds, 0.1), nearSurface ? CellSizeMeters * .45 : TravelLimitMeters);
|
||||
var end = Position.Add(movement.Normalized() * distance, CellSizeMeters);
|
||||
Position = SweepNeighborhood(Position, end, systems).Position;
|
||||
}
|
||||
|
||||
188
src/Universe.App/Runtime/GalaxyJourneyBenchmark.cs
Normal file
188
src/Universe.App/Runtime/GalaxyJourneyBenchmark.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Universe.Core.Generation;
|
||||
using Universe.Core.Math;
|
||||
using Universe.Core.World;
|
||||
|
||||
namespace Universe.App.Runtime;
|
||||
|
||||
internal sealed class GalaxyJourneyBenchmark : IBenchmarkSession
|
||||
{
|
||||
private readonly GalaxyUniverse _galaxies;
|
||||
private readonly UniversePosition _home, _homeBody, _milkyView, _otherView, _targetBody;
|
||||
private readonly DVector3 _homeForward, _homeUp, _homeRadial, _targetRadial, _targetHeading;
|
||||
private readonly double _homeRadius;
|
||||
private readonly List<(double Time, double Milliseconds, string Phase)> _frames = [];
|
||||
private readonly List<double> _graph = [];
|
||||
private readonly HashSet<string> _visited = [];
|
||||
private double _elapsed, _sampleTime, _totalFrameTime;
|
||||
private double _lastSampleSequenceTime;
|
||||
private int _sampleFrames;
|
||||
private bool _stopped, _finalRecorded;
|
||||
public GalaxyData HomeGalaxy { get; }
|
||||
public GalaxyData TargetGalaxy { get; }
|
||||
public StarSystemData TargetSystem { get; }
|
||||
public PlanetData TargetPlanet { get; }
|
||||
public string? ReportDirectory { get; private set; }
|
||||
public object? RenderEnvironment { get; set; }
|
||||
|
||||
public GalaxyJourneyBenchmark(GalaxyUniverse galaxies, UniverseGenerator generator, StarSystemData homeSystem, PlanetData earth,
|
||||
UniversePosition home, DVector3 homeForward, DVector3 homeUp)
|
||||
{
|
||||
_galaxies = galaxies; _home = home; _homeForward = homeForward; _homeUp = homeUp;
|
||||
_homeBody = UniversePosition.AtCellCenter(homeSystem.Coordinate, galaxies.CellSize).Add(earth.PositionMeters, galaxies.CellSize);
|
||||
_homeRadial = home.RelativeTo(_homeBody, galaxies.CellSize).Normalized();
|
||||
_homeRadius = home.RelativeTo(_homeBody, galaxies.CellSize).Length;
|
||||
HomeGalaxy = galaxies.GalaxyAt(home);
|
||||
var region = HomeGalaxy.Region;
|
||||
TargetGalaxy = galaxies.Get(new(checked(region.X + 1), region.Y, region.Z));
|
||||
TargetSystem = generator.Generate(galaxies.SelectSystem(TargetGalaxy));
|
||||
TargetPlanet = TargetSystem.Planets.Where(p => !p.IsGasPlanet && p.ParentPlanetIndex is null).OrderByDescending(p => p.RadiusMeters).First();
|
||||
_targetBody = UniversePosition.AtCellCenter(TargetSystem.Coordinate, galaxies.CellSize).Add(TargetPlanet.PositionMeters, galaxies.CellSize);
|
||||
// Exercise the illuminated terrain/shadow path, rather than accidentally
|
||||
// benchmarking the cheap night-side shading branch.
|
||||
var sun=(TargetSystem.Stars[TargetPlanet.HostStarIndex].PositionMeters-TargetPlanet.PositionMeters).Normalized();
|
||||
var side=DVector3.Cross(sun,System.Math.Abs(sun.Y)<.9?new(0,1,0):new DVector3(1,0,0)).Normalized();
|
||||
_targetRadial=(sun*.6+side*.8).Normalized();
|
||||
_targetHeading=DVector3.Cross(_targetRadial,sun).Normalized();
|
||||
_milkyView = HomeGalaxy.Center.Add(HomeGalaxy.Orientation.ToWorld(new DVector3(.1,1,.3).Normalized()) * HomeGalaxy.RadiusMeters * 2.5, galaxies.CellSize);
|
||||
_otherView = TargetGalaxy.Center.Add(TargetGalaxy.Orientation.ToWorld(new DVector3(.3,.8,1).Normalized()) * TargetGalaxy.RadiusMeters * 2.5, galaxies.CellSize);
|
||||
}
|
||||
public bool IsRunning => !_stopped && _elapsed < DurationSeconds;
|
||||
public bool IsComplete => !_stopped && _elapsed >= DurationSeconds;
|
||||
public bool HasRun => true;
|
||||
public double ElapsedSeconds => _elapsed;
|
||||
public double DurationSeconds => 94;
|
||||
public long GraphRevision { get; private set; }
|
||||
public IReadOnlyList<double> GraphSamples => _graph;
|
||||
public string TargetLabel => TargetPlanet.Name.ToUpperInvariant();
|
||||
public double TargetEarthRadii => TargetPlanet.RadiusMeters / UniverseScale.EarthRadiusMeters;
|
||||
public double CurrentFps { get; private set; }
|
||||
public double AverageFps => _totalFrameTime <= 0 ? 0 : _frames.Count / _totalFrameTime;
|
||||
public double MinimumFps => _frames.Count == 0 ? 0 : 1000 / _frames.Max(f => f.Milliseconds);
|
||||
public double OnePercentLowFps => Low(_frames.Select(f => f.Milliseconds).ToArray());
|
||||
public string Phase => _stopped ? "STOPPED" : IsComplete ? "COMPLETE" : PhaseAt(_elapsed);
|
||||
public IReadOnlyCollection<string> VisitedPhases => _visited;
|
||||
private static string PhaseAt(double t) => t < 18 ? "LEAVE EARTH" : t < 25 ? "MILKY WAY" : t < 40 ? "BETWEEN GALAXIES"
|
||||
: t < 55 ? "ENTER GALAXY" : t < 65 ? "TARGET SYSTEM" : t < 85 ? "LANDING" : "GROUND WALK";
|
||||
public void Stop() => _stopped = true;
|
||||
public void Update(double seconds, FlyCamera camera)
|
||||
{
|
||||
if (!IsRunning || !double.IsFinite(seconds) || seconds <= 0) return;
|
||||
_elapsed = System.Math.Min(DurationSeconds, _elapsed + seconds);
|
||||
_visited.Add(PhaseAt(System.Math.Min(_elapsed, DurationSeconds - .001)));
|
||||
var cell = _galaxies.CellSize;
|
||||
UniversePosition position; DVector3 forward; DVector3 up = _homeUp;
|
||||
if (_elapsed < 18)
|
||||
{
|
||||
var t = Smooth(_elapsed / 18);
|
||||
var viewRadial = _milkyView.RelativeTo(_homeBody,cell).Normalized();
|
||||
var radius = LogLerp(_homeRadius, _milkyView.RelativeTo(_homeBody,cell).Length, t);
|
||||
var direction = DVector3.Lerp(_homeRadial,viewRadial,Smooth(System.Math.Clamp((t-.6)/.4,0,1))).Normalized();
|
||||
position = _homeBody.Add(direction * radius, cell);
|
||||
var focus = System.Math.Clamp((System.Math.Log(System.Math.Max(radius,1)) - System.Math.Log(UniverseScale.LightYearMeters)) / 12, 0, 1);
|
||||
forward = DVector3.Lerp(_homeForward, HomeGalaxy.Center.RelativeTo(position, cell).Normalized(), Smooth(focus)).Normalized();
|
||||
}
|
||||
else if (_elapsed < 25)
|
||||
{
|
||||
position = _milkyView;
|
||||
forward = HomeGalaxy.Center.RelativeTo(position, cell).Normalized();
|
||||
}
|
||||
else if (_elapsed < 40)
|
||||
{
|
||||
var t = Smooth((_elapsed - 25) / 15);
|
||||
position = Interpolate(_milkyView, _otherView, t, cell);
|
||||
var from = HomeGalaxy.Center.RelativeTo(_milkyView, cell).Normalized();
|
||||
var to = _targetBody.RelativeTo(_otherView, cell).Normalized();
|
||||
forward = DVector3.Lerp(from, to, t).Normalized();
|
||||
}
|
||||
else if (_elapsed < 55)
|
||||
{
|
||||
var t = Smooth((_elapsed - 40) / 15);
|
||||
var radial = _otherView.RelativeTo(_targetBody, cell).Normalized();
|
||||
var radius = LogLerp(_otherView.RelativeTo(_targetBody, cell).Length, TargetPlanet.RadiusMeters * 4.8, t);
|
||||
// Rotate at galaxy distances; keep the final solar approach radial and precise.
|
||||
var direction = DVector3.Lerp(radial, _targetRadial, Smooth(System.Math.Min(t * 3, 1))).Normalized();
|
||||
position = _targetBody.Add(direction * radius, cell);
|
||||
forward = _targetBody.RelativeTo(position, cell).Normalized();
|
||||
up = new(0,1,0);
|
||||
}
|
||||
else if (_elapsed < 65)
|
||||
{
|
||||
position = _targetBody.Add(_targetRadial * TargetPlanet.RadiusMeters * 4.8, cell);
|
||||
forward = -_targetRadial; up = new(0,1,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var t = System.Math.Clamp((_elapsed - 65) / 20, 0, 1);
|
||||
var surface = TerrainHeight.Sample(TargetPlanet, _targetRadial);
|
||||
var radius = t < .5 ? TargetPlanet.RadiusMeters * 4.8 + (surface + 10001.8 - TargetPlanet.RadiusMeters * 4.8) * Smooth(t * 2)
|
||||
: surface + 1.8 + 10000 * (1 - Smooth((t - .5) * 2));
|
||||
var radial = _targetRadial;
|
||||
if (_elapsed >= 85)
|
||||
{
|
||||
var walk = (_elapsed - 85) / 9;
|
||||
radial = (_targetRadial + _targetHeading * (walk * 12 / TargetPlanet.RadiusMeters)).Normalized();
|
||||
radius = TerrainHeight.Sample(TargetPlanet, radial) + 1.8;
|
||||
}
|
||||
position = _targetBody.Add(radial * radius, cell);
|
||||
var landingLook = (_targetHeading * System.Math.Cos(.2) - radial * System.Math.Sin(.2)).Normalized();
|
||||
forward = DVector3.Lerp(-radial, landingLook, Smooth(System.Math.Clamp((t - .4) / .6,0,1))).Normalized();
|
||||
if (_elapsed >= 85)
|
||||
{
|
||||
var yaw = System.Math.Sin((_elapsed - 85) * .7) * .6;
|
||||
var right = DVector3.Cross(_targetHeading, radial).Normalized();
|
||||
forward = (_targetHeading * System.Math.Cos(yaw) + right * System.Math.Sin(yaw) - radial * .15).Normalized();
|
||||
}
|
||||
up = radial;
|
||||
}
|
||||
if (_elapsed >= DurationSeconds) { position = _home; forward = _homeForward; up = _homeUp; }
|
||||
up = (up - forward * DVector3.Dot(up,forward)).Normalized();
|
||||
if (up.LengthSquared < .1) up = DVector3.Cross(forward,new(1,0,0)).Normalized();
|
||||
camera.SetPlayerPose(position, forward, up, resetMotionHistory: IsComplete);
|
||||
}
|
||||
public void RecordFrame(double seconds)
|
||||
{
|
||||
if (_stopped || _finalRecorded || !double.IsFinite(seconds) || seconds <= 0) return;
|
||||
CurrentFps = 1 / seconds; _totalFrameTime += seconds;
|
||||
_frames.Add((_elapsed, seconds * 1000, PhaseAt(System.Math.Min(_elapsed,DurationSeconds - .001))));
|
||||
_sampleTime += seconds; _sampleFrames++;
|
||||
if (_elapsed - _lastSampleSequenceTime >= .2 || IsComplete)
|
||||
{
|
||||
_graph.Add(_sampleFrames / _sampleTime); _sampleTime = 0; _sampleFrames = 0; _lastSampleSequenceTime = _elapsed; GraphRevision++;
|
||||
}
|
||||
if (IsComplete) _finalRecorded = true;
|
||||
}
|
||||
public object[] PhaseResults() => _frames.GroupBy(f => f.Phase).Select(group =>
|
||||
{
|
||||
var values = group.Select(f => f.Milliseconds).Order().ToArray();
|
||||
var p99 = values[System.Math.Min(values.Length - 1,(int)System.Math.Ceiling(values.Length * .99) - 1)];
|
||||
return (object)new { Phase = group.Key, Frames = values.Length, AverageFps = 1000 / values.Average(), LowOnePercentFps = Low(values),
|
||||
P99Milliseconds = p99, MaximumMilliseconds = values.Max(),
|
||||
Passed = Low(values) >= _galaxies.Settings.MinimumBenchmarkFps && values.Max() <= _galaxies.Settings.MaximumBenchmarkFrameMilliseconds };
|
||||
}).ToArray();
|
||||
public bool PerformancePassed => IsComplete && _frames.Count > 0 && _frames.GroupBy(f => f.Phase).All(g =>
|
||||
Low(g.Select(f => f.Milliseconds).ToArray()) >= _galaxies.Settings.MinimumBenchmarkFps
|
||||
&& g.Max(f => f.Milliseconds) <= _galaxies.Settings.MaximumBenchmarkFrameMilliseconds);
|
||||
public string SaveReport(string? root = null)
|
||||
{
|
||||
if (ReportDirectory is not null) return ReportDirectory;
|
||||
var directory = Path.Combine(root ?? Path.Combine(AppContext.BaseDirectory,"benchmark-results"),"galaxy-journey-"+DateTime.UtcNow.ToString("yyyyMMdd-HHmmss"));
|
||||
Directory.CreateDirectory(directory);
|
||||
var csv = new StringBuilder("timeSeconds,phase,frameMilliseconds,fps\n");
|
||||
foreach (var f in _frames) csv.AppendLine(FormattableString.Invariant($"{f.Time:F4},{f.Phase},{f.Milliseconds:F4},{1000/f.Milliseconds:F4}"));
|
||||
File.WriteAllText(Path.Combine(directory,"frames.csv"),csv.ToString());
|
||||
File.WriteAllText(Path.Combine(directory,"summary.json"),JsonSerializer.Serialize(new { Completed = IsComplete, PerformancePassed,
|
||||
UniverseSeed = _galaxies.Seed.ToString("X16"), RenderEnvironment,
|
||||
Quality = new { _galaxies.Settings.GalaxyStarBudget, _galaxies.Settings.LocalStarBudget, _galaxies.Settings.VolumeSteps, _galaxies.Settings.SystemSpacingLightYears },
|
||||
TargetGalaxy = TargetGalaxy.Id, TargetSystem = TargetSystem.Id, TargetPlanet = TargetPlanet.Id,
|
||||
MinimumFps = _galaxies.Settings.MinimumBenchmarkFps, MaximumFrameMilliseconds = _galaxies.Settings.MaximumBenchmarkFrameMilliseconds,
|
||||
Visited = _visited, Phases = PhaseResults() },new JsonSerializerOptions { WriteIndented = true }));
|
||||
ReportDirectory = directory; return directory;
|
||||
}
|
||||
private static double Low(double[] milliseconds) => milliseconds.Length == 0 ? 0 : 1000 / milliseconds.OrderDescending().Take(System.Math.Max(1,(int)System.Math.Ceiling(milliseconds.Length * .01))).Average();
|
||||
private static double Smooth(double t) { t=System.Math.Clamp(t,0,1);return t*t*t*(t*(t*6-15)+10); }
|
||||
private static double LogLerp(double a,double b,double t) => System.Math.Exp(System.Math.Log(a)*(1-t)+System.Math.Log(b)*t);
|
||||
private static UniversePosition Interpolate(UniversePosition a,UniversePosition b,double t,double cell) => t<.5?a.Add(b.RelativeTo(a,cell)*t,cell):b.Add(a.RelativeTo(b,cell)*(1-t),cell);
|
||||
}
|
||||
24
src/Universe.App/Runtime/IBenchmarkSession.cs
Normal file
24
src/Universe.App/Runtime/IBenchmarkSession.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Universe.Core.Generation;
|
||||
|
||||
namespace Universe.App.Runtime;
|
||||
|
||||
internal interface IBenchmarkSession
|
||||
{
|
||||
bool IsRunning { get; }
|
||||
bool IsComplete { get; }
|
||||
bool HasRun { get; }
|
||||
double ElapsedSeconds { get; }
|
||||
double DurationSeconds { get; }
|
||||
long GraphRevision { get; }
|
||||
IReadOnlyList<double> GraphSamples { get; }
|
||||
string TargetLabel { get; }
|
||||
double TargetEarthRadii { get; }
|
||||
double CurrentFps { get; }
|
||||
double AverageFps { get; }
|
||||
double MinimumFps { get; }
|
||||
double OnePercentLowFps { get; }
|
||||
string Phase { get; }
|
||||
void Stop();
|
||||
void Update(double seconds, FlyCamera camera);
|
||||
void RecordFrame(double seconds);
|
||||
}
|
||||
29
src/Universe.App/Runtime/StarMotionHistory.cs
Normal file
29
src/Universe.App/Runtime/StarMotionHistory.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Universe.Core.Generation;
|
||||
using Universe.Core.Math;
|
||||
using Universe.Core.World;
|
||||
|
||||
namespace Universe.App.Runtime;
|
||||
|
||||
internal readonly record struct StarCameraPose(UniversePosition Position, DVector3 Forward, DVector3 Right, DVector3 Up,
|
||||
double TanHalfFov, int Width, int Height, long CutVersion);
|
||||
|
||||
internal sealed class StarMotionHistory
|
||||
{
|
||||
private bool _initialized;
|
||||
public StarCameraPose Current { get; private set; }
|
||||
public StarCameraPose Previous { get; private set; }
|
||||
public bool Valid { get; private set; }
|
||||
|
||||
public void Advance(FlyCamera camera, int width, int height, double frameSeconds, StarMotionBlurSettings settings)
|
||||
{
|
||||
var pose = new StarCameraPose(camera.Position, camera.Forward, camera.Right, camera.Up,
|
||||
System.Math.Tan(camera.FieldOfViewRadians * .5), width, height, camera.MotionHistoryVersion);
|
||||
Previous = Current;
|
||||
Valid = _initialized && settings.Enabled && double.IsFinite(frameSeconds) && frameSeconds > 0
|
||||
&& frameSeconds <= settings.HistoryResetSeconds && Previous.Width == width && Previous.Height == height
|
||||
&& Previous.CutVersion == pose.CutVersion && System.Math.Abs(Previous.TanHalfFov / pose.TanHalfFov - 1) < .15;
|
||||
Current = pose;
|
||||
if (!Valid) Previous = Current;
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user