Complete portable planetary presentation contracts
This commit is contained in:
parent
42fef7cdd7
commit
102403efac
74 changed files with 1166 additions and 31 deletions
2
src/CultMath/AdvancedErosionFilter.cs.meta
Normal file
2
src/CultMath/AdvancedErosionFilter.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bec15fdcbe97a7f44a24bdb3ff79e1e7
|
||||
2
src/CultMath/ErosionFrequencyBands.cs.meta
Normal file
2
src/CultMath/ErosionFrequencyBands.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 05e7969589dd09b43bd0fda672d25a0b
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public readonly record struct PlanetaryQueryScale(float FootprintMeters, float MaximumUnresolvedHeight)
|
||||
|
|
|
|||
2
src/CultMath/PlanetaryField.cs.meta
Normal file
2
src/CultMath/PlanetaryField.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b61d6354e3aab054e94721e77e619bfa
|
||||
55
src/CultMath/PlanetaryGpuPages.cs
Normal file
55
src/CultMath/PlanetaryGpuPages.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public readonly record struct PlanetaryGpuPageInput(float4 DirectionRadius, float4 Sampling);
|
||||
|
||||
public readonly record struct PlanetaryGpuPageMetadata(float4 Address, float4 Layout, float4 Bounds, float4 State);
|
||||
|
||||
public readonly record struct PlanetaryGpuPageContent(
|
||||
PlanetaryPageLayout Layout,
|
||||
PlanetaryGpuPageInput[] Inputs,
|
||||
float SampleSpacing,
|
||||
float ParentSampleSpacing);
|
||||
|
||||
public static class PlanetaryGpuPageBuilder
|
||||
{
|
||||
public static PlanetaryGpuPageContent BuildContent(PlanetaryPageLayout layout, float radius)
|
||||
{
|
||||
layout.Validate();
|
||||
if (!float.IsFinite(radius) || radius <= 0) throw new ArgumentOutOfRangeException(nameof(radius));
|
||||
var spacing = PlanetaryPageSampling.NominalAngularTexelSize(layout) * radius;
|
||||
var parentSpacing = layout.Tile.Level == 0
|
||||
? 0
|
||||
: PlanetaryPageSampling.NominalAngularTexelSize(new(layout.Tile.Parent(), layout.InteriorSize, layout.BorderSize)) * radius;
|
||||
var inputs = new PlanetaryGpuPageInput[layout.StorageSize * layout.StorageSize];
|
||||
for (var y = 0; y < layout.StorageSize; y++)
|
||||
for (var x = 0; x < layout.StorageSize; x++)
|
||||
{
|
||||
var direction = PlanetaryPageSampling.Direction(layout, x, y);
|
||||
inputs[y * layout.StorageSize + x] = new(
|
||||
new float4(direction, radius),
|
||||
new float4(spacing, parentSpacing, 0, 0));
|
||||
}
|
||||
return new(layout, inputs, spacing, parentSpacing);
|
||||
}
|
||||
|
||||
public static PlanetaryGpuPageMetadata Metadata(
|
||||
in PlanetaryGpuPageContent content,
|
||||
int outputOffset,
|
||||
float blend,
|
||||
float4 bounds = default)
|
||||
{
|
||||
content.Layout.Validate();
|
||||
if (content.Inputs == null || content.Inputs.Length != content.Layout.StorageSize * content.Layout.StorageSize)
|
||||
throw new ArgumentException("GPU page content does not match its layout.", nameof(content));
|
||||
if (outputOffset < 0) throw new ArgumentOutOfRangeException(nameof(outputOffset));
|
||||
if (!float.IsFinite(blend) || blend is < 0 or > 1) throw new ArgumentOutOfRangeException(nameof(blend));
|
||||
var tile = content.Layout.Tile;
|
||||
return new(
|
||||
new float4((int)tile.Face, tile.Level, tile.X, tile.Y),
|
||||
new float4(outputOffset, content.Layout.StorageSize, content.Layout.InteriorSize, content.Layout.BorderSize),
|
||||
bounds,
|
||||
new float4(1, blend, PlanetaryPageSampling.NominalAngularTexelSize(content.Layout), content.SampleSpacing));
|
||||
}
|
||||
}
|
||||
2
src/CultMath/PlanetaryGpuPages.cs.meta
Normal file
2
src/CultMath/PlanetaryGpuPages.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 42173c60766948a4e8c376687033532e
|
||||
117
src/CultMath/PlanetaryMapTileEncoding.cs
Normal file
117
src/CultMath/PlanetaryMapTileEncoding.cs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public static class PlanetaryMapTileEncoding
|
||||
{
|
||||
public const uint FormatVersion = 1;
|
||||
private static readonly byte[] Magic = Encoding.ASCII.GetBytes("CMPT");
|
||||
|
||||
public static void Write(Stream stream, in PlanetarySurfaceMapTile tile, bool leaveOpen = false)
|
||||
{
|
||||
if (stream == null || !stream.CanWrite) throw new ArgumentException("A writable stream is required.", nameof(stream));
|
||||
tile.Key.Validate();
|
||||
if (tile.Samples == null || tile.Validity == null || tile.Samples.Length != tile.Validity.Length)
|
||||
throw new ArgumentException("Tile samples and validity must have equal lengths.", nameof(tile));
|
||||
var expectedCount = tile.Key.Layout.StorageSize * tile.Key.Layout.StorageSize;
|
||||
if (tile.Samples.Length != expectedCount) throw new ArgumentException("Tile payload does not match its layout.", nameof(tile));
|
||||
|
||||
using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen);
|
||||
writer.Write(Magic);
|
||||
writer.Write(FormatVersion);
|
||||
writer.Write(tile.Key.FieldVersion);
|
||||
writer.Write(tile.Key.ProjectionVersion);
|
||||
writer.Write(tile.Key.LayerId);
|
||||
var projection = tile.Key.Layout.Projection;
|
||||
writer.Write((int)projection.Kind);
|
||||
writer.Write(projection.CenterLongitude);
|
||||
writer.Write(projection.CenterLatitude);
|
||||
writer.Write(projection.Scale);
|
||||
writer.Write(tile.Key.Layout.Level);
|
||||
writer.Write(tile.Key.Layout.X);
|
||||
writer.Write(tile.Key.Layout.Y);
|
||||
writer.Write(tile.Key.Layout.InteriorSize);
|
||||
writer.Write(tile.Key.Layout.BorderSize);
|
||||
writer.Write(tile.Key.QueryScale.FootprintMeters);
|
||||
writer.Write(tile.Key.QueryScale.MaximumUnresolvedHeight);
|
||||
writer.Write(tile.Samples.Length);
|
||||
for (var i = 0; i < tile.Samples.Length; i++)
|
||||
{
|
||||
writer.Write(tile.Validity[i]);
|
||||
if (!tile.Validity[i]) continue;
|
||||
WriteSample(writer, tile.Samples[i], tile.Key.FieldVersion);
|
||||
}
|
||||
}
|
||||
|
||||
public static PlanetarySurfaceMapTile Read(Stream stream, bool leaveOpen = false)
|
||||
{
|
||||
if (stream == null || !stream.CanRead) throw new ArgumentException("A readable stream is required.", nameof(stream));
|
||||
using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen);
|
||||
var magic = reader.ReadBytes(Magic.Length);
|
||||
if (magic.Length != Magic.Length || !magic.AsSpan().SequenceEqual(Magic)) throw new InvalidDataException("Not a CultMath planetary tile.");
|
||||
var version = reader.ReadUInt32();
|
||||
if (version != FormatVersion) throw new InvalidDataException($"Unsupported planetary tile version {version}.");
|
||||
var fieldVersion = reader.ReadUInt64();
|
||||
var projectionVersion = reader.ReadUInt32();
|
||||
var layerId = reader.ReadUInt32();
|
||||
var kindValue = reader.ReadInt32();
|
||||
if (!Enum.IsDefined(typeof(PlanetaryProjectionKind), kindValue)) throw new InvalidDataException("Unknown planetary projection kind.");
|
||||
var projection = new PlanetaryProjectionParameters(
|
||||
(PlanetaryProjectionKind)kindValue,
|
||||
reader.ReadDouble(), reader.ReadDouble(), reader.ReadDouble()).Validate();
|
||||
var layout = new PlanetaryMapTileLayout(
|
||||
projection,
|
||||
reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(),
|
||||
reader.ReadInt32(), reader.ReadInt32()).Validate();
|
||||
var scale = new PlanetaryQueryScale(reader.ReadSingle(), reader.ReadSingle()).Validate();
|
||||
var key = new PlanetaryMapTileKey(fieldVersion, projectionVersion, layerId, layout, scale).Validate();
|
||||
var count = reader.ReadInt32();
|
||||
if (count != layout.StorageSize * layout.StorageSize) throw new InvalidDataException("Planetary tile sample count does not match layout.");
|
||||
var samples = new PlanetarySurfaceSample[count];
|
||||
var validity = new bool[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
validity[i] = reader.ReadBoolean();
|
||||
if (validity[i]) samples[i] = ReadSample(reader, fieldVersion);
|
||||
}
|
||||
if (stream.CanSeek && stream.Position != stream.Length) throw new InvalidDataException("Planetary tile has trailing bytes.");
|
||||
return new(key, samples, validity);
|
||||
}
|
||||
|
||||
private static void WriteSample(BinaryWriter writer, PlanetarySurfaceSample sample, ulong fieldVersion)
|
||||
{
|
||||
if (sample.FieldVersion != fieldVersion) throw new ArgumentException("Sample field version does not match tile identity.");
|
||||
Write(writer, sample.UnitDirection);
|
||||
writer.Write(sample.Radius);
|
||||
writer.Write(sample.RadialDisplacement);
|
||||
Write(writer, sample.TangentGradient);
|
||||
Write(writer, sample.SurfaceNormal);
|
||||
writer.Write(sample.Slope);
|
||||
writer.Write(sample.Ridge);
|
||||
writer.Write(sample.Gully);
|
||||
writer.Write(sample.FinestResolvedWavelength);
|
||||
writer.Write(sample.UnresolvedHeightBound);
|
||||
}
|
||||
|
||||
private static PlanetarySurfaceSample ReadSample(BinaryReader reader, ulong fieldVersion)
|
||||
{
|
||||
var direction = ReadFloat3(reader);
|
||||
var radius = reader.ReadSingle();
|
||||
var displacement = reader.ReadSingle();
|
||||
var gradient = ReadFloat3(reader);
|
||||
var normal = ReadFloat3(reader);
|
||||
return new(
|
||||
fieldVersion, direction, radius, displacement, gradient, normal,
|
||||
reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(),
|
||||
reader.ReadSingle(), reader.ReadSingle());
|
||||
}
|
||||
|
||||
private static void Write(BinaryWriter writer, float3 value)
|
||||
{
|
||||
writer.Write(value.x); writer.Write(value.y); writer.Write(value.z);
|
||||
}
|
||||
|
||||
private static float3 ReadFloat3(BinaryReader reader) => new(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
|
||||
}
|
||||
2
src/CultMath/PlanetaryMapTileEncoding.cs.meta
Normal file
2
src/CultMath/PlanetaryMapTileEncoding.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6898f6de57df20646a0b9077bb923592
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public readonly record struct PlanetaryPageLayout(PlanetaryTileAddress Tile, int InteriorSize, int BorderSize)
|
||||
|
|
|
|||
2
src/CultMath/PlanetaryPages.cs.meta
Normal file
2
src/CultMath/PlanetaryPages.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7af9a0354faa56348a2e07188739a0ae
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public readonly record struct PlanetaryPatchVertex(float2 LocalCoordinate, float3 UnitDirection);
|
||||
|
|
|
|||
2
src/CultMath/PlanetaryPatch.cs.meta
Normal file
2
src/CultMath/PlanetaryPatch.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 04651571f7d240a418c1f6aafa217fa6
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public enum PlanetaryProjectionKind
|
||||
|
|
|
|||
2
src/CultMath/PlanetaryProjection.cs.meta
Normal file
2
src/CultMath/PlanetaryProjection.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5021202cf626f7442a57961255a8a217
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public readonly record struct PlanetaryRadialBounds(float MinimumDisplacement, float MaximumDisplacement)
|
||||
|
|
|
|||
2
src/CultMath/PlanetaryQueries.cs.meta
Normal file
2
src/CultMath/PlanetaryQueries.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2ec43c567214b8247bda19b5c120c200
|
||||
2
src/CultMath/PlanetaryRadialRefinement.cs.meta
Normal file
2
src/CultMath/PlanetaryRadialRefinement.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 82e4db521edc253499220458b417fd1e
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public readonly record struct PlanetaryLodParameters(
|
||||
|
|
@ -71,9 +75,9 @@ public readonly record struct PlanetaryResidencySnapshot(
|
|||
/// <summary>Owns presentation residency only. It never owns field content.</summary>
|
||||
public sealed class PlanetaryResidualResidency
|
||||
{
|
||||
private readonly Dictionary<ulong, PlanetaryTileAddress> residents = [];
|
||||
private readonly Dictionary<ulong, float> arrivals = [];
|
||||
private readonly Dictionary<ulong, (float Time, float StartingBlend)> departures = [];
|
||||
private readonly Dictionary<ulong, PlanetaryTileAddress> residents = new();
|
||||
private readonly Dictionary<ulong, float> arrivals = new();
|
||||
private readonly Dictionary<ulong, (float Time, float StartingBlend)> departures = new();
|
||||
private float lastTime = float.NegativeInfinity;
|
||||
|
||||
public void Reset()
|
||||
|
|
|
|||
2
src/CultMath/PlanetaryResidency.cs.meta
Normal file
2
src/CultMath/PlanetaryResidency.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0585ea15edd7f1c4fa272df4911a8d6f
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace CultMath;
|
||||
|
||||
public enum PlanetaryCubeFace
|
||||
|
|
@ -104,6 +106,14 @@ public static class PlanetaryTopology
|
|||
return math.normalize(cube);
|
||||
}
|
||||
|
||||
public static float3 NormalizedCubeDirection(PlanetaryFaceCoordinate coordinate)
|
||||
{
|
||||
coordinate.Validate(allowOutsideFace: true);
|
||||
var u = (float)coordinate.U;
|
||||
var v = (float)coordinate.V;
|
||||
return math.normalize(CubeVector(coordinate.Face, u, v));
|
||||
}
|
||||
|
||||
public static PlanetaryFaceCoordinate FaceCoordinate(float3 direction)
|
||||
{
|
||||
ValidateDirection(direction);
|
||||
|
|
@ -156,6 +166,17 @@ public static class PlanetaryTopology
|
|||
MathF.Atan(cubeU) / QuarterPi,
|
||||
MathF.Atan(cubeV) / QuarterPi);
|
||||
|
||||
private static float3 CubeVector(PlanetaryCubeFace face, float u, float v) => face switch
|
||||
{
|
||||
PlanetaryCubeFace.PositiveX => new float3(1, v, -u),
|
||||
PlanetaryCubeFace.NegativeX => new float3(-1, v, u),
|
||||
PlanetaryCubeFace.PositiveY => new float3(u, 1, -v),
|
||||
PlanetaryCubeFace.NegativeY => new float3(u, -1, v),
|
||||
PlanetaryCubeFace.PositiveZ => new float3(u, v, 1),
|
||||
PlanetaryCubeFace.NegativeZ => new float3(-u, v, -1),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(face)),
|
||||
};
|
||||
|
||||
internal static void ValidateDirection(float3 direction)
|
||||
{
|
||||
ValidateFinite(direction, nameof(direction));
|
||||
|
|
|
|||
2
src/CultMath/PlanetaryTopology.cs.meta
Normal file
2
src/CultMath/PlanetaryTopology.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 88f6789c4384f7846ac4be37613fe5ed
|
||||
2
src/CultMath/SphericalErosion.cs.meta
Normal file
2
src/CultMath/SphericalErosion.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 59ec2db73065f6c4483ad90f5e17850e
|
||||
Reference in a new issue