From 1789a03ea320e7a16be2daa8881b41dfc2966fdf Mon Sep 17 00:00:00 2001 From: "Alex.Kirel" Date: Thu, 27 Jul 2023 16:48:00 +0500 Subject: [PATCH] Added Highlighting System library. --- Assets/Highlighting System/Resources.meta | 5 + .../Resources/HighlightingBlur.shader | 100 +++ .../Resources/HighlightingBlur.shader.meta | 5 + .../Resources/HighlightingComposite.shader | 69 ++ .../HighlightingComposite.shader.meta | 5 + .../Resources/HighlightingCut.shader | 101 +++ .../Resources/HighlightingCut.shader.meta | 5 + .../Resources/HighlightingOpaque.shader | 54 ++ .../Resources/HighlightingOpaque.shader.meta | 5 + .../Resources/HighlightingTransparent.shader | 79 +++ .../HighlightingTransparent.shader.meta | 5 + Assets/Highlighting System/Scripts.meta | 5 + .../Scripts/Highlighter.cs | 344 ++++++++++ .../Scripts/Highlighter.cs.meta | 8 + .../Scripts/HighlightingRenderer.cs | 10 + .../Scripts/HighlightingRenderer.cs.meta | 15 + .../Highlighting System/Scripts/Internal.meta | 5 + .../Scripts/Internal/HighlighterInternal.cs | 542 ++++++++++++++++ .../Internal/HighlighterInternal.cs.meta | 8 + .../Scripts/Internal/HighlighterManager.cs | 46 ++ .../Internal/HighlighterManager.cs.meta | 12 + .../Scripts/Internal/HighlightingBase.cs | 593 ++++++++++++++++++ .../Scripts/Internal/HighlightingBase.cs.meta | 8 + .../Scripts/Internal/RendererCache.cs | 170 +++++ .../Scripts/Internal/RendererCache.cs.meta | 12 + .../Scripts/Internal/ShaderPropertyID.cs | 63 ++ .../Scripts/Internal/ShaderPropertyID.cs.meta | 8 + fie.csproj | 7 + 28 files changed, 2289 insertions(+) create mode 100644 Assets/Highlighting System/Resources.meta create mode 100644 Assets/Highlighting System/Resources/HighlightingBlur.shader create mode 100644 Assets/Highlighting System/Resources/HighlightingBlur.shader.meta create mode 100644 Assets/Highlighting System/Resources/HighlightingComposite.shader create mode 100644 Assets/Highlighting System/Resources/HighlightingComposite.shader.meta create mode 100644 Assets/Highlighting System/Resources/HighlightingCut.shader create mode 100644 Assets/Highlighting System/Resources/HighlightingCut.shader.meta create mode 100644 Assets/Highlighting System/Resources/HighlightingOpaque.shader create mode 100644 Assets/Highlighting System/Resources/HighlightingOpaque.shader.meta create mode 100644 Assets/Highlighting System/Resources/HighlightingTransparent.shader create mode 100644 Assets/Highlighting System/Resources/HighlightingTransparent.shader.meta create mode 100644 Assets/Highlighting System/Scripts.meta create mode 100644 Assets/Highlighting System/Scripts/Highlighter.cs create mode 100644 Assets/Highlighting System/Scripts/Highlighter.cs.meta create mode 100644 Assets/Highlighting System/Scripts/HighlightingRenderer.cs create mode 100644 Assets/Highlighting System/Scripts/HighlightingRenderer.cs.meta create mode 100644 Assets/Highlighting System/Scripts/Internal.meta create mode 100644 Assets/Highlighting System/Scripts/Internal/HighlighterInternal.cs create mode 100644 Assets/Highlighting System/Scripts/Internal/HighlighterInternal.cs.meta create mode 100644 Assets/Highlighting System/Scripts/Internal/HighlighterManager.cs create mode 100644 Assets/Highlighting System/Scripts/Internal/HighlighterManager.cs.meta create mode 100644 Assets/Highlighting System/Scripts/Internal/HighlightingBase.cs create mode 100644 Assets/Highlighting System/Scripts/Internal/HighlightingBase.cs.meta create mode 100644 Assets/Highlighting System/Scripts/Internal/RendererCache.cs create mode 100644 Assets/Highlighting System/Scripts/Internal/RendererCache.cs.meta create mode 100644 Assets/Highlighting System/Scripts/Internal/ShaderPropertyID.cs create mode 100644 Assets/Highlighting System/Scripts/Internal/ShaderPropertyID.cs.meta diff --git a/Assets/Highlighting System/Resources.meta b/Assets/Highlighting System/Resources.meta new file mode 100644 index 0000000..5740ab1 --- /dev/null +++ b/Assets/Highlighting System/Resources.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 2e2dd921d0779af4dad3fbd326388e14 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Highlighting System/Resources/HighlightingBlur.shader b/Assets/Highlighting System/Resources/HighlightingBlur.shader new file mode 100644 index 0000000..8a28374 --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingBlur.shader @@ -0,0 +1,100 @@ +// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + +Shader "Hidden/Highlighted/Blur" +{ + Properties + { + [HideInInspector] _MainTex ("", 2D) = "" {} + [HideInInspector] _Intensity ("", Range (0.25,0.5)) = 0.3 + } + + SubShader + { + Pass + { + ZTest Always + Cull Off + ZWrite Off + Lighting Off + Fog { Mode Off } + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma fragmentoption ARB_precision_hint_fastest + + #include "UnityCG.cginc" + + uniform sampler2D _MainTex; + uniform half4 _MainTex_TexelSize; + + uniform half _HighlightingBlurOffset; + uniform half _Intensity; + + struct v2f + { + float4 pos : POSITION; + float2 uv[4] : TEXCOORD0; // 8 to add straight directions + }; + + v2f vert (appdata_img v) + { + // Shader code optimized for the Unity shader compiler + v2f o; + o.pos = UnityObjectToClipPos(v.vertex); + float2 offs = _HighlightingBlurOffset * _MainTex_TexelSize.xy; + + // Diagonal directions + o.uv[0].x = v.texcoord.x - offs.x; + o.uv[0].y = v.texcoord.y - offs.y; + + o.uv[1].x = v.texcoord.x + offs.x; + o.uv[1].y = v.texcoord.y - offs.y; + + o.uv[2].x = v.texcoord.x + offs.x; + o.uv[2].y = v.texcoord.y + offs.y; + + o.uv[3].x = v.texcoord.x - offs.x; + o.uv[3].y = v.texcoord.y + offs.y; + + /* + // Straight directions + o.uv[4].x = v.texcoord.x - offs.x; + o.uv[4].y = v.texcoord.y; + + o.uv[5].x = v.texcoord.x + offs.x; + o.uv[5].y = v.texcoord.y; + + o.uv[6].x = v.texcoord.x; + o.uv[6].y = v.texcoord.y - offs.y; + + o.uv[7].x = v.texcoord.x; + o.uv[7].y = v.texcoord.y + offs.y; + */ + + return o; + } + + half4 frag(v2f i) : COLOR + { + int start = 0; + int end = 4; + + half4 color1 = tex2D(_MainTex, i.uv[start]); + fixed4 color2; + for (int n = start + 1; n < end; n++) + { + color2 = tex2D(_MainTex, i.uv[n]); + color1.rgb = max(color1.rgb, color2.rgb); + color1.a += color2.a; + } + + color1.a *= _Intensity; + return color1; + } + ENDCG + } + } + + Fallback off +} \ No newline at end of file diff --git a/Assets/Highlighting System/Resources/HighlightingBlur.shader.meta b/Assets/Highlighting System/Resources/HighlightingBlur.shader.meta new file mode 100644 index 0000000..9bf4d0c --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingBlur.shader.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 3070218404d1de94c9d081d248597046 +ShaderImporter: + defaultTextures: [] + userData: diff --git a/Assets/Highlighting System/Resources/HighlightingComposite.shader b/Assets/Highlighting System/Resources/HighlightingComposite.shader new file mode 100644 index 0000000..7f7c137 --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingComposite.shader @@ -0,0 +1,69 @@ +// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + +Shader "Hidden/Highlighted/Composite" +{ + Properties + { + [HideInInspector] _MainTex ("", 2D) = "" {} + } + + SubShader + { + Pass + { + Lighting Off + Fog { Mode off } + ZWrite Off + ZTest Always + Cull Off + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma fragmentoption ARB_precision_hint_fastest + + #include "UnityCG.cginc" + + struct v2f + { + float4 pos : POSITION; + half2 uv0 : TEXCOORD0; + half2 uv1 : TEXCOORD1; + }; + + uniform sampler2D _MainTex; + uniform float4 _MainTex_TexelSize; + uniform sampler2D _HighlightingBuffer; + + v2f vert(appdata_img v) + { + v2f o; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv0 = v.texcoord.xy; + o.uv1 = v.texcoord.xy; + + #if UNITY_UV_STARTS_AT_TOP + if (_MainTex_TexelSize.y < 0) + { + o.uv1.y = 1-o.uv1.y; + #if defined(UNITY_HALF_TEXEL_OFFSET) + o.uv1.y -= _MainTex_TexelSize.y; + #endif + } + #endif + + return o; + } + + fixed4 frag(v2f i) : COLOR + { + fixed4 c1 = tex2D(_MainTex, i.uv0); + fixed4 c2 = tex2D(_HighlightingBuffer, i.uv1); + c1.rgb = lerp(c1.rgb, c2.rgb, c2.a); + return c1; + } + ENDCG + } + } + FallBack Off +} \ No newline at end of file diff --git a/Assets/Highlighting System/Resources/HighlightingComposite.shader.meta b/Assets/Highlighting System/Resources/HighlightingComposite.shader.meta new file mode 100644 index 0000000..37eaf48 --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingComposite.shader.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: b0bd9de6a4f20954f9704e56568224a6 +ShaderImporter: + defaultTextures: [] + userData: diff --git a/Assets/Highlighting System/Resources/HighlightingCut.shader b/Assets/Highlighting System/Resources/HighlightingCut.shader new file mode 100644 index 0000000..23e0b26 --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingCut.shader @@ -0,0 +1,101 @@ +Shader "Hidden/Highlighted/Cut" +{ + SubShader + { + Lighting Off + Fog { Mode off } + ZWrite Off + ZTest Always + Cull Back + Blend Off + + Pass + { + Stencil + { + Ref 1 + Comp NotEqual + Pass Keep + ZFail Keep + } + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma fragmentoption ARB_precision_hint_fastest + + #include "UnityCG.cginc" + + struct v2f + { + float4 pos : POSITION; + half2 uv : TEXCOORD0; + }; + + uniform sampler2D _HighlightingBlurred; + uniform float2 _HighlightingBufferTexelSize; + + v2f vert(appdata_img v) + { + v2f o; + o.pos = v.vertex; + + #if defined(UNITY_HALF_TEXEL_OFFSET) + o.pos.xy += _HighlightingBufferTexelSize; + #endif + + o.uv = v.texcoord.xy; + return o; + } + + fixed4 frag(v2f i) : COLOR + { + return tex2D(_HighlightingBlurred, i.uv); + } + ENDCG + } + + Pass + { + Stencil + { + Ref 1 + Comp Equal + Pass Keep + ZFail Keep + } + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma fragmentoption ARB_precision_hint_fastest + + #include "UnityCG.cginc" + + struct appdata_vert + { + float4 vertex : POSITION; + }; + + uniform float2 _HighlightingBufferTexelSize; + + float4 vert(appdata_vert v) : POSITION + { + float4 pos = v.vertex; + + #if defined(UNITY_HALF_TEXEL_OFFSET) + pos.xy += _HighlightingBufferTexelSize; + #endif + + return pos; + } + + fixed4 frag() : COLOR + { + return 0; + } + ENDCG + } + } + FallBack Off +} diff --git a/Assets/Highlighting System/Resources/HighlightingCut.shader.meta b/Assets/Highlighting System/Resources/HighlightingCut.shader.meta new file mode 100644 index 0000000..3d3e60a --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingCut.shader.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 376af14ed82e64793b326012b8591fa9 +ShaderImporter: + defaultTextures: [] + userData: diff --git a/Assets/Highlighting System/Resources/HighlightingOpaque.shader b/Assets/Highlighting System/Resources/HighlightingOpaque.shader new file mode 100644 index 0000000..f127a68 --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingOpaque.shader @@ -0,0 +1,54 @@ +// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + +Shader "Hidden/Highlighted/Opaque" +{ + Properties + { + [HideInInspector] _ZTest ("", Float) = 8 // UnityEngine.Rendering.CompareFunction. 4 = LessEqual, 8 = Always + [HideInInspector] _StencilRef ("", Float) = 1 + } + + SubShader + { + Pass + { + Lighting Off + Fog { Mode Off } + ZWrite [_HighlightingZWrite] + ZTest [_ZTest] + Offset [_HighlightingOffsetFactor], [_HighlightingOffsetUnits] + Stencil + { + Ref [_StencilRef] + Comp Always + Pass Replace + ZFail Keep + } + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + + uniform fixed4 _Outline; + + struct appdata_vert + { + float4 vertex : POSITION; + }; + + float4 vert(appdata_vert v) : POSITION + { + return UnityObjectToClipPos(v.vertex); + } + + fixed4 frag() : COLOR + { + return _Outline; + } + ENDCG + } + } + Fallback Off +} \ No newline at end of file diff --git a/Assets/Highlighting System/Resources/HighlightingOpaque.shader.meta b/Assets/Highlighting System/Resources/HighlightingOpaque.shader.meta new file mode 100644 index 0000000..30ebfb1 --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingOpaque.shader.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: ce3b12ca636563c48afb814d9aeb5b90 +ShaderImporter: + defaultTextures: [] + userData: diff --git a/Assets/Highlighting System/Resources/HighlightingTransparent.shader b/Assets/Highlighting System/Resources/HighlightingTransparent.shader new file mode 100644 index 0000000..2435d11 --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingTransparent.shader @@ -0,0 +1,79 @@ +// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' + +Shader "Hidden/Highlighted/Transparent" +{ + Properties + { + [HideInInspector] _MainTex ("", 2D) = "" {} + [HideInInspector] _Cutoff ("", Float) = 0.5 + [HideInInspector] _ZTest ("", Float) = 8 // UnityEngine.Rendering.CompareFunction. 4 = LessEqual, 8 = Always + [HideInInspector] _StencilRef ("", Float) = 1 + [HideInInspector] _Cull ("", Float) = 2 // UnityEngine.Rendering.CullMode.Back + } + + SubShader + { + Pass + { + Lighting Off + Fog { Mode Off } + ZWrite [_HighlightingZWrite] + ZTest [_ZTest] + Offset [_HighlightingOffsetFactor], [_HighlightingOffsetUnits] + Cull [_Cull] + Stencil + { + Ref [_StencilRef] + Comp Always + Pass Replace + ZFail Keep + } + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma fragmentoption ARB_precision_hint_fastest + #include "UnityCG.cginc" + + uniform fixed4 _Outline; + uniform sampler2D _MainTex; + uniform float4 _MainTex_ST; + uniform fixed _Cutoff; + + struct appdata_vert_tex + { + float4 vertex : POSITION; + float2 texcoord : TEXCOORD0; + fixed4 color : COLOR; + }; + + struct v2f + { + float4 pos : POSITION; + float2 texcoord : TEXCOORD0; + fixed alpha : TEXCOORD1; + }; + + v2f vert(appdata_vert_tex v) + { + v2f o; + o.pos = UnityObjectToClipPos(v.vertex); + o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); + o.alpha = v.color.a; + return o; + } + + fixed4 frag(v2f i) : COLOR + { + fixed a = tex2D(_MainTex, i.texcoord).a; + clip(a - _Cutoff); + fixed4 c = _Outline; + c.a *= i.alpha; + return c; + } + ENDCG + } + } + + Fallback Off +} \ No newline at end of file diff --git a/Assets/Highlighting System/Resources/HighlightingTransparent.shader.meta b/Assets/Highlighting System/Resources/HighlightingTransparent.shader.meta new file mode 100644 index 0000000..9862945 --- /dev/null +++ b/Assets/Highlighting System/Resources/HighlightingTransparent.shader.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 9fc04a058c8768648a6532aa07540ca5 +ShaderImporter: + defaultTextures: [] + userData: diff --git a/Assets/Highlighting System/Scripts.meta b/Assets/Highlighting System/Scripts.meta new file mode 100644 index 0000000..8d6e05d --- /dev/null +++ b/Assets/Highlighting System/Scripts.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 4b92650ecd71b67409ab3aafea97bd7d +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Highlighting System/Scripts/Highlighter.cs b/Assets/Highlighting System/Scripts/Highlighter.cs new file mode 100644 index 0000000..9966d94 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Highlighter.cs @@ -0,0 +1,344 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +namespace HighlightingSystem +{ + public partial class Highlighter : MonoBehaviour + { + #region Editable Parameters + // Constant highlighting turning on speed (common property for all Highlighters) + static private float constantOnSpeed = 4.5f; + + // Constant highlighting turning off speed (common property for all Highlighters) + static private float constantOffSpeed = 4f; + + // Default transparency cutoff value (used for shaders without _Cutoff property) + static private float transparentCutoff = 0.5f; + + // Builtin layer reserved for the highlighting. This layer shouldn't be used for anything else in the project! + public const int highlightingLayer = 7; + + // Only these types of Renderers will be highlighted + static public readonly List types = new List() + { + typeof(MeshRenderer), + typeof(SkinnedMeshRenderer), + typeof(SpriteRenderer), + typeof(ParticleRenderer), + typeof(ParticleSystemRenderer), + }; + #endregion + + #region Public Methods + /// + /// Renderers reinitialization. + /// Call this method if your highlighted object has changed it's materials, renderers or child objects. + /// Can be called multiple times per update - renderers reinitialization will occur only once. + /// + public void ReinitMaterials() + { + renderersDirty = true; + } + + /// + /// Set color for one-frame highlighting mode. + /// + /// + /// Highlighting color. + /// + public void OnParams(Color color) + { + onceColor = color; + } + + /// + /// Turn on one-frame highlighting. + /// + public void On() + { + // Highlight object only in this frame + once = true; + } + + /// + /// Turn on one-frame highlighting with given color. + /// Can be called multiple times per update, color only from the latest call will be used. + /// + /// + /// Highlighting color. + /// + public void On(Color color) + { + // Set new color for one-frame highlighting + onceColor = color; + On(); + } + + /// + /// Set flashing parameters. + /// + /// + /// Starting color. + /// + /// + /// Ending color. + /// + /// + /// Flashing frequency (times per second). + /// + public void FlashingParams(Color color1, Color color2, float freq) + { + flashingColorMin = color1; + flashingColorMax = color2; + flashingFreq = freq; + } + + /// + /// Turn on flashing. + /// + public void FlashingOn() + { + flashing = true; + } + + /// + /// Turn on flashing from color1 to color2. + /// + /// + /// Starting color. + /// + /// + /// Ending color. + /// + public void FlashingOn(Color color1, Color color2) + { + flashingColorMin = color1; + flashingColorMax = color2; + FlashingOn(); + } + + /// + /// Turn on flashing from color1 to color2 with given frequency. + /// + /// + /// Starting color. + /// + /// + /// Ending color. + /// + /// + /// Flashing frequency (times per second). + /// + public void FlashingOn(Color color1, Color color2, float freq) + { + flashingFreq = freq; + FlashingOn(color1, color2); + } + + /// + /// Turn on flashing with given frequency. + /// + /// + /// Flashing frequency (times per second). + /// + public void FlashingOn(float freq) + { + flashingFreq = freq; + FlashingOn(); + } + + /// + /// Turn off flashing. + /// + public void FlashingOff() + { + flashing = false; + } + + /// + /// Switch flashing mode. + /// + public void FlashingSwitch() + { + flashing = !flashing; + } + + /// + /// Set constant highlighting color. + /// + /// + /// Constant highlighting color. + /// + public void ConstantParams(Color color) + { + constantColor = color; + } + + /// + /// Fade in constant highlighting. + /// + public void ConstantOn() + { + // Enable constant highlighting + constantly = true; + // Start transition + transitionActive = true; + } + + /// + /// Fade in constant highlighting with given color. + /// + /// + /// Constant highlighting color. + /// + public void ConstantOn(Color color) + { + // Set constant highlighting color + constantColor = color; + ConstantOn(); + } + + /// + /// Fade out constant highlighting. + /// + public void ConstantOff() + { + // Disable constant highlighting + constantly = false; + // Start transition + transitionActive = true; + } + + /// + /// Switch Constant Highlighting. + /// + public void ConstantSwitch() + { + // Switch constant highlighting + constantly = !constantly; + // Start transition + transitionActive = true; + } + + /// + /// Turn on constant highlighting immediately (without fading in). + /// + public void ConstantOnImmediate() + { + constantly = true; + // Set transition value to 1 + transitionValue = 1f; + // Stop transition + transitionActive = false; + } + + /// + /// Turn on constant highlighting with given color immediately (without fading in). + /// + /// + /// Constant highlighting color. + /// + public void ConstantOnImmediate(Color color) + { + // Set constant highlighting color + constantColor = color; + ConstantOnImmediate(); + } + + /// + /// Turn off constant highlighting immediately (without fading out). + /// + public void ConstantOffImmediate() + { + constantly = false; + // Set transition value to 0 + transitionValue = 0f; + // Stop transition + transitionActive = false; + } + + /// + /// Switch constant highlighting immediately (without fading in/out). + /// + public void ConstantSwitchImmediate() + { + constantly = !constantly; + // Set transition value to the final value + transitionValue = constantly ? 1f : 0f; + // Stop transition + transitionActive = false; + } + + /// + /// Enable see-through mode + /// + public void SeeThroughOn() + { + seeThrough = true; + } + + /// + /// Disable see-through mode + /// + public void SeeThroughOff() + { + seeThrough = false; + } + + /// + /// Switch see-through mode + /// + public void SeeThroughSwitch() + { + seeThrough = !seeThrough; + } + + /// + /// Enable occluder mode. Non-see-through occluders will be used only in case frame depth buffer is not accessible. + /// + public void OccluderOn() + { + occluder = true; + } + + /// + /// Disable occluder mode. Non-see-through occluders will be used only in case frame depth buffer is not accessible. + /// + public void OccluderOff() + { + occluder = false; + } + + /// + /// Switch occluder mode. Non-see-through occluders will be used only in case frame depth buffer is not accessible. + /// + public void OccluderSwitch() + { + occluder = !occluder; + } + + /// + /// Turn off all types of highlighting. + /// + public void Off() + { + once = false; + flashing = false; + constantly = false; + // Set transition value to 0 + transitionValue = 0f; + // Stop transition + transitionActive = false; + } + + /// + /// Destroy this Highlighter component. + /// + public void Die() + { + Destroy(this); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/Highlighting System/Scripts/Highlighter.cs.meta b/Assets/Highlighting System/Scripts/Highlighter.cs.meta new file mode 100644 index 0000000..48e3699 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Highlighter.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 67d4de287b46eb34e83a61e4f9115070 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Highlighting System/Scripts/HighlightingRenderer.cs b/Assets/Highlighting System/Scripts/HighlightingRenderer.cs new file mode 100644 index 0000000..1a0e714 --- /dev/null +++ b/Assets/Highlighting System/Scripts/HighlightingRenderer.cs @@ -0,0 +1,10 @@ +using UnityEngine; +using System.Collections; + +namespace HighlightingSystem +{ + public class HighlightingRenderer : HighlightingBase + { + + } +} \ No newline at end of file diff --git a/Assets/Highlighting System/Scripts/HighlightingRenderer.cs.meta b/Assets/Highlighting System/Scripts/HighlightingRenderer.cs.meta new file mode 100644 index 0000000..d89dd3d --- /dev/null +++ b/Assets/Highlighting System/Scripts/HighlightingRenderer.cs.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 8b765e0b5af3bcf4a9deb5895e477da8 +labels: +- Glow +- Effect +- Outline +- Highlight +- Selection +- System +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Highlighting System/Scripts/Internal.meta b/Assets/Highlighting System/Scripts/Internal.meta new file mode 100644 index 0000000..9d57011 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 3e2a98eec43f4f04788ccf35d4ae8ec4 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Highlighting System/Scripts/Internal/HighlighterInternal.cs b/Assets/Highlighting System/Scripts/Internal/HighlighterInternal.cs new file mode 100644 index 0000000..cb62cb9 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/HighlighterInternal.cs @@ -0,0 +1,542 @@ +using UnityEngine; +using UnityEngine.Rendering; +using System.Collections; +using System.Collections.Generic; + +namespace HighlightingSystem +{ + public partial class Highlighter : MonoBehaviour + { + // Constants (don't touch this!) + #region Constants + // 2 * PI constant required for flashing + private const float doublePI = 2f * Mathf.PI; + + // Occlusion color + private readonly Color occluderColor = new Color(0f, 0f, 0f, 0f); + + // ZTest LEqual + private const float zTestLessEqual = (float)CompareFunction.LessEqual; + + // ZTest Always + private const float zTestAlways = (float)CompareFunction.Always; + + // Cull Off + private const float cullOff = (float)CullMode.Off; + #endregion + + #region Static Fields + // Global highlighting shaders ZWrite property + static private float zWrite = -1f; // Set to unusual default value to force initialization on start + + // Global highlighting shaders Offset Factor property + static private float offsetFactor = float.NaN; // Set to unusual default value to force initialization on start + + // Global highlighting shaders Offset Units property + static private float offsetUnits = float.NaN; // Set to unusual default value to force initialization on start + #endregion + + #region Public Fields + // Current state of highlighting (true if highlighted and visible) + public bool highlighted { get; private set; } + #endregion + + #region Private Fields + // Cached transform component reference + private Transform tr; + + // Cached Renderers + private List highlightableRenderers; + + // Contains frame number, in which Highlighter visibility has been checked + private int visibilityCheckFrame = -1; + + // Visibility changed in this frame + private bool visibilityChanged = false; + + // At least 1 renderer is visible in this frame + private bool visible = false; + + // Materials reinitialization is required flag + private bool renderersDirty = true; + + // Current highlighting color + private Color currentColor; + + // Transition is active flag + private bool transitionActive = false; + + // Current transition value + private float transitionValue = 0f; + + // Flashing frequency (times per second) + private float flashingFreq = 2f; + + // One-frame highlighting flag + private int _once = 0; + private bool once + { + get { return _once == Time.frameCount; } + set { _once = value ? Time.frameCount : 0; } + } + + // One-frame highlighting color + private Color onceColor = Color.red; + + // Flashing state flag + private bool flashing = false; + + // Flashing from color + private Color flashingColorMin = new Color(0f, 1f, 1f, 0f); + + // Flashing to color + private Color flashingColorMax = new Color(0f, 1f, 1f, 1f); + + // Constant highlighting state flag + private bool constantly = false; + + // Constant highlighting color + private Color constantColor = Color.yellow; + + // Occluder mode enabled flag + private bool occluder = false; + + // See-through mode flag (should have same initial value with zTest and renderQueue variables!) + private bool seeThrough = true; + + // RenderQueue (0 = Geometry, 1 = Geometry+1 (for seethrough mode on by default), 2 = Geometry+2) + private int renderQueue = 1; + + // Current ZTest value (true = Always, false = LEqual) + private bool zTest = true; + + // Current Stencil Ref value (true = 1, false = 0) + private bool stencilRef = true; + + // Returns real ZTest float value which will be passed to the materials + private float zTestFloat { get { return zTest ? zTestAlways : zTestLessEqual; } } + + // Returns real Stencil Ref float value which will be passed to the materials + private float stencilRefFloat { get { return stencilRef ? 1f : 0f; } } + + // 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.SetFloat(ShaderPropertyID._ZTest, zTestFloat); + _opaqueMaterial.SetFloat(ShaderPropertyID._StencilRef, stencilRefFloat); + } + return _opaqueMaterial; + } + } + #endregion + + #region MonoBehaviour + // + private void Awake() + { + tr = GetComponent(); + ShaderPropertyID.Initialize(); + } + + // + private void OnEnable() + { + if (!CheckInstance()) { return; } + + HighlighterManager.Add(this); + } + + // + private void OnDisable() + { + HighlighterManager.Remove(this); + + // Clear cached renderers + if (highlightableRenderers != null) { highlightableRenderers.Clear(); } + + // Reset highlighting parameters to default values + renderersDirty = true; + highlighted = false; + currentColor = Color.clear; + transitionActive = false; + transitionValue = 0f; + once = false; + flashing = false; + constantly = false; + occluder = false; + seeThrough = false; + + /* + // Reset custom parameters of the highlighting + onceColor = Color.red; + flashingColorMin = new Color(0f, 1f, 1f, 0f); + flashingColorMax = new Color(0f, 1f, 1f, 1f); + flashingFreq = 2f; + constantColor = Color.yellow; + */ + } + + // + private void Update() + { + // Update transition value + PerformTransition(); + } + #endregion + + #region Public Methods + // Returns true in case CommandBuffer should be rebuilt + public bool UpdateHighlighting(bool isDepthAvailable) + { + bool wasHighlighted = highlighted; + bool changed = false; + + changed |= UpdateRenderers(); + + // Is any highlighting mode is enabled? + highlighted = (once || flashing || constantly || transitionActive); + + int rq = 0; + + // Render as highlighter + if (highlighted) + { + // ZTest = (seeThrough ? Always : LEqual), Stencil Ref = 1 + UpdateShaderParams(seeThrough, true); + // RenderQueue = (seeThrough ? Geometry+1 : Geometry) + rq = seeThrough ? 2 : 0; + } + // Render as occluder + else if (occluder && (seeThrough || !isDepthAvailable)) + { + // ZTest = (isDepthAvailable ? LEqual : Always), Stencil Ref = seeThrough ? 1 : 0 + UpdateShaderParams(false, seeThrough); + + // RenderQueue = (seeThrough ? Occluder queue : Geometry queue) + rq = seeThrough ? 1 : 0; + + highlighted = true; + } + + // In case renderer should be put to another render queue + if (renderQueue != rq) + { + renderQueue = rq; + changed = true; + } + + if (highlighted) + { + changed |= UpdateVisibility(); + + if (visible) + { + UpdateColors(); + } + else + { + highlighted = false; + } + } + + changed |= (wasHighlighted != highlighted); + + return changed; + } + + // Fills given CommandBuffer with this Highlighter rendering commands + public void FillBuffer(CommandBuffer buffer, int renderQueue) + { + if (!highlighted) { return; } + + if (this.renderQueue != renderQueue) { return; } + + for (int i = highlightableRenderers.Count - 1; i >= 0; i--) + { + RendererCache renderer = highlightableRenderers[i]; + if (!renderer.FillBuffer(buffer)) + { + highlightableRenderers.RemoveAt(i); + } + } + } + #endregion + + #region Private Methods + // Allow only single instance of the Highlighter component on a GameObject + private bool CheckInstance() + { + Highlighter[] highlighters = GetComponents(); + if (highlighters.Length > 1 && highlighters[0] != this) + { + enabled = false; + Debug.LogWarning("HighlightingSystem : Multiple Highlighter components on a single GameObject is not allowed! Highlighter has been disabled on a GameObject with name '" + gameObject.name + "'."); + return false; + } + return true; + } + + // This method defines the way in which renderers are initialized + private bool UpdateRenderers() + { + if (renderersDirty) + { + List renderers = new List(); + + // Find all renderers which should be controlled by this Highlighter component + GrabRenderers(tr, ref renderers); + + // Cache found renderers + highlightableRenderers = new List(); + int l = renderers.Count; + for (int i = 0; i < l; i++) + { + RendererCache cache = new RendererCache(renderers[i], opaqueMaterial, zTestFloat, stencilRefFloat); + highlightableRenderers.Add(cache); + } + + // Reset + highlighted = false; + renderersDirty = false; + currentColor = Color.clear; + + return true; + } + else + { + // To avoid null-reference exceptions when cached GameObject or Renderer has been removed but ReinitMaterials wasn't called + bool changed = false; + for (int i = highlightableRenderers.Count - 1; i >= 0; i--) + { + if (highlightableRenderers[i].IsDestroyed()) + { + highlightableRenderers.RemoveAt(i); + changed = true; + } + } + return changed; + } + } + + // Returns true in case visibility changed in this frame + private bool UpdateVisibility() + { + if (visibilityCheckFrame == Time.frameCount) { return visibilityChanged; } + visibilityCheckFrame = Time.frameCount; + + visible = false; + visibilityChanged = false; + for (int i = 0, imax = highlightableRenderers.Count; i < imax; i++) + { + RendererCache rc = highlightableRenderers[i]; + visibilityChanged |= rc.UpdateVisibility(); + visible |= rc.visible; + } + + return visibilityChanged; + } + + // Follows hierarchy of objects from t, searches for Renderers and adds them to the list. Breaks if another Highlighter component found + private void GrabRenderers(Transform t, ref List renderers) + { + GameObject g = t.gameObject; + IEnumerator e; + + // 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++) + { + Component[] c = g.GetComponents(types[i]); + + e = c.GetEnumerator(); + while (e.MoveNext()) + { + renderers.Add(e.Current as Renderer); + } + } + + // Return if transform t doesn't have any children + if (t.childCount == 0) { return; } + + // Recursively cache renderers on all child GameObjects + e = t.GetEnumerator(); + while (e.MoveNext()) + { + Transform childTransform = e.Current as Transform; + GameObject childGameObject = childTransform.gameObject; + Highlighter h = childGameObject.GetComponent(); + + // Do not cache Renderers of this childTransform in case it has it's own Highlighter component + if (h != null) { continue; } + + GrabRenderers(childTransform, ref renderers); + } + } + + // Sets RenderQueue, 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; + float ztf = zTestFloat; + opaqueMaterial.SetFloat(ShaderPropertyID._ZTest, ztf); + for (int i = 0; i < highlightableRenderers.Count; i++) + { + highlightableRenderers[i].SetZTestForTransparent(ztf); + } + } + + // Stencil Ref + if (stencilRef != sr) + { + stencilRef = sr; + float srf = stencilRefFloat; + opaqueMaterial.SetFloat(ShaderPropertyID._StencilRef, srf); + for (int i = 0; i < highlightableRenderers.Count; i++) + { + highlightableRenderers[i].SetStencilRefForTransparent(srf); + } + } + } + + // Update highlighting color if necessary + private void UpdateColors() + { + if (once) + { + SetColor(onceColor); + return; + } + + if (flashing) + { + // Flashing frequency is not affected by Time.timeScale + Color c = Color.Lerp(flashingColorMin, flashingColorMax, 0.5f * Mathf.Sin(Time.realtimeSinceStartup * flashingFreq * doublePI) + 0.5f); + SetColor(c); + return; + } + + if (transitionActive) + { + Color c = new Color(constantColor.r, constantColor.g, constantColor.b, constantColor.a * transitionValue); + SetColor(c); + return; + } + else if (constantly) + { + SetColor(constantColor); + return; + } + + if (occluder) + { + SetColor(occluderColor); + return; + } + } + + // Set given highlighting color + private void SetColor(Color value) + { + if (currentColor == value) { return; } + currentColor = value; + opaqueMaterial.SetColor(ShaderPropertyID._Outline, currentColor); + for (int i = 0; i < highlightableRenderers.Count; i++) + { + highlightableRenderers[i].SetColorForTransparent(currentColor); + } + } + + // Calculate new transition value if necessary + private void PerformTransition() + { + if (transitionActive == false) { return; } + + float targetValue = constantly ? 1f : 0f; + + // Is transition finished? + if (transitionValue == targetValue) + { + transitionActive = false; + return; + } + + if (Time.timeScale != 0f) + { + // Calculating delta time untouched by Time.timeScale + float unscaledDeltaTime = Time.deltaTime / Time.timeScale; + + // Calculating new transition value + transitionValue = Mathf.Clamp01(transitionValue + (constantly ? constantOnSpeed : -constantOffSpeed) * unscaledDeltaTime); + } + else { return; } + } + #endregion + + #region Static Methods + // Globally sets ZWrite shader parameter for all highlighting materials + static public void SetZWrite(float value) + { + if (zWrite == value) { return; } + zWrite = value; + Shader.SetGlobalFloat(ShaderPropertyID._HighlightingZWrite, zWrite); + } + + // Globally sets Offset Factor shader parameter for all highlighting materials + static public void SetOffsetFactor(float value) + { + if (offsetFactor == value) { return; } + offsetFactor = value; + Shader.SetGlobalFloat(ShaderPropertyID._HighlightingOffsetFactor, offsetFactor); + } + + // Globally sets Offset Units shader parameter for all highlighting materials + static public void SetOffsetUnits(float value) + { + if (offsetUnits == value) { return; } + offsetUnits = value; + Shader.SetGlobalFloat(ShaderPropertyID._HighlightingOffsetUnits, offsetUnits); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/Highlighting System/Scripts/Internal/HighlighterInternal.cs.meta b/Assets/Highlighting System/Scripts/Internal/HighlighterInternal.cs.meta new file mode 100644 index 0000000..a88fd65 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/HighlighterInternal.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bd0100e03c13140a894e7744d95e3a1b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Highlighting System/Scripts/Internal/HighlighterManager.cs b/Assets/Highlighting System/Scripts/Internal/HighlighterManager.cs new file mode 100644 index 0000000..a469053 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/HighlighterManager.cs @@ -0,0 +1,46 @@ +using UnityEngine; +using UnityEngine.Rendering; +using System.Collections; +using System.Collections.Generic; + +namespace HighlightingSystem +{ + static public class HighlighterManager + { + static private int dirtyFrame = -1; + static public bool isDirty + { + get + { + return dirtyFrame == Time.frameCount; + } + private set + { + dirtyFrame = value ? Time.frameCount : -1; + } + } + + static private HashSet highlighters = new HashSet(); + + // + static public void Add(Highlighter highlighter) + { + highlighters.Add(highlighter); + } + + // + static public void Remove(Highlighter instance) + { + if (highlighters.Remove(instance) && instance.highlighted) + { + isDirty = true; + } + } + + // + static public HashSet.Enumerator GetEnumerator() + { + return highlighters.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Assets/Highlighting System/Scripts/Internal/HighlighterManager.cs.meta b/Assets/Highlighting System/Scripts/Internal/HighlighterManager.cs.meta new file mode 100644 index 0000000..6779fc8 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/HighlighterManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 051745279da5dc949a624a73543c6373 +timeCreated: 1432018011 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Highlighting System/Scripts/Internal/HighlightingBase.cs b/Assets/Highlighting System/Scripts/Internal/HighlightingBase.cs new file mode 100644 index 0000000..663da77 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/HighlightingBase.cs @@ -0,0 +1,593 @@ +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Serialization; +using System.Collections; +using System.Collections.Generic; + +namespace HighlightingSystem +{ + [RequireComponent(typeof(Camera))] + public class HighlightingBase : MonoBehaviour + { + #region Static Fields and Constants + static protected readonly Color colorClear = new Color(0f, 0f, 0f, 0f); + static protected readonly string renderBufferName = "HighlightingSystem"; + static protected readonly Matrix4x4 identityMatrix = Matrix4x4.identity; + protected const CameraEvent queue = CameraEvent.BeforeImageEffectsOpaque; + + static protected RenderTargetIdentifier cameraTargetID; + + static protected Mesh quad; + + // Graphics device version identifiers + protected const int OGL = 0; + protected const int D3D9 = 1; + protected const int D3D11 = 2; + + // Current graphics device version: 0 = OpenGL or unknown (default), 1 = Direct3D 9, 2 = Direct3D 11 + static protected int graphicsDeviceVersion = D3D9; + #endregion + + #region Public Fields + // Depth offset factor for highlighting shaders + public float offsetFactor = 0f; + + // Depth offset units for highlighting shaders + public float offsetUnits = 0f; + + // Highlighting buffer size downsample factor + public int downsampleFactor + { + get { return _downsampleFactor; } + set + { + if (_downsampleFactor != value) + { + // Is power of two check + if ((value != 0) && ((value & (value - 1)) == 0)) + { + _downsampleFactor = value; + isDirty = true; + } + else + { + Debug.LogWarning("HighlightingSystem : Prevented attempt to set incorrect downsample factor value."); + } + } + } + } + + // Blur iterations + public int iterations + { + get { return _iterations; } + set + { + if (_iterations != value) + { + _iterations = value; + isDirty = true; + } + } + } + + // Blur minimal spread + public float blurMinSpread + { + get { return _blurMinSpread; } + set + { + if (_blurMinSpread != value) + { + _blurMinSpread = value; + isDirty = true; + } + } + } + + // Blur spread per iteration + public float blurSpread + { + get { return _blurSpread; } + set + { + if (_blurSpread != value) + { + _blurSpread = value; + isDirty = true; + } + } + } + + // Blurring intensity for the blur material + public float blurIntensity + { + get { return _blurIntensity; } + set + { + if (_blurIntensity != value) + { + _blurIntensity = value; + if (Application.isPlaying) + { + blurMaterial.SetFloat(ShaderPropertyID._Intensity, _blurIntensity); + } + } + } + } + #endregion + + #region Protected Fields + protected CommandBuffer renderBuffer; + + protected bool isDirty = true; + + protected int cachedWidth = -1; + protected int cachedHeight = -1; + protected int cachedAA = -1; + + [FormerlySerializedAs("downsampleFactor")] + [SerializeField] + protected int _downsampleFactor = 4; + + [FormerlySerializedAs("iterations")] + [SerializeField] + protected int _iterations = 2; + + [FormerlySerializedAs("blurMinSpread")] + [SerializeField] + protected float _blurMinSpread = 0.65f; + + [FormerlySerializedAs("blurSpread")] + [SerializeField] + protected float _blurSpread = 0.25f; + + [SerializeField] + protected float _blurIntensity = 0.3f; + + // RenderTargetidentifier for the highlightingBuffer RenderTexture + protected RenderTargetIdentifier highlightingBufferID; + + // RenderTexture with highlighting buffer + protected RenderTexture highlightingBuffer = null; + + // Camera reference + protected Camera cam = null; + + // True if HighlightingSystem is supported on this platform + protected bool isSupported = false; + + // True if framebuffer depth data is currently available (it is required for the highlighting occlusion feature) + protected bool isDepthAvailable = true; + + // Material parameters + protected const int BLUR = 0; + protected const int CUT = 1; + protected const int COMP = 2; + static protected readonly string[] shaderPaths = new string[] + { + "Hidden/Highlighted/Blur", + "Hidden/Highlighted/Cut", + "Hidden/Highlighted/Composite", + }; + static protected Shader[] shaders; + static protected Material[] materials; + + // Static materials + static protected Material cutMaterial; + static protected Material compMaterial; + + // Dynamic materials + protected Material blurMaterial; + + static protected bool initialized = false; + #endregion + + #region MonoBehaviour + // + protected virtual void OnEnable() + { + if (!CheckInstance()) { return; } + + Initialize(); + + isSupported = CheckSupported(); + if (!isSupported) + { + enabled = false; + Debug.LogError("HighlightingSystem : Highlighting System has been disabled due to unsupported Unity features on the current platform!"); + return; + } + + blurMaterial = new Material(materials[BLUR]); + + // Set initial intensity in blur material + blurMaterial.SetFloat(ShaderPropertyID._Intensity, _blurIntensity); + + renderBuffer = new CommandBuffer(); + renderBuffer.name = renderBufferName; + + cam = GetComponent(); + UpdateHighlightingBuffer(); + + // Force-rebuild renderBuffer + isDirty = true; + + cam.AddCommandBuffer(queue, renderBuffer); + } + + // + protected virtual void OnDisable() + { + if (renderBuffer != null) + { + cam.RemoveCommandBuffer(queue, renderBuffer); + renderBuffer = null; + } + + if (highlightingBuffer != null && highlightingBuffer.IsCreated()) + { + highlightingBuffer.Release(); + highlightingBuffer = null; + } + } + + // + protected virtual void OnPreRender() + { + UpdateHighlightingBuffer(); + + int aa = GetAA(); + + bool depthAvailable = (aa == 1); + + // In case MSAA is enabled in forward/vertex lit rendeirng paths - depth buffer is not available + if (aa > 1 && (cam.actualRenderingPath == RenderingPath.Forward || cam.actualRenderingPath == RenderingPath.VertexLit)) + { + depthAvailable = false; + } + + // Check if framebuffer depth data availability has changed + if (isDepthAvailable != depthAvailable) + { + isDepthAvailable = depthAvailable; + // Update ZWrite value for all highlighting shaders correspondingly (isDepthAvailable ? ZWrite Off : ZWrite On) + Highlighter.SetZWrite(isDepthAvailable ? 0f : 1f); + if (isDepthAvailable) + { + Debug.LogWarning("HighlightingSystem : Framebuffer depth data is available back again and will be used to occlude highlighting. Highlighting occluders disabled."); + } + else + { + Debug.LogWarning("HighlightingSystem : Framebuffer depth data is not available and can't be used to occlude highlighting. Highlighting occluders enabled."); + } + isDirty = true; + } + + // Set global depth offset properties for highlighting shaders to the values which has this HighlightingBase component + Highlighter.SetOffsetFactor(offsetFactor); + Highlighter.SetOffsetUnits(offsetUnits); + + isDirty |= HighlighterManager.isDirty; + isDirty |= HighlightersChanged(); + + if (isDirty) + { + RebuildCommandBuffer(); + isDirty = false; + } + } + + // + protected virtual void OnRenderImage(RenderTexture src, RenderTexture dst) + { + Graphics.Blit(src, dst, compMaterial); + } + #endregion + + #region Internal + // + static protected void Initialize() + { + if (initialized) { return; } + + // Determine graphics device version + string version = SystemInfo.graphicsDeviceVersion.ToLower(); + if (version.Contains("direct3d") || version.Contains("directx")) + { + if (version.Contains("direct3d 11") || version.Contains("directx 11")) { graphicsDeviceVersion = D3D11; } + else { graphicsDeviceVersion = D3D9; } + } + #if UNITY_EDITOR_WIN && (UNITY_ANDROID || UNITY_IOS) + else if (version.Contains("emulated")) + { + graphicsDeviceVersion = D3D9; + } + #endif + else + { + graphicsDeviceVersion = OGL; + } + + // Initialize shader property constants + ShaderPropertyID.Initialize(); + + // Initialize shaders and materials + int l = shaderPaths.Length; + shaders = new Shader[l]; + materials = new Material[l]; + for (int i = 0; i < l; i++) + { + Shader shader = Shader.Find(shaderPaths[i]); + shaders[i] = shader; + + Material material = new Material(shader); + materials[i] = material; + } + cutMaterial = materials[CUT]; + compMaterial = materials[COMP]; + + // Initialize static RenderTargetIdentifiers + cameraTargetID = new RenderTargetIdentifier(BuiltinRenderTextureType.CameraTarget); + + // Create static quad mesh + CreateQuad(); + + initialized = true; + } + + // + static protected void CreateQuad() + { + if (quad == null) + { + quad = new Mesh(); + } + else + { + quad.Clear(); + } + + float y1 = 1f; + float y2 = -1f; + + if (graphicsDeviceVersion == OGL) + { + y1 = -1f; + y2 = 1f; + } + + quad.vertices = new Vector3[] + { + new Vector3(-1f, y1, 0f), // Bottom-Left + new Vector3(-1f, y2, 0f), // Upper-Left + new Vector3( 1f, y2, 0f), // Upper-Right + new Vector3( 1f, y1, 0f) // Bottom-Right + }; + + quad.uv = new Vector2[] + { + new Vector2(0f, 0f), + new Vector2(0f, 1f), + new Vector2(1f, 1f), + new Vector2(1f, 0f) + }; + + quad.colors = new Color[] + { + colorClear, + colorClear, + colorClear, + colorClear + }; + + quad.triangles = new int[] { 0, 1, 2, 2, 3, 0 }; + } + + // + protected virtual int GetAA() + { + int aa = QualitySettings.antiAliasing; + if (aa == 0) { aa = 1; } + + // Reset aa value to 1 in case camera is in DeferredLighting or DeferredShading Rendering Path + if (cam.actualRenderingPath == RenderingPath.DeferredLighting || cam.actualRenderingPath == RenderingPath.DeferredShading) { aa = 1; } + + return aa; + } + + // + protected virtual void UpdateHighlightingBuffer() + { + int aa = GetAA(); + + if (cam.pixelWidth == cachedWidth && cam.pixelHeight == cachedHeight && aa == cachedAA) { return; } + + cachedWidth = cam.pixelWidth; + cachedHeight = cam.pixelHeight; + cachedAA = aa; + + if (highlightingBuffer != null && highlightingBuffer.IsCreated()) + { + highlightingBuffer.Release(); + } + + highlightingBuffer = new RenderTexture(cachedWidth, cachedHeight, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default); + highlightingBuffer.antiAliasing = cachedAA; + highlightingBuffer.filterMode = FilterMode.Point; + highlightingBuffer.useMipMap = false; + highlightingBuffer.wrapMode = TextureWrapMode.Clamp; + if (!highlightingBuffer.Create()) + { + Debug.LogError("HighlightingSystem : UpdateHighlightingBuffer() : Failed to create highlightingBuffer RenderTexture!"); + } + + highlightingBufferID = new RenderTargetIdentifier(highlightingBuffer); + Shader.SetGlobalTexture(ShaderPropertyID._HighlightingBuffer, highlightingBuffer); + + Vector4 v = new Vector4((graphicsDeviceVersion == OGL ? 1f : -1f) / (float)highlightingBuffer.width, 1f / (float)highlightingBuffer.height, 0f, 0f); + Shader.SetGlobalVector(ShaderPropertyID._HighlightingBufferTexelSize, v); + + // Always set as dirty, because camera width/height/anti-aliasing has changed + isDirty = true; + } + + // Allow only single instance of the HighlightingBase component on a GameObject + public virtual bool CheckInstance() + { + HighlightingBase[] highlightingBases = GetComponents(); + if (highlightingBases.Length > 1 && highlightingBases[0] != this) + { + enabled = false; + string className = this.GetType().ToString(); + Debug.LogWarning(string.Format("HighlightingSystem : Only single instance of the HighlightingRenderer component is allowed on a single Gameobject! {0} has been disabled on GameObject with name '{1}'.", className, name)); + return false; + } + return true; + } + + // + protected virtual bool CheckSupported() + { + // Image Effects supported? + if (!SystemInfo.supportsImageEffects) + { + Debug.LogError("HighlightingSystem : Image effects is not supported on this platform!"); + return false; + } + + // Render Textures supported? + if (!SystemInfo.supportsRenderTextures) + { + Debug.LogError("HighlightingSystem : RenderTextures is not supported on this platform!"); + return false; + } + + // Required Render Texture Format supported? + if (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGB32)) + { + Debug.LogError("HighlightingSystem : RenderTextureFormat.ARGB32 is not supported on this platform!"); + return false; + } + + if (SystemInfo.supportsStencil < 1) + { + Debug.LogError("HighlightingSystem : Stencil buffer is not supported on this platform!"); + return false; + } + + // HighlightingOpaque shader supported? + if (!Highlighter.opaqueShader.isSupported) + { + Debug.LogError("HighlightingSystem : HighlightingOpaque shader is not supported on this platform!"); + return false; + } + + // HighlightingTransparent shader supported? + if (!Highlighter.transparentShader.isSupported) + { + Debug.LogError("HighlightingSystem : HighlightingTransparent shader is not supported on this platform!"); + return false; + } + + // Required shaders supported? + for (int i = 0; i < shaders.Length; i++) + { + Shader shader = shaders[i]; + if (!shader.isSupported) + { + Debug.LogError("HighlightingSystem : Shader '" + shader.name + "' is not supported on this platform!"); + return false; + } + } + + return true; + } + + // + protected virtual bool HighlightersChanged() + { + bool changed = false; + + // Check if list of highlighted objects has changed + HashSet.Enumerator e = HighlighterManager.GetEnumerator(); + while (e.MoveNext()) + { + Highlighter highlighter = e.Current; + changed |= highlighter.UpdateHighlighting(isDepthAvailable); + } + + return changed; + } + + // + protected virtual void RebuildCommandBuffer() + { + renderBuffer.Clear(); + + RenderTargetIdentifier depthID = isDepthAvailable ? cameraTargetID : highlightingBufferID; + + // Prepare and clear render target + renderBuffer.SetRenderTarget(highlightingBufferID, depthID); + renderBuffer.ClearRenderTarget(!isDepthAvailable, true, colorClear); + + // Fill buffer with highlighters rendering commands + FillBuffer(renderBuffer, 0); // Highlighters + FillBuffer(renderBuffer, 1); // Occluders + FillBuffer(renderBuffer, 2); // See-through Highlighters + + // Create two buffers for blurring the image + RenderTargetIdentifier blur1ID = new RenderTargetIdentifier(ShaderPropertyID._HighlightingBlur1); + RenderTargetIdentifier blur2ID = new RenderTargetIdentifier(ShaderPropertyID._HighlightingBlur2); + + int width = highlightingBuffer.width / _downsampleFactor; + int height = highlightingBuffer.height / _downsampleFactor; + + renderBuffer.GetTemporaryRT(ShaderPropertyID._HighlightingBlur1, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default); + renderBuffer.GetTemporaryRT(ShaderPropertyID._HighlightingBlur2, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default); + + renderBuffer.Blit(highlightingBufferID, blur1ID); + + // Blur the small texture + bool oddEven = true; + for (int i = 0; i < _iterations; i++) + { + float off = _blurMinSpread + _blurSpread * i; + renderBuffer.SetGlobalFloat(ShaderPropertyID._HighlightingBlurOffset, off); + + if (oddEven) + { + renderBuffer.Blit(blur1ID, blur2ID, blurMaterial); + } + else + { + renderBuffer.Blit(blur2ID, blur1ID, blurMaterial); + } + + oddEven = !oddEven; + } + + // Upscale blurred texture and cut stencil from it + renderBuffer.SetGlobalTexture(ShaderPropertyID._HighlightingBlurred, oddEven ? blur1ID : blur2ID); + renderBuffer.SetRenderTarget(highlightingBufferID, depthID); + renderBuffer.DrawMesh(quad, identityMatrix, cutMaterial); + + // Cleanup + renderBuffer.ReleaseTemporaryRT(ShaderPropertyID._HighlightingBlur1); + renderBuffer.ReleaseTemporaryRT(ShaderPropertyID._HighlightingBlur2); + } + + // + protected virtual void FillBuffer(CommandBuffer buffer, int renderQueue) + { + HashSet.Enumerator e; + e = HighlighterManager.GetEnumerator(); + while (e.MoveNext()) + { + Highlighter highlighter = e.Current; + highlighter.FillBuffer(renderBuffer, renderQueue); + } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/Highlighting System/Scripts/Internal/HighlightingBase.cs.meta b/Assets/Highlighting System/Scripts/Internal/HighlightingBase.cs.meta new file mode 100644 index 0000000..5b7e435 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/HighlightingBase.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c9a69acb2622340e39a2b84d908232e3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Highlighting System/Scripts/Internal/RendererCache.cs b/Assets/Highlighting System/Scripts/Internal/RendererCache.cs new file mode 100644 index 0000000..2a3256a --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/RendererCache.cs @@ -0,0 +1,170 @@ +using UnityEngine; +using UnityEngine.Rendering; +using System.Collections; +using System.Collections.Generic; + +namespace HighlightingSystem +{ + public partial class Highlighter : MonoBehaviour + { + // Internal class for renderers caching + private class RendererCache + { + private static readonly string sRenderType = "RenderType"; + private static readonly string sOpaque = "Opaque"; + private static readonly string sTransparent = "Transparent"; + private static readonly string sTransparentCutout = "TransparentCutout"; + private static readonly string sMainTex = "_MainTex"; + + private struct Data + { + public Material material; + public int submeshIndex; + public bool transparent; + } + + private const int opaquePassID = 0; + private const int transparentPassID = 1; + + public bool visible { get; private set; } + + private GameObject go; + private Renderer renderer; + private List data; + + // Constructor + public RendererCache(Renderer r, Material sharedOpaqueMaterial, float zTest, float stencilRef) + { + data = new List(); + renderer = r; + go = r.gameObject; + + 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.SetFloat(ShaderPropertyID._ZTest, zTest); + replacementMat.SetFloat(ShaderPropertyID._StencilRef, stencilRef); + if (r is SpriteRenderer) { replacementMat.SetFloat(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); + } + } + + visible = !IsDestroyed() && IsVisible(); + } + + // + public bool UpdateVisibility() + { + bool visibleNow = !IsDestroyed() && IsVisible(); + + if (visible != visibleNow) + { + visible = visibleNow; + return true; + } + return false; + } + + // Fills given command buffer with this highlighter rendering commands + public bool FillBuffer(CommandBuffer buffer) + { + if (IsDestroyed()) { return false; } + + if (IsVisible()) + { + for (int i = 0, imax = data.Count; i < imax; i++) + { + Data d = data[i]; + buffer.DrawRenderer(renderer, 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._Outline, clr); + } + } + } + + // Sets ZTest parameter on all transparent materials of this renderer + public void SetZTestForTransparent(float zTest) + { + for (int i = 0, imax = data.Count; i < imax; i++) + { + Data d = data[i]; + if (d.transparent) + { + d.material.SetFloat(ShaderPropertyID._ZTest, zTest); + } + } + } + + // Sets Stencil Ref parameter on all transparent materials of this renderer + public void SetStencilRefForTransparent(float stencilRef) + { + for (int i = 0, imax = data.Count; i < imax; i++) + { + Data d = data[i]; + if (d.transparent) + { + d.material.SetFloat(ShaderPropertyID._StencilRef, stencilRef); + } + } + } + + // + private bool IsVisible() + { + return renderer.enabled && renderer.isVisible; + } + + // + public bool IsDestroyed() + { + return go == null || renderer == null; + } + } + } +} \ No newline at end of file diff --git a/Assets/Highlighting System/Scripts/Internal/RendererCache.cs.meta b/Assets/Highlighting System/Scripts/Internal/RendererCache.cs.meta new file mode 100644 index 0000000..3ad58bd --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/RendererCache.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f97757299618eab4490066e41a69a6ca +timeCreated: 1432110880 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Highlighting System/Scripts/Internal/ShaderPropertyID.cs b/Assets/Highlighting System/Scripts/Internal/ShaderPropertyID.cs new file mode 100644 index 0000000..f2ec4b3 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/ShaderPropertyID.cs @@ -0,0 +1,63 @@ +using UnityEngine; +using System.Collections; + +namespace HighlightingSystem +{ + static public class ShaderPropertyID + { + #region PUBLIC FIELDS + // Common + static public int _MainTex { get; private set; } + + // HighlightingSystem + static public int _Outline { get; private set; } + static public int _Cutoff { get; private set; } + static public int _Intensity { get; private set; } + static public int _ZTest { get; private set; } + static public int _StencilRef { get; private set; } + static public int _Cull { get; private set; } + static public int _HighlightingBlur1 { get; private set; } + static public int _HighlightingBlur2 { get; private set; } + + // HighlightingSystem global shader properties. Should be unique! + static public int _HighlightingBuffer { get; private set; } + static public int _HighlightingBlurred { get; private set; } + static public int _HighlightingBlurOffset { get; private set; } + static public int _HighlightingZWrite { get; private set; } + static public int _HighlightingOffsetFactor { get; private set; } + static public int _HighlightingOffsetUnits { get; private set; } + static public int _HighlightingBufferTexelSize { get; private set; } + #endregion + + #region PRIVATE FIELDS + static private bool initialized = false; + #endregion + + // + static public void Initialize() + { + if (initialized) { return; } + + _MainTex = Shader.PropertyToID("_MainTex"); + + _Outline = Shader.PropertyToID("_Outline"); + _Cutoff = Shader.PropertyToID("_Cutoff"); + _Intensity = Shader.PropertyToID("_Intensity"); + _ZTest = Shader.PropertyToID("_ZTest"); + _StencilRef = Shader.PropertyToID("_StencilRef"); + _Cull = Shader.PropertyToID("_Cull"); + _HighlightingBlur1 = Shader.PropertyToID("_HighlightingBlur1"); + _HighlightingBlur2 = Shader.PropertyToID("_HighlightingBlur2"); + + _HighlightingBuffer = Shader.PropertyToID("_HighlightingBuffer"); + _HighlightingBlurred = Shader.PropertyToID("_HighlightingBlurred"); + _HighlightingBlurOffset = Shader.PropertyToID("_HighlightingBlurOffset"); + _HighlightingZWrite = Shader.PropertyToID("_HighlightingZWrite"); + _HighlightingOffsetFactor = Shader.PropertyToID("_HighlightingOffsetFactor"); + _HighlightingOffsetUnits = Shader.PropertyToID("_HighlightingOffsetUnits"); + _HighlightingBufferTexelSize = Shader.PropertyToID("_HighlightingBufferTexelSize"); + + initialized = true; + } + } +} \ No newline at end of file diff --git a/Assets/Highlighting System/Scripts/Internal/ShaderPropertyID.cs.meta b/Assets/Highlighting System/Scripts/Internal/ShaderPropertyID.cs.meta new file mode 100644 index 0000000..fcab021 --- /dev/null +++ b/Assets/Highlighting System/Scripts/Internal/ShaderPropertyID.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bdf8fd90b69cf684cbbb8f6ec27bf953 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/fie.csproj b/fie.csproj index 52ab4e1..3d38ac8 100644 --- a/fie.csproj +++ b/fie.csproj @@ -2259,6 +2259,13 @@ + + + + + + +