YiHe_AllVersion/YHWeb/Assets/Plugins/HighlightingSystem/Scripts/Internal/HighlighterInternal.cs

505 lines
13 KiB
C#

using UnityEngine;
using UnityEngine.Rendering;
using System.Collections;
using System.Collections.Generic;
namespace HighlightingSystem
{
[DisallowMultipleComponent]
public partial class Highlighter : MonoBehaviour
{
private enum Mode
{
None,
Highlighter,
Occluder,
HighlighterSeeThrough,
OccluderSeeThrough,
}
// Constants (don't touch this!)
#region Constants
// 2 * PI constant for sine flashing
private const float doublePI = 2f * Mathf.PI;
// Occlusion color
private readonly Color occluderColor = new Color(0f, 0f, 0f, 0f);
// ZTest LEqual
private const int zTestLessEqual = (int)CompareFunction.LessEqual;
// ZTest Always
private const int zTestAlways = (int)CompareFunction.Always;
// Highlighting modes rendered in that order
static private readonly Mode[] renderingOrder = new Mode[] { Mode.Highlighter, Mode.Occluder, Mode.HighlighterSeeThrough, Mode.OccluderSeeThrough };
#endregion
#region Static Fields
// List of all highlighters in scene
static private HashSet<Highlighter> highlighters = new HashSet<Highlighter>();
// Global highlighting shaders ZWrite property
// Set to unusual default value to force initialization on start
static private int zWrite = -1;
// Global highlighting shaders Offset Factor property
// Set to unusual default value to force initialization on start
static private float offsetFactor = float.NaN;
// Global highlighting shaders Offset Units property
// Set to unusual default value to force initialization on start
static private float offsetUnits = float.NaN;
#endregion
#region Public Fields
/// <summary>
/// Controls see-through mode for highlighters or occluders. When set to true - highlighter in this mode will not be occluded by anything (except for see-through occluders). Occluder in this mode will overlap any highlighting.
/// </summary>
[HideInInspector]
public bool seeThrough;
/// <summary>
/// Controls occluder mode. Note that non-see-through highlighting occluders will be enabled only when frame depth buffer is not available!
/// </summary>
[HideInInspector]
public bool occluder;
#endregion
#region Private Fields
// Cached transform component reference
private Transform tr;
// Cached Renderers
private List<HighlighterRenderer> highlightableRenderers = new List<HighlighterRenderer>();
// Renderers reinitialization is required flag
private bool renderersDirty;
// Static list to prevent unnecessary memory allocations when grabbing renderer components
static private List<Component> sComponents = new List<Component>(4);
// Highlighting mode
private Mode mode;
// Current ZTest value
private bool zTest;
// Current Stencil Ref value
private bool stencilRef;
// One-frame highlighting flag
private int _once = -1;
private bool once
{
get { return _once == Time.frameCount; }
set { _once = value ? Time.frameCount : -1; }
}
// Flashing enabled flag
private bool flashing;
// Current highlighting color
private Color currentColor;
// Current transition value
private float transitionValue;
// Current Transition target
private float transitionTarget;
// Transition duration
private float transitionTime;
// One-frame highlighting color
private Color onceColor;
// Flashing frequency (times per second)
private float flashingFreq;
// Flashing from color
private Color flashingColorMin;
// Flashing to color
private Color flashingColorMax;
// Constant highlighting color
private Color constantColor;
// Opaque shader cached reference
static private Shader _opaqueShader;
static public Shader opaqueShader
{
get
{
if (_opaqueShader == null)
{
_opaqueShader = Shader.Find("Hidden/Highlighted/Opaque");
}
return _opaqueShader;
}
}
// Transparent shader cached reference
static private Shader _transparentShader;
static public Shader transparentShader
{
get
{
if (_transparentShader == null)
{
_transparentShader = Shader.Find("Hidden/Highlighted/Transparent");
}
return _transparentShader;
}
}
// Shared (for this component) replacement material for opaque geometry highlighting
private Material _opaqueMaterial;
private Material opaqueMaterial
{
get
{
if (_opaqueMaterial == null)
{
_opaqueMaterial = new Material(opaqueShader);
// Make sure that ShaderPropertyIDs is initialized
ShaderPropertyID.Initialize();
// Make sure that shader will have proper default values
_opaqueMaterial.SetInt(ShaderPropertyID._ZTest, GetZTest(zTest));
_opaqueMaterial.SetInt(ShaderPropertyID._StencilRef, GetStencilRef(stencilRef));
}
return _opaqueMaterial;
}
}
#endregion
#region MonoBehaviour
//
private void Awake()
{
ShaderPropertyID.Initialize();
tr = GetComponent<Transform>();
renderersDirty = true;
seeThrough = zTest = true;
mode = Mode.None;
stencilRef = true;
// Initial highlighting state
once = false;
flashing = false;
occluder = false;
transitionValue = transitionTarget = 0f;
onceColor = Color.red;
flashingFreq = 2f;
flashingColorMin = new Color(0f, 1f, 1f, 0f);
flashingColorMax = new Color(0f, 1f, 1f, 1f);
constantColor = Color.yellow;
}
//
private void OnEnable()
{
highlighters.Add(this);
}
//
private void OnDisable()
{
highlighters.Remove(this);
ClearRenderers();
// Reset highlighting parameters to default values
renderersDirty = true;
once = false;
flashing = false;
transitionValue = transitionTarget = 0f;
/*
// Reset custom parameters of the highlighting
occluder = false;
seeThrough = false;
onceColor = Color.red;
flashingFreq = 2f;
flashingColorMin = new Color(0f, 1f, 1f, 0f);
flashingColorMax = new Color(0f, 1f, 1f, 1f);
constantColor = Color.yellow;
transitionTime = 0f;
*/
}
//
private void Update()
{
UpdateTransition();
}
#endregion
#region Private Methods
// Clear cached renderers
private void ClearRenderers()
{
for (int i = highlightableRenderers.Count - 1; i >= 0; i--)
{
HighlighterRenderer renderer = highlightableRenderers[i];
renderer.SetState(false);
}
highlightableRenderers.Clear();
}
// This method defines the way in which renderers are initialized
private void UpdateRenderers()
{
if (renderersDirty)
{
ClearRenderers();
// Find all renderers which should be controlled with this Highlighter component
List<Renderer> renderers = new List<Renderer>();
GrabRenderers(tr, renderers);
// Cache found renderers
for (int i = 0, imax = renderers.Count; i < imax; i++)
{
GameObject rg = renderers[i].gameObject;
HighlighterRenderer renderer = rg.GetComponent<HighlighterRenderer>();
if (renderer == null) { renderer = rg.AddComponent<HighlighterRenderer>(); }
renderer.SetState(true);
renderer.Initialize(opaqueMaterial, transparentShader);
highlightableRenderers.Add(renderer);
}
renderersDirty = false;
}
}
// Recursively follows hierarchy of objects from t, searches for Renderers and adds them to the list.
// Breaks if HighlighterBlocker or another Highlighter component found
private void GrabRenderers(Transform t, List<Renderer> renderers)
{
GameObject g = t.gameObject;
// Find all Renderers of all types on current GameObject g and add them to the renderers list
for (int i = 0, imax = types.Count; i < imax; i++)
{
g.GetComponents(types[i], sComponents);
for (int j = 0, jmax = sComponents.Count; j < jmax; j++)
{
renderers.Add(sComponents[j] as Renderer);
}
}
sComponents.Clear();
// Return if transform t doesn't have any children
int childCount = t.childCount;
if (childCount == 0) { return; }
// Recursively cache renderers on all child GameObjects
for (int i = 0; i < childCount; i++)
{
Transform childTransform = t.GetChild(i);
// Do not cache Renderers of this childTransform in case it has it's own Highlighter component
Highlighter h = childTransform.GetComponent<Highlighter>();
if (h != null) { continue; }
// Do not cache Renderers of this childTransform in case HighlighterBlocker found
HighlighterBlocker hb = childTransform.GetComponent<HighlighterBlocker>();
if (hb != null) { continue; }
GrabRenderers(childTransform, renderers);
}
}
// Sets ZTest and Stencil Ref parameters on all materials of all renderers of this object
private void UpdateShaderParams(bool zt, bool sr)
{
// ZTest
if (zTest != zt)
{
zTest = zt;
int ztInt = GetZTest(zTest);
opaqueMaterial.SetInt(ShaderPropertyID._ZTest, ztInt);
for (int i = 0; i < highlightableRenderers.Count; i++)
{
highlightableRenderers[i].SetZTestForTransparent(ztInt);
}
}
// Stencil Ref
if (stencilRef != sr)
{
stencilRef = sr;
int srInt = GetStencilRef(stencilRef);
opaqueMaterial.SetInt(ShaderPropertyID._StencilRef, srInt);
for (int i = 0; i < highlightableRenderers.Count; i++)
{
highlightableRenderers[i].SetStencilRefForTransparent(srInt);
}
}
}
// Updates highlighting color
private void UpdateColors()
{
if (once)
{
currentColor = onceColor;
}
else if (flashing)
{
// Flashing frequency is not affected by Time.timeScale
currentColor = Color.Lerp(flashingColorMin, flashingColorMax, 0.5f * Mathf.Sin(Time.realtimeSinceStartup * flashingFreq * doublePI) + 0.5f);
}
else if (transitionValue > 0f)
{
currentColor = constantColor;
currentColor.a *= transitionValue;
}
else if (occluder)
{
currentColor = occluderColor;
}
else
{
return;
}
// Apply color
opaqueMaterial.SetColor(ShaderPropertyID._Color, currentColor);
for (int i = 0; i < highlightableRenderers.Count; i++)
{
highlightableRenderers[i].SetColorForTransparent(currentColor);
}
}
// Update transition value
private void UpdateTransition()
{
if (transitionValue != transitionTarget)
{
if (transitionTime <= 0f)
{
transitionValue = transitionTarget;
}
else
{
float dir = (transitionTarget > 0f ? 1f : -1f);
transitionValue = Mathf.Clamp01(transitionValue + (dir * Time.unscaledDeltaTime) / transitionTime);
}
}
}
//
private void FillBufferInternal(CommandBuffer buffer, Mode m, bool depthAvailable)
{
UpdateRenderers();
bool isHighlighter = once || flashing || (transitionValue > 0f);
bool isOccluder = occluder && (seeThrough || !depthAvailable);
// Update mode
mode = Mode.None;
if (isHighlighter)
{
mode = seeThrough ? Mode.HighlighterSeeThrough : Mode.Highlighter;
}
else if (isOccluder)
{
mode = seeThrough ? Mode.OccluderSeeThrough : Mode.Occluder;
}
if (mode == Mode.None || mode != m) { return; }
if (isHighlighter)
{
// ZTest = (seeThrough ? Always : LEqual), StencilRef = 1
UpdateShaderParams(seeThrough, true);
}
else if (isOccluder)
{
// ZTest = LEqual, StencilRef = seeThrough ? 1 : 0
UpdateShaderParams(false, seeThrough);
}
UpdateColors();
// Fill CommandBuffer with this highlighter rendering commands
for (int i = highlightableRenderers.Count - 1; i >= 0; i--)
{
// To avoid null-reference exceptions when cached renderer has been removed but ReinitMaterials wasn't been called
HighlighterRenderer renderer = highlightableRenderers[i];
if (renderer == null)
{
highlightableRenderers.RemoveAt(i);
}
// Try to fill buffer
else if (!renderer.FillBuffer(buffer))
{
highlightableRenderers.RemoveAt(i);
renderer.SetState(false);
}
}
}
#endregion
#region Static Methods
// Fill CommandBuffer with highlighters rendering commands
static public void FillBuffer(CommandBuffer buffer, bool depthAvailable)
{
for (int i = 0; i < renderingOrder.Length; i++)
{
Mode mode = renderingOrder[i];
var e = highlighters.GetEnumerator();
while (e.MoveNext())
{
Highlighter highlighter = e.Current;
highlighter.FillBufferInternal(buffer, mode, depthAvailable);
}
}
}
// Returns integer value for ZTest, which can be passed directly to the shaders (true = Always, false = LEqual)
static private int GetZTest(bool enabled)
{
return enabled ? zTestAlways : zTestLessEqual;
}
// Returns integer value for StencilRef, which can be passed directly to the shaders (true = 1, false = 0)
static private int GetStencilRef(bool enabled)
{
return enabled ? 1 : 0;
}
// Set highlighting shaders global ZWrite property
static public void SetZWrite(int value)
{
if (zWrite == value) { return; }
zWrite = value;
Shader.SetGlobalInt(ShaderPropertyID._HighlightingZWrite, zWrite);
}
// Set highlighting shaders global OffsetFactor property
static public void SetOffsetFactor(float value)
{
if (offsetFactor == value) { return; }
offsetFactor = value;
Shader.SetGlobalFloat(ShaderPropertyID._HighlightingOffsetFactor, offsetFactor);
}
// Set highlighting shaders global OffsetUnits property
static public void SetOffsetUnits(float value)
{
if (offsetUnits == value) { return; }
offsetUnits = value;
Shader.SetGlobalFloat(ShaderPropertyID._HighlightingOffsetUnits, offsetUnits);
}
#endregion
}
}