Integrate Aetheria reactive state worktree

This commit is contained in:
Meta Vens 2026-06-28 01:18:21 +01:00
commit 65133ac8a0
61 changed files with 3427 additions and 402 deletions

View file

@ -25,24 +25,27 @@ public class MapRenderer : MonoBehaviour
public Camera InfluenceCamera;
public Image OverlayDisplay;
public Image GravityDisplay;
public Material GravityBackdropMaterial;
public Image TintDisplay;
public Image InfluenceDisplay;
public AetheriaRenderSplatLayerRenderer SplatLayerRenderer;
public AetheriaDaemonRenderAssetCatalog AssetCatalog;
public RectTransform RtsIconRoot;
public RawImage RtsIconPrototype;
public float Scale;
public float2 Position;
public float IconSize = 1f/128;
private RectTransform _rect;
private RenderTexture _mapTexture;
private RenderTexture _gravityTexture;
private RenderTexture _tintTexture;
private RenderTexture _influenceTexture;
private int2 _size;
private bool _init;
private AetheriaClient _client;
private string _clientStatePath = "";
private AetheriaRuntimeObjectsViewportDocument _objectsViewport;
private AetheriaRuntimeGravityViewportDocument _gravityViewport;
private AetheriaRuntimeRenderSplatsViewportDocument _renderSplatsViewport;
private float _nextViewportRefreshTime;
private readonly List<RawImage> _rtsIconPool = new List<RawImage>();
void Start()
{
@ -52,10 +55,16 @@ public class MapRenderer : MonoBehaviour
private void OnEnable()
{
_init = true;
MapOverlayCamera.gameObject.SetActive(true);
GravityCamera.gameObject.SetActive(true);
TintCamera.gameObject.SetActive(true);
InfluenceCamera.gameObject.SetActive(true);
SetCameraActive(MapOverlayCamera, false);
SetCameraActive(GravityCamera, false);
SetCameraActive(TintCamera, false);
SetCameraActive(InfluenceCamera, false);
if (OverlayDisplay != null)
OverlayDisplay.gameObject.SetActive(false);
if (GravityBackdropMaterial != null && GravityDisplay != null)
GravityDisplay.material = GravityBackdropMaterial;
EnsureRtsIconRoot();
SetRtsIconsActive(true);
RefreshViewportDocuments(force: true);
// If hiding minimap asteroids, turn them back on for the map screen
@ -69,10 +78,13 @@ public class MapRenderer : MonoBehaviour
{
ReleaseTextures();
}
MapOverlayCamera.gameObject.SetActive(false);
GravityCamera.gameObject.SetActive(false);
TintCamera.gameObject.SetActive(false);
InfluenceCamera.gameObject.SetActive(false);
SetCameraActive(MapOverlayCamera, false);
SetCameraActive(GravityCamera, false);
SetCameraActive(TintCamera, false);
SetCameraActive(InfluenceCamera, false);
if (OverlayDisplay != null)
OverlayDisplay.gameObject.SetActive(false);
SetRtsIconsActive(false);
// If hiding minimap asteroids, turn them back off when leaving the map screen
if (!ResolveShowAsteroidsInMinimap())
@ -88,12 +100,6 @@ public class MapRenderer : MonoBehaviour
{
_mapTexture.Release();
_mapTexture = null;
_gravityTexture.Release();
_gravityTexture = null;
_tintTexture.Release();
_tintTexture = null;
_influenceTexture.Release();
_influenceTexture = null;
}
void LateUpdate()
@ -108,40 +114,12 @@ public class MapRenderer : MonoBehaviour
ReleaseTextures();
}
_mapTexture = new RenderTexture(_size.x, _size.y, 0, RenderTextureFormat.Default);
MapOverlayCamera.targetTexture = _mapTexture;
OverlayDisplay.material.SetTexture("_DetailTex", _mapTexture);
_gravityTexture = new RenderTexture(_size.x, _size.y, 0, RenderTextureFormat.RFloat);
GravityCamera.targetTexture = _gravityTexture;
GravityDisplay.material.SetTexture("_DetailTex", _gravityTexture);
_tintTexture = new RenderTexture(_size.x / 2, _size.y / 2, 0, RenderTextureFormat.RGB111110Float);
TintCamera.targetTexture = _tintTexture;
TintDisplay.material.SetTexture("_DetailTex", _tintTexture);
_influenceTexture = new RenderTexture(_size.x, _size.y, 0, RenderTextureFormat.RFloat);
InfluenceCamera.targetTexture = _influenceTexture;
InfluenceDisplay.material.SetTexture("_DetailTex", _influenceTexture);
}
var pos = ((Vector2) Position).Flatland(1);
MapOverlayCamera.transform.position = pos;
MapOverlayCamera.orthographicSize = _size.y * Scale * .5f;
GravityCamera.transform.position = pos;
GravityCamera.orthographicSize = _size.y * Scale * .5f;
GravityDisplay.material.SetFloat("_Scale", Scale / 2);
TintCamera.transform.position = pos;
TintCamera.orthographicSize = _size.y * Scale * .5f;
InfluenceCamera.transform.position = pos;
InfluenceCamera.orthographicSize = _size.y * Scale * .5f;
ZoneRenderer.SetIconSize(IconSize * Scale);
RefreshViewportDocuments(force: false);
RenderSplatLayers();
RenderRtsIcons();
}
private void RefreshViewportDocuments(bool force)
@ -158,8 +136,8 @@ public class MapRenderer : MonoBehaviour
.ObjectsViewportAsync(viewport)
.GetAwaiter()
.GetResult();
_gravityViewport = client
.GravityViewportAsync(viewport)
_renderSplatsViewport = client
.RenderSplatsViewportAsync(viewport)
.GetAwaiter()
.GetResult();
@ -175,6 +153,153 @@ public class MapRenderer : MonoBehaviour
}
}
private void RenderSplatLayers()
{
if (_renderSplatsViewport == null)
return;
if (SplatLayerRenderer == null)
SplatLayerRenderer = GetComponent<AetheriaRenderSplatLayerRenderer>() ??
gameObject.AddComponent<AetheriaRenderSplatLayerRenderer>();
SplatLayerRenderer.Render(_renderSplatsViewport, _size.x, _size.y);
ApplyLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.GravityHeight, GravityDisplay);
ApplyLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogTint, TintDisplay);
ApplyLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.Influence, InfluenceDisplay);
}
private void ApplyLayerTexture(string layerKey, Image display)
{
if (display == null ||
SplatLayerRenderer == null ||
!SplatLayerRenderer.TryGetTexture(layerKey, out var texture) ||
texture == null)
{
return;
}
display.material.SetTexture("_DetailTex", texture);
}
private static void SetCameraActive(Camera camera, bool active)
{
if (camera != null)
camera.gameObject.SetActive(active);
}
private void RenderRtsIcons()
{
EnsureRtsIconRoot();
if (RtsIconRoot == null)
return;
var objects = _objectsViewport?.Objects ?? Array.Empty<AetheriaRuntimeRtsViewportObject>();
for (var i = 0; i < objects.Count; i++)
{
var icon = ResolveRtsIcon(i);
ApplyRtsIcon(icon, objects[i]);
}
for (var i = objects.Count; i < _rtsIconPool.Count; i++)
if (_rtsIconPool[i] != null)
_rtsIconPool[i].gameObject.SetActive(false);
}
private RawImage ResolveRtsIcon(int index)
{
while (_rtsIconPool.Count <= index)
{
RawImage icon;
if (RtsIconPrototype != null)
{
icon = Instantiate(RtsIconPrototype, RtsIconRoot);
}
else
{
var go = new GameObject("RTS Map Icon", typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage));
go.transform.SetParent(RtsIconRoot, false);
icon = go.GetComponent<RawImage>();
icon.texture = Texture2D.whiteTexture;
icon.raycastTarget = false;
}
_rtsIconPool.Add(icon);
}
return _rtsIconPool[index];
}
private void ApplyRtsIcon(RawImage icon, AetheriaRuntimeRtsViewportObject obj)
{
if (icon == null || obj == null)
return;
icon.gameObject.SetActive(true);
if (AssetCatalog != null && AssetCatalog.TryResolveTexture(obj.IconAsset, out var texture))
icon.texture = texture;
icon.color = ResolveIconColor(obj);
var rectTransform = icon.rectTransform;
var pixelSize = Mathf.Max(4f, IconSize * (obj.Controlled ? 1.25f : 1f));
rectTransform.anchorMin = rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.sizeDelta = new Vector2(pixelSize, pixelSize);
rectTransform.anchoredPosition = WorldToMapPosition(obj.X, obj.Y);
rectTransform.localRotation = Quaternion.Euler(
0,
0,
Mathf.Atan2((float)obj.DirectionY, (float)obj.DirectionX) * Mathf.Rad2Deg - 90f);
}
private Vector2 WorldToMapPosition(double worldX, double worldY)
{
var viewport = _objectsViewport?.Viewport ?? ResolveViewportBounds();
var minX = Math.Min(viewport.MinX, viewport.MaxX);
var maxX = Math.Max(viewport.MinX, viewport.MaxX);
var minY = Math.Min(viewport.MinY, viewport.MaxY);
var maxY = Math.Max(viewport.MinY, viewport.MaxY);
var width = Math.Max(0.0001, maxX - minX);
var height = Math.Max(0.0001, maxY - minY);
var rootRect = RtsIconRoot != null ? RtsIconRoot.rect : new Rect(0, 0, _size.x, _size.y);
var u = (float)((worldX - minX) / width);
var v = (float)((worldY - minY) / height);
return new Vector2(
(u - 0.5f) * rootRect.width,
(v - 0.5f) * rootRect.height);
}
private static Color ResolveIconColor(AetheriaRuntimeRtsViewportObject obj)
{
var color = obj.Controlled
? new Color(0.3f, 0.9f, 1.0f, 1.0f)
: Color.HSVToRGB(frac((obj.FactionKey ?? obj.Kind ?? "").GetHashCode() * 0.0137f), 0.55f, 0.95f);
color.a = Mathf.Clamp01(Mathf.Max(0.25f, (float)obj.Visibility));
return color;
}
private void EnsureRtsIconRoot()
{
if (RtsIconRoot != null)
return;
var parent = GravityDisplay != null
? GravityDisplay.transform.parent
: transform;
var go = new GameObject("RTS Command Icons", typeof(RectTransform));
go.transform.SetParent(parent, false);
RtsIconRoot = go.GetComponent<RectTransform>();
RtsIconRoot.anchorMin = Vector2.zero;
RtsIconRoot.anchorMax = Vector2.one;
RtsIconRoot.offsetMin = Vector2.zero;
RtsIconRoot.offsetMax = Vector2.zero;
RtsIconRoot.SetAsLastSibling();
}
private void SetRtsIconsActive(bool active)
{
if (RtsIconRoot != null)
RtsIconRoot.gameObject.SetActive(active);
}
private AetheriaRuntimeRtsViewportBounds ResolveViewportBounds()
{
var screenHeight = _size.y <= 0 ? Math.Max(1, Screen.height) : _size.y;