// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using UnityEngine;
namespace Microsoft.MixedReality.GraphicsTools
{
///
/// Utility component to animate and visualize a light that can be used with the GraphicsTools/Standard and
/// GraphicsTools/Standard Canvas shaders that have the "_DISTANT_LIGHT" keyword enabled.
///
/// A DistantLight can be used as a replacement for a Unity DirectionalLight. The main purpose of DistantLights is to recreate
/// important light sources for environments, i.e. the sun or moon.
///
/// By default The Graphics Tools/Standard and Graphics Tools/Standard Canvas shaders use the first Unity DirectionalLight
/// added to a scene. But, there are some cases where you need a light that is decoupled from the environment (such as
/// in user interfaces). Unity normally performs this decoupling with light masks. But, light masks can be expensive on some platforms.
///
[ExecuteInEditMode]
[AddComponentMenu("Scripts/GraphicsTools/DistantLight")]
public class DistantLight : BaseLight
{
// The Graphics Tools/Standard and Graphics Tools/Standard Canvas shaders supports up to one (1) distant lights.
private const int distantLightCount = 1;
private const int distantLightDataSize = 2;
private static readonly Vector4 invalidLightDirection = new Vector4(0.0f, 0.0f, 1.0f, 0.0f);
private static List activeDistantLights = new List(distantLightCount);
private static Vector4[] distantLightData = new Vector4[distantLightDataSize * distantLightCount];
private static int _DistantLightDataID;
private static int lastDistantLightUpdate = -1;
[Tooltip("Specifies the light color.")]
[SerializeField]
private Color color = new Color(255.0f / 255.0f, 244.0f / 255.0f, 214.0f / 255.0f, 1.0f);
///
/// Specifies the light color.
///
public Color Color
{
get => color;
set => color = value;
}
[Tooltip("Scales the brightness of the light.")]
[SerializeField, Min(0.0f)]
private float intensity = 1.0f;
///
/// Scales the brightness of the light.
///
public float Intensity
{
get => intensity;
set => intensity = Mathf.Max(0.0f, value);
}
#region BaseLight Implementation
///
protected override void Initialize()
{
_DistantLightDataID = Shader.PropertyToID("_DistantLightData");
}
///
protected override void AddLight()
{
if (activeDistantLights.Count == distantLightCount)
{
Debug.LogWarningFormat("Max distant light count {0} exceeded. {1} will not be considered by the Graphics Tools/Standard shader until other lights are removed.", distantLightCount, gameObject.name);
}
activeDistantLights.Add(this);
}
///
protected override void RemoveLight()
{
activeDistantLights.Remove(this);
}
///
protected override void UpdateLights(bool forceUpdate = false)
{
if (lastDistantLightUpdate == -1)
{
Initialize();
}
if (!forceUpdate && (Time.frameCount == lastDistantLightUpdate))
{
return;
}
for (int i = 0; i < distantLightCount; ++i)
{
DistantLight light = (i >= activeDistantLights.Count) ? null : activeDistantLights[i];
int dataIndex = i * distantLightDataSize;
if (light)
{
Vector4 direction = -light.transform.forward;
distantLightData[dataIndex] = new Vector4(direction.x,
direction.y,
direction.z,
1.0f);
distantLightData[dataIndex + 1] = new Vector4(light.Color.r * intensity,
light.Color.g * intensity,
light.Color.b * intensity,
1.0f);
}
else
{
distantLightData[dataIndex] = invalidLightDirection;
distantLightData[dataIndex + 1] = Vector4.zero;
}
}
Shader.SetGlobalVectorArray(_DistantLightDataID, distantLightData);
lastDistantLightUpdate = Time.frameCount;
}
#endregion BaseLight Implementation
}
}