190 lines
8.7 KiB
C#
190 lines
8.7 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace Ironspore
|
|
{
|
|
public sealed class WorldLighting : MonoBehaviour
|
|
{
|
|
readonly List<Light> fixtures = new List<Light>(2);
|
|
WorldLightingProfile profile;
|
|
Material sky;
|
|
ReflectionProbe environment;
|
|
Camera observer;
|
|
bool ownsProfile;
|
|
int shadowBudget;
|
|
int qualityLevel = -1;
|
|
Light sun;
|
|
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<WorldLightingProfile>();
|
|
ownsProfile = true;
|
|
}
|
|
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);
|
|
sun = sunObject.AddComponent<Light>();
|
|
sun.type = LightType.Directional;
|
|
sun.color = profile.sunColor;
|
|
sun.intensity = hangar ? profile.hangarSunIntensity : profile.sunIntensity;
|
|
sun.renderMode = LightRenderMode.ForcePixel;
|
|
sun.shadowStrength = 1;
|
|
sun.shadowBias = profile.bias;
|
|
sun.shadowNormalBias = profile.normalBias;
|
|
sun.shadowNearPlane = profile.shadowNearPlane;
|
|
RenderSettings.sun = sun;
|
|
ApplyQuality();
|
|
}
|
|
|
|
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<ReflectionProbe>();
|
|
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>();
|
|
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 ApplyQuality()
|
|
{
|
|
qualityLevel = QualitySettings.GetQualityLevel();
|
|
shadowBudget = qualityLevel <= 1 ? 0 : qualityLevel >= 4 ? 2 : 1;
|
|
sun.shadows = qualityLevel <= 1 ? LightShadows.Hard : LightShadows.Soft;
|
|
sun.shadowCustomResolution = qualityLevel <= 1 ? 1024 : qualityLevel >= 4 ? 4096 : 2048;
|
|
captureQueued = true;
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
if (qualityLevel != QualitySettings.GetQualityLevel()) ApplyQuality();
|
|
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;
|
|
// Change the probe size only after the previous capture has completed.
|
|
environment.resolution = QualitySettings.GetQualityLevel() >= 4 ? 256 : 128;
|
|
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);
|
|
}
|
|
}
|
|
}
|