浏览代码

Merge pull request #631 from Unity-Technologies/volume-framework

Generic volume framework
/main
GitHub 7 年前
当前提交
d8ba0411
共有 37 个文件被更改,包括 2341 次插入0 次删除
  1. 88
      ScriptableRenderPipeline/Core/Editor/CoreEditorUtils.cs
  2. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDCustomSamplerId.cs
  3. 6
      ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs
  4. 42
      ScriptableRenderPipeline/Core/Editor/CoreEditorStyles.cs
  5. 11
      ScriptableRenderPipeline/Core/Editor/CoreEditorStyles.cs.meta
  6. 8
      ScriptableRenderPipeline/Core/Volume.meta
  7. 8
      ScriptableRenderPipeline/Core/Volume/Editor.meta
  8. 8
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers.meta
  9. 77
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ClampedFloatParameterDrawer.cs
  10. 11
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ClampedFloatParameterDrawer.cs.meta
  11. 77
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ClampedIntParameterDrawer.cs
  12. 11
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ClampedIntParameterDrawer.cs.meta
  13. 21
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ColorParameterDrawer.cs
  14. 11
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ColorParameterDrawer.cs.meta
  15. 42
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/RangeParameterDrawer.cs
  16. 11
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/RangeParameterDrawer.cs.meta
  17. 20
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/Vector4ParameterDrawer.cs
  18. 11
      ScriptableRenderPipeline/Core/Volume/Editor/Drawers/Vector4ParameterDrawer.cs.meta
  19. 57
      ScriptableRenderPipeline/Core/Volume/Editor/SerializedDataParameter.cs
  20. 11
      ScriptableRenderPipeline/Core/Volume/Editor/SerializedDataParameter.cs.meta
  21. 266
      ScriptableRenderPipeline/Core/Volume/Editor/VolumeComponentEditor.cs
  22. 11
      ScriptableRenderPipeline/Core/Volume/Editor/VolumeComponentEditor.cs.meta
  23. 388
      ScriptableRenderPipeline/Core/Volume/Editor/VolumeEditor.cs
  24. 11
      ScriptableRenderPipeline/Core/Volume/Editor/VolumeEditor.cs.meta
  25. 31
      ScriptableRenderPipeline/Core/Volume/Editor/VolumeParameterDrawer.cs
  26. 11
      ScriptableRenderPipeline/Core/Volume/Editor/VolumeParameterDrawer.cs.meta
  27. 11
      ScriptableRenderPipeline/Core/Volume/Volume.cs.meta
  28. 73
      ScriptableRenderPipeline/Core/Volume/VolumeComponent.cs
  29. 11
      ScriptableRenderPipeline/Core/Volume/VolumeComponent.cs.meta
  30. 336
      ScriptableRenderPipeline/Core/Volume/VolumeManager.cs
  31. 11
      ScriptableRenderPipeline/Core/Volume/VolumeManager.cs.meta
  32. 424
      ScriptableRenderPipeline/Core/Volume/VolumeParameter.cs
  33. 11
      ScriptableRenderPipeline/Core/Volume/VolumeParameter.cs.meta
  34. 212
      ScriptableRenderPipeline/Core/Volume/Volume.cs

88
ScriptableRenderPipeline/Core/Editor/CoreEditorUtils.cs


{
public static class CoreEditorUtils
{
// GUIContent cache utilities
static Dictionary<string, GUIContent> s_GUIContentCache = new Dictionary<string, GUIContent>();
public static GUIContent GetContent(string textAndTooltip)
{
if (string.IsNullOrEmpty(textAndTooltip))
return GUIContent.none;
GUIContent content;
if (!s_GUIContentCache.TryGetValue(textAndTooltip, out content))
{
var s = textAndTooltip.Split('|');
content = new GUIContent(s[0]);
if (s.Length > 1 && !string.IsNullOrEmpty(s[1]))
content.tooltip = s[1];
s_GUIContentCache.Add(textAndTooltip, content);
}
return content;
}
// Serialization helpers
public static string FindProperty<T, TValue>(Expression<Func<T, TValue>> expr)
{

}
return state;
}
public static bool DrawHeaderToggle(string title, SerializedProperty group, SerializedProperty activeField, Action<Vector2> contextAction = null)
{
var backgroundRect = GUILayoutUtility.GetRect(1f, 17f);
var labelRect = backgroundRect;
labelRect.xMin += 16f;
labelRect.xMax -= 20f;
var toggleRect = backgroundRect;
toggleRect.y += 2f;
toggleRect.width = 13f;
toggleRect.height = 13f;
// Background rect should be full-width
backgroundRect.xMin = 0f;
backgroundRect.width += 4f;
// Background
float backgroundTint = EditorGUIUtility.isProSkin ? 0.1f : 1f;
EditorGUI.DrawRect(backgroundRect, new Color(backgroundTint, backgroundTint, backgroundTint, 0.2f));
// Title
using (new EditorGUI.DisabledScope(!activeField.boolValue))
EditorGUI.LabelField(labelRect, GetContent(title), EditorStyles.boldLabel);
// Active checkbox
activeField.serializedObject.Update();
activeField.boolValue = GUI.Toggle(toggleRect, activeField.boolValue, GUIContent.none, CoreEditorStyles.smallTickbox);
activeField.serializedObject.ApplyModifiedProperties();
// Context menu
var menuIcon = EditorGUIUtility.isProSkin
? CoreEditorStyles.paneOptionsIconDark
: CoreEditorStyles.paneOptionsIconLight;
var menuRect = new Rect(labelRect.xMax + 4f, labelRect.y + 4f, menuIcon.width, menuIcon.height);
if (contextAction != null)
GUI.DrawTexture(menuRect, menuIcon);
// Handle events
var e = Event.current;
if (e.type == EventType.MouseDown)
{
if (contextAction != null && menuRect.Contains(e.mousePosition))
{
contextAction(new Vector2(menuRect.x, menuRect.yMax));
e.Use();
}
else if (labelRect.Contains(e.mousePosition))
{
if (e.button == 0)
group.isExpanded = !group.isExpanded;
else if (contextAction != null)
contextAction(e.mousePosition);
e.Use();
}
}
return group.isExpanded;
}
public static void RemoveMaterialKeywords(Material material)

3
ScriptableRenderPipeline/HDRenderPipeline/HDCustomSamplerId.cs


TPDisplayShadows,
TPRenderDeferredLighting,
// Misc
VolumeUpdate,
Max
}
}

6
ScriptableRenderPipeline/HDRenderPipeline/HDRenderPipeline.cs


else
{
m_CurrentDebugDisplaySettings = m_DebugDisplaySettings;
using (new ProfilingSample(cmd, "Volume Update", GetSampler(CustomSamplerId.VolumeUpdate)))
{
// TODO: Transform & layer should be configurable per camera
VolumeManager.instance.Update(camera.transform, -1);
}
}
ApplyDebugDisplaySettings(cmd);

42
ScriptableRenderPipeline/Core/Editor/CoreEditorStyles.cs


using UnityEngine;
namespace UnityEditor.Experimental.Rendering
{
public static class CoreEditorStyles
{
public static readonly GUIStyle smallTickbox;
public static readonly GUIStyle miniLabelButton;
public static readonly Texture2D paneOptionsIconDark;
public static readonly Texture2D paneOptionsIconLight;
static CoreEditorStyles()
{
smallTickbox = new GUIStyle("ShurikenCheckMark");
var transparentTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
transparentTexture.SetPixel(0, 0, Color.clear);
transparentTexture.Apply();
miniLabelButton = new GUIStyle(EditorStyles.miniLabel);
miniLabelButton.normal = new GUIStyleState
{
background = transparentTexture,
scaledBackgrounds = null,
textColor = Color.grey
};
var activeState = new GUIStyleState
{
background = transparentTexture,
scaledBackgrounds = null,
textColor = Color.white
};
miniLabelButton.active = activeState;
miniLabelButton.onNormal = activeState;
miniLabelButton.onActive = activeState;
paneOptionsIconDark = (Texture2D)EditorGUIUtility.Load("Builtin Skins/DarkSkin/Images/pane options.png");
paneOptionsIconLight = (Texture2D)EditorGUIUtility.Load("Builtin Skins/LightSkin/Images/pane options.png");
}
}
}

11
ScriptableRenderPipeline/Core/Editor/CoreEditorStyles.cs.meta


fileFormatVersion: 2
guid: 106fd77ef6b30234597e56c849578bad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
ScriptableRenderPipeline/Core/Volume.meta


fileFormatVersion: 2
guid: 1189cbf559a12c64e92584c52d17a4a0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
ScriptableRenderPipeline/Core/Volume/Editor.meta


fileFormatVersion: 2
guid: 138c0b127ef07fd428612196694d782f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
ScriptableRenderPipeline/Core/Volume/Editor/Drawers.meta


fileFormatVersion: 2
guid: 8c667ca46d23aa3468b6c4243b135b0d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

77
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ClampedFloatParameterDrawer.cs


using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace UnityEditor.Experimental.Rendering
{
[VolumeParameterDrawer(typeof(ClampedFloatParameter))]
sealed class ClampedFloatParameterDrawer : VolumeParameterDrawer
{
public override bool OnGUI(SerializedDataParameter parameter, GUIContent title)
{
var value = parameter.value;
if (value.propertyType != SerializedPropertyType.Float)
return false;
var o = parameter.GetObjectRef<ClampedFloatParameter>();
if (o.clampMode == ParameterClampMode.MinMax)
{
EditorGUILayout.Slider(value, o.min, o.max, title);
value.floatValue = Mathf.Clamp(value.floatValue, o.min, o.max);
}
else if (o.clampMode == ParameterClampMode.Min)
{
float v = EditorGUILayout.FloatField(title, value.floatValue);
value.floatValue = Mathf.Max(v, o.min);
}
else if (o.clampMode == ParameterClampMode.Max)
{
float v = EditorGUILayout.FloatField(title, value.floatValue);
value.floatValue = Mathf.Min(v, o.max);
}
else
{
return false;
}
return true;
}
}
[VolumeParameterDrawer(typeof(InstantClampedFloatParameter))]
sealed class InstantClampedFloatParameterDrawer : VolumeParameterDrawer
{
public override bool OnGUI(SerializedDataParameter parameter, GUIContent title)
{
var value = parameter.value;
if (value.propertyType != SerializedPropertyType.Float)
return false;
var o = parameter.GetObjectRef<InstantClampedFloatParameter>();
if (o.clampMode == ParameterClampMode.MinMax)
{
EditorGUILayout.Slider(value, o.min, o.max, title);
value.floatValue = Mathf.Clamp(value.floatValue, o.min, o.max);
}
else if (o.clampMode == ParameterClampMode.Min)
{
float v = EditorGUILayout.FloatField(title, value.floatValue);
value.floatValue = Mathf.Max(v, o.min);
}
else if (o.clampMode == ParameterClampMode.Max)
{
float v = EditorGUILayout.FloatField(title, value.floatValue);
value.floatValue = Mathf.Min(v, o.max);
}
else
{
return false;
}
return true;
}
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ClampedFloatParameterDrawer.cs.meta


fileFormatVersion: 2
guid: f9816364c4bdf314db38db52fc67a3f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

77
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ClampedIntParameterDrawer.cs


using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace UnityEditor.Experimental.Rendering
{
[VolumeParameterDrawer(typeof(ClampedIntParameter))]
sealed class ClampedIntParameterDrawer : VolumeParameterDrawer
{
public override bool OnGUI(SerializedDataParameter parameter, GUIContent title)
{
var value = parameter.value;
if (value.propertyType != SerializedPropertyType.Integer)
return false;
var o = parameter.GetObjectRef<ClampedIntParameter>();
if (o.clampMode == ParameterClampMode.MinMax)
{
EditorGUILayout.IntSlider(value, o.min, o.max, title);
value.intValue = Mathf.Clamp(value.intValue, o.min, o.max);
}
else if (o.clampMode == ParameterClampMode.Min)
{
int v = EditorGUILayout.IntField(title, value.intValue);
value.intValue = Mathf.Max(v, o.min);
}
else if (o.clampMode == ParameterClampMode.Max)
{
int v = EditorGUILayout.IntField(title, value.intValue);
value.intValue = Mathf.Min(v, o.max);
}
else
{
return false;
}
return true;
}
}
[VolumeParameterDrawer(typeof(InstantClampedIntParameter))]
sealed class InstantClampedIntParameterDrawer : VolumeParameterDrawer
{
public override bool OnGUI(SerializedDataParameter parameter, GUIContent title)
{
var value = parameter.value;
if (value.propertyType != SerializedPropertyType.Integer)
return false;
var o = parameter.GetObjectRef<InstantClampedIntParameter>();
if (o.clampMode == ParameterClampMode.MinMax)
{
EditorGUILayout.IntSlider(value, o.min, o.max, title);
value.intValue = Mathf.Clamp(value.intValue, o.min, o.max);
}
else if (o.clampMode == ParameterClampMode.Min)
{
int v = EditorGUILayout.IntField(title, value.intValue);
value.intValue = Mathf.Max(v, o.min);
}
else if (o.clampMode == ParameterClampMode.Max)
{
int v = EditorGUILayout.IntField(title, value.intValue);
value.intValue = Mathf.Min(v, o.max);
}
else
{
return false;
}
return true;
}
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ClampedIntParameterDrawer.cs.meta


fileFormatVersion: 2
guid: 6d2f589334fbc8444b30925a405106d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

21
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ColorParameterDrawer.cs


using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace UnityEditor.Experimental.Rendering
{
[VolumeParameterDrawer(typeof(ColorParameter))]
sealed class ColorParameterDrawer : VolumeParameterDrawer
{
public override bool OnGUI(SerializedDataParameter parameter, GUIContent title)
{
var value = parameter.value;
if (value.propertyType != SerializedPropertyType.Color)
return false;
var o = parameter.GetObjectRef<ColorParameter>();
value.colorValue = EditorGUILayout.ColorField(title, value.colorValue, o.showEyeDropper, o.showAlpha, o.showAlpha);
return true;
}
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/ColorParameterDrawer.cs.meta


fileFormatVersion: 2
guid: 34ad702e46732ff438113b62a10ae80e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

42
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/RangeParameterDrawer.cs


using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace UnityEditor.Experimental.Rendering
{
[VolumeParameterDrawer(typeof(RangeParameter))]
sealed class RangeParameterDrawer : VolumeParameterDrawer
{
public override bool OnGUI(SerializedDataParameter parameter, GUIContent title)
{
var value = parameter.value;
if (value.propertyType != SerializedPropertyType.Vector2)
return false;
var o = parameter.GetObjectRef<RangeParameter>();
var v = value.vector2Value;
// The layout system breaks alignement when mixing inspector fields with custom layouted
// fields as soon as a scrollbar is needed in the inspector, so we'll do the layout
// manually instead
const int kFloatFieldWidth = 50;
const int kSeparatorWidth = 5;
float indentOffset = EditorGUI.indentLevel * 15f;
var lineRect = GUILayoutUtility.GetRect(1, EditorGUIUtility.singleLineHeight);
lineRect.xMin += 4f;
lineRect.y += 2f;
var labelRect = new Rect(lineRect.x, lineRect.y, EditorGUIUtility.labelWidth - indentOffset, lineRect.height);
var floatFieldLeft = new Rect(labelRect.xMax, lineRect.y, kFloatFieldWidth + indentOffset, lineRect.height);
var sliderRect = new Rect(floatFieldLeft.xMax + kSeparatorWidth - indentOffset, lineRect.y, lineRect.width - labelRect.width - kFloatFieldWidth * 2 - kSeparatorWidth * 2, lineRect.height);
var floatFieldRight = new Rect(sliderRect.xMax + kSeparatorWidth - indentOffset, lineRect.y, kFloatFieldWidth + indentOffset, lineRect.height);
EditorGUI.PrefixLabel(labelRect, title);
v.x = EditorGUI.FloatField(floatFieldLeft, v.x);
EditorGUI.MinMaxSlider(sliderRect, ref v.x, ref v.y, o.min, o.max);
v.y = EditorGUI.FloatField(floatFieldRight, v.y);
value.vector2Value = v;
return true;
}
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/RangeParameterDrawer.cs.meta


fileFormatVersion: 2
guid: 54ad01bd6d555e0438e364d8b0f17e06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

20
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/Vector4ParameterDrawer.cs


using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace UnityEditor.Experimental.Rendering
{
[VolumeParameterDrawer(typeof(Vector4Parameter))]
sealed class Vector4ParametrDrawer : VolumeParameterDrawer
{
public override bool OnGUI(SerializedDataParameter parameter, GUIContent title)
{
var value = parameter.value;
if (value.propertyType != SerializedPropertyType.Vector4)
return false;
value.vector4Value = EditorGUILayout.Vector4Field(title, value.vector4Value);
return true;
}
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/Drawers/Vector4ParameterDrawer.cs.meta


fileFormatVersion: 2
guid: 9c51e2ea627a393478d0b5c2a086136d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

57
ScriptableRenderPipeline/Core/Volume/Editor/SerializedDataParameter.cs


using System;
using System.Linq;
using System.Reflection;
using UnityEngine.Assertions;
namespace UnityEditor.Experimental.Rendering
{
public sealed class SerializedDataParameter
{
public SerializedProperty overrideState { get; private set; }
public SerializedProperty value { get; private set; }
public Attribute[] attributes { get; private set; }
public Type referenceType { get; private set; }
SerializedProperty m_BaseProperty;
object m_ReferenceValue;
public string displayName
{
get { return m_BaseProperty.displayName; }
}
internal SerializedDataParameter(SerializedProperty property)
{
// Find the actual property type, optional attributes & reference
var path = property.propertyPath.Split('.');
object obj = property.serializedObject.targetObject;
FieldInfo field = null;
foreach (var p in path)
{
field = obj.GetType().GetField(p, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
obj = field.GetValue(obj);
}
Assert.IsNotNull(field);
m_BaseProperty = property.Copy();
overrideState = m_BaseProperty.FindPropertyRelative("m_OverrideState");
value = m_BaseProperty.FindPropertyRelative("m_Value");
attributes = field.GetCustomAttributes(false).Cast<Attribute>().ToArray();
referenceType = obj.GetType();
m_ReferenceValue = obj;
}
public T GetAttribute<T>()
where T : Attribute
{
return (T)attributes.FirstOrDefault(x => x is T);
}
public T GetObjectRef<T>()
{
return (T)m_ReferenceValue;
}
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/SerializedDataParameter.cs.meta


fileFormatVersion: 2
guid: abf61146103c4444290de1048016607b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

266
ScriptableRenderPipeline/Core/Volume/Editor/VolumeComponentEditor.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Experimental.Rendering;
namespace UnityEditor.Experimental.Rendering
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class VolumeComponentEditorAttribute : Attribute
{
public readonly Type componentType;
public VolumeComponentEditorAttribute(Type componentType)
{
this.componentType = componentType;
}
}
public class VolumeComponentEditor
{
internal VolumeComponent target { get; private set; }
internal SerializedObject serializedObject { get; private set; }
internal SerializedProperty baseProperty;
internal SerializedProperty activeProperty;
Editor m_Inspector;
List<SerializedDataParameter> m_Parameters;
static Dictionary<Type, VolumeParameterDrawer> s_ParameterDrawers;
static VolumeComponentEditor()
{
s_ParameterDrawers = new Dictionary<Type, VolumeParameterDrawer>();
ReloadDecoratorTypes();
}
[Callbacks.DidReloadScripts]
static void OnEditorReload()
{
ReloadDecoratorTypes();
}
static void ReloadDecoratorTypes()
{
s_ParameterDrawers.Clear();
// Look for all the valid parameter drawers
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(
a => a.GetTypes()
.Where(
t => t.IsSubclassOf(typeof(VolumeParameterDrawer))
&& t.IsDefined(typeof(VolumeParameterDrawerAttribute), false)
)
);
// Store them
foreach (var type in types)
{
var attr = (VolumeParameterDrawerAttribute)type.GetCustomAttributes(typeof(VolumeParameterDrawerAttribute), false)[0];
var decorator = (VolumeParameterDrawer)Activator.CreateInstance(type);
s_ParameterDrawers.Add(attr.parameterType, decorator);
}
}
public void Repaint()
{
m_Inspector.Repaint();
}
internal void Init(VolumeComponent target, Editor inspector)
{
this.target = target;
m_Inspector = inspector;
serializedObject = new SerializedObject(target);
activeProperty = serializedObject.FindProperty("active");
OnEnable();
}
public virtual void OnEnable()
{
m_Parameters = new List<SerializedDataParameter>();
// Grab all valid serializable field on the VolumeComponent
var fields = target.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(t => t.FieldType.IsSubclassOf(typeof(VolumeParameter)))
.Where(t =>
(t.IsPublic && t.GetCustomAttributes(typeof(NonSerializedAttribute), false).Length == 0) ||
(t.GetCustomAttributes(typeof(SerializeField), false).Length > 0)
)
.Where(t => t.GetCustomAttributes(typeof(HideInInspector), false).Length == 0)
.ToList();
// Prepare all serialized objects for this editor
foreach (var field in fields)
{
var property = serializedObject.FindProperty(field.Name);
var parameter = new SerializedDataParameter(property);
m_Parameters.Add(parameter);
}
}
public virtual void OnDisable()
{
}
internal void OnInternalInspectorGUI()
{
serializedObject.Update();
TopRowFields();
OnInspectorGUI();
EditorGUILayout.Space();
serializedObject.ApplyModifiedProperties();
}
public virtual void OnInspectorGUI()
{
// Display every field as-is
foreach (var parameter in m_Parameters)
PropertyField(parameter);
}
public virtual string GetDisplayTitle()
{
return ObjectNames.NicifyVariableName(target.GetType().Name);
}
void TopRowFields()
{
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button(CoreEditorUtils.GetContent("All|Toggle all overrides on. To maximize performances you should only toggle overrides that you actually need."), CoreEditorStyles.miniLabelButton, GUILayout.Width(17f), GUILayout.ExpandWidth(false)))
SetAllOverridesTo(true);
if (GUILayout.Button(CoreEditorUtils.GetContent("None|Toggle all overrides off."), CoreEditorStyles.miniLabelButton, GUILayout.Width(32f), GUILayout.ExpandWidth(false)))
SetAllOverridesTo(false);
GUILayout.FlexibleSpace();
}
}
internal void SetAllOverridesTo(bool state)
{
Undo.RecordObject(target, "Toggle All");
target.SetAllOverridesTo(state);
serializedObject.Update();
}
// Takes a serialized VolumeParameter<T> as input
protected SerializedDataParameter Unpack(SerializedProperty property)
{
Assert.IsNotNull(property);
return new SerializedDataParameter(property);
}
protected void PropertyField(SerializedDataParameter property)
{
var title = CoreEditorUtils.GetContent(property.displayName);
PropertyField(property, title);
}
protected void PropertyField(SerializedDataParameter property, GUIContent title)
{
// Handle unity built-in decorators (Space, Header, Tooltip etc)
foreach (var attr in property.attributes)
{
if (attr is PropertyAttribute)
{
if (attr is SpaceAttribute)
{
EditorGUILayout.GetControlRect(false, (attr as SpaceAttribute).height);
}
else if (attr is HeaderAttribute)
{
var rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight);
rect.y += 0f;
rect = EditorGUI.IndentedRect(rect);
EditorGUI.LabelField(rect, (attr as HeaderAttribute).header, EditorStyles.miniLabel);
}
else if (attr is TooltipAttribute)
{
if (string.IsNullOrEmpty(title.tooltip))
title.tooltip = (attr as TooltipAttribute).tooltip;
}
}
}
// Custom parameter drawer
VolumeParameterDrawer drawer;
s_ParameterDrawers.TryGetValue(property.referenceType, out drawer);
bool invalidProp = false;
if (drawer != null && !drawer.IsAutoProperty())
{
if (drawer.OnGUI(property, title))
return;
invalidProp = true;
}
// ObjectParameter<T> is a special case
if (VolumeParameter.IsObjectParameter(property.referenceType))
{
bool expanded = property.value.isExpanded;
expanded = EditorGUILayout.Foldout(expanded, title, true);
if (expanded)
{
EditorGUI.indentLevel++;
// Not the fastest way to do it but that'll do just fine for now
var it = property.value.Copy();
var end = it.GetEndProperty();
bool first = true;
while (it.Next(first) && !SerializedProperty.EqualContents(it, end))
{
PropertyField(Unpack(it));
first = false;
}
EditorGUI.indentLevel--;
}
property.value.isExpanded = expanded;
return;
}
using (new EditorGUILayout.HorizontalScope())
{
// Override checkbox
var overrideRect = GUILayoutUtility.GetRect(17f, 17f, GUILayout.ExpandWidth(false));
overrideRect.yMin += 4f;
overrideRect.xMin += EditorGUI.indentLevel * 15f;
DrawOverrideCheckbox(overrideRect, property.overrideState);
// Property
using (new EditorGUI.DisabledScope(!property.overrideState.boolValue))
{
if (drawer != null && !invalidProp)
{
if (drawer.OnGUI(property, title))
return;
}
// Default unity field
EditorGUILayout.PropertyField(property.value, title);
}
}
}
void DrawOverrideCheckbox(Rect rect, SerializedProperty property)
{
var oldColor = GUI.color;
GUI.color = new Color(0.6f, 0.6f, 0.6f, 0.75f);
property.boolValue = GUI.Toggle(rect, property.boolValue, CoreEditorUtils.GetContent("|Override this setting for this volume."), CoreEditorStyles.smallTickbox);
GUI.color = oldColor;
}
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/VolumeComponentEditor.cs.meta


fileFormatVersion: 2
guid: d6bb22372ebe2bc42a227c8f8c1795ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

388
ScriptableRenderPipeline/Core/Volume/Editor/VolumeEditor.cs


using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Experimental.Rendering;
namespace UnityEditor.Experimental.Rendering
{
[CustomEditor(typeof(Volume))]
public sealed class VolumeEditor : Editor
{
SerializedProperty m_IsGlobal;
SerializedProperty m_BlendRadius;
SerializedProperty m_Weight;
SerializedProperty m_Priority;
SerializedProperty m_Components;
Dictionary<Type, Type> m_EditorTypes; // Component type => Editor type
List<VolumeComponentEditor> m_Editors;
static VolumeComponent s_ClipboardContent;
Volume actualTarget
{
get { return target as Volume; }
}
void OnEnable()
{
var o = new PropertyFetcher<Volume>(serializedObject);
m_IsGlobal = o.Find(x => x.isGlobal);
m_BlendRadius = o.Find(x => x.blendDistance);
m_Weight = o.Find(x => x.weight);
m_Priority = o.Find(x => x.priority);
m_Components = o.Find(x => x.components);
m_EditorTypes = new Dictionary<Type, Type>();
m_Editors = new List<VolumeComponentEditor>();
// Gets the list of all available component editors
var editorTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(
a => a.GetTypes()
.Where(
t => t.IsSubclassOf(typeof(VolumeComponentEditor))
&& t.IsDefined(typeof(VolumeComponentEditorAttribute), false)
)
).ToList();
// Map them to their corresponding component type
foreach (var editorType in editorTypes)
{
var attribute = (VolumeComponentEditorAttribute)editorType.GetCustomAttributes(typeof(VolumeComponentEditorAttribute), false)[0];
m_EditorTypes.Add(attribute.componentType, editorType);
}
// Create editors for existing components
var components = actualTarget.components;
for (int i = 0; i < components.Count; i++)
CreateEditor(components[i], m_Components.GetArrayElementAtIndex(i));
// Keep track of undo/redo to redraw the inspector when that happens
Undo.undoRedoPerformed += OnUndoRedoPerformed;
}
void OnDisable()
{
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
if (m_Editors == null)
return; // Hasn't been inited yet
foreach (var editor in m_Editors)
editor.OnDisable();
m_Editors.Clear();
m_EditorTypes.Clear();
}
public override void OnInspectorGUI()
{
serializedObject.Update();
if (actualTarget.isDirty)
{
RefreshEditors();
actualTarget.isDirty = false;
}
using (var scope = new EditorGUILayout.VerticalScope())
{
EditorGUILayout.PropertyField(m_IsGlobal);
if (!m_IsGlobal.boolValue) // Blend radius is not needed for global volumes
{
EditorGUILayout.PropertyField(m_BlendRadius);
m_BlendRadius.floatValue = Mathf.Max(m_BlendRadius.floatValue, 0f);
}
EditorGUILayout.PropertyField(m_Weight);
EditorGUILayout.PropertyField(m_Priority);
EditorGUILayout.Space();
// Component list
for (int i = 0; i < m_Editors.Count; i++)
{
var editor = m_Editors[i];
string title = editor.GetDisplayTitle();
int id = i; // Needed for closure capture below
CoreEditorUtils.DrawSplitter();
bool displayContent = CoreEditorUtils.DrawHeaderToggle(
title,
editor.baseProperty,
editor.activeProperty,
pos => OnContextClick(pos, editor.target, id)
);
if (displayContent)
{
using (new EditorGUI.DisabledScope(!editor.activeProperty.boolValue))
editor.OnInternalInspectorGUI();
}
}
if (m_Editors.Count > 0)
CoreEditorUtils.DrawSplitter();
else
EditorGUILayout.HelpBox("No override set on this volume.", MessageType.Info);
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
//if (GUILayout.Button(CoreEditorUtils.GetContent("Add Component"), GUILayout.Width(230f), GUILayout.Height(24f)))
//{
//}
GUILayout.FlexibleSpace();
}
// Handle components drag'n'drop
var e = Event.current;
if (e.type == EventType.DragUpdated)
{
if (IsDragValid(scope.rect, e.mousePosition))
{
DragAndDrop.visualMode = DragAndDropVisualMode.Link;
e.Use();
}
else
{
DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
}
}
else if (e.type == EventType.DragPerform)
{
if (IsDragValid(scope.rect, e.mousePosition))
{
DragAndDrop.AcceptDrag();
var objs = DragAndDrop.objectReferences;
foreach (var o in objs)
{
var compType = ((MonoScript)o).GetClass();
AddComponent(compType);
}
e.Use();
}
}
}
serializedObject.ApplyModifiedProperties();
}
bool IsDragValid(Rect rect, Vector2 mousePos)
{
if (!rect.Contains(mousePos))
return false;
var objs = DragAndDrop.objectReferences;
foreach (var o in objs)
{
if (o.GetType() != typeof(MonoScript))
return false;
var script = (MonoScript)o;
var scriptType = script.GetClass();
if (!scriptType.IsSubclassOf(typeof(VolumeComponent)))
return false;
if (actualTarget.components.Exists(t => t.GetType() == scriptType))
return false;
}
return true;
}
void RefreshEditors()
{
// Disable all editors first
foreach (var editor in m_Editors)
editor.OnDisable();
// Remove them
m_Editors.Clear();
// Recreate editors for existing settings, if any
for (int i = 0; i < actualTarget.components.Count; i++)
CreateEditor(actualTarget.components[i], m_Components.GetArrayElementAtIndex(i));
}
// index is only used when we need to re-create a component in a specific spot (e.g. reset)
void CreateEditor(VolumeComponent settings, SerializedProperty property, int index = -1)
{
var settingsType = settings.GetType();
Type editorType;
if (!m_EditorTypes.TryGetValue(settingsType, out editorType))
editorType = typeof(VolumeComponentEditor);
var editor = (VolumeComponentEditor)Activator.CreateInstance(editorType);
editor.Init(settings, this);
editor.baseProperty = property.Copy();
if (index < 0)
m_Editors.Add(editor);
else
m_Editors[index] = editor;
}
void AddComponent(Type type)
{
serializedObject.Update();
var component = (VolumeComponent)CreateInstance(type);
Undo.RegisterCreatedObjectUndo(component, "Add Volume Component");
// Grow the list first, then add - that's how serialized lists work in Unity
m_Components.arraySize++;
var effectProp = m_Components.GetArrayElementAtIndex(m_Components.arraySize - 1);
effectProp.objectReferenceValue = component;
// Create & store the internal editor object for this effect
CreateEditor(component, effectProp);
serializedObject.ApplyModifiedProperties();
}
void RemoveComponent(int id)
{
// Huh. Hack to keep foldout state on the next element...
bool nextFoldoutState = false;
if (id < m_Editors.Count - 1)
nextFoldoutState = m_Editors[id + 1].baseProperty.isExpanded;
// Remove from the cached editors list
m_Editors[id].OnDisable();
m_Editors.RemoveAt(id);
serializedObject.Update();
var property = m_Components.GetArrayElementAtIndex(id);
var effect = property.objectReferenceValue;
// Unassign it (should be null already but serialization does funky things
property.objectReferenceValue = null;
// ...and remove the array index itself from the list
m_Components.DeleteArrayElementAtIndex(id);
// Finally refresh editor reference to the serialized settings list
for (int i = 0; i < m_Editors.Count; i++)
m_Editors[i].baseProperty = m_Components.GetArrayElementAtIndex(i).Copy();
// Set the proper foldout state if needed
if (id < m_Editors.Count)
m_Editors[id].baseProperty.isExpanded = nextFoldoutState;
serializedObject.ApplyModifiedProperties();
// Destroy the setting object after ApplyModifiedProperties(). If we do it before, redo
// actions will be in the wrong order and the reference to the setting object in the
// list will be lost.
Undo.DestroyObjectImmediate(effect);
}
// Reset is done by deleting and removing the object from the list and adding a new one in
// the same spot as it was before
void ResetComponent(Type type, int id)
{
// Remove from the cached editors list
m_Editors[id].OnDisable();
m_Editors[id] = null;
serializedObject.Update();
var property = m_Components.GetArrayElementAtIndex(id);
var prevSettings = property.objectReferenceValue;
// Unassign it but down remove it from the array to keep the index available
property.objectReferenceValue = null;
// Create a new object
var newEffect = (VolumeComponent)CreateInstance(type);
Undo.RegisterCreatedObjectUndo(newEffect, "Reset Volume Component");
// Put it in the reserved space
property.objectReferenceValue = newEffect;
// Create & store the internal editor object for this effect
CreateEditor(newEffect, property, id);
serializedObject.ApplyModifiedProperties();
// Same as RemoveComponent, destroy at the end so it's recreated first on Undo to make
// sure the GUID exists before undoing the list state
Undo.DestroyObjectImmediate(prevSettings);
}
void OnContextClick(Vector2 position, VolumeComponent targetComponent, int id)
{
var menu = new GenericMenu();
menu.AddItem(CoreEditorUtils.GetContent("Reset"), false, () => ResetComponent(targetComponent.GetType(), id));
menu.AddItem(CoreEditorUtils.GetContent("Remove"), false, () => RemoveComponent(id));
menu.AddSeparator(string.Empty);
menu.AddItem(CoreEditorUtils.GetContent("Copy Settings"), false, () => CopySettings(targetComponent));
if (CanPaste(targetComponent))
menu.AddItem(CoreEditorUtils.GetContent("Paste Settings"), false, () => PasteSettings(targetComponent));
else
menu.AddDisabledItem(CoreEditorUtils.GetContent("Paste Settings"));
menu.AddSeparator(string.Empty);
menu.AddItem(CoreEditorUtils.GetContent("Toggle All"), false, () => m_Editors[id].SetAllOverridesTo(true));
menu.AddItem(CoreEditorUtils.GetContent("Toggle None"), false, () => m_Editors[id].SetAllOverridesTo(false));
menu.DropDown(new Rect(position, Vector2.zero));
}
// Copy/pasting is simply done by creating an in memory copy of the selected component and
// copying over the serialized data to another; it doesn't use nor affect the OS clipboard
bool CanPaste(VolumeComponent targetComponent)
{
return s_ClipboardContent != null
&& s_ClipboardContent.GetType() == targetComponent.GetType();
}
void CopySettings(VolumeComponent targetComponent)
{
if (s_ClipboardContent != null)
{
CoreUtils.Destroy(s_ClipboardContent);
s_ClipboardContent = null;
}
s_ClipboardContent = (VolumeComponent)CreateInstance(targetComponent.GetType());
EditorUtility.CopySerializedIfDifferent(targetComponent, s_ClipboardContent);
}
void PasteSettings(VolumeComponent targetComponent)
{
Assert.IsNotNull(s_ClipboardContent);
Assert.AreEqual(s_ClipboardContent.GetType(), targetComponent.GetType());
Undo.RecordObject(targetComponent, "Paste Settings");
EditorUtility.CopySerializedIfDifferent(s_ClipboardContent, targetComponent);
}
void OnUndoRedoPerformed()
{
actualTarget.isDirty = true;
// Dumb hack to make sure the serialized object is up to date on undo
serializedObject.Update();
serializedObject.ApplyModifiedProperties();
// Seems like there's an issue with the inspector not repainting after some undo events
// This will take care of that
Repaint();
}
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/VolumeEditor.cs.meta


fileFormatVersion: 2
guid: 9a033a261241d6e48af8ca011ab7c5c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

31
ScriptableRenderPipeline/Core/Volume/Editor/VolumeParameterDrawer.cs


using System;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class VolumeParameterDrawerAttribute : Attribute
{
public readonly Type parameterType;
public VolumeParameterDrawerAttribute(Type parameterType)
{
this.parameterType = parameterType;
}
}
// Default parameter drawer - simply displays the serialized property as Unity would
public abstract class VolumeParameterDrawer
{
// Override this and return false if you want to customize the override checkbox position,
// else it'll automatically draw it and put the property content in a horizontal scope.
public virtual bool IsAutoProperty()
{
return true;
}
// Return false is the input parameter is invalid - unity will display the default editor
// for this control then
public abstract bool OnGUI(SerializedDataParameter parameter, GUIContent title);
}
}

11
ScriptableRenderPipeline/Core/Volume/Editor/VolumeParameterDrawer.cs.meta


fileFormatVersion: 2
guid: d8ce10b4215bd81438b65f7e23ac0946
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

11
ScriptableRenderPipeline/Core/Volume/Volume.cs.meta


fileFormatVersion: 2
guid: 172515602e62fb746b5d573b38a5fe58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

73
ScriptableRenderPipeline/Core/Volume/VolumeComponent.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Linq;
namespace UnityEngine.Experimental.Rendering
{
[Serializable]
public class VolumeComponent : ScriptableObject
{
// Used to control the state of this override - handy to quickly turn a volume override
// on & off in the editor
public bool active = true;
internal ReadOnlyCollection<VolumeParameter> parameters { get; private set; }
void OnEnable()
{
// Automatically grab all fields of type VolumeParameter for this instance
parameters = this.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(t => t.FieldType.IsSubclassOf(typeof(VolumeParameter)))
.OrderBy(t => t.MetadataToken) // Guaranteed order
.Select(t => (VolumeParameter)t.GetValue(this))
.ToList()
.AsReadOnly();
}
public void SetAllOverridesTo(bool state)
{
SetAllOverridesTo(parameters, state);
}
void SetAllOverridesTo(IEnumerable<VolumeParameter> enumerable, bool state)
{
foreach (var prop in enumerable)
{
prop.overrideState = state;
var t = prop.GetType();
if (VolumeParameter.IsObjectParameter(t))
{
// This method won't be called a lot but this is sub-optimal, fix me
var innerParams = (ReadOnlyCollection<VolumeParameter>)
t.GetProperty("parameters", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(prop, null);
if (innerParams != null)
SetAllOverridesTo(innerParams, state);
}
}
}
// Custom hashing function used to compare the state of settings (it's not meant to be
// unique but to be a quick way to check if two setting sets have the same state or not).
// Hash collision rate should be pretty low.
public override int GetHashCode()
{
unchecked
{
//return parameters.Aggregate(17, (i, p) => i * 23 + p.GetHash());
int hash = 17;
foreach (var p in parameters)
hash = hash * 23 + p.GetHashCode();
return hash;
}
}
}
}

11
ScriptableRenderPipeline/Core/Volume/VolumeComponent.cs.meta


fileFormatVersion: 2
guid: 556096baecf4aeb4e9f0d3efce77e08f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

336
ScriptableRenderPipeline/Core/Volume/VolumeManager.cs


using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Assertions;
namespace UnityEngine.Experimental.Rendering
{
public sealed class VolumeManager
{
//>>> System.Lazy<T> is broken in Unity (legacy runtime) so we'll have to do it ourselves :|
static volatile VolumeManager s_Instance;
static object s_LockObj = new Object();
public static VolumeManager instance
{
get
{
// Double-lock checking
if (s_Instance == null)
{
lock (s_LockObj) // Lock on a separate object to avoid deadlocks
{
if (s_Instance == null)
s_Instance = new VolumeManager();
}
}
return s_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;
// Keep track of sorting states for all layers
readonly bool[] m_SortNeeded;
// Internal state of all component types
readonly Dictionary<Type, VolumeComponent> m_Components;
// Internal list of default state for each component type - this is used to reset component
// states on update instead of having to implement a Reset method on all components (which
// would be error-prone)
readonly List<VolumeComponent> m_ComponentsDefaultState;
// Recycled list used for volume traversal
readonly List<Collider> m_TempColliders;
// In the editor, when entering play-mode, it will call the constructor and OnEditorReload()
// which in turn will call ReloadBaseTypes() twice, so we need to keep track of the reloads
// to avoid wasting any more CPU than required
static bool s_StopReloads = false;
VolumeManager()
{
m_Volumes = new List<Volume>[k_MaxLayerCount];
m_SortNeeded = new bool[k_MaxLayerCount];
m_TempColliders = new List<Collider>(8);
m_Components = new Dictionary<Type, VolumeComponent>();
m_ComponentsDefaultState = new List<VolumeComponent>();
ReloadBaseTypes();
}
#if UNITY_EDITOR
// Called every time Unity recompiles scripts in the editor. We need this to keep track of
// any new custom component the user might add to the project.
[UnityEditor.Callbacks.DidReloadScripts]
static void OnEditorReload()
{
if (!s_StopReloads)
instance.ReloadBaseTypes();
s_StopReloads = false;
}
#endif
// This will be called only once at runtime and everytime script reload kicks-in in the
// editor as we need to keep track of any compatible component in the project
void ReloadBaseTypes()
{
// Clean component map & default states
foreach (var component in m_Components)
CoreUtils.Destroy(component.Value);
foreach (var component in m_ComponentsDefaultState)
CoreUtils.Destroy(component);
m_Components.Clear();
m_ComponentsDefaultState.Clear();
// Rebuild it from scratch
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(
a => a.GetTypes()
.Where(
t => t.IsSubclassOf(typeof(VolumeComponent))
)
);
foreach (var type in types)
{
// We need two instances, one for global state tracking and another one to keep a
// default state that will act as the lowest priority global volume (so that we have
// a state to fallback to when exiting volumes)
var inst = (VolumeComponent)ScriptableObject.CreateInstance(type);
m_Components.Add(type, inst);
inst = (VolumeComponent)ScriptableObject.CreateInstance(type);
inst.SetAllOverridesTo(true);
m_ComponentsDefaultState.Add(inst);
}
s_StopReloads = true;
}
public T GetComponent<T>()
where T : VolumeComponent
{
var comp = GetComponent(typeof(T));
return (T)comp;
}
public VolumeComponent GetComponent(Type type)
{
VolumeComponent comp;
m_Components.TryGetValue(type, out comp);
Assert.IsNotNull(comp, "Component map is corrupted, \"" + type + "\" not found");
return comp;
}
public void Register(Volume volume, int layer)
{
var volumes = m_Volumes[layer];
if (volumes == null)
{
volumes = new List<Volume>();
m_Volumes[layer] = volumes;
}
Assert.IsFalse(volumes.Contains(volume), "Volume has already been registered");
volumes.Add(volume);
SetLayerDirty(layer);
}
public void Unregister(Volume volume, int layer)
{
var volumes = m_Volumes[layer];
if (volumes == null)
return;
volumes.Remove(volume);
}
internal void SetLayerDirty(int layer)
{
Assert.IsTrue(layer >= 0 && layer <= k_MaxLayerCount, "Invalid layer bit");
m_SortNeeded[layer] = true;
}
internal void UpdateVolumeLayer(Volume volume, int prevLayer, int newLayer)
{
Assert.IsTrue(prevLayer >= 0 && prevLayer <= k_MaxLayerCount, "Invalid layer bit");
Unregister(volume, prevLayer);
Register(volume, newLayer);
}
// Go through all listed components and lerp overriden values in the global state
void OverrideData(List<VolumeComponent> components, float interpFactor)
{
foreach (var component in components)
{
if (!component.active)
continue;
var target = GetComponent(component.GetType());
int count = component.parameters.Count;
for (int i = 0; i < count; i++)
{
var toParam = component.parameters[i];
if (toParam.overrideState)
{
var fromParam = target.parameters[i];
fromParam.Interp(fromParam, toParam, interpFactor);
}
}
}
}
// Faster version of OverrideData to force replace values in the global state
void ReplaceData(List<VolumeComponent> components)
{
foreach (var component in components)
{
var target = GetComponent(component.GetType());
int count = component.parameters.Count;
for (int i = 0; i < count; i++)
target.parameters[i].SetValue(component.parameters[i]);
}
}
// Update the global state - should be called once per frame in the update loop before
// anything else
public void Update(Transform trigger, LayerMask layerMask)
{
#if UNITY_EDITOR
// Editor specific hack to work around serialization doing funky things when exiting
// play mode -> re-create the world when bad things happen
if (m_ComponentsDefaultState == null
|| (m_ComponentsDefaultState.Count > 0 && m_ComponentsDefaultState[0] == null))
{
ReloadBaseTypes();
}
else
#endif
{
// Start by resetting the global state to default values
ReplaceData(m_ComponentsDefaultState);
}
// Do magic
bool onlyGlobal = trigger == null;
int mask = layerMask.value;
var triggerPos = onlyGlobal ? Vector3.zero : trigger.position;
for (int i = 0; i < k_MaxLayerCount; i++)
{
// Skip layers not in the mask
if ((mask & (1 << i)) == 0)
continue;
// Skip empty layers
var volumes = m_Volumes[i];
if (volumes == null)
continue;
// Sort the volume list if needed
if (m_SortNeeded[i])
{
SortByPriority(volumes);
m_SortNeeded[i] = false;
}
// Traverse all volumes
foreach (var volume in volumes)
{
// Skip disabled volumes and volumes without any data or weight
if (!volume.enabled || volume.weight <= 0f)
continue;
var components = volume.components;
// Global volumes always have influence
if (volume.isGlobal)
{
OverrideData(components, Mathf.Clamp01(volume.weight));
continue;
}
if (onlyGlobal)
continue;
// 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;
// Find closest distance to volume, 0 means it's inside it
float closestDistanceSqr = float.PositiveInfinity;
foreach (var collider in colliders)
{
if (!collider.enabled)
continue;
var closestPoint = collider.ClosestPoint(triggerPos);
var d = (closestPoint - triggerPos).sqrMagnitude;
if (d < closestDistanceSqr)
closestDistanceSqr = d;
}
colliders.Clear();
float blendDistSqr = volume.blendDistance * volume.blendDistance;
// 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;
// Volume has influence
float interpFactor = 1f;
if (blendDistSqr > 0f)
interpFactor = 1f - (closestDistanceSqr / blendDistSqr);
// No need to clamp01 the interpolation factor as it'll always be in [0;1[ range
OverrideData(components, interpFactor * Mathf.Clamp01(volume.weight));
}
}
}
// Stable insertion sort. Faster than List<T>.Sort() for our needs.
static void SortByPriority(List<Volume> volumes)
{
Assert.IsNotNull(volumes, "Trying to sort volumes of non-initialized layer");
for (int i = 1; i < volumes.Count; i++)
{
var temp = volumes[i];
int j = i - 1;
// Sort order is ascending
while (j >= 0 && volumes[j].priority > temp.priority)
{
volumes[j + 1] = volumes[j];
j--;
}
volumes[j + 1] = temp;
}
}
}
}

11
ScriptableRenderPipeline/Core/Volume/VolumeManager.cs.meta


fileFormatVersion: 2
guid: f709aacbf8d0b0048889f52315d67d58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

424
ScriptableRenderPipeline/Core/Volume/VolumeParameter.cs


using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
namespace UnityEngine.Experimental.Rendering
{
// We need this base class to be able to store a list of VolumeParameter in collections as we
// can't store VolumeParameter<T> with variable T types in the same collection. As a result some
// of the following is a bit hacky...
public abstract class VolumeParameter
{
[SerializeField]
protected bool m_OverrideState;
public virtual bool overrideState
{
get { return m_OverrideState; }
set { m_OverrideState = value; }
}
internal abstract void Interp(VolumeParameter from, VolumeParameter to, float t);
public T GetValue<T>()
{
return ((VolumeParameter<T>)this).value;
}
internal abstract void SetValue(VolumeParameter parameter);
public static bool IsObjectParameter(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ObjectParameter<>))
return true;
return type.BaseType != null
&& IsObjectParameter(type.BaseType);
}
}
[Serializable]
public class VolumeParameter<T> : VolumeParameter
{
[SerializeField]
protected T m_Value;
public virtual T value
{
get { return m_Value; }
set { m_Value = value; }
}
public VolumeParameter()
: this(default(T), false)
{
}
protected VolumeParameter(T value, bool overrideState)
{
m_Value = value;
this.overrideState = overrideState;
}
internal override void Interp(VolumeParameter from, VolumeParameter to, float t)
{
// Note: this is relatively unsafe (assumes that from and to are both holding type T)
Interp(from.GetValue<T>(), to.GetValue<T>(), t);
}
public virtual void Interp(T from, T to, float t)
{
// Returns `b` if `dt > 0` by default so we don't have to write overrides for bools and
// enumerations.
m_Value = t > 0f ? to : from;
}
public void Override(T x)
{
overrideState = true;
m_Value = x;
}
internal override void SetValue(VolumeParameter parameter)
{
m_Value = parameter.GetValue<T>();
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + overrideState.GetHashCode();
hash = hash * 23 + value.GetHashCode();
return hash;
}
}
//
// Implicit conversion; assuming the following:
//
// var myFloatProperty = new ParameterOverride<float> { value = 42f; };
//
// It allows for implicit casts:
//
// float myFloat = myFloatProperty.value; // No implicit cast
// float myFloat = myFloatProperty; // Implicit cast
//
// For safety reason this is one-way only.
//
public static implicit operator T(VolumeParameter<T> prop)
{
return prop.m_Value;
}
}
public enum ParameterClampMode
{
Min,
Max,
MinMax
}
//
// The serialization system in Unity can't serialize generic types, the workaround is to extend
// and flatten pre-defined generic types.
// For enums it's recommended to make your own types on the spot, like so:
//
// [Serializable]
// public sealed class MyEnumParameter : VolumeParameter<MyEnum> { }
// public enum MyEnum { One, Two }
//
[Serializable]
public sealed class BoolParameter : VolumeParameter<bool> { }
[Serializable]
public sealed class IntParameter : VolumeParameter<int>
{
public override void Interp(int from, int to, float t)
{
// Int snapping interpolation. Don't use this for enums as they don't necessarily have
// contiguous values. Use the default interpolator instead (same as bool).
m_Value = (int)(from + (to - from) * t);
}
}
[Serializable]
public sealed class InstantIntParameter : VolumeParameter<int> { }
[Serializable]
public sealed class ClampedIntParameter : VolumeParameter<int>
{
public ParameterClampMode clampMode = ParameterClampMode.MinMax;
public int min = 0;
public int max = 10;
public override int value
{
get { return m_Value; }
set
{
switch (clampMode)
{
case ParameterClampMode.Min: m_Value = Mathf.Max(min, value); break;
case ParameterClampMode.Max: m_Value = Mathf.Min(max, value); break;
case ParameterClampMode.MinMax: m_Value = Mathf.Clamp(value, min, max); break;
}
}
}
public override void Interp(int from, int to, float t)
{
m_Value = (int)(from + (to - from) * t);
}
}
[Serializable]
public sealed class InstantClampedIntParameter : VolumeParameter<int>
{
public ParameterClampMode clampMode = ParameterClampMode.MinMax;
public int min = 0;
public int max = 10;
public override int value
{
get { return m_Value; }
set
{
switch (clampMode)
{
case ParameterClampMode.Min: m_Value = Mathf.Max(min, value); break;
case ParameterClampMode.Max: m_Value = Mathf.Min(max, value); break;
case ParameterClampMode.MinMax: m_Value = Mathf.Clamp(value, min, max); break;
}
}
}
}
[Serializable]
public sealed class FloatParameter : VolumeParameter<float>
{
public override void Interp(float from, float to, float t)
{
m_Value = from + (to - from) * t;
}
}
[Serializable]
public sealed class InstantFloatParameter : VolumeParameter<float> { }
[Serializable]
public sealed class ClampedFloatParameter : VolumeParameter<float>
{
public ParameterClampMode clampMode = ParameterClampMode.MinMax;
public float min = 0f;
public float max = 1f;
public override float value
{
get { return m_Value; }
set
{
switch (clampMode)
{
case ParameterClampMode.Min: m_Value = Mathf.Max(min, value); break;
case ParameterClampMode.Max: m_Value = Mathf.Min(max, value); break;
case ParameterClampMode.MinMax: m_Value = Mathf.Clamp(value, min, max); break;
}
}
}
// We could override FloatParameter here but that would require making it not-sealed which
// will stop the compiler from doing specific optimizations on virtual methods - considering
// how often float is used, duplicating the code in this case is a definite win
public override void Interp(float from, float to, float t)
{
m_Value = from + (to - from) * t;
}
}
[Serializable]
public sealed class InstantClampedFloatParameter : VolumeParameter<float>
{
public ParameterClampMode clampMode = ParameterClampMode.MinMax;
public float min = 0f;
public float max = 1f;
public override float value
{
get { return m_Value; }
set
{
switch (clampMode)
{
case ParameterClampMode.Min: m_Value = Mathf.Max(min, value); break;
case ParameterClampMode.Max: m_Value = Mathf.Min(max, value); break;
case ParameterClampMode.MinMax: m_Value = Mathf.Clamp(value, min, max); break;
}
}
}
}
// Holds a min & a max values clamped in a range (MinMaxSlider in the editor)
[Serializable]
public sealed class RangeParameter : VolumeParameter<Vector2>
{
public float min = 0;
public float max = 1;
public override Vector2 value
{
get { return m_Value; }
set
{
m_Value.x = Mathf.Max(value.x, min);
m_Value.y = Mathf.Min(value.y, max);
}
}
public override void Interp(Vector2 from, Vector2 to, float t)
{
m_Value.x = from.x + (to.x - from.x) * t;
m_Value.y = from.y + (to.y - from.y) * t;
}
}
[Serializable]
public sealed class InstantRangeParameter : VolumeParameter<Vector2>
{
public float min = 0;
public float max = 1;
public override Vector2 value
{
get { return m_Value; }
set
{
m_Value.x = Mathf.Max(value.x, min);
m_Value.y = Mathf.Min(value.y, max);
}
}
}
// 32-bit RGBA
[Serializable]
public sealed class ColorParameter : VolumeParameter<Color>
{
public bool hdr = false;
public bool showAlpha = true;
public bool showEyeDropper = true;
public override void Interp(Color from, Color to, float t)
{
// Lerping color values is a sensitive subject... We looked into lerping colors using
// HSV and LCH but they have some downsides that make them not work correctly in all
// situations, so we stick with RGB lerping for now, at least its behavior is
// predictable despite looking desaturated when `t ~= 0.5` and it's faster anyway.
m_Value.r = from.r + (to.r - from.r) * t;
m_Value.g = from.g + (to.g - from.g) * t;
m_Value.b = from.b + (to.b - from.b) * t;
m_Value.a = from.a + (to.a - from.a) * t;
}
}
[Serializable]
public sealed class InstantColorParameter : VolumeParameter<Color> { }
[Serializable]
public sealed class Vector2Parameter : VolumeParameter<Vector2>
{
public override void Interp(Vector2 from, Vector2 to, float t)
{
m_Value.x = from.x + (to.x - from.x) * t;
m_Value.y = from.y + (to.y - from.y) * t;
}
}
[Serializable]
public sealed class InstantVector2Parameter : VolumeParameter<Vector2> { }
[Serializable]
public sealed class Vector3Parameter : VolumeParameter<Vector3>
{
public override void Interp(Vector3 from, Vector3 to, float t)
{
m_Value.x = from.x + (to.x - from.x) * t;
m_Value.y = from.y + (to.y - from.y) * t;
m_Value.z = from.z + (to.z - from.z) * t;
}
}
[Serializable]
public sealed class InstantVector3Parameter : VolumeParameter<Vector3> { }
[Serializable]
public sealed class Vector4Parameter : VolumeParameter<Vector4>
{
public override void Interp(Vector4 from, Vector4 to, float t)
{
m_Value.x = from.x + (to.x - from.x) * t;
m_Value.y = from.y + (to.y - from.y) * t;
m_Value.z = from.z + (to.z - from.z) * t;
m_Value.w = from.w + (to.w - from.w) * t;
}
}
[Serializable]
public sealed class InstantVector4Parameter : VolumeParameter<Vector4> { }
// Used as a container to store custom serialized classes/structs inside volume components
[Serializable]
public class ObjectParameter<T> : VolumeParameter<T>
{
internal ReadOnlyCollection<VolumeParameter> parameters { get; private set; }
// Force override state to true for container objects
public override bool overrideState
{
get { return true; }
set { m_OverrideState = true; }
}
public override T value
{
get { return m_Value; }
set
{
m_Value = value;
if (m_Value == null)
{
parameters = null;
return;
}
// Automatically grab all fields of type VolumeParameter contained in this instance
parameters = m_Value.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(t => t.FieldType.IsSubclassOf(typeof(VolumeParameter)))
.OrderBy(t => t.MetadataToken) // Guaranteed order
.Select(t => (VolumeParameter)t.GetValue(m_Value))
.ToList()
.AsReadOnly();
}
}
internal override void Interp(VolumeParameter from, VolumeParameter to, float t)
{
if (m_Value == null)
return;
var paramOrigin = parameters;
var paramFrom = ((ObjectParameter<T>)from).parameters;
var paramTo = ((ObjectParameter<T>)to).parameters;
for (int i = 0; i < paramFrom.Count; i++)
{
if (paramOrigin[i].overrideState)
paramOrigin[i].Interp(paramFrom[i], paramTo[i], t);
}
}
}
}

11
ScriptableRenderPipeline/Core/Volume/VolumeParameter.cs.meta


fileFormatVersion: 2
guid: d53242bc2649d524aa42f2a84eb077b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

212
ScriptableRenderPipeline/Core/Volume/Volume.cs


using System;
using System.Collections.Generic;
namespace UnityEngine.Experimental.Rendering
{
[ExecuteInEditMode]
public class Volume : MonoBehaviour
{
[Tooltip("A global volume is applied to the whole scene.")]
public bool isGlobal = false;
[Tooltip("Volume priority in the stack. Higher number means higher priority. Negative values are supported.")]
public float priority = 0f;
[Tooltip("Outer distance to start blending from. A value of 0 means no blending and the volume overrides will be applied immediatly upon entry.")]
public float blendDistance = 0f;
[Range(0f, 1f), Tooltip("Total weight of this volume in the scene. 0 means it won't do anything, 1 means full effect.")]
public float weight = 1f;
public List<VolumeComponent> components = new List<VolumeComponent>();
// Editor-only
[NonSerialized]
public bool isDirty;
// Needed for state tracking (see the comments in Update)
int m_PreviousLayer;
float m_PreviousPriority;
void OnEnable()
{
// Make sure every setting is valid. If a profile holds a script that doesn't exist
// anymore, nuke it to keep the volume clean. Note that if you delete a script that is
// currently in use in a volume you'll still get a one-time error in the console, it's
// harmless and happens because Unity does a redraw of the editor (and thus the current
// frame) before the recompilation step.
components.RemoveAll(x => x == null);
m_PreviousLayer = gameObject.layer;
VolumeManager.instance.Register(this, m_PreviousLayer);
}
void OnDisable()
{
VolumeManager.instance.Unregister(this, gameObject.layer);
}
void Reset()
{
isDirty = true;
}
void Update()
{
// Unfortunately we need to track the current layer to update the volume manager in
// real-time as the user could change it at any time in the editor or at runtime.
// Because no event is raised when the layer changes, we have to track it on every
// frame :/
int layer = gameObject.layer;
if (layer != m_PreviousLayer)
{
VolumeManager.instance.UpdateVolumeLayer(this, m_PreviousLayer, layer);
m_PreviousLayer = layer;
}
// Same for priority. We could use a property instead, but it doesn't play nice with the
// serialization system. Using a custom Attribute/PropertyDrawer for a property is
// possible but it doesn't work with Undo/Redo in the editor, which makes it useless for
// our case.
if (priority != m_PreviousPriority)
{
VolumeManager.instance.SetLayerDirty(layer);
m_PreviousPriority = priority;
}
}
public T Add<T>(bool overrides = false)
where T : VolumeComponent
{
if (Has<T>())
throw new InvalidOperationException("Component already exists in the volume");
var component = ScriptableObject.CreateInstance<T>();
component.SetAllOverridesTo(overrides);
isDirty = true;
return component;
}
public void Remove<T>()
where T : VolumeComponent
{
int toRemove = -1;
var type = typeof(T);
for (int i = 0; i < components.Count; i++)
{
if (components[i].GetType() == type)
{
toRemove = i;
break;
}
}
if (toRemove >= 0)
{
components.RemoveAt(toRemove);
isDirty = true;
}
}
public bool Has<T>()
where T : VolumeComponent
{
var type = typeof(T);
foreach (var component in components)
{
if (component.GetType() == type)
return true;
}
return false;
}
public bool TryGet<T>(out T component)
where T : VolumeComponent
{
var type = typeof(T);
component = null;
foreach (var comp in components)
{
if (comp.GetType() == type)
{
component = (T)comp;
return true;
}
}
return false;
}
#if UNITY_EDITOR
// TODO: Look into a better volume previsualization system
List<Collider> m_TempColliders;
void OnDrawGizmos()
{
if (m_TempColliders == null)
m_TempColliders = new List<Collider>();
var colliders = m_TempColliders;
GetComponents(colliders);
if (isGlobal || colliders == null)
return;
var scale = transform.localScale;
var invScale = new Vector3(1f / scale.x, 1f / scale.y, 1f / scale.z);
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, scale);
Gizmos.color = new Color(0f, 1f, 0.1f, 0.6f);
// Draw a separate gizmo for each collider
foreach (var collider in colliders)
{
if (!collider.enabled)
continue;
// We'll just use scaling as an approximation for volume skin. It's far from being
// correct (and is completely wrong in some cases). Ultimately we'd use a distance
// field or at least a tesselate + push modifier on the collider's mesh to get a
// better approximation, but the current Gizmo system is a bit limited and because
// everything is dynamic in Unity and can be changed at anytime, it's hard to keep
// track of changes in an elegant way (which we'd need to implement a nice cache
// system for generated volume meshes).
var type = collider.GetType();
if (type == typeof(BoxCollider))
{
var c = (BoxCollider)collider;
Gizmos.DrawCube(c.center, c.size);
Gizmos.DrawWireCube(c.center, c.size + invScale * blendDistance * 2f);
}
else if (type == typeof(SphereCollider))
{
var c = (SphereCollider)collider;
Gizmos.DrawSphere(c.center, c.radius);
Gizmos.DrawWireSphere(c.center, c.radius + invScale.x * blendDistance);
}
else if (type == typeof(MeshCollider))
{
var c = (MeshCollider)collider;
// Only convex mesh colliders are allowed
if (!c.convex)
c.convex = true;
// Mesh pivot should be centered or this won't work
Gizmos.DrawMesh(c.sharedMesh);
Gizmos.DrawWireMesh(c.sharedMesh, Vector3.zero, Quaternion.identity, Vector3.one + invScale * blendDistance * 2f);
}
// Nothing for capsule (DrawCapsule isn't exposed in Gizmo), terrain, wheel and
// other colliders...
}
colliders.Clear();
}
#endif
}
}
正在加载...
取消
保存