using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
/// Reachable landmarks at the gaps between four repeated server clusters.
[ExecuteAlways, DisallowMultipleComponent, RequireComponent(typeof(PortalMeadow)), DefaultExecutionOrder(205)]
public sealed class PortalExitMarker : MonoBehaviour
{
[SerializeField] private Material exitMaterial;
[SerializeField] private float rotationY = 45;
[SerializeField] private Vector3 dimensions = new Vector3(1.5f, 3.2f, 0.2f);
private PortalMeadow meadow;
private readonly List markers = new List();
private PortalServerGrid grid;
private float period;
private Material runtimeMaterial;
private bool rebuild;
private Vector3 initialCentre;
private Material lastMaterial;
private void OnEnable()
{
meadow = GetComponent(); grid = GetComponent(); rebuild = true;
RenderPipelineManager.beginCameraRendering += Sync;
}
private void OnValidate() { rebuild = true; }
private void Update()
{
if (!meadow || !exitMaterial || !grid || !grid.enabled || grid.RepetitionPeriod <= 0) { Clear(); return; }
if (!rebuild && markers.Count > 0 && initialCentre == meadow.Center && lastMaterial == exitMaterial && period == grid.RepetitionPeriod) return;
Clear(); rebuild = false; initialCentre = meadow.Center; lastMaterial = exitMaterial;
period = grid.RepetitionPeriod;
runtimeMaterial = new Material(exitMaterial) { hideFlags = HideFlags.DontSave, name = "Distant Exit (runtime)" };
for (int z = -1; z <= 1; z++)
for (int x = -1; x <= 1; x++)
{
var marker = GameObject.CreatePrimitive(PrimitiveType.Cube);
marker.name = "Luminous Gap Exit (pooled)";
marker.hideFlags = HideFlags.DontSave;
marker.transform.SetParent(transform, false);
marker.transform.localPosition = initialCentre + new Vector3((x + 0.5f) * period, 0, (z + 0.5f) * period)
+ Vector3.up * Mathf.Max(0.1f, dimensions.y) * 0.5f;
marker.transform.localRotation = Quaternion.Euler(0, rotationY, 0);
marker.transform.localScale = new Vector3(Mathf.Max(0.1f, dimensions.x), Mathf.Max(0.1f, dimensions.y), Mathf.Max(0.01f, dimensions.z));
marker.GetComponent().isTrigger = true;
var renderer = marker.GetComponent();
renderer.sharedMaterial = runtimeMaterial;
renderer.renderingLayerMask = meadow.RenderingLayers;
renderer.shadowCastingMode = ShadowCastingMode.Off;
renderer.allowOcclusionWhenDynamic = false;
Bounds bounds = renderer.localBounds;
float smallest = Mathf.Min(marker.transform.localScale.x, marker.transform.localScale.y, marker.transform.localScale.z);
bounds.Expand(meadow.BoundsPadding * 2 / smallest);
renderer.localBounds = bounds;
markers.Add(marker.transform);
}
}
private void LateUpdate()
{
if (!meadow || !meadow.Following || period <= 0) return;
float poolPeriod = period * 3;
foreach (Transform marker in markers)
{
Vector3 p = marker.localPosition;
p.x += Mathf.Floor((meadow.SurfaceCenter.x - p.x + poolPeriod * 0.5f) / poolPeriod) * poolPeriod;
p.z += Mathf.Floor((meadow.SurfaceCenter.z - p.z + poolPeriod * 0.5f) / poolPeriod) * poolPeriod;
marker.localPosition = p;
}
}
private void Sync(ScriptableRenderContext context, Camera camera)
{
if (!runtimeMaterial || !meadow || !meadow.MeadowMaterial) return;
runtimeMaterial.CopyPropertiesFromMaterial(exitMaterial);
meadow.ApplyBend(runtimeMaterial);
meadow.ConfigureRegion(runtimeMaterial);
}
private void OnDisable() { RenderPipelineManager.beginCameraRendering -= Sync; Clear(); }
private void Clear()
{
foreach (Transform marker in markers)
if (marker) { marker.gameObject.SetActive(false); Release(marker.gameObject); }
markers.Clear();
if (runtimeMaterial) Release(runtimeMaterial);
runtimeMaterial = null;
}
private static void Release(Object value)
{
if (Application.isPlaying) Destroy(value); else DestroyImmediate(value);
}
}