Position asteroid belts from daemon poses

This commit is contained in:
Meta Vens 2026-06-21 15:30:55 +01:00
commit 4e68ce29af
4 changed files with 166 additions and 8 deletions

View file

@ -1816,7 +1816,11 @@ static void RequireDaemonRenderQueryAuthority(string root)
"zone.GravityTerrainWaveFrequency",
"public static AetheriaRuntimeDaemonBodyPose[] QueryBodyPoses(",
"public static int QueryBodyPoses(",
"new AetheriaRuntimeDaemonBodyPose("
"new AetheriaRuntimeDaemonBodyPose(",
"AetheriaRuntimeDaemonAsteroidBeltPose",
"public static AetheriaRuntimeDaemonAsteroidBeltPose[] QueryAsteroidBeltPoses(",
"public static int QueryAsteroidBeltPoses(",
"ResolveAsteroidBeltRadius(body)"
};
var missingQuerySymbols = requiredQuerySymbols
@ -1894,16 +1898,27 @@ static void RequireDaemonRenderQueryAuthority(string root)
"ZoneRenderer frame updates must read body render state from daemon body poses instead of indexed mirrored Unity planets.");
}
if (zoneRenderer.Contains("belt.OrbitPosition", StringComparison.Ordinal) ||
zoneRenderer.Contains("belt.Radius", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"ZoneRenderer frame updates must read asteroid belt pose and radius from daemon belt poses instead of mirrored Unity asteroid belts.");
}
var requiredZoneRendererSymbols = new[]
{
"private AetheriaRuntimeZoneSnapshotCommit _daemonZoneSnapshot;",
"private readonly List<AetheriaRuntimeDaemonBodyPose> _daemonBodyPoses",
"private readonly Dictionary<string, AetheriaRuntimeDaemonBodyPose> _daemonBodyPosesByBodyKey",
"private readonly List<AetheriaRuntimeDaemonAsteroidBeltPose> _daemonAsteroidBeltPoses",
"private readonly Dictionary<string, AetheriaRuntimeDaemonAsteroidBeltPose> _daemonAsteroidBeltPosesByBodyKey",
"public void LoadZone(Zone zone, AetheriaRuntimeZoneSnapshotCommit daemonZone = null)",
"_daemonZoneSnapshot = daemonZone;",
"AetheriaRuntimeDaemonRenderQueries.EvaluateGravityTerrainHeight(",
"AetheriaRuntimeDaemonRenderQueries.QueryBodyPoses(_daemonZoneSnapshot, _daemonBodyPoses);",
"AetheriaRuntimeDaemonRenderQueries.QueryAsteroidBeltPoses(_daemonZoneSnapshot, _daemonAsteroidBeltPoses);",
"_daemonBodyPosesByBodyKey.TryGetValue(planet.Key, out var pose)",
"_daemonAsteroidBeltPosesByBodyKey.TryGetValue(key, out var beltPose)",
"pose.GravityWaveSpeed"
};
var missingZoneRendererSymbols = requiredZoneRendererSymbols

View file

@ -786,6 +786,54 @@ public class DaemonRuntimeDocumentTests
Assert.AreEqual(3.5, poses[1].GravityWaveSpeed, 0.0001);
}
[Test]
public void DaemonRenderQueriesPublishAsteroidBeltPosesFromZoneSnapshot()
{
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
Orbits = new[]
{
new AetheriaRuntimeOrbitSnapshotCommit
{
OrbitKey = "orbit:belt",
FixedPositionX = 11,
FixedPositionY = -4
}
},
Bodies = new[]
{
new AetheriaRuntimeBodySnapshotCommit
{
BodyKey = "body:belt",
OrbitKey = "orbit:belt",
Kind = "asteroid_belt",
Asteroids = new[]
{
new AetheriaRuntimeAsteroidCommit { Distance = 3 },
new AetheriaRuntimeAsteroidCommit { Distance = 7 },
new AetheriaRuntimeAsteroidCommit { Distance = 5 }
}
},
new AetheriaRuntimeBodySnapshotCommit
{
BodyKey = "body:planet",
OrbitKey = "orbit:belt",
Kind = "planet"
}
}
};
var poses = AetheriaRuntimeDaemonRenderQueries.QueryAsteroidBeltPoses(zone);
Assert.AreEqual(1, poses.Length);
Assert.AreEqual("body:belt", poses[0].BodyKey);
Assert.AreEqual("orbit:belt", poses[0].OrbitKey);
Assert.AreEqual(11, poses[0].CenterX, 0.0001);
Assert.AreEqual(-4, poses[0].CenterZ, 0.0001);
Assert.AreEqual(7, poses[0].Radius, 0.0001);
Assert.AreEqual(3, poses[0].AsteroidCount);
}
[Test]
public void SoaViewIndexRejectsInvalidRenderGroups()
{

View file

@ -89,6 +89,10 @@ public class ZoneRenderer : MonoBehaviour
private readonly List<AetheriaRuntimeDaemonBodyPose> _daemonBodyPoses = new List<AetheriaRuntimeDaemonBodyPose>();
private readonly Dictionary<string, AetheriaRuntimeDaemonBodyPose> _daemonBodyPosesByBodyKey =
new Dictionary<string, AetheriaRuntimeDaemonBodyPose>(StringComparer.Ordinal);
private readonly List<AetheriaRuntimeDaemonAsteroidBeltPose> _daemonAsteroidBeltPoses =
new List<AetheriaRuntimeDaemonAsteroidBeltPose>();
private readonly Dictionary<string, AetheriaRuntimeDaemonAsteroidBeltPose> _daemonAsteroidBeltPosesByBodyKey =
new Dictionary<string, AetheriaRuntimeDaemonAsteroidBeltPose>(StringComparer.Ordinal);
public Dictionary<Wormhole, (GameObject gravity, CompassIcon icon)> WormholeInstances = new Dictionary<Wormhole, (GameObject, CompassIcon)>();
private List<ItemPickup> _loot = new List<ItemPickup>();
@ -470,16 +474,21 @@ public class ZoneRenderer : MonoBehaviour
Shader.SetGlobalFloat("_AsteroidVerticalOffset", ActionGameManager.Instance.Settings.PlanetSettings.AsteroidVerticalOffset);
RefreshDaemonBodyPoses();
RefreshDaemonAsteroidBeltPoses();
Plane[] planes = GeometryUtility.CalculateFrustumPlanes(MainCamera);
bool isVisible(Bounds bounds) => GeometryUtility.TestPlanesAABB(planes, bounds);
foreach (var (key, belt) in Zone.AsteroidBelts)
{
var height = GetTerrainHeight(belt.OrbitPosition);
if (!_daemonAsteroidBeltPosesByBodyKey.TryGetValue(key, out var beltPose))
continue;
var center = new float2((float)beltPose.CenterX, (float)beltPose.CenterZ);
var height = GetTerrainHeight(center);
if(isVisible(new Bounds(
new Vector3(belt.OrbitPosition.x,height,belt.OrbitPosition.y),
new Vector3(belt.Radius * 2,100,belt.Radius * 2))))
new Vector3(center.x,height,center.y),
new Vector3((float)beltPose.Radius * 2,100,(float)beltPose.Radius * 2))))
{
var meshes = _beltMeshes[key];
var matrices = _beltMatrices[key];
@ -503,7 +512,7 @@ public class ZoneRenderer : MonoBehaviour
}
if(_showAsteroidUI)
_beltObjects[key].Update(belt.Transforms, height);
_beltObjects[key].Update(belt.Transforms, center, height, (float)beltPose.Radius);
}
foreach (var planet in Planets)
@ -583,6 +592,17 @@ public class ZoneRenderer : MonoBehaviour
}
}
private void RefreshDaemonAsteroidBeltPoses()
{
AetheriaRuntimeDaemonRenderQueries.QueryAsteroidBeltPoses(_daemonZoneSnapshot, _daemonAsteroidBeltPoses);
_daemonAsteroidBeltPosesByBodyKey.Clear();
foreach (var pose in _daemonAsteroidBeltPoses)
{
if (!string.IsNullOrWhiteSpace(pose.BodyKey))
_daemonAsteroidBeltPosesByBodyKey[pose.BodyKey] = pose;
}
}
private float GetTerrainHeight(float2 position)
{
return (float)AetheriaRuntimeDaemonRenderQueries.EvaluateGravityTerrainHeight(
@ -716,9 +736,8 @@ public class AsteroidBeltUI
//_collider.sharedMesh = _mesh;
}
public void Update(float4[] transforms, float height)
public void Update(float4[] transforms, float2 center, float height, float radius)
{
var parentPosition = _belt.OrbitPosition;
for (var i = 0; i < transforms.Length; i++)
{
var transform = transforms[i];
@ -730,7 +749,8 @@ public class AsteroidBeltUI
_vertices[i * 4 + 3] = rotation * new Vector3(transform.w * _scale, -transform.w * _scale, 0) + position;
}
_mesh.bounds = new Bounds(new Vector3(parentPosition.x, 0, parentPosition.y), Vector3.one * (_size * 2));
var boundsRadius = Math.Max(_size, radius);
_mesh.bounds = new Bounds(new Vector3(center.x, 0, center.y), Vector3.one * (boundsRadius * 2));
_mesh.vertices = _vertices;
//_collider.sharedMesh = _mesh;
}

View file

@ -104,6 +104,32 @@ namespace GameCult.Aetheria.State.Verse
public double GravityWaveSpeed { get; }
}
public readonly struct AetheriaRuntimeDaemonAsteroidBeltPose
{
public AetheriaRuntimeDaemonAsteroidBeltPose(
string bodyKey,
string orbitKey,
double centerX,
double centerZ,
double radius,
int asteroidCount)
{
BodyKey = bodyKey ?? "";
OrbitKey = orbitKey ?? "";
CenterX = centerX;
CenterZ = centerZ;
Radius = Math.Max(0, radius);
AsteroidCount = Math.Max(0, asteroidCount);
}
public string BodyKey { get; }
public string OrbitKey { get; }
public double CenterX { get; }
public double CenterZ { get; }
public double Radius { get; }
public int AsteroidCount { get; }
}
public static class AetheriaRuntimeDaemonRenderQueries
{
public static AetheriaRuntimeGravityInfluenceBrush[] QueryGravityInfluences(
@ -198,6 +224,43 @@ namespace GameCult.Aetheria.State.Verse
return poses.Count;
}
public static AetheriaRuntimeDaemonAsteroidBeltPose[] QueryAsteroidBeltPoses(
AetheriaRuntimeZoneSnapshotCommit? zone)
{
var poses = new List<AetheriaRuntimeDaemonAsteroidBeltPose>();
QueryAsteroidBeltPoses(zone, poses);
return poses.Count == 0 ? Array.Empty<AetheriaRuntimeDaemonAsteroidBeltPose>() : poses.ToArray();
}
public static int QueryAsteroidBeltPoses(
AetheriaRuntimeZoneSnapshotCommit? zone,
List<AetheriaRuntimeDaemonAsteroidBeltPose> poses)
{
if (poses == null) throw new ArgumentNullException(nameof(poses));
poses.Clear();
if (zone == null)
return 0;
var orbitPositions = BuildOrbitPositions(zone);
foreach (var body in zone.Bodies ?? Array.Empty<AetheriaRuntimeBodySnapshotCommit>())
{
if (body == null ||
!string.Equals(body.Kind, "asteroid_belt", StringComparison.OrdinalIgnoreCase) ||
!TryResolveBodyCenter(body, orbitPositions, out var center))
continue;
poses.Add(new AetheriaRuntimeDaemonAsteroidBeltPose(
body.BodyKey,
body.OrbitKey,
center.x,
center.z,
ResolveAsteroidBeltRadius(body),
body.Asteroids?.Count ?? 0));
}
return poses.Count;
}
public static AetheriaRuntimeDaemonRenderGroupDocument[] QueryRenderGroups(
AetheriaRuntimeDaemonSoaViewIndex? index,
double minX,
@ -419,6 +482,18 @@ namespace GameCult.Aetheria.State.Verse
return body.GravityWaveSpeed;
}
private static double ResolveAsteroidBeltRadius(AetheriaRuntimeBodySnapshotCommit body)
{
var radius = 0.0;
foreach (var asteroid in body.Asteroids ?? Array.Empty<AetheriaRuntimeAsteroidCommit>())
{
if (asteroid != null && asteroid.Distance > radius)
radius = asteroid.Distance;
}
return radius;
}
private static double Clamp(double value, double min, double max)
{
return value < min ? min : value > max ? max : value;