From 140028d0db9698b0a4a06b3d7db39fec84b4e3a4 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 21 Jun 2026 15:20:29 +0100 Subject: [PATCH] Position rendered zone bodies from daemon poses --- Aetheria.State.Verify/Program.cs | 22 ++++- .../Tests/DaemonRuntimeDocumentTests.cs | 54 +++++++++++ Assets/Scripts/Zone Display/ZoneRenderer.cs | 25 ++++- .../AetheriaRuntimeDaemonRenderQueries.cs | 95 +++++++++++++++++-- 4 files changed, 183 insertions(+), 13 deletions(-) diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index b1c157427..996a90b3b 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -1501,8 +1501,9 @@ static void RequireTypedOrbitConsumerKeys(string root) [Path.Combine(root, "Assets", "Scripts", "Zone Display", "ZoneRenderer.cs")] = new[] { "body => body.OrbitKey == orbit.OrbitKey", - "Zone.GetOrbitPosition(planetInstance.OrbitKey)", - "Zone.GetOrbitPosition(planetInstance.Orbit.ParentOrbitKey)" + "_daemonBodyPosesByBodyKey.TryGetValue(planet.Key, out var pose)", + "var p = new float2((float)pose.CenterX, (float)pose.CenterZ);", + "var parent = new float2((float)pose.ParentCenterX, (float)pose.ParentCenterZ);" }, [Path.Combine(root, "Assets", "Scripts", "ServerShared", "Behaviors", "ResourceScanner.cs")] = new[] { @@ -1812,7 +1813,10 @@ static void RequireDaemonRenderQueryAuthority(string root) "TryResolveBodyCenter(body, orbitPositions, out var center)", "public static double EvaluateGravityTerrainHeight(", "zone.GravityTerrainRadius", - "zone.GravityTerrainWaveFrequency" + "zone.GravityTerrainWaveFrequency", + "public static AetheriaRuntimeDaemonBodyPose[] QueryBodyPoses(", + "public static int QueryBodyPoses(", + "new AetheriaRuntimeDaemonBodyPose(" }; var missingQuerySymbols = requiredQuerySymbols @@ -1878,12 +1882,22 @@ static void RequireDaemonRenderQueryAuthority(string root) "ZoneRenderer must evaluate gravity terrain from daemon render queries instead of the mirrored Unity Zone height evaluator."); } + if (zoneRenderer.Contains("Zone.GetOrbitPosition(", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "ZoneRenderer must position bodies from daemon render body poses instead of the mirrored Unity Zone orbit evaluator."); + } + var requiredZoneRendererSymbols = new[] { "private AetheriaRuntimeZoneSnapshotCommit _daemonZoneSnapshot;", + "private readonly List _daemonBodyPoses", + "private readonly Dictionary _daemonBodyPosesByBodyKey", "public void LoadZone(Zone zone, AetheriaRuntimeZoneSnapshotCommit daemonZone = null)", "_daemonZoneSnapshot = daemonZone;", - "AetheriaRuntimeDaemonRenderQueries.EvaluateGravityTerrainHeight(" + "AetheriaRuntimeDaemonRenderQueries.EvaluateGravityTerrainHeight(", + "AetheriaRuntimeDaemonRenderQueries.QueryBodyPoses(_daemonZoneSnapshot, _daemonBodyPoses);", + "_daemonBodyPosesByBodyKey.TryGetValue(planet.Key, out var pose)" }; var missingZoneRendererSymbols = requiredZoneRendererSymbols .Where(symbol => !zoneRenderer.Contains(symbol, StringComparison.Ordinal)) diff --git a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs index 0658bcb97..e4e02ee6b 100644 --- a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs +++ b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs @@ -730,6 +730,60 @@ public class DaemonRuntimeDocumentTests Assert.AreEqual(-24.9968, height, 0.0001); } + [Test] + public void DaemonRenderQueriesPublishBodyPosesFromZoneSnapshot() + { + var zone = new AetheriaRuntimeZoneSnapshotCommit + { + Orbits = new[] + { + new AetheriaRuntimeOrbitSnapshotCommit + { + OrbitKey = "orbit:parent", + FixedPositionX = 10, + FixedPositionY = 20 + }, + new AetheriaRuntimeOrbitSnapshotCommit + { + OrbitKey = "orbit:child", + ParentOrbitKey = "orbit:parent", + Distance = 5, + Phase = 0.25 + } + }, + Bodies = new[] + { + new AetheriaRuntimeBodySnapshotCommit + { + BodyKey = "body:child", + OrbitKey = "orbit:child", + Kind = "planet" + }, + new AetheriaRuntimeBodySnapshotCommit + { + BodyKey = "body:explicit", + OrbitKey = "orbit:missing", + Kind = "gas_giant", + GravityInfluenceCenterX = -3, + GravityInfluenceCenterZ = 7 + } + } + }; + + var poses = AetheriaRuntimeDaemonRenderQueries.QueryBodyPoses(zone); + + Assert.AreEqual(2, poses.Length); + Assert.AreEqual("body:child", poses[0].BodyKey); + Assert.AreEqual("orbit:parent", poses[0].ParentOrbitKey); + Assert.AreEqual(10, poses[0].CenterX, 0.0001); + Assert.AreEqual(25, poses[0].CenterZ, 0.0001); + Assert.AreEqual(10, poses[0].ParentCenterX, 0.0001); + Assert.AreEqual(20, poses[0].ParentCenterZ, 0.0001); + Assert.AreEqual("body:explicit", poses[1].BodyKey); + Assert.AreEqual(-3, poses[1].CenterX, 0.0001); + Assert.AreEqual(7, poses[1].CenterZ, 0.0001); + } + [Test] public void SoaViewIndexRejectsInvalidRenderGroups() { diff --git a/Assets/Scripts/Zone Display/ZoneRenderer.cs b/Assets/Scripts/Zone Display/ZoneRenderer.cs index ac0601004..511598b87 100644 --- a/Assets/Scripts/Zone Display/ZoneRenderer.cs +++ b/Assets/Scripts/Zone Display/ZoneRenderer.cs @@ -86,6 +86,9 @@ public class ZoneRenderer : MonoBehaviour private PlanetObject[] _suns; private bool _showAsteroidUI; private AetheriaRuntimeZoneSnapshotCommit _daemonZoneSnapshot; + private readonly List _daemonBodyPoses = new List(); + private readonly Dictionary _daemonBodyPosesByBodyKey = + new Dictionary(StringComparer.Ordinal); public Dictionary WormholeInstances = new Dictionary(); private List _loot = new List(); @@ -466,6 +469,7 @@ public class ZoneRenderer : MonoBehaviour // } Shader.SetGlobalFloat("_AsteroidVerticalOffset", ActionGameManager.Instance.Settings.PlanetSettings.AsteroidVerticalOffset); + RefreshDaemonBodyPoses(); Plane[] planes = GeometryUtility.CalculateFrustumPlanes(MainCamera); bool isVisible(Bounds bounds) => GeometryUtility.TestPlanesAABB(planes, bounds); @@ -504,8 +508,11 @@ public class ZoneRenderer : MonoBehaviour foreach (var planet in Planets) { + if (!_daemonBodyPosesByBodyKey.TryGetValue(planet.Key, out var pose)) + continue; + var planetInstance = Zone.PlanetInstances[planet.Key]; - var p = Zone.GetOrbitPosition(planetInstance.OrbitKey); + var p = new float2((float)pose.CenterX, (float)pose.CenterZ); var height = GetTerrainHeight(p); if (-height > maxDepth) maxDepth = -height; planet.Value.transform.position = new Vector3(p.x, 0, p.y); @@ -513,10 +520,11 @@ public class ZoneRenderer : MonoBehaviour if (planet.Value is GasGiantObject gasGiantObject) { gasGiantObject.GravityWaves.material.SetFloat("_Phase", - Zone.Time * ((GasGiant) Zone.PlanetInstances[planet.Key]).GravityWavesSpeed); + Zone.Time * ((GasGiant) Zone.PlanetInstances[planet.Key]).GravityWavesSpeed); if (!(planet.Value is SunObject)) { - var toParent = normalize(Zone.GetOrbitPosition(planetInstance.Orbit.ParentOrbitKey) - p); + var parent = new float2((float)pose.ParentCenterX, (float)pose.ParentCenterZ); + var toParent = normalize(parent - p); gasGiantObject.SunMaterial.LightingDirection = new Vector3(toParent.x, 0, toParent.y); } } @@ -564,6 +572,17 @@ public class ZoneRenderer : MonoBehaviour // MinimapTintQuad.transform.position = gravPos - Vector3.up*10; } + private void RefreshDaemonBodyPoses() + { + AetheriaRuntimeDaemonRenderQueries.QueryBodyPoses(_daemonZoneSnapshot, _daemonBodyPoses); + _daemonBodyPosesByBodyKey.Clear(); + foreach (var pose in _daemonBodyPoses) + { + if (!string.IsNullOrWhiteSpace(pose.BodyKey)) + _daemonBodyPosesByBodyKey[pose.BodyKey] = pose; + } + } + private float GetTerrainHeight(float2 position) { return (float)AetheriaRuntimeDaemonRenderQueries.EvaluateGravityTerrainHeight( diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs index b70681aad..700d6ce7b 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs @@ -69,6 +69,38 @@ namespace GameCult.Aetheria.State.Verse public double WaveSpeed { get; } } + public readonly struct AetheriaRuntimeDaemonBodyPose + { + public AetheriaRuntimeDaemonBodyPose( + string bodyKey, + string orbitKey, + string parentOrbitKey, + string kind, + double centerX, + double centerZ, + double parentCenterX, + double parentCenterZ) + { + BodyKey = bodyKey ?? ""; + OrbitKey = orbitKey ?? ""; + ParentOrbitKey = parentOrbitKey ?? ""; + Kind = kind ?? ""; + CenterX = centerX; + CenterZ = centerZ; + ParentCenterX = parentCenterX; + ParentCenterZ = parentCenterZ; + } + + public string BodyKey { get; } + public string OrbitKey { get; } + public string ParentOrbitKey { get; } + public string Kind { get; } + public double CenterX { get; } + public double CenterZ { get; } + public double ParentCenterX { get; } + public double ParentCenterZ { get; } + } + public static class AetheriaRuntimeDaemonRenderQueries { public static AetheriaRuntimeGravityInfluenceBrush[] QueryGravityInfluences( @@ -119,6 +151,49 @@ namespace GameCult.Aetheria.State.Verse return brushes.Count; } + public static AetheriaRuntimeDaemonBodyPose[] QueryBodyPoses( + AetheriaRuntimeZoneSnapshotCommit? zone) + { + var poses = new List(); + QueryBodyPoses(zone, poses); + return poses.Count == 0 ? Array.Empty() : poses.ToArray(); + } + + public static int QueryBodyPoses( + AetheriaRuntimeZoneSnapshotCommit? zone, + List poses) + { + if (poses == null) throw new ArgumentNullException(nameof(poses)); + poses.Clear(); + if (zone == null) + return 0; + + var orbitPositions = BuildOrbitPositions(zone); + var orbits = BuildOrbitMap(zone); + foreach (var body in zone.Bodies ?? Array.Empty()) + { + if (body == null || !TryResolveBodyCenter(body, orbitPositions, out var center)) + continue; + + var orbitKey = body.OrbitKey ?? ""; + var parentOrbitKey = orbits.TryGetValue(orbitKey, out var orbit) ? orbit.ParentOrbitKey ?? "" : ""; + var parentCenter = orbitPositions.TryGetValue(parentOrbitKey, out var parent) + ? parent + : new AetheriaRuntimeXzPoint(0, 0); + poses.Add(new AetheriaRuntimeDaemonBodyPose( + body.BodyKey, + orbitKey, + parentOrbitKey, + body.Kind, + center.x, + center.z, + parentCenter.x, + parentCenter.z)); + } + + return poses.Count; + } + public static AetheriaRuntimeDaemonRenderGroupDocument[] QueryRenderGroups( AetheriaRuntimeDaemonSoaViewIndex? index, double minX, @@ -211,12 +286,7 @@ namespace GameCult.Aetheria.State.Verse private static Dictionary BuildOrbitPositions(AetheriaRuntimeZoneSnapshotCommit zone) { - var source = new Dictionary(StringComparer.Ordinal); - foreach (var orbit in zone.Orbits ?? Array.Empty()) - { - if (orbit != null && !string.IsNullOrWhiteSpace(orbit.OrbitKey)) - source[orbit.OrbitKey] = orbit; - } + var source = BuildOrbitMap(zone); var positions = new Dictionary(StringComparer.Ordinal) { @@ -228,6 +298,19 @@ namespace GameCult.Aetheria.State.Verse return positions; } + private static Dictionary BuildOrbitMap( + AetheriaRuntimeZoneSnapshotCommit zone) + { + var source = new Dictionary(StringComparer.Ordinal); + foreach (var orbit in zone.Orbits ?? Array.Empty()) + { + if (orbit != null && !string.IsNullOrWhiteSpace(orbit.OrbitKey)) + source[orbit.OrbitKey] = orbit; + } + + return source; + } + private static AetheriaRuntimeXzPoint ResolveOrbitPosition( string orbitKey, Dictionary source,