123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- // Copyright (C) 2019-2021 Alexander Bogarsukov. All rights reserved.
- // See the LICENSE.md file in the project root for more information.
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.Rendering;
- namespace UnityFx.Outline
- {
- /// <summary>
- /// Renders outlines at specific camera. Should be attached to camera to function.
- /// </summary>
- /// <seealso cref="OutlineLayer"/>
- /// <seealso cref="OutlineBehaviour"/>
- /// <seealso cref="OutlineSettings"/>
- /// <seealso href="https://willweissman.wordpress.com/tutorials/shaders/unity-shaderlab-object-outlines/"/>
- [ExecuteInEditMode]
- [RequireComponent(typeof(Camera))]
- public sealed partial class OutlineEffect : MonoBehaviour
- {
- #region data
- [SerializeField, Tooltip(OutlineResources.OutlineResourcesTooltip)]
- private OutlineResources _outlineResources;
- [SerializeField, Tooltip(OutlineResources.OutlineLayerCollectionTooltip)]
- private OutlineLayerCollection _outlineLayers;
- [SerializeField, HideInInspector]
- private CameraEvent _cameraEvent = OutlineRenderer.RenderEvent;
- private Camera _camera;
- private CommandBuffer _commandBuffer;
- private List<OutlineRenderObject> _renderObjects = new List<OutlineRenderObject>(16);
- #endregion
- #region interface
- /// <summary>
- /// Gets or sets resources used by the effect implementation.
- /// </summary>
- /// <exception cref="ArgumentNullException">Thrown if setter argument is <see langword="null"/>.</exception>
- public OutlineResources OutlineResources
- {
- get
- {
- return _outlineResources;
- }
- set
- {
- if (value is null)
- {
- throw new ArgumentNullException(nameof(OutlineResources));
- }
- _outlineResources = value;
- }
- }
- /// <summary>
- /// Gets collection of outline layers.
- /// </summary>
- public OutlineLayerCollection OutlineLayers
- {
- get
- {
- return _outlineLayers;
- }
- set
- {
- _outlineLayers = value;
- }
- }
- /// <summary>
- /// Gets outline layers (for internal use only).
- /// </summary>
- internal OutlineLayerCollection OutlineLayersInternal => _outlineLayers;
- /// <summary>
- /// Gets or sets <see cref="CameraEvent"/> used to render the outlines.
- /// </summary>
- public CameraEvent RenderEvent
- {
- get
- {
- return _cameraEvent;
- }
- set
- {
- if (_cameraEvent != value)
- {
- if (_commandBuffer != null)
- {
- var camera = GetComponent<Camera>();
- if (camera)
- {
- camera.RemoveCommandBuffer(_cameraEvent, _commandBuffer);
- camera.AddCommandBuffer(value, _commandBuffer);
- }
- }
- _cameraEvent = value;
- }
- }
- }
- /// <summary>
- /// Adds the <see cref="GameObject"/> passed to the first outline layer. Creates the layer if needed.
- /// </summary>
- /// <param name="go">The <see cref="GameObject"/> to add and render outline for.</param>
- /// <seealso cref="AddGameObject(GameObject, int)"/>
- public void AddGameObject(GameObject go)
- {
- AddGameObject(go, 0);
- }
- /// <summary>
- /// Adds the <see cref="GameObject"/> passed to the specified outline layer. Creates the layer if needed.
- /// </summary>
- /// <param name="go">The <see cref="GameObject"/> to add and render outline for.</param>
- /// <seealso cref="AddGameObject(GameObject)"/>
- public void AddGameObject(GameObject go, int layerIndex)
- {
- if (layerIndex < 0)
- {
- throw new ArgumentOutOfRangeException("layerIndex");
- }
- CreateLayersIfNeeded();
- while (_outlineLayers.Count <= layerIndex)
- {
- _outlineLayers.Add(new OutlineLayer());
- }
- _outlineLayers[layerIndex].Add(go);
- }
- /// <summary>
- /// Removes the specified <see cref="GameObject"/> from <see cref="OutlineLayers"/>.
- /// </summary>
- /// <param name="go">A <see cref="GameObject"/> to remove.</param>
- public void RemoveGameObject(GameObject go)
- {
- if (_outlineLayers)
- {
- _outlineLayers.Remove(go);
- }
- }
- #endregion
- #region MonoBehaviour
- private void Awake()
- {
- OutlineResources.LogSrpNotSupported(this);
- OutlineResources.LogPpNotSupported(this);
- }
- private void OnEnable()
- {
- InitCameraAndCommandBuffer();
- }
- private void OnDisable()
- {
- ReleaseCameraAndCommandBuffer();
- }
- private void OnPreRender()
- {
- FillCommandBuffer();
- }
- private void OnDestroy()
- {
- // TODO: Find a way to do this once per OutlineLayerCollection instance.
- if (_outlineLayers)
- {
- _outlineLayers.Reset();
- }
- }
- #if UNITY_EDITOR
- //private void OnValidate()
- //{
- // InitCameraAndCommandBuffer();
- // FillCommandBuffer();
- //}
- private void Reset()
- {
- _outlineLayers = null;
- }
- #endif
- #endregion
- #region implementation
- private void InitCameraAndCommandBuffer()
- {
- _camera = GetComponent<Camera>();
- if (_camera && _commandBuffer is null)
- {
- _commandBuffer = new CommandBuffer
- {
- name = string.Format("{0} - {1}", GetType().Name, name)
- };
- _camera.depthTextureMode |= DepthTextureMode.Depth;
- _camera.AddCommandBuffer(_cameraEvent, _commandBuffer);
- }
- }
- private void ReleaseCameraAndCommandBuffer()
- {
- if (_commandBuffer != null)
- {
- if (_camera)
- {
- _camera.RemoveCommandBuffer(_cameraEvent, _commandBuffer);
- }
- _commandBuffer.Dispose();
- _commandBuffer = null;
- }
- _camera = null;
- }
- private void FillCommandBuffer()
- {
- if (_camera && _outlineLayers && _commandBuffer != null)
- {
- _commandBuffer.Clear();
- if (_outlineResources && _outlineResources.IsValid)
- {
- using (var renderer = new OutlineRenderer(_commandBuffer, _outlineResources, _camera.actualRenderingPath))
- {
- _renderObjects.Clear();
- _outlineLayers.GetRenderObjects(_renderObjects);
- renderer.Render(_renderObjects);
- }
- }
- }
- }
- private void CreateLayersIfNeeded()
- {
- if (_outlineLayers is null)
- {
- _outlineLayers = ScriptableObject.CreateInstance<OutlineLayerCollection>();
- _outlineLayers.name = "OutlineLayers";
- }
- }
- #endregion
- }
- }
|