using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.Assertions;
|
|
|
|
|
|
//=================================================================================================
|
|
/**
|
|
* @brief This class manages all lights for a particular FogVolume.
|
|
*
|
|
*************************************************************************************************/
|
|
public class FogVolumeLightManager : MonoBehaviour
|
|
{
|
|
public int CurrentLightCount { get; private set; }
|
|
|
|
public int VisibleLightCount { get; private set; }
|
|
|
|
public bool DrawDebugData { get; set; }
|
|
|
|
public bool AlreadyUsesTransformForPoI { get { return m_pointOfInterestTf != null; } }
|
|
|
|
// Very slow and garbage allocating. Only call this once!
|
|
public void FindLightsInScene()
|
|
{
|
|
CurrentLightCount = 0;
|
|
VisibleLightCount = 0;
|
|
m_lights.Clear();
|
|
m_lightsInFrustum.Clear();
|
|
for (int i = 0; i < MaxLightCount; i++)
|
|
{
|
|
m_lights.Add(new LightData());
|
|
m_lightsInFrustum.Add(new LightData());
|
|
}
|
|
|
|
FogVolumeLight[] lights = FindObjectsOfType<FogVolumeLight>();
|
|
for (int i = 0; i < lights.Length; i++)
|
|
{
|
|
Light unityLight = lights[i].GetComponent<Light>();
|
|
if (unityLight != null)
|
|
{
|
|
switch (unityLight.type)
|
|
{
|
|
case LightType.Point:
|
|
{
|
|
AddPointLight(unityLight);
|
|
lights[i].IsAddedToNormalLight = true;
|
|
break;
|
|
}
|
|
case LightType.Spot:
|
|
{
|
|
AddSpotLight(unityLight);
|
|
lights[i].IsAddedToNormalLight = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (lights[i].IsPointLight)
|
|
{
|
|
AddSimulatedPointLight(lights[i]);
|
|
lights[i].IsAddedToNormalLight = false;
|
|
}
|
|
else
|
|
{
|
|
AddSimulatedSpotLight(lights[i]);
|
|
lights[i].IsAddedToNormalLight = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Very slow and garbage allocating. Only call this once!
|
|
public void FindLightsInFogVolume()
|
|
{
|
|
CurrentLightCount = 0;
|
|
VisibleLightCount = 0;
|
|
m_lights.Clear();
|
|
m_lightsInFrustum.Clear();
|
|
for (int i = 0; i < MaxLightCount; i++)
|
|
{
|
|
m_lights.Add(new LightData());
|
|
m_lightsInFrustum.Add(new LightData());
|
|
}
|
|
|
|
if (m_boxCollider == null) { m_boxCollider = gameObject.GetComponent<BoxCollider>(); }
|
|
Bounds boundingBox = m_boxCollider.bounds;
|
|
|
|
FogVolumeLight[] lights = FindObjectsOfType<FogVolumeLight>();
|
|
for (int i = 0; i < lights.Length; i++)
|
|
{
|
|
if (boundingBox.Intersects(new Bounds(lights[i].gameObject.transform.position,
|
|
Vector3.one * LightInVolumeBoundsSize)))
|
|
{
|
|
Light unityLight = lights[i].GetComponent<Light>();
|
|
if (unityLight != null)
|
|
{
|
|
switch (unityLight.type)
|
|
{
|
|
case LightType.Point:
|
|
{
|
|
AddPointLight(unityLight);
|
|
lights[i].IsAddedToNormalLight = true;
|
|
break;
|
|
}
|
|
case LightType.Spot:
|
|
{
|
|
AddSpotLight(unityLight);
|
|
lights[i].IsAddedToNormalLight = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (lights[i].IsPointLight)
|
|
{
|
|
AddSimulatedPointLight(lights[i]);
|
|
lights[i].IsAddedToNormalLight = false;
|
|
}
|
|
else
|
|
{
|
|
AddSimulatedSpotLight(lights[i]);
|
|
lights[i].IsAddedToNormalLight = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add a simulated point light to the manager.
|
|
*
|
|
* Note that a simulated point light is any GameObject with a SimulatedPointLight component on
|
|
* it.
|
|
*
|
|
* @param _light The SimulatedPointLight component of an existing GameObject.
|
|
*
|
|
* @return True if the light was added to the managers list of lights.
|
|
* @return False if the manager already contains the maximum amount of lights that was
|
|
* specified in the constructor.
|
|
*
|
|
*********************************************************************************************/
|
|
public bool AddSimulatedPointLight(FogVolumeLight _light)
|
|
{
|
|
Assert.IsTrue(CurrentLightCount < MaxLightCount,
|
|
"The maximum number of lights is already reached!");
|
|
|
|
int index = _FindFirstFreeLight();
|
|
if (index != InvalidIndex)
|
|
{
|
|
LightData data = m_lights[index];
|
|
CurrentLightCount++;
|
|
data.LightType = EFogVolumeLightType.FogVolumePointLight;
|
|
data.Transform = _light.transform;
|
|
data.Light = null;
|
|
data.FogVolumeLight = _light;
|
|
data.Bounds = new Bounds(data.Transform.position,
|
|
Vector3.one * data.FogVolumeLight.Range * 2.5f);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add a simulated spot light to the manager.
|
|
*
|
|
* Note that a simulated spot light is any GameObject with a SimulatedSpotLight component on
|
|
* it.
|
|
*
|
|
* @param _light The SimulatedSpotLight component of an existing GameObject.
|
|
*
|
|
* @return True if the light was added to the managers list of lights.
|
|
* @return False if the manager already contains the maximum amount of lights that was
|
|
* specified in the constructor.
|
|
*
|
|
*********************************************************************************************/
|
|
public bool AddSimulatedSpotLight(FogVolumeLight _light)
|
|
{
|
|
Assert.IsTrue(CurrentLightCount < MaxLightCount,
|
|
"The maximum number of lights is already reached!");
|
|
|
|
int index = _FindFirstFreeLight();
|
|
if (index != InvalidIndex)
|
|
{
|
|
LightData data = m_lights[index];
|
|
CurrentLightCount++;
|
|
data.LightType = EFogVolumeLightType.FogVolumeSpotLight;
|
|
data.Transform = _light.transform;
|
|
data.Light = null;
|
|
data.FogVolumeLight = _light;
|
|
Vector3 center = data.Transform.position +
|
|
data.Transform.forward * data.FogVolumeLight.Range * 0.5f;
|
|
data.Bounds = new Bounds(center,
|
|
Vector3.one * data.FogVolumeLight.Range *
|
|
(0.75f + data.FogVolumeLight.Angle * 0.03f));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add an existing point light to the manager.
|
|
*
|
|
* @param _light The light component of an existing point light.
|
|
*
|
|
* @return True if the light was added to the managers list of lights.
|
|
* @return False if the manager already contains the maximum amount of lights that was
|
|
* specified in the constructor.
|
|
*
|
|
*********************************************************************************************/
|
|
public bool AddPointLight(Light _light)
|
|
{
|
|
Assert.IsTrue(CurrentLightCount < MaxLightCount,
|
|
"The maximum number of lights is already reached!");
|
|
|
|
int index = _FindFirstFreeLight();
|
|
if (index != InvalidIndex)
|
|
{
|
|
LightData data = m_lights[index];
|
|
CurrentLightCount++;
|
|
data.LightType = EFogVolumeLightType.PointLight;
|
|
data.Transform = _light.transform;
|
|
data.Light = _light;
|
|
data.FogVolumeLight = null;
|
|
data.Bounds = new Bounds(data.Transform.position,
|
|
Vector3.one * data.Light.range * 2.5f);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Add an existing spot light to the manager.
|
|
*
|
|
* @param _light The light component of an existing spot light.
|
|
*
|
|
* @return True if the light was added to the managers list of lights.
|
|
* @return False if the manager already contains the maximum amount of lights that was
|
|
* specified in the constructor.
|
|
*
|
|
*********************************************************************************************/
|
|
public bool AddSpotLight(Light _light)
|
|
{
|
|
Assert.IsTrue(CurrentLightCount < MaxLightCount,
|
|
"The maximum number of lights is already reached!");
|
|
|
|
int index = _FindFirstFreeLight();
|
|
if (index != InvalidIndex)
|
|
{
|
|
LightData data = m_lights[index];
|
|
CurrentLightCount++;
|
|
data.LightType = EFogVolumeLightType.SpotLight;
|
|
data.Transform = _light.transform;
|
|
data.Light = _light;
|
|
data.FogVolumeLight = null;
|
|
Vector3 center = data.Transform.position +
|
|
data.Transform.forward * data.Light.range * 0.5f;
|
|
data.Bounds = new Bounds(center,
|
|
Vector3.one * data.Light.range *
|
|
(0.75f + data.Light.spotAngle * 0.03f));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Removes the light with the specified transform.
|
|
*
|
|
* Note that nothing will happen if the light is not currently present in the manager.
|
|
*
|
|
* @param _lightToRemove The light that will be removed from this manager.
|
|
*
|
|
* @return True if the light was found inside the manager and removed successfully.
|
|
* @return False if the light was not found an thus not removed.
|
|
*
|
|
*********************************************************************************************/
|
|
public bool RemoveLight(Transform _lightToRemove)
|
|
{
|
|
int count = m_lights.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (ReferenceEquals(m_lights[i].Transform, _lightToRemove))
|
|
{
|
|
m_lights[i].LightType = EFogVolumeLightType.None;
|
|
CurrentLightCount--;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Updates the lights that will be rendered.
|
|
*
|
|
* Note that this method should be called once per frame, before any data is sent to the
|
|
* shaders.
|
|
*
|
|
*********************************************************************************************/
|
|
public void ManualUpdate(ref Plane[] _frustumPlanes)
|
|
{
|
|
FrustumPlanes = _frustumPlanes;
|
|
m_camera = m_fogVolumeData != null ? m_fogVolumeData.GameCamera : null;
|
|
if (m_camera == null) { return; }
|
|
|
|
if (m_boxCollider == null) { m_boxCollider = m_fogVolume.GetComponent<BoxCollider>(); }
|
|
if (m_pointOfInterestTf != null) { m_pointOfInterest = m_pointOfInterestTf.position; }
|
|
_UpdateBounds();
|
|
_FindLightsInFrustum();
|
|
if (m_lightsInFrustum.Count > MaxVisibleLights) { _SortLightsInFrustum(); }
|
|
_PrepareShaderArrays();
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Draws the gizmos if the user wants to see debug data.
|
|
*
|
|
*********************************************************************************************/
|
|
public void OnDrawGizmos()
|
|
{
|
|
hideFlags = HideFlags.HideInInspector;
|
|
if (m_camera == null) { return; }
|
|
if (!DrawDebugData) { return; }
|
|
|
|
Color tempColor = Gizmos.color;
|
|
|
|
Gizmos.color = Color.green;
|
|
for (int i = 0; i < VisibleLightCount; i++)
|
|
{
|
|
Gizmos.DrawWireCube(m_lightsInFrustum[i].Bounds.center,
|
|
m_lightsInFrustum[i].Bounds.size);
|
|
}
|
|
|
|
Gizmos.color = Color.magenta;
|
|
Matrix4x4 currentMatrix = Gizmos.matrix;
|
|
Gizmos.matrix = Matrix4x4.TRS(m_camera.transform.position,
|
|
m_camera.transform.rotation,
|
|
Vector3.one);
|
|
|
|
//Gizmos.matrix = transform.localToWorldMatrix;
|
|
Gizmos.DrawFrustum(m_camera.transform.position,
|
|
m_camera.fieldOfView,
|
|
m_camera.nearClipPlane,
|
|
m_fogVolume.PointLightingDistance2Camera,
|
|
m_camera.aspect);
|
|
|
|
Gizmos.color = tempColor;
|
|
Gizmos.matrix = currentMatrix;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Sets the multiplier that is applied to the bounding box size of point lights.
|
|
*
|
|
* @param _cullSizeMultiplier The multiplier that is applied to the AABB of point lights.
|
|
*
|
|
*********************************************************************************************/
|
|
public void SetPointLightCullSizeMultiplier(float _cullSizeMultiplier)
|
|
{
|
|
m_pointLightCullSizeMultiplier = _cullSizeMultiplier;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Sets the point of interest to a fixed position.
|
|
*
|
|
* @param _pointOfInterest The point that will be used for prioritizing which lights need to
|
|
* be rendered.
|
|
*
|
|
*********************************************************************************************/
|
|
public void SetPointOfInterest(Vector3 _pointOfInterest)
|
|
{
|
|
m_pointOfInterestTf = null;
|
|
m_pointOfInterest = _pointOfInterest;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Sets the point of interest to the specified transform.
|
|
*
|
|
* The point of interest will be updated from the position of the transform. It is therefore
|
|
* not necessary to call this method more than once.
|
|
*
|
|
*********************************************************************************************/
|
|
public void SetPointOfInterest(Transform _pointOfInterest)
|
|
{
|
|
Assert.IsTrue(_pointOfInterest != null, "_pointOfInterest must not be null!");
|
|
m_pointOfInterestTf = _pointOfInterest;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Returns the array that contains all light positions.
|
|
*
|
|
* Remember to call Update() before using this method, otherwise old data will be sent to the
|
|
* shaders.
|
|
*
|
|
*********************************************************************************************/
|
|
public Vector4[] GetLightPositionArray() { return m_lightPos; }
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Returns the array that contains all light rotations.
|
|
*
|
|
* Remember to call Update() before using this method, otherwise old data will be sent to the
|
|
* shaders.
|
|
*
|
|
*********************************************************************************************/
|
|
public Vector4[] GetLightRotationArray() { return m_lightRot; }
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Returns the array that contains all light colors.
|
|
*
|
|
* Remember to call Update() before using this method, otherwise old data will be sent to the
|
|
* shaders.
|
|
*********************************************************************************************/
|
|
public Color[] GetLightColorArray() { return m_lightColor; }
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Returns the array that contains all light data (intensity, range, spotlight angle,
|
|
* none).
|
|
*
|
|
* Remember to call Update() before using this method, otherwise old data will be sent to the
|
|
* shaders.
|
|
*
|
|
*********************************************************************************************/
|
|
public Vector4[] GetLightData() { return m_lightData; }
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Initializes the LightManager with default values.
|
|
*
|
|
*********************************************************************************************/
|
|
public void Initialize()
|
|
{
|
|
m_fogVolume = gameObject.GetComponent<FogVolume>();
|
|
m_fogVolumeData = FindObjectOfType<FogVolumeData>();
|
|
m_camera = null;
|
|
m_boxCollider = null;
|
|
CurrentLightCount = 0;
|
|
DrawDebugData = false;
|
|
|
|
if (m_lights == null)
|
|
{
|
|
m_lights = new List<LightData>(MaxLightCount);
|
|
m_lightsInFrustum = new List<LightData>(MaxLightCount);
|
|
|
|
for (int i = 0; i < MaxLightCount; i++)
|
|
{
|
|
m_lights.Add(new LightData());
|
|
m_lightsInFrustum.Add(new LightData());
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Clears the LightManager and prepares it toi be reinitialized.
|
|
*
|
|
*********************************************************************************************/
|
|
public void Deinitialize()
|
|
{
|
|
VisibleLightCount = 0;
|
|
DrawDebugData = false;
|
|
}
|
|
|
|
|
|
public void SetFrustumPlanes(ref Plane[] _frustumPlanes) { FrustumPlanes = _frustumPlanes; }
|
|
|
|
#if UNITY_EDITOR
|
|
private void Update() { hideFlags = HideFlags.HideInInspector; }
|
|
#endif
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Updates the axis aligned bounding boxes of all registered lights.
|
|
*
|
|
*********************************************************************************************/
|
|
private void _UpdateBounds()
|
|
{
|
|
int count = m_lights.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
LightData data = m_lights[i];
|
|
|
|
if (data.LightType == EFogVolumeLightType.None) { continue; }
|
|
|
|
|
|
switch (data.LightType)
|
|
{
|
|
case EFogVolumeLightType.None:
|
|
{
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.PointLight:
|
|
{
|
|
data.Bounds = new Bounds(data.Transform.position,
|
|
Vector3.one * data.Light.range *
|
|
m_pointLightCullSizeMultiplier);
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.SpotLight:
|
|
{
|
|
Vector3 center = data.Transform.position +
|
|
data.Transform.forward * data.Light.range * 0.5f;
|
|
data.Bounds = new Bounds(center,
|
|
Vector3.one * data.Light.range *
|
|
m_pointLightCullSizeMultiplier * 1.25f);
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.FogVolumePointLight:
|
|
{
|
|
data.Bounds = new Bounds(data.Transform.position,
|
|
Vector3.one * data.FogVolumeLight.Range *
|
|
m_pointLightCullSizeMultiplier);
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.FogVolumeSpotLight:
|
|
{
|
|
Vector3 center = data.Transform.position +
|
|
data.Transform.forward * data.FogVolumeLight.Range * 0.5f;
|
|
data.Bounds = new Bounds(center,
|
|
Vector3.one * data.FogVolumeLight.Range *
|
|
m_pointLightCullSizeMultiplier * 1.25f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Finds the first free light in the list of all lights.
|
|
*
|
|
* Note that this method is necessary to ensure that adding/removing lights does not allocate
|
|
* any garbage.
|
|
*
|
|
*********************************************************************************************/
|
|
private int _FindFirstFreeLight()
|
|
{
|
|
if (CurrentLightCount < MaxLightCount)
|
|
{
|
|
int count = m_lights.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (m_lights[i].LightType == EFogVolumeLightType.None) { return i; }
|
|
}
|
|
}
|
|
|
|
return InvalidIndex;
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Finds all lights that are currently in the view frustum of the camera and calculates
|
|
* all necessary data for them.
|
|
*
|
|
*********************************************************************************************/
|
|
private void _FindLightsInFrustum()
|
|
{
|
|
m_inFrustumCount = 0;
|
|
Vector3 CameraPos = m_camera.gameObject.transform.position;
|
|
int count = m_lights.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (m_lights[i].Transform == null) { m_lights[i].LightType = EFogVolumeLightType.None; }
|
|
|
|
if (m_lights[i].LightType == EFogVolumeLightType.None) { continue; }
|
|
|
|
float distanceToCamera = (m_lights[i].Transform.position - CameraPos).magnitude;
|
|
if (distanceToCamera > m_fogVolume.PointLightingDistance2Camera) { continue; }
|
|
|
|
switch (m_lights[i].LightType)
|
|
{
|
|
case EFogVolumeLightType.None:
|
|
{
|
|
continue;
|
|
}
|
|
case EFogVolumeLightType.PointLight:
|
|
case EFogVolumeLightType.SpotLight:
|
|
{
|
|
if (m_lights[i].Light.enabled == false) { continue; }
|
|
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.FogVolumePointLight:
|
|
{
|
|
if (m_lights[i].FogVolumeLight.Enabled == false) { continue; }
|
|
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.FogVolumeSpotLight:
|
|
{
|
|
if (m_lights[i].FogVolumeLight.Enabled == false) { continue; }
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (GeometryUtility.TestPlanesAABB(FrustumPlanes, m_lights[i].Bounds))
|
|
{
|
|
LightData light = m_lights[i];
|
|
Vector3 lightPos = light.Transform.position;
|
|
light.SqDistance = (lightPos - m_pointOfInterest).sqrMagnitude;
|
|
light.Distance2Camera = (lightPos - CameraPos).magnitude;
|
|
m_lightsInFrustum[m_inFrustumCount++] = light;
|
|
|
|
if (light.FogVolumeLight != null)
|
|
{
|
|
if (light.LightType == EFogVolumeLightType.FogVolumePointLight &&
|
|
!light.FogVolumeLight.IsPointLight)
|
|
{
|
|
light.LightType = EFogVolumeLightType.FogVolumeSpotLight;
|
|
}
|
|
else if (light.LightType == EFogVolumeLightType.FogVolumeSpotLight &&
|
|
light.FogVolumeLight.IsPointLight)
|
|
{
|
|
light.LightType = EFogVolumeLightType.FogVolumePointLight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Sorts the lights that are currently within the view frustum of the camera by
|
|
* distance to the camera.
|
|
*
|
|
* This method will only be called when there are more than MaxVisibleLights in the view
|
|
* frustum of the camera.
|
|
*
|
|
*********************************************************************************************/
|
|
private void _SortLightsInFrustum()
|
|
{
|
|
bool finishedSorting = false;
|
|
do
|
|
{
|
|
finishedSorting = true;
|
|
for (int i = 0; i < m_inFrustumCount - 1; i++)
|
|
{
|
|
if (m_lightsInFrustum[i].SqDistance > m_lightsInFrustum[i + 1].SqDistance)
|
|
{
|
|
LightData tempData = m_lightsInFrustum[i];
|
|
m_lightsInFrustum[i] = m_lightsInFrustum[i + 1];
|
|
m_lightsInFrustum[i + 1] = tempData;
|
|
finishedSorting = false;
|
|
}
|
|
}
|
|
}
|
|
while (!finishedSorting);
|
|
}
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Prepares the data of the currently visible light and writes it to the arrays that
|
|
* can then be sent to the shaders.
|
|
*
|
|
*********************************************************************************************/
|
|
private void _PrepareShaderArrays()
|
|
{
|
|
VisibleLightCount = 0;
|
|
for (int i = 0; i < MaxVisibleLights; i++)
|
|
{
|
|
if (i >= m_inFrustumCount) { break; }
|
|
|
|
LightData data = m_lightsInFrustum[i];
|
|
|
|
switch (data.LightType)
|
|
{
|
|
case EFogVolumeLightType.FogVolumePointLight:
|
|
{
|
|
FogVolumeLight light = data.FogVolumeLight;
|
|
m_lightPos[i] =
|
|
gameObject.transform.InverseTransformPoint(data.Transform.position);
|
|
m_lightRot[i] =
|
|
gameObject.transform.InverseTransformVector(data.Transform.forward);
|
|
m_lightColor[i] = light.Color;
|
|
m_lightData[i] =
|
|
new Vector4(light.Intensity * m_fogVolume.PointLightsIntensity *
|
|
(1.0f - Mathf.Clamp01(data.Distance2Camera / m_fogVolume
|
|
.PointLightingDistance2Camera)
|
|
),
|
|
light.Range / PointLightRangeDivider,
|
|
InvalidSpotLightAngle,
|
|
NoData);
|
|
VisibleLightCount++;
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.FogVolumeSpotLight:
|
|
{
|
|
FogVolumeLight light = data.FogVolumeLight;
|
|
m_lightPos[i] =
|
|
gameObject.transform.InverseTransformPoint(data.Transform.position);
|
|
m_lightRot[i] =
|
|
gameObject.transform.InverseTransformVector(data.Transform.forward);
|
|
m_lightColor[i] = light.Color;
|
|
m_lightData[i] =
|
|
new Vector4(light.Intensity * m_fogVolume.PointLightsIntensity *
|
|
(1.0f - Mathf.Clamp01(data.Distance2Camera / m_fogVolume
|
|
.PointLightingDistance2Camera)
|
|
),
|
|
light.Range / SpotLightRangeDivider,
|
|
light.Angle,
|
|
NoData);
|
|
VisibleLightCount++;
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.PointLight:
|
|
{
|
|
Light light = data.Light;
|
|
m_lightPos[i] =
|
|
gameObject.transform.InverseTransformPoint(data.Transform.position);
|
|
m_lightRot[i] =
|
|
gameObject.transform.InverseTransformVector(data.Transform.forward);
|
|
m_lightColor[i] = light.color;
|
|
m_lightData[i] =
|
|
new Vector4(light.intensity * m_fogVolume.PointLightsIntensity *
|
|
(1.0f - Mathf.Clamp01(data.Distance2Camera / m_fogVolume
|
|
.PointLightingDistance2Camera)
|
|
),
|
|
light.range / PointLightRangeDivider,
|
|
InvalidSpotLightAngle,
|
|
NoData);
|
|
VisibleLightCount++;
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.SpotLight:
|
|
{
|
|
Light light = data.Light;
|
|
m_lightPos[i] =
|
|
gameObject.transform.InverseTransformPoint(data.Transform.position);
|
|
m_lightRot[i] =
|
|
gameObject.transform.InverseTransformVector(data.Transform.forward);
|
|
m_lightColor[i] = light.color;
|
|
m_lightData[i] =
|
|
new Vector4(light.intensity * m_fogVolume.PointLightsIntensity *
|
|
(1.0f - Mathf.Clamp01(data.Distance2Camera / m_fogVolume
|
|
.PointLightingDistance2Camera)
|
|
),
|
|
light.range / SpotLightRangeDivider,
|
|
light.spotAngle,
|
|
NoData);
|
|
VisibleLightCount++;
|
|
|
|
break;
|
|
}
|
|
case EFogVolumeLightType.None:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private float m_pointLightCullSizeMultiplier = 1.0f;
|
|
|
|
private FogVolume m_fogVolume = null;
|
|
|
|
private FogVolumeData m_fogVolumeData = null;
|
|
|
|
private Camera m_camera = null;
|
|
|
|
private BoxCollider m_boxCollider = null;
|
|
|
|
private Transform m_pointOfInterestTf = null;
|
|
|
|
private Vector3 m_pointOfInterest = Vector3.zero;
|
|
|
|
private readonly Vector4[] m_lightPos = new Vector4[MaxVisibleLights];
|
|
|
|
private readonly Vector4[] m_lightRot = new Vector4[MaxVisibleLights];
|
|
|
|
private readonly Color[] m_lightColor = new Color[MaxVisibleLights];
|
|
|
|
private readonly Vector4[] m_lightData = new Vector4[MaxVisibleLights];
|
|
|
|
private List<LightData> m_lights = null;
|
|
|
|
private List<LightData> m_lightsInFrustum = null;
|
|
|
|
private int m_inFrustumCount = 0;
|
|
|
|
private Plane[] FrustumPlanes = null;
|
|
|
|
private const int InvalidIndex = -1;
|
|
|
|
private const int MaxVisibleLights = 64;
|
|
|
|
private const float InvalidSpotLightAngle = -1.0f;
|
|
|
|
private const float NoData = 0.0f;
|
|
|
|
private const float PointLightRangeDivider = 5.0f;
|
|
|
|
private const float SpotLightRangeDivider = 5.0f;
|
|
|
|
private const int MaxLightCount = 1000;
|
|
|
|
/// The assumed size of a light. Used by realtime search when searching for lights inside a FV.
|
|
private const float LightInVolumeBoundsSize = 5.0f;
|
|
|
|
protected class LightData
|
|
{
|
|
public LightData()
|
|
{
|
|
LightType = EFogVolumeLightType.None;
|
|
Light = null;
|
|
FogVolumeLight = null;
|
|
Transform = null;
|
|
SqDistance = 0.0f;
|
|
Distance2Camera = 0.0f;
|
|
Bounds = new Bounds();
|
|
}
|
|
|
|
public EFogVolumeLightType LightType { get; set; }
|
|
|
|
public Light Light { get; set; }
|
|
|
|
public FogVolumeLight FogVolumeLight { get; set; }
|
|
|
|
public Transform Transform { get; set; }
|
|
|
|
public float SqDistance { get; set; }
|
|
|
|
public float Distance2Camera { get; set; }
|
|
|
|
public Bounds Bounds { get; set; }
|
|
}
|
|
}
|