99 lines
4.4 KiB
C#
99 lines
4.4 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
/// <summary>A local sky, independent of RenderSettings.skybox, ready for the existing projection masks.</summary>
|
|
[ExecuteAlways, DisallowMultipleComponent, RequireComponent(typeof(PortalMeadow)), DefaultExecutionOrder(210)]
|
|
public sealed class PortalSkyDome : MonoBehaviour
|
|
{
|
|
public enum SkyVisibility { Preview, PortalOnly, Hidden }
|
|
[SerializeField] private Material skyMaterial;
|
|
[SerializeField] private SkyVisibility visibility = SkyVisibility.Preview;
|
|
[SerializeField] private bool insidePortalB;
|
|
[Tooltip("The sphere also expands automatically to contain the terrain and its bend bounds.")]
|
|
[SerializeField, Min(10)] private float minimumRadius = 100;
|
|
private PortalMeadow meadow;
|
|
private GameObject dome;
|
|
private Mesh mesh;
|
|
private Material runtimeMaterial;
|
|
private Material sourceMaterial;
|
|
|
|
private void OnEnable() { meadow = GetComponent<PortalMeadow>(); }
|
|
private void LateUpdate()
|
|
{
|
|
if (!meadow) meadow = GetComponent<PortalMeadow>();
|
|
if (!meadow || !skyMaterial) { Clear(); return; }
|
|
if (!dome || sourceMaterial != skyMaterial) Build();
|
|
// The centre is the same initial snapshot used by the terrain and the server rings.
|
|
dome.transform.localPosition = meadow.SurfaceCenter;
|
|
float radius = Mathf.Max(minimumRadius, meadow.Size.magnitude + meadow.BoundsPadding + 20);
|
|
dome.transform.localScale = Vector3.one * radius;
|
|
runtimeMaterial.CopyPropertiesFromMaterial(skyMaterial);
|
|
runtimeMaterial.SetFloat("_Visibility", (float)visibility);
|
|
runtimeMaterial.SetFloat("_InsidePortalB", insidePortalB ? 1 : 0);
|
|
}
|
|
|
|
// Ready to connect to the future portal's enter/exit UnityEvents. No distance trigger is inferred.
|
|
public void EnterPortalB() { SetInsidePortalB(true); }
|
|
public void ExitPortalB() { SetInsidePortalB(false); }
|
|
public void SetInsidePortalB(bool inside)
|
|
{
|
|
insidePortalB = inside;
|
|
visibility = SkyVisibility.PortalOnly;
|
|
if (runtimeMaterial)
|
|
{
|
|
runtimeMaterial.SetFloat("_Visibility", (float)visibility);
|
|
runtimeMaterial.SetFloat("_InsidePortalB", inside ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
private void Build()
|
|
{
|
|
Clear();
|
|
sourceMaterial = skyMaterial;
|
|
runtimeMaterial = new Material(skyMaterial) { name = "PortalB Sky (runtime)", hideFlags = HideFlags.DontSave };
|
|
dome = new GameObject("PortalB Sky Sphere (generated)") { hideFlags = HideFlags.DontSave, layer = gameObject.layer };
|
|
dome.transform.SetParent(transform, false);
|
|
const int segments = 64, rings = 32;
|
|
var positions = new Vector3[(segments + 1) * (rings + 1)];
|
|
var triangles = new int[segments * rings * 6];
|
|
for (int y = 0; y <= rings; y++)
|
|
for (int x = 0; x <= segments; x++)
|
|
{
|
|
float latitude = Mathf.PI * y / rings, longitude = 2 * Mathf.PI * x / segments;
|
|
positions[y * (segments + 1) + x] = new Vector3(Mathf.Sin(latitude) * Mathf.Cos(longitude),
|
|
Mathf.Cos(latitude), Mathf.Sin(latitude) * Mathf.Sin(longitude));
|
|
}
|
|
int index = 0;
|
|
for (int y = 0; y < rings; y++)
|
|
for (int x = 0; x < segments; x++)
|
|
{
|
|
int a = y * (segments + 1) + x, b = a + segments + 1;
|
|
triangles[index++] = a; triangles[index++] = a + 1; triangles[index++] = b;
|
|
triangles[index++] = b; triangles[index++] = a + 1; triangles[index++] = b + 1;
|
|
}
|
|
mesh = new Mesh { name = "Portal sky sphere", hideFlags = HideFlags.DontSave };
|
|
mesh.vertices = positions;
|
|
mesh.triangles = triangles;
|
|
mesh.RecalculateBounds();
|
|
dome.AddComponent<MeshFilter>().sharedMesh = mesh;
|
|
var renderer = dome.AddComponent<MeshRenderer>();
|
|
renderer.sharedMaterial = runtimeMaterial;
|
|
renderer.shadowCastingMode = ShadowCastingMode.Off;
|
|
renderer.receiveShadows = false;
|
|
renderer.allowOcclusionWhenDynamic = false;
|
|
}
|
|
private void OnDisable() { Clear(); }
|
|
private void Clear()
|
|
{
|
|
if (dome) { dome.SetActive(false); Release(dome); }
|
|
if (mesh) Release(mesh);
|
|
if (runtimeMaterial) Release(runtimeMaterial);
|
|
dome = null; mesh = null; runtimeMaterial = null; sourceMaterial = null;
|
|
}
|
|
private static void Release(Object value)
|
|
{
|
|
if (Application.isPlaying) Destroy(value);
|
|
else DestroyImmediate(value);
|
|
}
|
|
}
|