SkeletonRenderTexture.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated July 28, 2023. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2023, Esoteric Software LLC
  6. *
  7. * Integration of the Spine Runtimes into software or otherwise creating
  8. * derivative works of the Spine Runtimes is permitted under the terms and
  9. * conditions of Section 2 of the Spine Editor License Agreement:
  10. * http://esotericsoftware.com/spine-editor-license
  11. *
  12. * Otherwise, it is permitted to integrate the Spine Runtimes into software or
  13. * otherwise create derivative works of the Spine Runtimes (collectively,
  14. * "Products"), provided that each user of the Products must obtain their own
  15. * Spine Editor license and redistribution of the Products in any form must
  16. * include this license and copyright notice.
  17. *
  18. * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
  27. * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *****************************************************************************/
  29. #if UNITY_2019_3_OR_NEWER
  30. #define HAS_FORCE_RENDER_OFF
  31. #endif
  32. #if UNITY_2018_2_OR_NEWER
  33. #define HAS_GET_SHARED_MATERIALS
  34. #endif
  35. using System.Collections.Generic;
  36. using UnityEngine;
  37. using UnityEngine.Rendering;
  38. namespace Spine.Unity.Examples {
  39. /// <summary>
  40. /// When enabled, this component renders a skeleton to a RenderTexture and
  41. /// then draws this RenderTexture at a quad of the same size.
  42. /// This allows changing transparency at a single quad, which produces a more
  43. /// natural fadeout effect.
  44. /// Note: It is recommended to keep this component disabled as much as possible
  45. /// because of the additional rendering overhead. Only enable it when alpha blending is required.
  46. /// </summary>
  47. [RequireComponent(typeof(SkeletonRenderer))]
  48. public class SkeletonRenderTexture : SkeletonRenderTextureBase {
  49. #if HAS_GET_SHARED_MATERIALS
  50. protected SkeletonRenderer skeletonRenderer;
  51. protected MeshRenderer meshRenderer;
  52. protected MeshFilter meshFilter;
  53. protected MeshRenderer quadMeshRenderer;
  54. protected MeshFilter quadMeshFilter;
  55. private MaterialPropertyBlock propertyBlock;
  56. private readonly List<Material> materials = new List<Material>();
  57. protected override void Awake () {
  58. base.Awake();
  59. meshRenderer = this.GetComponent<MeshRenderer>();
  60. meshFilter = this.GetComponent<MeshFilter>();
  61. skeletonRenderer = this.GetComponent<SkeletonRenderer>();
  62. if (targetCamera == null)
  63. targetCamera = Camera.main;
  64. propertyBlock = new MaterialPropertyBlock();
  65. CreateQuadChild();
  66. }
  67. #if UNITY_EDITOR
  68. protected void Reset () {
  69. string[] folders = { "Assets", "Packages" };
  70. string[] assets = UnityEditor.AssetDatabase.FindAssets("t:material RenderQuadMaterial", folders);
  71. if (assets.Length > 0) {
  72. string materialPath = UnityEditor.AssetDatabase.GUIDToAssetPath(assets[0]);
  73. quadMaterial = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(materialPath);
  74. }
  75. }
  76. #endif
  77. void CreateQuadChild () {
  78. quad = new GameObject(this.name + " RenderTexture", typeof(MeshRenderer), typeof(MeshFilter));
  79. quad.transform.SetParent(this.transform.parent, false);
  80. quadMeshRenderer = quad.GetComponent<MeshRenderer>();
  81. quadMeshFilter = quad.GetComponent<MeshFilter>();
  82. quadMeshRenderer.sortingOrder = meshRenderer.sortingOrder;
  83. quadMeshRenderer.sortingLayerID = meshRenderer.sortingLayerID;
  84. quadMesh = new Mesh();
  85. quadMesh.MarkDynamic();
  86. quadMesh.name = "RenderTexture Quad";
  87. quadMesh.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
  88. if (quadMaterial != null)
  89. quadMeshRenderer.material = new Material(quadMaterial);
  90. else
  91. quadMeshRenderer.material = new Material(Shader.Find("Spine/RenderQuad"));
  92. }
  93. void OnEnable () {
  94. skeletonRenderer.OnMeshAndMaterialsUpdated += RenderOntoQuad;
  95. #if HAS_FORCE_RENDER_OFF
  96. meshRenderer.forceRenderingOff = true;
  97. #else
  98. Debug.LogError("This component requires Unity 2019.3 or newer for meshRenderer.forceRenderingOff. " +
  99. "Otherwise you will see the mesh rendered twice.");
  100. #endif
  101. if (quadMeshRenderer)
  102. quadMeshRenderer.gameObject.SetActive(true);
  103. }
  104. void OnDisable () {
  105. skeletonRenderer.OnMeshAndMaterialsUpdated -= RenderOntoQuad;
  106. #if HAS_FORCE_RENDER_OFF
  107. meshRenderer.forceRenderingOff = false;
  108. #endif
  109. if (quadMeshRenderer)
  110. quadMeshRenderer.gameObject.SetActive(false);
  111. if (renderTexture)
  112. RenderTexture.ReleaseTemporary(renderTexture);
  113. allocatedRenderTextureSize = Vector2Int.zero;
  114. }
  115. void RenderOntoQuad (SkeletonRenderer skeletonRenderer) {
  116. Vector3 size = meshFilter.sharedMesh.bounds.size;
  117. if (size.x == 0f || size.y == 0f) {
  118. AssignNullMeshAtQuad();
  119. return;
  120. }
  121. PrepareForMesh();
  122. RenderToRenderTexture();
  123. AssignAtQuad();
  124. }
  125. protected void PrepareForMesh () {
  126. // We need to get the min/max of all four corners, rotation of the skeleton
  127. // in combination with perspective projection otherwise might lead to incorrect
  128. // screen space min/max.
  129. Bounds boundsLocalSpace = meshFilter.sharedMesh.bounds;
  130. Vector3 localCorner0 = boundsLocalSpace.min;
  131. Vector3 localCorner3 = boundsLocalSpace.max;
  132. Vector3 localCorner1 = new Vector3(localCorner0.x, localCorner3.y, localCorner0.z);
  133. Vector3 localCorner2 = new Vector3(localCorner3.x, localCorner0.y, localCorner3.z);
  134. Vector3 worldCorner0 = transform.TransformPoint(localCorner0);
  135. Vector3 worldCorner1 = transform.TransformPoint(localCorner1);
  136. Vector3 worldCorner2 = transform.TransformPoint(localCorner2);
  137. Vector3 worldCorner3 = transform.TransformPoint(localCorner3);
  138. Vector3 screenCorner0 = targetCamera.WorldToScreenPoint(worldCorner0);
  139. Vector3 screenCorner1 = targetCamera.WorldToScreenPoint(worldCorner1);
  140. Vector3 screenCorner2 = targetCamera.WorldToScreenPoint(worldCorner2);
  141. Vector3 screenCorner3 = targetCamera.WorldToScreenPoint(worldCorner3);
  142. // To avoid perspective distortion when rotated, we project all vertices
  143. // onto a plane parallel to the view frustum near plane.
  144. // Avoids the requirement of 'noperspective' vertex attribute interpolation modifier in shaders.
  145. float averageScreenDepth = (screenCorner0.z + screenCorner1.z + screenCorner2.z + screenCorner3.z) / 4.0f;
  146. screenCorner0.z = screenCorner1.z = screenCorner2.z = screenCorner3.z = averageScreenDepth;
  147. worldCornerNoDistortion0 = targetCamera.ScreenToWorldPoint(screenCorner0);
  148. worldCornerNoDistortion1 = targetCamera.ScreenToWorldPoint(screenCorner1);
  149. worldCornerNoDistortion2 = targetCamera.ScreenToWorldPoint(screenCorner2);
  150. worldCornerNoDistortion3 = targetCamera.ScreenToWorldPoint(screenCorner3);
  151. Vector3 screenSpaceMin, screenSpaceMax;
  152. PrepareTextureMapping(out screenSpaceMin, out screenSpaceMax,
  153. screenCorner0, screenCorner1, screenCorner2, screenCorner3);
  154. PrepareCommandBuffer(targetCamera, screenSpaceMin, screenSpaceMax);
  155. }
  156. protected void PrepareCommandBuffer (Camera targetCamera, Vector3 screenSpaceMin, Vector3 screenSpaceMax) {
  157. commandBuffer.Clear();
  158. commandBuffer.SetRenderTarget(renderTexture);
  159. commandBuffer.ClearRenderTarget(true, true, Color.clear);
  160. commandBuffer.SetProjectionMatrix(targetCamera.projectionMatrix);
  161. commandBuffer.SetViewMatrix(targetCamera.worldToCameraMatrix);
  162. Vector2 targetCameraViewportSize = targetCamera.pixelRect.size;
  163. Rect viewportRect = new Rect(-screenSpaceMin * downScaleFactor, targetCameraViewportSize * downScaleFactor);
  164. commandBuffer.SetViewport(viewportRect);
  165. }
  166. protected void RenderToRenderTexture () {
  167. meshRenderer.GetPropertyBlock(propertyBlock);
  168. meshRenderer.GetSharedMaterials(materials);
  169. for (int i = 0; i < materials.Count; i++) {
  170. foreach (int shaderPass in shaderPasses)
  171. commandBuffer.DrawMesh(meshFilter.sharedMesh, transform.localToWorldMatrix,
  172. materials[i], meshRenderer.subMeshStartIndex + i, shaderPass, propertyBlock);
  173. }
  174. Graphics.ExecuteCommandBuffer(commandBuffer);
  175. }
  176. protected override void AssignMeshAtRenderer () {
  177. quadMeshFilter.mesh = quadMesh;
  178. quadMeshRenderer.sharedMaterial.mainTexture = this.renderTexture;
  179. quadMeshRenderer.sharedMaterial.color = color;
  180. }
  181. protected void AssignNullMeshAtQuad () {
  182. quadMeshFilter.mesh = null;
  183. }
  184. #endif
  185. }
  186. }