Position rendered zone bodies from daemon poses

This commit is contained in:
Meta Vens 2026-06-21 15:20:29 +01:00
commit 140028d0db
4 changed files with 183 additions and 13 deletions

View file

@ -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<AetheriaRuntimeDaemonBodyPose> _daemonBodyPoses",
"private readonly Dictionary<string, AetheriaRuntimeDaemonBodyPose> _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))

View file

@ -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()
{

View file

@ -86,6 +86,9 @@ public class ZoneRenderer : MonoBehaviour
private PlanetObject[] _suns;
private bool _showAsteroidUI;
private AetheriaRuntimeZoneSnapshotCommit _daemonZoneSnapshot;
private readonly List<AetheriaRuntimeDaemonBodyPose> _daemonBodyPoses = new List<AetheriaRuntimeDaemonBodyPose>();
private readonly Dictionary<string, AetheriaRuntimeDaemonBodyPose> _daemonBodyPosesByBodyKey =
new Dictionary<string, AetheriaRuntimeDaemonBodyPose>(StringComparer.Ordinal);
public Dictionary<Wormhole, (GameObject gravity, CompassIcon icon)> WormholeInstances = new Dictionary<Wormhole, (GameObject, CompassIcon)>();
private List<ItemPickup> _loot = new List<ItemPickup>();
@ -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(

View file

@ -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<AetheriaRuntimeDaemonBodyPose>();
QueryBodyPoses(zone, poses);
return poses.Count == 0 ? Array.Empty<AetheriaRuntimeDaemonBodyPose>() : poses.ToArray();
}
public static int QueryBodyPoses(
AetheriaRuntimeZoneSnapshotCommit? zone,
List<AetheriaRuntimeDaemonBodyPose> 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<AetheriaRuntimeBodySnapshotCommit>())
{
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<string, AetheriaRuntimeXzPoint> BuildOrbitPositions(AetheriaRuntimeZoneSnapshotCommit zone)
{
var source = new Dictionary<string, AetheriaRuntimeOrbitSnapshotCommit>(StringComparer.Ordinal);
foreach (var orbit in zone.Orbits ?? Array.Empty<AetheriaRuntimeOrbitSnapshotCommit>())
{
if (orbit != null && !string.IsNullOrWhiteSpace(orbit.OrbitKey))
source[orbit.OrbitKey] = orbit;
}
var source = BuildOrbitMap(zone);
var positions = new Dictionary<string, AetheriaRuntimeXzPoint>(StringComparer.Ordinal)
{
@ -228,6 +298,19 @@ namespace GameCult.Aetheria.State.Verse
return positions;
}
private static Dictionary<string, AetheriaRuntimeOrbitSnapshotCommit> BuildOrbitMap(
AetheriaRuntimeZoneSnapshotCommit zone)
{
var source = new Dictionary<string, AetheriaRuntimeOrbitSnapshotCommit>(StringComparer.Ordinal);
foreach (var orbit in zone.Orbits ?? Array.Empty<AetheriaRuntimeOrbitSnapshotCommit>())
{
if (orbit != null && !string.IsNullOrWhiteSpace(orbit.OrbitKey))
source[orbit.OrbitKey] = orbit;
}
return source;
}
private static AetheriaRuntimeXzPoint ResolveOrbitPosition(
string orbitKey,
Dictionary<string, AetheriaRuntimeOrbitSnapshotCommit> source,