207 lines
5.2 KiB
C#
207 lines
5.2 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
namespace HighlightingSystem
|
|
{
|
|
[DisallowMultipleComponent]
|
|
public class HighlighterRenderer : MonoBehaviour
|
|
{
|
|
private struct Data
|
|
{
|
|
public Material material;
|
|
public int submeshIndex;
|
|
public bool transparent;
|
|
}
|
|
|
|
#region Constants
|
|
// Default transparency cutoff value (used for shaders without _Cutoff property)
|
|
static private float transparentCutoff = 0.5f;
|
|
|
|
// Flags to hide and don't save this component in editor
|
|
private const HideFlags flags = HideFlags.HideInInspector | HideFlags.DontSaveInEditor | HideFlags.NotEditable | HideFlags.DontSaveInBuild;
|
|
|
|
// Cull Off
|
|
private const int cullOff = (int)CullMode.Off;
|
|
#endregion
|
|
|
|
static private readonly string sRenderType = "RenderType";
|
|
static private readonly string sOpaque = "Opaque";
|
|
static private readonly string sTransparent = "Transparent";
|
|
static private readonly string sTransparentCutout = "TransparentCutout";
|
|
static private readonly string sMainTex = "_MainTex";
|
|
|
|
private Renderer r;
|
|
private List<Data> data;
|
|
private Camera lastCamera = null;
|
|
private bool isAlive;
|
|
private Coroutine endOfFrame;
|
|
|
|
#region MonoBehaviour
|
|
//
|
|
void OnEnable()
|
|
{
|
|
endOfFrame = StartCoroutine(EndOfFrame());
|
|
}
|
|
|
|
//
|
|
void OnDisable()
|
|
{
|
|
lastCamera = null;
|
|
if (endOfFrame != null)
|
|
{
|
|
StopCoroutine(endOfFrame);
|
|
}
|
|
}
|
|
|
|
// Called once (before OnPreRender) for each camera if the object is visible
|
|
void OnWillRenderObject()
|
|
{
|
|
Camera cam = Camera.current;
|
|
// Another camera may intercept rendering and send it's own OnWillRenderObject events (i.e. water rendering),
|
|
// so we're caching currently rendering camera only if it has HighlighterBase component
|
|
if (HighlightingBase.IsHighlightingCamera(cam))
|
|
{
|
|
// VR Camera renders twice per frame (once for each eye), but OnWillRenderObject is called once so we have to cache reference to the camera
|
|
lastCamera = cam;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
//
|
|
IEnumerator EndOfFrame()
|
|
{
|
|
while (true)
|
|
{
|
|
yield return new WaitForEndOfFrame();
|
|
|
|
lastCamera = null;
|
|
|
|
if (!isAlive)
|
|
{
|
|
Destroy(this);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
//
|
|
public void Initialize(Material sharedOpaqueMaterial, Shader transparentShader) // int zTest, int stencilRef
|
|
{
|
|
data = new List<Data>();
|
|
|
|
r = GetComponent<Renderer>();
|
|
this.hideFlags = flags;
|
|
Material[] materials = r.sharedMaterials;
|
|
|
|
if (materials != null)
|
|
{
|
|
for (int i = 0; i < materials.Length; i++)
|
|
{
|
|
Material sourceMat = materials[i];
|
|
|
|
if (sourceMat == null) { continue; }
|
|
|
|
Data d = new Data();
|
|
|
|
string tag = sourceMat.GetTag(sRenderType, true, sOpaque);
|
|
if (tag == sTransparent || tag == sTransparentCutout)
|
|
{
|
|
Material replacementMat = new Material(transparentShader);
|
|
//replacementMat.SetInt(ShaderPropertyID._ZTest, zTest);
|
|
//replacementMat.SetInt(ShaderPropertyID._StencilRef, stencilRef);
|
|
|
|
// To render both sides of the Sprite
|
|
if (r is SpriteRenderer) { replacementMat.SetInt(ShaderPropertyID._Cull, cullOff); }
|
|
|
|
if (sourceMat.HasProperty(ShaderPropertyID._MainTex))
|
|
{
|
|
replacementMat.SetTexture(ShaderPropertyID._MainTex, sourceMat.mainTexture);
|
|
replacementMat.SetTextureOffset(sMainTex, sourceMat.mainTextureOffset);
|
|
replacementMat.SetTextureScale(sMainTex, sourceMat.mainTextureScale);
|
|
}
|
|
|
|
int cutoff = ShaderPropertyID._Cutoff;
|
|
replacementMat.SetFloat(cutoff, sourceMat.HasProperty(cutoff) ? sourceMat.GetFloat(cutoff) : transparentCutoff);
|
|
|
|
d.material = replacementMat;
|
|
d.transparent = true;
|
|
}
|
|
else
|
|
{
|
|
d.material = sharedOpaqueMaterial;
|
|
d.transparent = false;
|
|
}
|
|
|
|
d.submeshIndex = i;
|
|
data.Add(d);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
public bool FillBuffer(CommandBuffer buffer)
|
|
{
|
|
if (r == null) { return false; }
|
|
|
|
if (lastCamera == Camera.current)
|
|
{
|
|
for (int i = 0, imax = data.Count; i < imax; i++)
|
|
{
|
|
Data d = data[i];
|
|
buffer.DrawRenderer(r, d.material, d.submeshIndex);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Sets given color as highlighting color on all transparent materials of this renderer
|
|
public void SetColorForTransparent(Color clr)
|
|
{
|
|
for (int i = 0, imax = data.Count; i < imax; i++)
|
|
{
|
|
Data d = data[i];
|
|
if (d.transparent)
|
|
{
|
|
d.material.SetColor(ShaderPropertyID._Color, clr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sets ZTest parameter on all transparent materials of this renderer
|
|
public void SetZTestForTransparent(int zTest)
|
|
{
|
|
for (int i = 0, imax = data.Count; i < imax; i++)
|
|
{
|
|
Data d = data[i];
|
|
if (d.transparent)
|
|
{
|
|
d.material.SetInt(ShaderPropertyID._ZTest, zTest);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sets Stencil Ref parameter on all transparent materials of this renderer
|
|
public void SetStencilRefForTransparent(int stencilRef)
|
|
{
|
|
for (int i = 0, imax = data.Count; i < imax; i++)
|
|
{
|
|
Data d = data[i];
|
|
if (d.transparent)
|
|
{
|
|
d.material.SetInt(ShaderPropertyID._StencilRef, stencilRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
public void SetState(bool alive)
|
|
{
|
|
isAlive = alive;
|
|
}
|
|
#endregion
|
|
}
|
|
} |