浏览代码

Merge branch 'master' into HDRP-GraphicTests

/namespace
Remy 7 年前
当前提交
76db5308
共有 14 个文件被更改,包括 1261 次插入95 次删除
  1. 43
      ScriptableRenderPipeline/Core/Editor/Volume/VolumeEditor.cs
  2. 216
      ScriptableRenderPipeline/Core/ShaderLibrary/Color.hlsl
  3. 22
      ScriptableRenderPipeline/Core/Volume/Volume.cs
  4. 18
      ScriptableRenderPipeline/Core/Volume/VolumeComponent.cs
  5. 116
      ScriptableRenderPipeline/Core/Volume/VolumeManager.cs
  6. 30
      ScriptableRenderPipeline/Core/Volume/VolumeParameter.cs
  7. 4
      ScriptableRenderPipeline/HDRenderPipeline/Editor/Lighting/HDLightEditor.cs
  8. 2
      ScriptableRenderPipeline/HDRenderPipeline/Lighting/LightLoop/LightLoop.hlsl
  9. 691
      ScriptableRenderPipeline/Core/Editor/FilterWindow.cs
  10. 11
      ScriptableRenderPipeline/Core/Editor/FilterWindow.cs.meta
  11. 137
      ScriptableRenderPipeline/Core/Editor/Volume/VolumeComponentProvider.cs
  12. 11
      ScriptableRenderPipeline/Core/Editor/Volume/VolumeComponentProvider.cs.meta
  13. 44
      ScriptableRenderPipeline/Core/Volume/VolumeStack.cs
  14. 11
      ScriptableRenderPipeline/Core/Volume/VolumeStack.cs.meta

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


actualTarget.isDirty = false;
}
using (var scope = new EditorGUILayout.VerticalScope())
using (var vscope = new EditorGUILayout.VerticalScope())
{
EditorGUILayout.PropertyField(m_IsGlobal);

if (m_Editors.Count > 0)
CoreEditorUtils.DrawSplitter();
else
EditorGUILayout.HelpBox("No override set on this volume.", MessageType.Info);
EditorGUILayout.HelpBox("No override set on this volume. Drop a component here or use the Add button.", MessageType.Info);
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
EditorGUILayout.Space();
//if (GUILayout.Button(CoreEditorUtils.GetContent("Add Component"), GUILayout.Width(230f), GUILayout.Height(24f)))
//{
using (var hscope = new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button(CoreEditorUtils.GetContent("Add component overrides..."), EditorStyles.miniButton))
{
var r = hscope.rect;
var pos = new Vector2(r.x + r.width / 2f, r.yMax + 18f);
FilterWindow.Show(pos, new VolumeComponentProvider(actualTarget, this));
}
}
//}
GUILayout.FlexibleSpace();
}
EditorGUILayout.Space();
if (IsDragValid(scope.rect, e.mousePosition))
if (IsDragValid(vscope.rect, e.mousePosition))
{
DragAndDrop.visualMode = DragAndDropVisualMode.Link;
e.Use();

}
else if (e.type == EventType.DragPerform)
{
if (IsDragValid(scope.rect, e.mousePosition))
if (IsDragValid(vscope.rect, e.mousePosition))
{
DragAndDrop.AcceptDrag();

}
// 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)
void CreateEditor(VolumeComponent settings, SerializedProperty property, int index = -1, bool forceOpen = false)
{
var settingsType = settings.GetType();
Type editorType;

var editor = (VolumeComponentEditor)Activator.CreateInstance(editorType);
editor.Init(settings, this);
editor.baseProperty = property.Copy();
if (forceOpen)
editor.baseProperty.isExpanded = true;
if (index < 0)
m_Editors.Add(editor);

void AddComponent(Type type)
internal void AddComponent(Type type)
{
serializedObject.Update();

effectProp.objectReferenceValue = component;
// Create & store the internal editor object for this effect
CreateEditor(component, effectProp);
CreateEditor(component, effectProp, forceOpen: true);
void RemoveComponent(int id)
internal void RemoveComponent(int id)
{
// Huh. Hack to keep foldout state on the next element...
bool nextFoldoutState = false;

// 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)
internal void ResetComponent(Type type, int id)
{
// Remove from the cached editors list
m_Editors[id].OnDisable();

Undo.DestroyObjectImmediate(prevSettings);
}
void MoveComponent(int id, int offset)
internal void MoveComponent(int id, int offset)
{
// Move components
serializedObject.Update();

216
ScriptableRenderPipeline/Core/ShaderLibrary/Color.hlsl


return dot(linearRgb, float3(0.2126729f, 0.7151522f, 0.0721750f));
}
float Luminance(float4 linearRgba)
{
return Luminance(linearRgba.rgb);
}
// This function take a rgb color (best is to provide color in sRGB space)
// and return a YCoCg color in [0..1] space for 8bit (An offset is apply in the function)
// Ref: http://www.nvidia.com/object/real-time-ycocg-dxt-compression.html

float W = w.x + w.y + w.z + w.w;
// handle the special case where all the weights are zero.
return (W == 0.0) ? a0.y : (w.x * a0.y + w.y* a1.y + w.z* a2.y + w.w * a3.y) / W;
}
// Hue, Saturation, Value
// Ranges:
// Hue [0.0, 1.0]
// Sat [0.0, 1.0]
// Lum [0.0, HALF_MAX]
float3 RgbToHsv(float3 c)
{
const float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
const float e = 1.0e-4;
return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
float3 HsvToRgb(float3 c)
{
const float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * lerp(K.xxx, saturate(p - K.xxx), c.y);
}
// SMPTE ST.2084 (PQ) transfer functions
// 1.0 = 100nits, 100.0 = 10knits
#define DEFAULT_MAX_PQ 100.0
struct ParamsPQ
{
float N, M;
float C1, C2, C3;
};
static const ParamsPQ PQ =
{
2610.0 / 4096.0 / 4.0, // N
2523.0 / 4096.0 * 128.0, // M
3424.0 / 4096.0, // C1
2413.0 / 4096.0 * 32.0, // C2
2392.0 / 4096.0 * 32.0, // C3
};
float3 LinearToPQ(float3 x, float maxPQValue)
{
x = PositivePow(x / maxPQValue, PQ.N);
float3 nd = (PQ.C1 + PQ.C2 * x) / (1.0 + PQ.C3 * x);
return PositivePow(nd, PQ.M);
}
float3 LinearToPQ(float3 x)
{
return LinearToPQ(x, DEFAULT_MAX_PQ);
}
float3 PQToLinear(float3 x, float maxPQValue)
{
x = PositivePow(x, rcp(PQ.M));
float3 nd = max(x - PQ.C1, 0.0) / (PQ.C2 - (PQ.C3 * x));
return PositivePow(nd, rcp(PQ.N)) * maxPQValue;
}
float3 PQToLinear(float3 x)
{
return PQToLinear(x, DEFAULT_MAX_PQ);
}
// Alexa LogC converters (El 1000)
// See http://www.vocas.nl/webfm_send/964
// Max range is ~58.85666
// Set to 1 to use more precise but more expensive log/linear conversions. I haven't found a proper
// use case for the high precision version yet so I'm leaving this to 0.
#define USE_PRECISE_LOGC 0
struct ParamsLogC
{
float cut;
float a, b, c, d, e, f;
};
static const ParamsLogC LogC =
{
0.011361, // cut
5.555556, // a
0.047996, // b
0.244161, // c
0.386036, // d
5.301883, // e
0.092819 // f
};
float LinearToLogC_Precise(half x)
{
float o;
if (x > LogC.cut)
o = LogC.c * log10(LogC.a * x + LogC.b) + LogC.d;
else
o = LogC.e * x + LogC.f;
return o;
}
float3 LinearToLogC(float3 x)
{
#if USE_PRECISE_LOGC
return float3(
LinearToLogC_Precise(x.x),
LinearToLogC_Precise(x.y),
LinearToLogC_Precise(x.z)
);
#else
return LogC.c * log10(LogC.a * x + LogC.b) + LogC.d;
#endif
}
float LogCToLinear_Precise(float x)
{
float o;
if (x > LogC.e * LogC.cut + LogC.f)
o = (pow(10.0, (x - LogC.d) / LogC.c) - LogC.b) / LogC.a;
else
o = (x - LogC.f) / LogC.e;
return o;
}
float3 LogCToLinear(float3 x)
{
#if USE_PRECISE_LOGC
return float3(
LogCToLinear_Precise(x.x),
LogCToLinear_Precise(x.y),
LogCToLinear_Precise(x.z)
);
#else
return (pow(10.0, (x - LogC.d) / LogC.c) - LogC.b) / LogC.a;
#endif
}
//-----------------------------------------------------------------------------
// Utilities
//-----------------------------------------------------------------------------
// Fast reversible tonemapper
// http://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
float3 FastTonemap(float3 c)
{
return c * rcp(Max3(c.r, c.g, c.b) + 1.0);
}
float4 FastTonemap(float4 c)
{
return float4(FastTonemap(c.rgb), c.a);
}
float3 FastTonemap(float3 c, float w)
{
return c * (w * rcp(Max3(c.r, c.g, c.b) + 1.0));
}
float4 FastTonemap(float4 c, float w)
{
return float4(FastTonemap(c.rgb, w), c.a);
}
float3 FastTonemapInvert(float3 c)
{
return c * rcp(1.0 - Max3(c.r, c.g, c.b));
}
float4 FastTonemapInvert(float4 c)
{
return float4(FastTonemapInvert(c.rgb), c.a);
}
// 3D LUT grading
// scaleOffset = (1 / lut_size, lut_size - 1)
float3 ApplyLut3D(TEXTURE3D_ARGS(tex, samplerTex), float3 uvw, float2 scaleOffset)
{
float shift = floor(uvw.z);
uvw.xy = uvw.xy * scaleOffset.y * scaleOffset.xx + scaleOffset.xx * 0.5;
uvw.x += shift * scaleOffset.x;
return SAMPLE_TEXTURE3D(tex, samplerTex, uvw).rgb;
}
// 2D LUT grading
// scaleOffset = (1 / lut_width, 1 / lut_height, lut_height - 1)
float3 ApplyLut2D(TEXTURE2D_ARGS(tex, samplerTex), float3 uvw, float3 scaleOffset)
{
// Strip format where `height = sqrt(width)`
uvw.z *= scaleOffset.z;
float shift = floor(uvw.z);
uvw.xy = uvw.xy * scaleOffset.z * scaleOffset.xy + scaleOffset.xy * 0.5;
uvw.x += shift * scaleOffset.y;
uvw.xyz = lerp(
SAMPLE_TEXTURE2D(tex, samplerTex, uvw.xy).rgb,
SAMPLE_TEXTURE2D(tex, samplerTex, uvw.xy + float2(scaleOffset.y, 0.0)).rgb,
uvw.z - shift
);
return uvw;
}
// Returns the default value for a given position on a 2D strip-format color lookup table
// params = (lut_height, 0.5 / lut_width, 0.5 / lut_height, lut_height / lut_height - 1)
float3 GetLutStripValue(float2 uv, float4 params)
{
uv -= params.yz;
float3 color;
color.r = frac(uv.x * params.x);
color.b = uv.x - color.r / params.x;
color.g = uv.y;
return color * params.w;
}
#endif // UNITY_COLOR_INCLUDED

22
ScriptableRenderPipeline/Core/Volume/Volume.cs


public T Add<T>(bool overrides = false)
where T : VolumeComponent
{
if (Has<T>())
return (T)Add(typeof(T), overrides);
}
public VolumeComponent Add(Type type, bool overrides = false)
{
if (Has(type))
var component = ScriptableObject.CreateInstance<T>();
var component = (VolumeComponent)ScriptableObject.CreateInstance(type);
component.SetAllOverridesTo(overrides);
components.Add(component);
isDirty = true;

public void Remove<T>()
where T : VolumeComponent
{
Remove(typeof(T));
}
public void Remove(Type type)
{
var type = typeof(T);
for (int i = 0; i < components.Count; i++)
{

public bool Has<T>()
where T : VolumeComponent
{
var type = typeof(T);
return Has(typeof(T));
}
public bool Has(Type type)
{
foreach (var component in components)
{
if (component.GetType() == type)

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);
Gizmos.color = new Color(0f, 1f, 0.1f, 0.35f);
// Draw a separate gizmo for each collider
foreach (var collider in colliders)

18
ScriptableRenderPipeline/Core/Volume/VolumeComponent.cs


namespace UnityEngine.Experimental.Rendering
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class VolumeComponentMenu : Attribute
{
public readonly string menu;
// TODO: Add support for component icons
public VolumeComponentMenu(string menu)
{
this.menu = menu;
}
}
[Serializable]
public class VolumeComponent : ScriptableObject
{

internal ReadOnlyCollection<VolumeParameter> parameters { get; private set; }
void OnEnable()
protected virtual void OnEnable()
{
// Automatically grab all fields of type VolumeParameter for this instance
parameters = this.GetType()

.Select(t => (VolumeParameter)t.GetValue(this))
.ToList()
.AsReadOnly();
}
protected virtual void OnDisable()
{
}
public void SetAllOverridesTo(bool state)

116
ScriptableRenderPipeline/Core/Volume/VolumeManager.cs


}
//<<<
// Internal stack
public VolumeStack stack { get; private set; }
// Current list of tracked component types
public IEnumerable<Type> baseComponentTypes { get; private set; }
// Max amount of layers available in Unity
const int k_MaxLayerCount = 32;

// 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;
// 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<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_SortedVolumes = new Dictionary<LayerMask, List<Volume>>();

m_Components = new Dictionary<Type, VolumeComponent>();
}
#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;
stack = CreateStack();
#endif
// 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 = CoreUtils.GetAllAssemblyTypes()
// Grab all the component types we can find
baseComponentTypes = CoreUtils.GetAllAssemblyTypes()
foreach (var type in types)
// Keep an instance of each type to be used in a virtual lowest priority global volume
// so that we have a default state to fallback to when exiting volumes
foreach (var type in baseComponentTypes)
// 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)
m_Components.Add(type, inst);
inst = (VolumeComponent)ScriptableObject.CreateInstance(type);
s_StopReloads = true;
public T GetComponent<T>()
where T : VolumeComponent
{
var comp = GetComponent(typeof(T));
return (T)comp;
}
public VolumeComponent GetComponent(Type type)
public VolumeStack CreateStack()
VolumeComponent comp;
m_Components.TryGetValue(type, out comp);
return comp;
return new VolumeStack(baseComponentTypes);
}
public void Register(Volume volume, int layer)

// Look for existing cached layer masks and add it there if needed
foreach (var kvp in m_SortedVolumes)
{
var mask = kvp.Key;
if ((mask & (1 << layer)) != 0)
if ((kvp.Key & (1 << layer)) != 0)
kvp.Value.Add(volume);
}

foreach (var kvp in m_SortedVolumes)
{
var mask = kvp.Key;
if ((mask & (1 << layer)) == 0)
if ((kvp.Key & (1 << layer)) == 0)
continue;
kvp.Value.Remove(volume);

public bool IsComponentActiveInMask<T>(LayerMask layerMask)
where T : VolumeComponent
{
int mask = layerMask.value;
foreach (var kvp in m_SortedVolumes)
{
if ((kvp.Key & mask) == 0)
continue;
foreach (var volume in kvp.Value)
{
T component;
if (volume.TryGet(out component) && component.active)
return true;
}
}
return false;
}
internal void SetLayerDirty(int layer)
{
Assert.IsTrue(layer >= 0 && layer <= k_MaxLayerCount, "Invalid layer bit");

}
// Go through all listed components and lerp overriden values in the global state
void OverrideData(List<VolumeComponent> components, float interpFactor)
void OverrideData(VolumeStack stack, List<VolumeComponent> components, float interpFactor)
{
foreach (var component in components)
{

var target = GetComponent(component.GetType());
var target = stack.GetComponent(component.GetType());
int count = component.parameters.Count;
for (int i = 0; i < count; i++)

}
// Faster version of OverrideData to force replace values in the global state
void ReplaceData(List<VolumeComponent> components)
void ReplaceData(VolumeStack stack, List<VolumeComponent> components)
var target = GetComponent(component.GetType());
var target = stack.GetComponent(component.GetType());
int count = component.parameters.Count;
for (int i = 0; i < count; i++)

// in the update loop before rendering
public void Update(Transform trigger, LayerMask layerMask)
{
Update(stack, trigger, layerMask);
}
// Update a specific stack - can be used to manage your own stack and store it for later use
public void Update(VolumeStack stack, Transform trigger, LayerMask layerMask)
{
ReplaceData(m_ComponentsDefaultState);
ReplaceData(stack, m_ComponentsDefaultState);
bool onlyGlobal = trigger == null;
var triggerPos = onlyGlobal ? Vector3.zero : trigger.position;

// Global volumes always have influence
if (volume.isGlobal)
{
OverrideData(volume.components, Mathf.Clamp01(volume.weight));
OverrideData(stack, volume.components, Mathf.Clamp01(volume.weight));
continue;
}

interpFactor = 1f - (closestDistanceSqr / blendDistSqr);
// No need to clamp01 the interpolation factor as it'll always be in [0;1[ range
OverrideData(volume.components, interpFactor * Mathf.Clamp01(volume.weight));
OverrideData(stack, volume.components, interpFactor * Mathf.Clamp01(volume.weight));
}
}

30
ScriptableRenderPipeline/Core/Volume/VolumeParameter.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;

}
[Serializable, DebuggerDisplay(k_DebuggerDisplay)]
public class VolumeParameter<T> : VolumeParameter
public class VolumeParameter<T> : VolumeParameter, IEquatable<VolumeParameter<T>>
{
[SerializeField]
protected T m_Value;

public static bool operator ==(VolumeParameter<T> lhs, T rhs)
{
return lhs.value != null && lhs.value.Equals(rhs);
return lhs != null && lhs.value != null && lhs.value.Equals(rhs);
}
public bool Equals(VolumeParameter<T> other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return EqualityComparer<T>.Default.Equals(m_Value, other.m_Value);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != GetType())
return false;
return Equals((VolumeParameter<T>)obj);
}
//

4
ScriptableRenderPipeline/HDRenderPipeline/Editor/Lighting/HDLightEditor.cs


{
m_LightShape = (LightShape)EditorGUILayout.Popup(s_Styles.shape, (int)m_LightShape, s_Styles.shapeNames);
if (m_LightShape != LightShape.Directional)
settings.DrawRange(false);
// LightShape is HD specific, it need to drive LightType from the original LightType
// when it make sense, so the GI is still in sync with the light shape
switch (m_LightShape)

settings.DrawColor();
settings.DrawIntensity();
settings.DrawBounceIntensity();
settings.DrawRange(false);
settings.DrawLightmapping();
// No cookie with area light (maybe in future textured area light ?)

2
ScriptableRenderPipeline/HDRenderPipeline/Lighting/LightLoop/LightLoop.hlsl


lightData = FetchLight(lightStart, min(++i, last));
}
while (i <= last && lightData.lightType == GPULIGHTTYPE_RECTANGLE)
while (i <= last) // GPULIGHTTYPE_RECTANGLE
{
lightData.lightType = GPULIGHTTYPE_RECTANGLE; // Enforce constant propagation

691
ScriptableRenderPipeline/Core/Editor/FilterWindow.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
namespace UnityEditor.Experimental.Rendering
{
[InitializeOnLoad]
public class FilterWindow : EditorWindow
{
public interface IProvider
{
Vector2 position { get; set; }
void CreateComponentTree(List<Element> tree);
bool GoToChild(Element element, bool addIfComponent);
}
public static readonly float DefaultWidth = 250f;
public static readonly float DefaultHeight = 300f;
#region BaseElements
public class Element : IComparable
{
public int level;
public GUIContent content;
public string name
{
get { return content.text; }
}
public int CompareTo(object o)
{
return name.CompareTo((o as Element).name);
}
}
[Serializable]
public class GroupElement : Element
{
public Vector2 scroll;
public int selectedIndex;
public bool WantsFocus { get; protected set; }
public virtual bool ShouldDisable
{
get { return false; }
}
public GroupElement(int level, string name)
{
this.level = level;
content = new GUIContent(name);
}
public virtual bool HandleKeyboard(Event evt, FilterWindow window, Action goToParent)
{
return false;
}
public virtual bool OnGUI(FilterWindow sFilterWindow)
{
return false;
}
}
#endregion
// Styles
class Styles
{
public GUIStyle header = (GUIStyle)typeof(EditorStyles).GetProperty("inspectorBig", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null);
public GUIStyle componentButton = new GUIStyle("PR Label");
public GUIStyle groupButton;
public GUIStyle background = "grey_border";
public GUIStyle rightArrow = "AC RightArrow";
public GUIStyle leftArrow = "AC LeftArrow";
public Styles()
{
header.font = EditorStyles.boldLabel.font;
componentButton.alignment = TextAnchor.MiddleLeft;
componentButton.fixedHeight = 20;
componentButton.imagePosition = ImagePosition.ImageAbove;
groupButton = new GUIStyle(componentButton);
}
}
const int k_HeaderHeight = 30;
const int k_WindowHeight = 400 - 80;
const int k_HelpHeight = 80 * 0;
const string k_ComponentSearch = "NodeSearchString";
static Styles s_Styles;
static FilterWindow s_FilterWindow;
static long s_LastClosedTime;
static bool s_DirtyList;
IProvider m_Provider;
Element[] m_Tree;
Element[] m_SearchResultTree;
List<GroupElement> m_Stack = new List<GroupElement>();
float m_Anim = 1;
int m_AnimTarget = 1;
long m_LastTime;
bool m_ScrollToSelected;
string m_DelayedSearch;
string m_Search = "";
bool m_HasSearch { get { return !string.IsNullOrEmpty(m_Search); } }
GroupElement m_ActiveParent { get { return m_Stack[m_Stack.Count - 2 + m_AnimTarget]; } }
Element[] m_ActiveTree { get { return m_HasSearch ? m_SearchResultTree : m_Tree; } }
Element m_ActiveElement
{
get
{
if (m_ActiveTree == null)
return null;
var children = GetChildren(m_ActiveTree, m_ActiveParent);
return children.Count == 0
? null
: children[m_ActiveParent.selectedIndex];
}
}
bool m_IsAnimating { get { return !Mathf.Approximately(m_Anim, m_AnimTarget); } }
static FilterWindow()
{
s_DirtyList = true;
}
void OnEnable()
{
s_FilterWindow = this;
m_Search = "";
}
void OnDisable()
{
s_LastClosedTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
s_FilterWindow = null;
}
internal static bool ValidateAddComponentMenuItem()
{
return true;
}
internal static bool Show(Vector2 position, IProvider provider)
{
// If the window is already open, close it instead.
var wins = Resources.FindObjectsOfTypeAll(typeof(FilterWindow));
if (wins.Length > 0)
{
try
{
((EditorWindow)wins[0]).Close();
return false;
}
catch (Exception)
{
s_FilterWindow = null;
}
}
// We could not use realtimeSinceStartUp since it is set to 0 when entering/exitting
// playmode, we assume an increasing time when comparing time.
long nowMilliSeconds = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
bool justClosed = nowMilliSeconds < s_LastClosedTime + 50;
if (!justClosed)
{
Event.current.Use();
if (s_FilterWindow == null)
{
s_FilterWindow = CreateInstance<FilterWindow>();
s_FilterWindow.hideFlags = HideFlags.HideAndDontSave;
}
s_FilterWindow.Init(position, provider);
return true;
}
return false;
}
static object Invoke(Type t, object inst, string method, params object[] args)
{
var flags = (inst == null ? BindingFlags.Static : BindingFlags.Instance) | BindingFlags.NonPublic;
var mi = t.GetMethod(method, flags);
return mi.Invoke(inst, args);
}
void Init(Vector2 position, IProvider provider)
{
m_Provider = provider;
// Has to be done before calling Show / ShowWithMode
m_Provider.position = position;
position = GUIUtility.GUIToScreenPoint(position);
var buttonRect = new Rect(position.x - DefaultWidth / 2, position.y - 16, DefaultWidth, 1);
CreateComponentTree();
ShowAsDropDown(buttonRect, new Vector2(buttonRect.width, k_WindowHeight));
Focus();
wantsMouseMove = true;
}
void CreateComponentTree()
{
var tree = new List<Element>();
m_Provider.CreateComponentTree(tree);
m_Tree = tree.ToArray();
// Rebuild stack
if (m_Stack.Count == 0)
{
m_Stack.Add(m_Tree[0] as GroupElement);
}
else
{
// The root is always the match for level 0
var match = m_Tree[0] as GroupElement;
int level = 0;
while (true)
{
// Assign the match for the current level
var oldElement = m_Stack[level];
m_Stack[level] = match;
m_Stack[level].selectedIndex = oldElement.selectedIndex;
m_Stack[level].scroll = oldElement.scroll;
// See if we reached last element of stack
level++;
if (level == m_Stack.Count)
break;
// Try to find a child of the same name as we had before
var children = GetChildren(m_ActiveTree, match);
var childMatch = children.FirstOrDefault(c => c.name == m_Stack[level].name);
if (childMatch is GroupElement)
{
match = childMatch as GroupElement;
}
else
{
// If we couldn't find the child, remove all further elements from the stack
while (m_Stack.Count > level)
m_Stack.RemoveAt(level);
}
}
}
s_DirtyList = false;
RebuildSearch();
}
internal void OnGUI()
{
if (s_Styles == null)
s_Styles = new Styles();
GUI.Label(new Rect(0, 0, position.width, position.height), GUIContent.none, s_Styles.background);
if (s_DirtyList)
CreateComponentTree();
// Keyboard
HandleKeyboard();
GUILayout.Space(7);
// Search
if (!m_ActiveParent.WantsFocus)
{
EditorGUI.FocusTextInControl("ComponentSearch");
Focus();
}
var searchRect = GUILayoutUtility.GetRect(10, 20);
searchRect.x += 8;
searchRect.width -= 16;
GUI.SetNextControlName("ComponentSearch");
using (new EditorGUI.DisabledScope(m_ActiveParent.ShouldDisable))
{
string newSearch = (string)Invoke(typeof(EditorGUI), null, "SearchField", searchRect, m_DelayedSearch ?? m_Search);
if (newSearch != m_Search || m_DelayedSearch != null)
{
if (!m_IsAnimating)
{
m_Search = m_DelayedSearch ?? newSearch;
EditorPrefs.SetString(k_ComponentSearch, m_Search);
RebuildSearch();
m_DelayedSearch = null;
}
else
{
m_DelayedSearch = newSearch;
}
}
}
// Show lists
ListGUI(m_ActiveTree, m_Anim, GetElementRelative(0), GetElementRelative(-1));
if (m_Anim < 1)
ListGUI(m_ActiveTree, m_Anim + 1, GetElementRelative(-1), GetElementRelative(-2));
// Animate
if (m_IsAnimating && Event.current.type == EventType.Repaint)
{
long now = DateTime.Now.Ticks;
float deltaTime = (now - m_LastTime) / (float)TimeSpan.TicksPerSecond;
m_LastTime = now;
m_Anim = Mathf.MoveTowards(m_Anim, m_AnimTarget, deltaTime * 4);
if (m_AnimTarget == 0 && Mathf.Approximately(m_Anim, 0))
{
m_Anim = 1;
m_AnimTarget = 1;
m_Stack.RemoveAt(m_Stack.Count - 1);
}
Repaint();
}
}
void HandleKeyboard()
{
var evt = Event.current;
if (evt.type == EventType.KeyDown)
{
// Special handling when in new script panel
if (!m_ActiveParent.HandleKeyboard(evt, s_FilterWindow, GoToParent))
{
// Always do these
if (evt.keyCode == KeyCode.DownArrow)
{
m_ActiveParent.selectedIndex++;
m_ActiveParent.selectedIndex = Mathf.Min(m_ActiveParent.selectedIndex, GetChildren(m_ActiveTree, m_ActiveParent).Count - 1);
m_ScrollToSelected = true;
evt.Use();
}
if (evt.keyCode == KeyCode.UpArrow)
{
m_ActiveParent.selectedIndex--;
m_ActiveParent.selectedIndex = Mathf.Max(m_ActiveParent.selectedIndex, 0);
m_ScrollToSelected = true;
evt.Use();
}
if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
{
GoToChild(m_ActiveElement, true);
evt.Use();
}
// Do these if we're not in search mode
if (!m_HasSearch)
{
if (evt.keyCode == KeyCode.LeftArrow || evt.keyCode == KeyCode.Backspace)
{
GoToParent();
evt.Use();
}
if (evt.keyCode == KeyCode.RightArrow)
{
GoToChild(m_ActiveElement, false);
evt.Use();
}
if (evt.keyCode == KeyCode.Escape)
{
Close();
evt.Use();
}
}
}
}
}
const string k_SearchHeader = "Search";
void RebuildSearch()
{
if (!m_HasSearch)
{
m_SearchResultTree = null;
if (m_Stack[m_Stack.Count - 1].name == k_SearchHeader)
{
m_Stack.Clear();
m_Stack.Add(m_Tree[0] as GroupElement);
}
m_AnimTarget = 1;
m_LastTime = DateTime.Now.Ticks;
return;
}
// Support multiple search words separated by spaces.
var searchWords = m_Search.ToLower().Split(' ');
// We keep two lists. Matches that matches the start of an item always get first priority.
var matchesStart = new List<Element>();
var matchesWithin = new List<Element>();
foreach (var e in m_Tree)
{
if (e is GroupElement)
continue;
string name = e.name.ToLower().Replace(" ", "");
bool didMatchAll = true;
bool didMatchStart = false;
// See if we match ALL the seaarch words.
for (int w = 0; w < searchWords.Length; w++)
{
string search = searchWords[w];
if (name.Contains(search))
{
// If the start of the item matches the first search word, make a note of that.
if (w == 0 && name.StartsWith(search))
didMatchStart = true;
}
else
{
// As soon as any word is not matched, we disregard this item.
didMatchAll = false;
break;
}
}
// We always need to match all search words.
// If we ALSO matched the start, this item gets priority.
if (didMatchAll)
{
if (didMatchStart)
matchesStart.Add(e);
else
matchesWithin.Add(e);
}
}
matchesStart.Sort();
matchesWithin.Sort();
// Create search tree
var tree = new List<Element>();
// Add parent
tree.Add(new GroupElement(0, k_SearchHeader));
// Add search results
tree.AddRange(matchesStart);
tree.AddRange(matchesWithin);
// Create search result tree
m_SearchResultTree = tree.ToArray();
m_Stack.Clear();
m_Stack.Add(m_SearchResultTree[0] as GroupElement);
// Always select the first search result when search is changed (e.g. a character was typed in or deleted),
// because it's usually the best match.
if (GetChildren(m_ActiveTree, m_ActiveParent).Count >= 1)
m_ActiveParent.selectedIndex = 0;
else
m_ActiveParent.selectedIndex = -1;
}
GroupElement GetElementRelative(int rel)
{
int i = m_Stack.Count + rel - 1;
if (i < 0)
return null;
return m_Stack[i];
}
void GoToParent()
{
if (m_Stack.Count > 1)
{
m_AnimTarget = 0;
m_LastTime = DateTime.Now.Ticks;
}
}
void ListGUI(Element[] tree, float anim, GroupElement parent, GroupElement grandParent)
{
// Smooth the fractional part of the anim value
anim = Mathf.Floor(anim) + Mathf.SmoothStep(0, 1, Mathf.Repeat(anim, 1));
// Calculate rect for animated area
var animRect = position;
animRect.x = position.width * (1 - anim) + 1;
animRect.y = k_HeaderHeight;
animRect.height -= k_HeaderHeight + k_HelpHeight;
animRect.width -= 2;
// Start of animated area (the part that moves left and right)
GUILayout.BeginArea(animRect);
// Header
var headerRect = GUILayoutUtility.GetRect(10, 25);
string name = parent.name;
GUI.Label(headerRect, name, s_Styles.header);
// Back button
if (grandParent != null)
{
var arrowRect = new Rect(headerRect.x + 4, headerRect.y + 7, 13, 13);
var e = Event.current;
if (e.type == EventType.Repaint)
s_Styles.leftArrow.Draw(arrowRect, false, false, false, false);
if (e.type == EventType.MouseDown && headerRect.Contains(e.mousePosition))
{
GoToParent();
e.Use();
}
}
if (!parent.OnGUI(s_FilterWindow))
ListGUI(tree, parent);
GUILayout.EndArea();
}
void GoToChild(Element e, bool addIfComponent)
{
if (m_Provider.GoToChild(e, addIfComponent))
{
Close();
}
else if (!m_HasSearch)
{
m_LastTime = DateTime.Now.Ticks;
if (m_AnimTarget == 0)
{
m_AnimTarget = 1;
}
else if (Mathf.Approximately(m_Anim, 1f))
{
m_Anim = 0;
m_Stack.Add(e as GroupElement);
}
}
}
void ListGUI(Element[] tree, GroupElement parent)
{
// Start of scroll view list
parent.scroll = GUILayout.BeginScrollView(parent.scroll);
EditorGUIUtility.SetIconSize(new Vector2(16, 16));
var children = GetChildren(tree, parent);
var selectedRect = new Rect();
var evt = Event.current;
// Iterate through the children
for (int i = 0; i < children.Count; i++)
{
var e = children[i];
var r = GUILayoutUtility.GetRect(16, 20, GUILayout.ExpandWidth(true));
// Select the element the mouse cursor is over.
// Only do it on mouse move - keyboard controls are allowed to overwrite this until the next time the mouse moves.
if (evt.type == EventType.MouseMove || evt.type == EventType.MouseDown)
{
if (parent.selectedIndex != i && r.Contains(evt.mousePosition))
{
parent.selectedIndex = i;
Repaint();
}
}
bool selected = false;
// Handle selected item
if (i == parent.selectedIndex)
{
selected = true;
selectedRect = r;
}
// Draw element
if (evt.type == EventType.Repaint)
{
var labelStyle = (e is GroupElement) ? s_Styles.groupButton : s_Styles.componentButton;
labelStyle.Draw(r, e.content, false, false, selected, selected);
if (e is GroupElement)
{
var arrowRect = new Rect(r.x + r.width - 13, r.y + 4, 13, 13);
s_Styles.rightArrow.Draw(arrowRect, false, false, false, false);
}
}
if (evt.type == EventType.MouseDown && r.Contains(evt.mousePosition))
{
evt.Use();
parent.selectedIndex = i;
GoToChild(e, true);
}
}
EditorGUIUtility.SetIconSize(Vector2.zero);
GUILayout.EndScrollView();
// Scroll to show selected
if (m_ScrollToSelected && evt.type == EventType.Repaint)
{
m_ScrollToSelected = false;
var scrollRect = GUILayoutUtility.GetLastRect();
if (selectedRect.yMax - scrollRect.height > parent.scroll.y)
{
parent.scroll.y = selectedRect.yMax - scrollRect.height;
Repaint();
}
if (selectedRect.y < parent.scroll.y)
{
parent.scroll.y = selectedRect.y;
Repaint();
}
}
}
List<Element> GetChildren(Element[] tree, Element parent)
{
var children = new List<Element>();
int level = -1;
int i;
for (i = 0; i < tree.Length; i++)
{
if (tree[i] == parent)
{
level = parent.level + 1;
i++;
break;
}
}
if (level == -1)
return children;
for (; i < tree.Length; i++)
{
var e = tree[i];
if (e.level < level)
break;
if (e.level > level && !m_HasSearch)
continue;
children.Add(e);
}
return children;
}
}
}

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


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

137
ScriptableRenderPipeline/Core/Editor/Volume/VolumeComponentProvider.cs


using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
namespace UnityEditor.Experimental.Rendering
{
using IProvider = FilterWindow.IProvider;
using Element = FilterWindow.Element;
using GroupElement = FilterWindow.GroupElement;
class VolumeComponentProvider : IProvider
{
class VolumeComponentElement : Element
{
public Type type;
public VolumeComponentElement(int level, string label, Type type)
{
this.level = level;
this.type = type;
// TODO: Add support for custom icons
content = new GUIContent(label);
}
}
class PathNode : IComparable<PathNode>
{
public List<PathNode> nodes = new List<PathNode>();
public string name;
public Type type;
public int CompareTo(PathNode other)
{
return name.CompareTo(other.name);
}
}
public Vector2 position { get; set; }
Volume m_Target;
VolumeEditor m_TargetEditor;
public VolumeComponentProvider(Volume target, VolumeEditor targetEditor)
{
m_Target = target;
m_TargetEditor = targetEditor;
}
public void CreateComponentTree(List<Element> tree)
{
tree.Add(new GroupElement(0, "Volume Components"));
var attrType = typeof(VolumeComponentMenu);
var types = VolumeManager.instance.baseComponentTypes;
var rootNode = new PathNode();
foreach (var t in types)
{
// Skip components that have already been added to the volume
if (m_Target.Has(t))
continue;
string path = string.Empty;
// Look for a VolumeComponentMenu attribute
var attrs = t.GetCustomAttributes(attrType, false);
if (attrs.Length > 0)
{
var attr = attrs[0] as VolumeComponentMenu;
if (attr != null)
path = attr.menu;
}
// If no attribute or in case something went wrong when grabbing it, fallback to a
// beautified class name
if (string.IsNullOrEmpty(path))
path = ObjectNames.NicifyVariableName(t.Name);
// Prep the categories & types tree
AddNode(rootNode, path, t);
}
// Recursively add all elements to the tree
Traverse(rootNode, 1, tree);
}
public bool GoToChild(Element element, bool addIfComponent)
{
if (element is VolumeComponentElement)
{
var e = (VolumeComponentElement)element;
m_TargetEditor.AddComponent(e.type);
return true;
}
return false;
}
void AddNode(PathNode root, string path, Type type)
{
var current = root;
var parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
var child = current.nodes.Find(x => x.name == part);
if (child == null)
{
child = new PathNode { name = part, type = type };
current.nodes.Add(child);
}
current = child;
}
}
void Traverse(PathNode node, int depth, List<Element> tree)
{
node.nodes.Sort();
foreach (var n in node.nodes)
{
if (n.nodes.Count > 0) // Group
{
tree.Add(new GroupElement(depth, n.name));
Traverse(n, depth + 1, tree);
}
else // Element
{
tree.Add(new VolumeComponentElement(depth, n.name, n.type));
}
}
}
}
}

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


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

44
ScriptableRenderPipeline/Core/Volume/VolumeStack.cs


using System;
using System.Collections.Generic;
namespace UnityEngine.Experimental.Rendering
{
public sealed class VolumeStack : IDisposable
{
// Holds the state of _all_ component types you can possibly add on volumes
public readonly Dictionary<Type, VolumeComponent> components;
internal VolumeStack(IEnumerable<Type> baseTypes)
{
components = new Dictionary<Type, VolumeComponent>();
foreach (var type in baseTypes)
{
var inst = (VolumeComponent)ScriptableObject.CreateInstance(type);
components.Add(type, inst);
}
}
public T GetComponent<T>()
where T : VolumeComponent
{
var comp = GetComponent(typeof(T));
return (T)comp;
}
public VolumeComponent GetComponent(Type type)
{
VolumeComponent comp;
components.TryGetValue(type, out comp);
return comp;
}
public void Dispose()
{
foreach (var component in components)
CoreUtils.Destroy(component.Value);
components.Clear();
}
}
}

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


fileFormatVersion: 2
guid: 7af226ce5122db0409963decbabf9d9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存