91 lines
4.3 KiB
C#
91 lines
4.3 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
/// <summary>Reachable landmarks at the gaps between four repeated server clusters.</summary>
|
|
[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<Transform> markers = new List<Transform>();
|
|
private PortalServerGrid grid;
|
|
private float period;
|
|
private Material runtimeMaterial;
|
|
private bool rebuild;
|
|
private Vector3 initialCentre;
|
|
private Material lastMaterial;
|
|
|
|
private void OnEnable()
|
|
{
|
|
meadow = GetComponent<PortalMeadow>(); grid = GetComponent<PortalServerGrid>(); 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<BoxCollider>().isTrigger = true;
|
|
var renderer = marker.GetComponent<MeshRenderer>();
|
|
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.Endless || 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);
|
|
}
|
|
}
|