using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace Ironspore { public sealed class WorldLighting : MonoBehaviour { readonly List fixtures = new List(2); WorldLightingProfile profile; Material sky; ReflectionProbe environment; Camera observer; bool ownsProfile; int shadowBudget; bool captureQueued, capturing; float captureAfter; public bool ReflectionReady { get; private set; } public void Initialize(WorldLightingProfile settings, bool hangar) { profile = settings; if (!profile) { profile = ScriptableObject.CreateInstance(); ownsProfile = true; } int level = QualitySettings.GetQualityLevel(); shadowBudget = level <= 1 ? 0 : level >= 4 ? 2 : 1; RenderSettings.ambientMode = AmbientMode.Trilight; RenderSettings.ambientSkyColor = profile.ambientSky; RenderSettings.ambientEquatorColor = profile.ambientEquator; RenderSettings.ambientGroundColor = profile.ambientGround; RenderSettings.fog = !hangar; RenderSettings.fogMode = FogMode.Linear; RenderSettings.fogColor = profile.fogColor; RenderSettings.fogStartDistance = profile.fogStart; RenderSettings.fogEndDistance = profile.fogEnd; RenderSettings.reflectionIntensity = profile.reflectionIntensity; var sourceSky = RenderSettings.skybox; sky = sourceSky ? new Material(sourceSky) : new Material(Shader.Find("Skybox/Procedural")); sky.name = "RuntimeDaySky"; if (sky.HasProperty("_Exposure")) sky.SetFloat("_Exposure", profile.skyExposure); RenderSettings.skybox = sky; var sunObject = new GameObject("Sun"); sunObject.transform.SetParent(transform, false); sunObject.transform.rotation = Quaternion.Euler(profile.sunEuler); var sun = sunObject.AddComponent(); sun.type = LightType.Directional; sun.color = profile.sunColor; sun.intensity = hangar ? profile.hangarSunIntensity : profile.sunIntensity; sun.renderMode = LightRenderMode.ForcePixel; sun.shadows = level <= 1 ? LightShadows.Hard : LightShadows.Soft; sun.shadowStrength = 1; sun.shadowBias = profile.bias; sun.shadowNormalBias = profile.normalBias; sun.shadowNearPlane = profile.shadowNearPlane; sun.shadowCustomResolution = level <= 1 ? 1024 : level >= 4 ? 4096 : 2048; RenderSettings.sun = sun; } public void CompleteEnvironment(Camera camera, bool hangar) { observer = camera; if (hangar) { WorldFactory.Box("HangarWorkFixture", new Vector3(2, 8.2f, 1), new Vector3(2, .18f, .65f), new Color(.8f, .87f, 1), transform, false, true); AddSpot("HangarWorkLight", new Vector3(2, 8, 1), new Vector3(2, 1, 2), new Color(.82f, .9f, 1), profile.hangarIntensity, 17, 84); } else { WorldFactory.Box("NavigationWorkFixture", new Vector3(0, 3.4f, 22.7f), new Vector3(1.2f, .16f, .4f), new Color(.55f, 1, .85f), transform, false, true); AddSpot("NavigationWorkLight", new Vector3(0, 3.25f, 22.3f), new Vector3(0, 0, 20.6f), new Color(.65f, 1, .85f), profile.navigationIntensity, 11, 78); WorldFactory.Box("ExtractionLampPost", new Vector3(-22.4f, 3.1f, -8), new Vector3(.16f, 6.2f, .16f), new Color(.2f, .25f, .28f), transform, false); WorldFactory.Box("ExtractionLampArm", new Vector3(-20.7f, 6.1f, -8), new Vector3(3.5f, .15f, .18f), new Color(.2f, .25f, .28f), transform, false); WorldFactory.Box("ExtractionWorkFixture", new Vector3(-19, 6, -8), new Vector3(1, .18f, .6f), new Color(1, .77f, .45f), transform, false, true); AddSpot("ExtractionWorkLight", new Vector3(-19, 5.8f, -8), new Vector3(-19, 0, -8), new Color(1, .8f, .55f), profile.extractionIntensity, 13, 76); } var probeObject = new GameObject("EnvironmentReflection"); probeObject.transform.SetParent(transform, false); probeObject.transform.position = hangar ? new Vector3(2, 3, 2) : new Vector3(0, 3, 8); environment = probeObject.AddComponent(); environment.mode = ReflectionProbeMode.Realtime; environment.refreshMode = ReflectionProbeRefreshMode.ViaScripting; environment.timeSlicingMode = ReflectionProbeTimeSlicingMode.IndividualFaces; environment.resolution = QualitySettings.GetQualityLevel() >= 4 ? 256 : 128; environment.hdr = true; environment.boxProjection = true; environment.blendDistance = 5; environment.size = hangar ? new Vector3(66, 18, 45) : new Vector3(74, 20, 104); // The captured environment must not freeze actors, transient props or world labels. environment.cullingMask = ~((1 << 8) | (1 << 9) | (1 << 10) | (1 << 11)); environment.nearClipPlane = .2f; environment.farClipPlane = 150; environment.intensity = profile.reflectionIntensity; SelectLights(); StartCoroutine(CaptureEnvironment()); } void AddSpot(string name, Vector3 position, Vector3 target, Color color, float intensity, float range, float angle) { var obj = new GameObject(name); obj.transform.SetParent(transform, false); obj.transform.position = position; obj.transform.LookAt(target); var light = obj.AddComponent(); light.type = LightType.Spot; light.renderMode = LightRenderMode.ForcePixel; light.color = color; light.intensity = intensity; light.range = range; light.spotAngle = angle; light.innerSpotAngle = angle * .65f; light.shadows = LightShadows.Soft; light.shadowCustomResolution = 1024; light.shadowBias = profile.bias; light.shadowNormalBias = profile.normalBias; light.shadowNearPlane = profile.shadowNearPlane; light.enabled = false; fixtures.Add(light); } void LateUpdate() { SelectLights(); if (environment && captureQueued && !capturing && Time.unscaledTime >= captureAfter) StartCoroutine(CaptureEnvironment()); } void SelectLights() { if (!observer) return; Vector3 position = observer.transform.position; float limit = profile.localLightCullDistance * profile.localLightCullDistance; Light nearest = null; float distance = limit; foreach (var light in fixtures) { float candidate = (light.transform.position - position).sqrMagnitude; if (candidate < distance) { distance = candidate; nearest = light; } } foreach (var light in fixtures) { bool active = shadowBudget > 0 && (light == nearest || shadowBudget > 1 && (light.transform.position - position).sqrMagnitude < limit); if (light.enabled == active) continue; light.enabled = active; captureQueued = true; } } IEnumerator CaptureEnvironment() { capturing = true; captureQueued = false; yield return null; DynamicGI.UpdateEnvironment(); int render = environment.RenderProbe(); while (environment && !environment.IsFinishedRendering(render)) yield return null; ReflectionReady = environment != null; capturing = false; captureAfter = Time.unscaledTime + 8; } void OnDestroy() { if (sky) { if (RenderSettings.skybox == sky) RenderSettings.skybox = null; Destroy(sky); } if (ownsProfile && profile) Destroy(profile); } } }