using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
using UnityEngine;
|
|
using UnityEngine.Assertions;
|
|
|
|
|
|
public class FogVolumePrimitiveManager : MonoBehaviour
|
|
{
|
|
public int CurrentPrimitiveCount { get; private set; }
|
|
|
|
public int VisiblePrimitiveCount { get; private set; }
|
|
|
|
public bool AlreadyUsesTransformForPoI { get { return m_pointOfInterestTf != null; } }
|
|
|
|
public void FindPrimitivesInFogVolume()
|
|
{
|
|
CurrentPrimitiveCount = 0;
|
|
VisiblePrimitiveCount = 0;
|
|
m_primitives.Clear();
|
|
m_primitivesInFrustum.Clear();
|
|
for (int i = 0; i < MaxPrimitivesCount; i++)
|
|
{
|
|
m_primitives.Add(new PrimitiveData());
|
|
m_primitivesInFrustum.Add(new PrimitiveData());
|
|
}
|
|
|
|
if (m_boxCollider == null) { m_boxCollider = gameObject.GetComponent<BoxCollider>(); }
|
|
Bounds boundingBox = m_boxCollider.bounds;
|
|
|
|
FogVolumePrimitive[] primitives = FindObjectsOfType<FogVolumePrimitive>();
|
|
for (int i = 0; i < primitives.Length; i++)
|
|
{
|
|
var data = primitives[i];
|
|
|
|
if (boundingBox.Intersects(data.Bounds))
|
|
{
|
|
if (data.BoxColl != null) { data.Type = EFogVolumePrimitiveType.Box; }
|
|
else if (data.SphereColl != null) { data.Type = EFogVolumePrimitiveType.Sphere; }
|
|
else
|
|
{
|
|
data.BoxColl = data.GetTransform.gameObject.AddComponent<BoxCollider>();
|
|
data.Type = EFogVolumePrimitiveType.Box;
|
|
}
|
|
|
|
if (data.Type == EFogVolumePrimitiveType.Box)
|
|
{
|
|
AddPrimitiveBox(data);
|
|
}
|
|
else if (data.Type == EFogVolumePrimitiveType.Sphere)
|
|
{
|
|
AddPrimitiveSphere(data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool AddPrimitiveBox(FogVolumePrimitive _box)
|
|
{
|
|
Assert.IsTrue(CurrentPrimitiveCount < MaxPrimitivesCount,
|
|
"The maximum amount of primitives is already reached!");
|
|
|
|
int index = _FindFirstFreePrimitive();
|
|
if (index != InvalidIndex)
|
|
{
|
|
PrimitiveData data = m_primitives[index];
|
|
CurrentPrimitiveCount++;
|
|
data.PrimitiveType = EFogVolumePrimitiveType.Box;
|
|
data.Transform = _box.transform;
|
|
data.Renderer = _box.GetComponent<Renderer>();
|
|
data.Primitive = _box;
|
|
data.Bounds = new Bounds(data.Transform.position, _box.GetPrimitiveScale);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool AddPrimitiveSphere(FogVolumePrimitive _sphere)
|
|
{
|
|
Assert.IsTrue(CurrentPrimitiveCount < MaxPrimitivesCount,
|
|
"The maximum amount of primitives is already reached!");
|
|
|
|
int index = _FindFirstFreePrimitive();
|
|
if (index != InvalidIndex)
|
|
{
|
|
PrimitiveData data = m_primitives[index];
|
|
CurrentPrimitiveCount++;
|
|
data.PrimitiveType = EFogVolumePrimitiveType.Sphere;
|
|
data.Transform = _sphere.transform;
|
|
data.Renderer = _sphere.GetComponent<Renderer>();
|
|
data.Primitive = _sphere;
|
|
data.Bounds = new Bounds(data.Transform.position, _sphere.GetPrimitiveScale);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool RemovePrimitive(Transform _primitiveToRemove)
|
|
{
|
|
int count = m_primitives.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
PrimitiveData data = m_primitives[i];
|
|
if (ReferenceEquals(m_primitives[i].Transform, _primitiveToRemove))
|
|
{
|
|
data.Reset();
|
|
CurrentPrimitiveCount--;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//=============================================================================================
|
|
/**
|
|
* @brief Sets the point of interest to a fixed position.
|
|
*
|
|
* @param _pointOfInterest The point that will be used for prioritizing which primitives 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;
|
|
}
|
|
|
|
|
|
public void OnDrawGizmos() { hideFlags = HideFlags.HideInInspector; }
|
|
|
|
|
|
public void ManualUpdate(ref Plane[] _frustumPlanes)
|
|
{
|
|
m_camera = m_fogVolumeData != null ? m_fogVolumeData.GameCamera : null;
|
|
if (m_camera == null) { return; }
|
|
FrustumPlanes = _frustumPlanes;
|
|
if (m_boxCollider == null) { m_boxCollider = m_fogVolume.GetComponent<BoxCollider>(); }
|
|
_UpdateBounds();
|
|
_FindPrimitivesInFrustum();
|
|
if (m_primitivesInFrustum.Count > MaxVisiblePrimitives) { _SortPrimitivesInFrustum(); }
|
|
_PrepareShaderArrays();
|
|
}
|
|
|
|
public void SetVisibility(bool _enabled)
|
|
{
|
|
int count = m_primitives.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (m_primitives[i].Renderer != null) { m_primitives[i].Renderer.enabled = _enabled; }
|
|
}
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
m_fogVolume = gameObject.GetComponent<FogVolume>();
|
|
m_fogVolumeData = FindObjectOfType<FogVolumeData>();
|
|
m_camera = null;
|
|
m_boxCollider = null;
|
|
CurrentPrimitiveCount = 0;
|
|
|
|
if (m_primitives == null)
|
|
{
|
|
m_primitives = new List<PrimitiveData>(MaxPrimitivesCount);
|
|
m_primitivesInFrustum = new List<PrimitiveData>();
|
|
|
|
for (int i = 0; i < MaxPrimitivesCount; i++)
|
|
{
|
|
m_primitives.Add(new PrimitiveData());
|
|
m_primitivesInFrustum.Add(new PrimitiveData());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void Deinitialize() { VisiblePrimitiveCount = 0; }
|
|
|
|
|
|
public Vector4[] GetPrimitivePositionArray() { return m_primitivePos; }
|
|
|
|
|
|
public Vector4[] GetPrimitiveScaleArray() { return m_primitiveScale; }
|
|
|
|
|
|
public Matrix4x4[] GetPrimitiveTransformArray() { return m_primitiveTf; }
|
|
|
|
|
|
public Vector4[] GetPrimitiveDataArray() { return m_primitiveData; }
|
|
|
|
|
|
private void _UpdateBounds()
|
|
{
|
|
int count = m_primitives.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
PrimitiveData data = m_primitives[i];
|
|
|
|
if (data.PrimitiveType == EFogVolumePrimitiveType.None) { continue; }
|
|
|
|
if (data.Primitive == null)
|
|
{
|
|
RemovePrimitive(data.Transform);
|
|
continue;
|
|
}
|
|
|
|
if (data.PrimitiveType == EFogVolumePrimitiveType.Box)
|
|
{
|
|
// Check if collider was removed by the user and add it again.
|
|
if (data.Primitive.BoxColl == null)
|
|
{
|
|
Debug.LogWarning("FogVolumePrimitive requires a collider.\nThe collider will be automatically created.");
|
|
data.Primitive.AddColliderIfNeccessary(EFogVolumePrimitiveType.Box);
|
|
}
|
|
data.Bounds = data.Primitive.BoxColl.bounds;
|
|
|
|
}
|
|
else if (data.PrimitiveType == EFogVolumePrimitiveType.Sphere)
|
|
{
|
|
// Check if collider was removed by the user and add it again.
|
|
if (data.Primitive.SphereColl == null)
|
|
{
|
|
Debug.LogWarning("FogVolumePrimitive requires a collider.\nThe collider will be automatically created.");
|
|
data.Primitive.AddColliderIfNeccessary(EFogVolumePrimitiveType.Sphere);
|
|
}
|
|
data.Bounds = data.Primitive.SphereColl.bounds;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private int _FindFirstFreePrimitive()
|
|
{
|
|
if (CurrentPrimitiveCount < MaxPrimitivesCount)
|
|
{
|
|
int count = m_primitives.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if (m_primitives[i].PrimitiveType == EFogVolumePrimitiveType.None) { return i; }
|
|
}
|
|
}
|
|
|
|
return InvalidIndex;
|
|
}
|
|
|
|
private void _FindPrimitivesInFrustum()
|
|
{
|
|
m_inFrustumCount = 0;
|
|
Vector3 cameraPos = m_camera.gameObject.transform.position;
|
|
int count = m_primitives.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
PrimitiveData primitive = m_primitives[i];
|
|
|
|
if (primitive.Transform == null)
|
|
{
|
|
primitive.PrimitiveType = EFogVolumePrimitiveType.None;
|
|
}
|
|
if (primitive.PrimitiveType == EFogVolumePrimitiveType.None) { continue; }
|
|
|
|
if (primitive.Primitive.IsPersistent)
|
|
{
|
|
Vector3 pos = primitive.Transform.position;
|
|
primitive.SqDistance = (pos - m_pointOfInterest).sqrMagnitude;
|
|
primitive.Distance2Camera = (pos - cameraPos).magnitude;
|
|
m_primitivesInFrustum[m_inFrustumCount++] = primitive;
|
|
}
|
|
else if (GeometryUtility.TestPlanesAABB(FrustumPlanes, m_primitives[i].Bounds))
|
|
{
|
|
Vector3 pos = primitive.Transform.position;
|
|
primitive.SqDistance = (pos - m_pointOfInterest).sqrMagnitude;
|
|
primitive.Distance2Camera = (pos - cameraPos).magnitude;
|
|
m_primitivesInFrustum[m_inFrustumCount++] = primitive;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void _SortPrimitivesInFrustum()
|
|
{
|
|
bool finishedSorting = false;
|
|
do
|
|
{
|
|
finishedSorting = true;
|
|
for (int i = 0; i < m_inFrustumCount - 1; i++)
|
|
{
|
|
if (m_primitivesInFrustum[i].SqDistance > m_primitivesInFrustum[i + 1].SqDistance)
|
|
{
|
|
PrimitiveData tempData = m_primitivesInFrustum[i];
|
|
m_primitivesInFrustum[i] = m_primitivesInFrustum[i + 1];
|
|
m_primitivesInFrustum[i + 1] = tempData;
|
|
finishedSorting = false;
|
|
}
|
|
}
|
|
}
|
|
while (!finishedSorting);
|
|
}
|
|
|
|
private void _PrepareShaderArrays()
|
|
{
|
|
VisiblePrimitiveCount = 0;
|
|
|
|
Quaternion fogVolumeRotation = m_fogVolume.gameObject.transform.rotation;
|
|
|
|
for (int i = 0; i < MaxVisiblePrimitives; i++)
|
|
{
|
|
if (i >= m_inFrustumCount) { break; }
|
|
|
|
PrimitiveData data = m_primitivesInFrustum[i];
|
|
Vector3 position = data.Transform.position;
|
|
m_primitivePos[i] = gameObject.transform.InverseTransformPoint(position);
|
|
m_primitiveTf[i].SetTRS(position,
|
|
Quaternion.Inverse(data.Transform.rotation) * fogVolumeRotation,
|
|
Vector3.one);
|
|
m_primitiveScale[i] = data.Primitive.GetPrimitiveScale;
|
|
m_primitiveData[i] =
|
|
new Vector4(data.PrimitiveType == EFogVolumePrimitiveType.Box ? 0.5f : 1.5f,
|
|
data.Primitive.IsSubtractive ? 1.5f : 0.5f,
|
|
0.0f,
|
|
0.0f);
|
|
VisiblePrimitiveCount++;
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
private void Update() { hideFlags = HideFlags.HideInInspector; }
|
|
#endif
|
|
|
|
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 List<PrimitiveData> m_primitives = null;
|
|
|
|
private List<PrimitiveData> m_primitivesInFrustum = null;
|
|
|
|
private int m_inFrustumCount = 0;
|
|
|
|
private Plane[] FrustumPlanes = null;
|
|
|
|
private readonly Vector4[] m_primitivePos = new Vector4[MaxVisiblePrimitives];
|
|
|
|
private readonly Vector4[] m_primitiveScale = new Vector4[MaxVisiblePrimitives];
|
|
|
|
private readonly Matrix4x4[] m_primitiveTf = new Matrix4x4[MaxVisiblePrimitives];
|
|
|
|
private readonly Vector4[] m_primitiveData = new Vector4[MaxVisiblePrimitives];
|
|
|
|
private const int InvalidIndex = -1;
|
|
|
|
private const int MaxVisiblePrimitives = 20;
|
|
|
|
private const int MaxPrimitivesCount = 1000;
|
|
|
|
protected class PrimitiveData
|
|
{
|
|
public PrimitiveData()
|
|
{
|
|
PrimitiveType = EFogVolumePrimitiveType.None;
|
|
Primitive = null;
|
|
Transform = null;
|
|
Renderer = null;
|
|
SqDistance = 0.0f;
|
|
Distance2Camera = 0.0f;
|
|
Bounds = new Bounds();
|
|
}
|
|
|
|
public EFogVolumePrimitiveType PrimitiveType { get; set; }
|
|
|
|
public FogVolumePrimitive Primitive { get; set; }
|
|
|
|
public Transform Transform { get; set; }
|
|
|
|
public Renderer Renderer { get; set; }
|
|
|
|
public float SqDistance { get; set; }
|
|
|
|
public float Distance2Camera { get; set; }
|
|
|
|
public Bounds Bounds { get; set; }
|
|
|
|
public void Reset()
|
|
{
|
|
PrimitiveType = EFogVolumePrimitiveType.None;
|
|
Primitive = null;
|
|
Transform = null;
|
|
Renderer = null;
|
|
SqDistance = 0.0f;
|
|
Distance2Camera = 0.0f;
|
|
}
|
|
}
|
|
}
|