Files
2024-12-18 15:25:43 +01:00

548 lines
15 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
namespace EPOOutline
{
public enum DilateQuality
{
Base,
High,
Ultra
}
public enum RenderingMode
{
LDR,
HDR
}
public enum OutlineRenderingStrategy
{
Default,
PerObject
}
public enum RenderStage
{
BeforeTransparents,
AfterTransparents
}
[ExecuteAlways]
[RequireComponent(typeof(Camera))]
public class Outliner : MonoBehaviour
{
#if UNITY_EDITOR
private static GameObject lastSelectedOutliner;
private static List<Outliner> outliners = new List<Outliner>();
#endif
private static List<Outlinable> temporaryOutlinables = new List<Outlinable>();
private OutlineParameters parameters = new OutlineParameters();
#if UNITY_EDITOR
private OutlineParameters editorPreviewParameters = new OutlineParameters();
#endif
private Camera targetCamera;
[SerializeField]
private RenderStage stage = RenderStage.AfterTransparents;
[SerializeField]
private OutlineRenderingStrategy renderingStrategy = OutlineRenderingStrategy.Default;
[SerializeField]
private string renderTargetName = string.Empty;
[SerializeField]
private RenderingMode renderingMode;
[SerializeField]
private long outlineLayerMask = -1;
[SerializeField]
private bool scaleIndepented = true;
[SerializeField]
[Range(0.15f, 1.0f)]
private float primaryRendererScale = 0.75f;
[SerializeField]
[Range(0.0f, 2.0f)]
private float blurShift = 1.0f;
[SerializeField]
[Range(0.0f, 2.0f)]
private float dilateShift = 1.0f;
[SerializeField]
[FormerlySerializedAs("dilateIterrations")]
private int dilateIterations = 1;
[SerializeField]
private DilateQuality dilateQuality;
[SerializeField]
[FormerlySerializedAs("blurIterrations")]
private int blurIterations = 1;
[SerializeField]
private BlurType blurType = BlurType.Box;
[SerializeField]
[Range(0.05f, 1.0f)]
private float infoRendererScale = 0.75f;
private CameraEvent Event
{
get
{
return stage == RenderStage.BeforeTransparents ? CameraEvent.AfterForwardOpaque : CameraEvent.BeforeImageEffects;
}
}
public OutlineRenderingStrategy RenderingStrategy
{
get
{
return renderingStrategy;
}
set
{
renderingStrategy = value;
}
}
public RenderStage RenderStage
{
get
{
return stage;
}
set
{
stage = value;
}
}
public DilateQuality DilateQuality
{
get
{
return dilateQuality;
}
set
{
dilateQuality = value;
}
}
public bool HasCutomRenderTarget
{
get
{
return !string.IsNullOrEmpty(renderTargetName);
}
}
private RenderingMode RenderingMode
{
get
{
return renderingMode;
}
set
{
renderingMode = value;
}
}
public float BlurShift
{
get
{
return blurShift;
}
set
{
blurShift = Mathf.Clamp(value, 0, 2.0f);
}
}
public float DilateShift
{
get
{
return dilateShift;
}
set
{
dilateShift = Mathf.Clamp(value, 0, 2.0f);
}
}
public long OutlineLayerMask
{
get
{
return outlineLayerMask;
}
set
{
outlineLayerMask = value;
}
}
public bool ScaleIndependent
{
get
{
return scaleIndepented;
}
set
{
scaleIndepented = value;
}
}
public float InfoRendererScale
{
get
{
return infoRendererScale;
}
set
{
infoRendererScale = Mathf.Clamp01(value);
}
}
public float PrimaryRendererScale
{
get
{
return primaryRendererScale;
}
set
{
primaryRendererScale = Mathf.Clamp01(value);
}
}
[Obsolete("Fixed incorrect spelling. Use BlurIterations instead")]
public int BlurIterrations
{
get
{
return BlurIterations;
}
set
{
BlurIterations = value;
}
}
public int BlurIterations
{
get
{
return blurIterations;
}
set
{
blurIterations = value > 0 ? value : 0;
}
}
public BlurType BlurType
{
get
{
return blurType;
}
set
{
blurType = value;
}
}
[Obsolete("Fixed incorrect spelling. Use DilateIterations instead")]
public int DilateIterration
{
get
{
return DilateIterations;
}
set
{
DilateIterations = value;
}
}
public int DilateIterations
{
get
{
return dilateIterations;
}
set
{
dilateIterations = value > 0 ? value : 0;
}
}
public RenderTargetIdentifier GetRenderTarget(OutlineParameters parameters)
{
return TargetsHolder.Instance.GetTarget(parameters, renderTargetName);
}
private void OnValidate()
{
if (blurIterations < 0)
blurIterations = 0;
if (dilateIterations < 0)
dilateIterations = 0;
}
private void OnEnable()
{
if (targetCamera == null)
targetCamera = GetComponent<Camera>();
targetCamera.forceIntoRenderTexture = targetCamera.stereoTargetEye == StereoTargetEyeMask.None || !UnityEngine.XR.XRSettings.enabled;
#if UNITY_EDITOR
outliners.Add(this);
#endif
parameters.CheckInitialization();
parameters.Buffer.name = "Outline";
#if UNITY_EDITOR
editorPreviewParameters.CheckInitialization();
editorPreviewParameters.Buffer.name = "Editor outline";
#endif
}
private void OnDestroy()
{
#if UNITY_EDITOR
GameObject.DestroyImmediate(editorPreviewParameters.BlitMesh);
if (editorPreviewParameters.Buffer != null)
editorPreviewParameters.Buffer.Dispose();
#endif
GameObject.DestroyImmediate(parameters.BlitMesh);
if (parameters.Buffer != null)
parameters.Buffer.Dispose();
}
private void OnDisable()
{
if (targetCamera != null)
UpdateBuffer(targetCamera, parameters.Buffer, true);
#if UNITY_EDITOR
RemoveFromAllSceneViews();
outliners.Remove(this);
#endif
#if UNITY_EDITOR
foreach (var view in UnityEditor.SceneView.sceneViews)
{
var viewToUpdate = (UnityEditor.SceneView)view;
viewToUpdate.camera.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, editorPreviewParameters.Buffer);
viewToUpdate.camera.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, editorPreviewParameters.Buffer);
}
#endif
}
private void UpdateBuffer(Camera targetCamera, CommandBuffer buffer, bool removeOnly)
{
targetCamera.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, buffer);
targetCamera.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, buffer);
if (removeOnly)
return;
targetCamera.AddCommandBuffer(Event, buffer);
}
private void OnPreRender()
{
if (GraphicsSettings.defaultRenderPipeline != null)
return;
parameters.OutlinablesToRender.Clear();
SetupOutline(targetCamera, parameters, false);
}
private void SetupOutline(Camera cameraToUse, OutlineParameters parametersToUse, bool isEditor)
{
UpdateBuffer(cameraToUse, parametersToUse.Buffer, false);
UpdateParameters(parametersToUse, cameraToUse, isEditor);
parametersToUse.Buffer.Clear();
if (renderingStrategy == OutlineRenderingStrategy.Default)
{
OutlineEffect.SetupOutline(parametersToUse);
parametersToUse.BlitMesh = null;
parametersToUse.MeshPool.ReleaseAllMeshes();
}
else
{
temporaryOutlinables.Clear();
temporaryOutlinables.AddRange(parametersToUse.OutlinablesToRender);
parametersToUse.OutlinablesToRender.Clear();
parametersToUse.OutlinablesToRender.Add(null);
foreach (var outlinable in temporaryOutlinables)
{
parametersToUse.OutlinablesToRender[0] = outlinable;
OutlineEffect.SetupOutline(parametersToUse);
parametersToUse.BlitMesh = null;
}
parametersToUse.MeshPool.ReleaseAllMeshes();
}
}
#if UNITY_EDITOR
private void RemoveFromAllSceneViews()
{
foreach (var view in UnityEditor.SceneView.sceneViews)
{
var viewToUpdate = (UnityEditor.SceneView)view;
var eventTransferer = viewToUpdate.camera.GetComponent<OnPreRenderEventTransferer>();
if (eventTransferer != null)
eventTransferer.OnPreRenderEvent -= UpdateEditorCamera;
viewToUpdate.camera.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, editorPreviewParameters.Buffer);
viewToUpdate.camera.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, editorPreviewParameters.Buffer);
}
}
private void LateUpdate()
{
if (lastSelectedOutliner == null && outliners.Count > 0)
lastSelectedOutliner = outliners[0].gameObject;
var isSelected = Array.Find(UnityEditor.Selection.gameObjects, x => x == gameObject) ?? lastSelectedOutliner != null;
if (isSelected)
lastSelectedOutliner = gameObject;
foreach (var view in UnityEditor.SceneView.sceneViews)
{
var viewToUpdate = (UnityEditor.SceneView)view;
var eventTransferer = viewToUpdate.camera.GetComponent<OnPreRenderEventTransferer>();
if (eventTransferer != null)
eventTransferer.OnPreRenderEvent -= UpdateEditorCamera;
UpdateBuffer(viewToUpdate.camera, editorPreviewParameters.Buffer, true);
}
if (!isSelected)
return;
foreach (var view in UnityEditor.SceneView.sceneViews)
{
var viewToUpdate = (UnityEditor.SceneView)view;
if (!viewToUpdate.sceneViewState.showImageEffects)
continue;
var eventTransferer = viewToUpdate.camera.GetComponent<OnPreRenderEventTransferer>();
if (eventTransferer == null)
eventTransferer = viewToUpdate.camera.gameObject.AddComponent<OnPreRenderEventTransferer>();
eventTransferer.OnPreRenderEvent += UpdateEditorCamera;
}
}
private void UpdateEditorCamera(Camera camera)
{
SetupOutline(camera, editorPreviewParameters, true);
}
#endif
public void UpdateSharedParameters(OutlineParameters parameters, Camera camera, bool editorCamera)
{
parameters.DilateQuality = DilateQuality;
parameters.ScaleIndependent = scaleIndepented;
parameters.Camera = camera;
parameters.IsEditorCamera = editorCamera;
parameters.PrimaryBufferScale = primaryRendererScale;
parameters.InfoBufferScale = infoRendererScale;
parameters.BlurIterations = blurIterations;
parameters.BlurType = blurType;
parameters.DilateIterations = dilateIterations;
parameters.BlurShift = blurShift;
parameters.DilateShift = dilateShift;
parameters.UseHDR = camera.allowHDR && (RenderingMode == RenderingMode.HDR);
parameters.EyeMask = camera.stereoTargetEye;
parameters.OutlineLayerMask = outlineLayerMask;
parameters.Prepare();
}
private void UpdateParameters(OutlineParameters parameters, Camera camera, bool editorCamera)
{
parameters.DepthTarget = RenderTargetUtility.ComposeTarget(parameters, BuiltinRenderTextureType.CameraTarget);
var targetTexture = camera.targetTexture == null ? camera.activeTexture : camera.targetTexture;
if (UnityEngine.XR.XRSettings.enabled
&& !parameters.IsEditorCamera
&& parameters.EyeMask != StereoTargetEyeMask.None)
{
var descriptor = UnityEngine.XR.XRSettings.eyeTextureDesc;
parameters.TargetWidth = descriptor.width;
parameters.TargetHeight = descriptor.height;
}
else
{
parameters.TargetWidth = targetTexture != null ? targetTexture.width : camera.scaledPixelWidth;
parameters.TargetHeight = targetTexture != null ? targetTexture.height : camera.scaledPixelHeight;
}
parameters.Antialiasing = editorCamera ? (targetTexture == null ? 1 : targetTexture.antiAliasing) : CameraUtility.GetMSAA(targetCamera);
parameters.Target = RenderTargetUtility.ComposeTarget(parameters, HasCutomRenderTarget && !editorCamera ? GetRenderTarget(parameters) :
BuiltinRenderTextureType.CameraTarget);
parameters.Camera = camera;
Outlinable.GetAllActiveOutlinables(parameters.Camera, parameters.OutlinablesToRender);
RendererFilteringUtility.Filter(parameters.Camera, parameters);
UpdateSharedParameters(parameters, camera, editorCamera);
}
}
}