浏览代码

Fixed a big logic issue wrt layer masking and priorities

/main
Thomas 7 年前
当前提交
93721f83
共有 1 个文件被更改,包括 121 次插入90 次删除
  1. 211
      ScriptableRenderPipeline/Core/Volume/VolumeManager.cs

211
ScriptableRenderPipeline/Core/Volume/VolumeManager.cs


using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityObject = UnityEngine.Object;
static object s_LockObj = new Object();
static object s_LockObj = new UnityObject();
public static VolumeManager instance
{

// Max amount of layers available in Unity
const int k_MaxLayerCount = 32;
// List of all volumes (sorted by priority) per layer
readonly List<Volume>[] m_Volumes;
// Cached lists of all volumes (sorted by priority) by layer mask
readonly Dictionary<LayerMask, List<Volume>> m_SortedVolumes;
// Holds all the registered volumes
readonly List<Volume> m_Volumes;
// Keep track of sorting states for all layers
readonly bool[] m_SortNeeded;
// Keep track of sorting states for layer masks
readonly Dictionary<LayerMask, bool> m_SortNeeded;
// Internal state of all component types
readonly Dictionary<Type, VolumeComponent> m_Components;

VolumeManager()
{
m_Volumes = new List<Volume>[k_MaxLayerCount];
m_SortNeeded = new bool[k_MaxLayerCount];
m_SortedVolumes = new Dictionary<LayerMask, List<Volume>>();
m_Volumes = new List<Volume>();
m_SortNeeded = new Dictionary<LayerMask, bool>();
m_TempColliders = new List<Collider>(8);
m_Components = new Dictionary<Type, VolumeComponent>();
m_ComponentsDefaultState = new List<VolumeComponent>();

public void Register(Volume volume, int layer)
{
var volumes = m_Volumes[layer];
m_Volumes.Add(volume);
if (volumes == null)
// Look for existing cached layer masks and add it there if needed
foreach (var kvp in m_SortedVolumes)
volumes = new List<Volume>();
m_Volumes[layer] = volumes;
var mask = kvp.Key;
if ((mask & (1 << layer)) != 0)
kvp.Value.Add(volume);
Assert.IsFalse(volumes.Contains(volume), "Volume has already been registered");
volumes.Add(volume);
var volumes = m_Volumes[layer];
m_Volumes.Remove(volume);
if (volumes == null)
return;
foreach (var kvp in m_SortedVolumes)
{
var mask = kvp.Key;
volumes.Remove(volume);
// Skip layer masks this volume doesn't belong to
if ((mask & (1 << layer)) == 0)
continue;
kvp.Value.Remove(volume);
}
m_SortNeeded[layer] = true;
foreach (var kvp in m_SortedVolumes)
{
var mask = kvp.Key;
if ((mask & (1 << layer)) != 0)
m_SortNeeded[mask] = true;
}
}
internal void UpdateVolumeLayer(Volume volume, int prevLayer, int newLayer)

}
}
// Update the global state - should be called once per frame in the update loop before
// anything else
public void Update(Transform trigger, LayerMask layerMask)
[Conditional("UNITY_EDITOR")]
public void CheckBaseTypes()
#if UNITY_EDITOR
// play mode -> re-create the world when bad things happen
if (m_ComponentsDefaultState == null
|| (m_ComponentsDefaultState.Count > 0 && m_ComponentsDefaultState[0] == null))
{
if (m_ComponentsDefaultState == null || (m_ComponentsDefaultState.Count > 0 && m_ComponentsDefaultState[0] == null))
}
else
#endif
{
// Start by resetting the global state to default values
ReplaceData(m_ComponentsDefaultState);
}
}
// Update the global state - should be called once per frame per transform/layer mask combo
// in the update loop before rendering
public void Update(Transform trigger, LayerMask layerMask)
{
CheckBaseTypes();
// Start by resetting the global state to default values
ReplaceData(m_ComponentsDefaultState);
// Do magic
int mask = layerMask.value;
for (int i = 0; i < k_MaxLayerCount; i++)
// Sort the cached volume list(s) for the given layer mask if needed and return it
var volumes = GrabVolumes(layerMask);
// Traverse all volumes
foreach (var volume in volumes)
// Skip layers not in the mask
if ((mask & (1 << i)) == 0)
// Skip disabled volumes and volumes without any data or weight
if (!volume.enabled || volume.weight <= 0f)
// Skip empty layers
var volumes = m_Volumes[i];
// Global volumes always have influence
if (volume.isGlobal)
{
OverrideData(volume.components, Mathf.Clamp01(volume.weight));
continue;
}
if (onlyGlobal)
continue;
if (volumes == null)
// If volume isn't global and has no collider, skip it as it's useless
var colliders = m_TempColliders;
volume.GetComponents(colliders);
if (colliders.Count == 0)
// Sort the volume list if needed
if (m_SortNeeded[i])
{
SortByPriority(volumes);
m_SortNeeded[i] = false;
}
// Find closest distance to volume, 0 means it's inside it
float closestDistanceSqr = float.PositiveInfinity;
// Traverse all volumes
foreach (var volume in volumes)
foreach (var collider in colliders)
// Skip disabled volumes and volumes without any data or weight
if (!volume.enabled || volume.weight <= 0f)
if (!collider.enabled)
var components = volume.components;
var closestPoint = collider.ClosestPoint(triggerPos);
var d = (closestPoint - triggerPos).sqrMagnitude;
// Global volumes always have influence
if (volume.isGlobal)
{
OverrideData(components, Mathf.Clamp01(volume.weight));
continue;
}
if (d < closestDistanceSqr)
closestDistanceSqr = d;
}
if (onlyGlobal)
continue;
colliders.Clear();
float blendDistSqr = volume.blendDistance * volume.blendDistance;
// If volume isn't global and has no collider, skip it as it's useless
var colliders = m_TempColliders;
volume.GetComponents(colliders);
if (colliders.Count == 0)
continue;
// Volume has no influence, ignore it
// Note: Volume doesn't do anything when `closestDistanceSqr = blendDistSqr` but
// we can't use a >= comparison as blendDistSqr could be set to 0 in which
// case volume would have total influence
if (closestDistanceSqr > blendDistSqr)
continue;
// Find closest distance to volume, 0 means it's inside it
float closestDistanceSqr = float.PositiveInfinity;
// Volume has influence
float interpFactor = 1f;
foreach (var collider in colliders)
{
if (!collider.enabled)
continue;
if (blendDistSqr > 0f)
interpFactor = 1f - (closestDistanceSqr / blendDistSqr);
var closestPoint = collider.ClosestPoint(triggerPos);
var d = (closestPoint - triggerPos).sqrMagnitude;
// No need to clamp01 the interpolation factor as it'll always be in [0;1[ range
OverrideData(volume.components, interpFactor * Mathf.Clamp01(volume.weight));
}
}
if (d < closestDistanceSqr)
closestDistanceSqr = d;
}
List<Volume> GrabVolumes(LayerMask mask)
{
List<Volume> list;
colliders.Clear();
float blendDistSqr = volume.blendDistance * volume.blendDistance;
if (!m_SortedVolumes.TryGetValue(mask, out list))
{
// New layer mask detected, create a new list and cache all the volumes that belong
// to this mask in it
list = new List<Volume>();
// Volume has no influence, ignore it
// Note: Volume doesn't do anything when `closestDistanceSqr = blendDistSqr` but
// we can't use a >= comparison as blendDistSqr could be set to 0 in which
// case volume would have total influence
if (closestDistanceSqr > blendDistSqr)
foreach (var volume in m_Volumes)
{
if ((mask & (1 << volume.gameObject.layer)) == 0)
// Volume has influence
float interpFactor = 1f;
list.Add(volume);
m_SortNeeded[mask] = true;
}
if (blendDistSqr > 0f)
interpFactor = 1f - (closestDistanceSqr / blendDistSqr);
m_SortedVolumes.Add(mask, list);
}
// No need to clamp01 the interpolation factor as it'll always be in [0;1[ range
OverrideData(components, interpFactor * Mathf.Clamp01(volume.weight));
}
// Check sorting state
bool sortNeeded;
if (m_SortNeeded.TryGetValue(mask, out sortNeeded) && sortNeeded)
{
m_SortNeeded[mask] = false;
SortByPriority(list);
return list;
}
// Stable insertion sort. Faster than List<T>.Sort() for our needs.

正在加载...
取消
保存