SkeletonRenderTexture.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated September 24, 2021. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2022, 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
  13. * or 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
  27. * THE 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. public Material quadMaterial;
  51. protected SkeletonRenderer skeletonRenderer;
  52. protected MeshRenderer meshRenderer;
  53. protected MeshFilter meshFilter;
  54. protected MeshRenderer quadMeshRenderer;
  55. protected MeshFilter quadMeshFilter;
  56. private MaterialPropertyBlock propertyBlock;
  57. private readonly List<Material> materials = new List<Material>();
  58. protected override void Awake () {
  59. base.Awake();
  60. meshRenderer = this.GetComponent<MeshRenderer>();
  61. meshFilter = this.GetComponent<MeshFilter>();
  62. skeletonRenderer = this.GetComponent<SkeletonRenderer>();
  63. if (targetCamera == null)
  64. targetCamera = Camera.main;
  65. propertyBlock = new MaterialPropertyBlock();
  66. CreateQuadChild();
  67. }
  68. void CreateQuadChild () {
  69. quad = new GameObject(this.name + " RenderTexture", typeof(MeshRenderer), typeof(MeshFilter));
  70. quad.transform.SetParent(this.transform.parent, false);
  71. quadMeshRenderer = quad.GetComponent<MeshRenderer>();
  72. quadMeshFilter = quad.GetComponent<MeshFilter>();
  73. quadMesh = new Mesh();
  74. quadMesh.MarkDynamic();
  75. quadMesh.name = "RenderTexture Quad";
  76. quadMesh.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
  77. if (quadMaterial != null)
  78. quadMeshRenderer.material = new Material(quadMaterial);
  79. else
  80. quadMeshRenderer.material = new Material(Shader.Find("Spine/RenderQuad"));
  81. }
  82. void OnEnable () {
  83. skeletonRenderer.OnMeshAndMaterialsUpdated += RenderOntoQuad;
  84. #if HAS_FORCE_RENDER_OFF
  85. meshRenderer.forceRenderingOff = true;
  86. #else
  87. Debug.LogError("This component requires Unity 2019.3 or newer for meshRenderer.forceRenderingOff. " +
  88. "Otherwise you will see the mesh rendered twice.");
  89. #endif
  90. if (quadMeshRenderer)
  91. quadMeshRenderer.gameObject.SetActive(true);
  92. }
  93. void OnDisable () {
  94. skeletonRenderer.OnMeshAndMaterialsUpdated -= RenderOntoQuad;
  95. #if HAS_FORCE_RENDER_OFF
  96. meshRenderer.forceRenderingOff = false;
  97. #endif
  98. if (quadMeshRenderer)
  99. quadMeshRenderer.gameObject.SetActive(false);
  100. if (renderTexture)
  101. RenderTexture.ReleaseTemporary(renderTexture);
  102. allocatedRenderTextureSize = Vector2Int.zero;
  103. }
  104. void RenderOntoQuad (SkeletonRenderer skeletonRenderer) {
  105. PrepareForMesh();
  106. RenderToRenderTexture();
  107. AssignAtQuad();
  108. }
  109. protected void PrepareForMesh () {
  110. // We need to get the min/max of all four corners, rotation of the skeleton
  111. // in combination with perspective projection otherwise might lead to incorrect
  112. // screen space min/max.
  113. Bounds boundsLocalSpace = meshFilter.sharedMesh.bounds;
  114. Vector3 localCorner0 = boundsLocalSpace.min;
  115. Vector3 localCorner3 = boundsLocalSpace.max;
  116. Vector3 localCorner1 = new Vector3(localCorner0.x, localCorner3.y, localCorner0.z);
  117. Vector3 localCorner2 = new Vector3(localCorner3.x, localCorner0.y, localCorner3.z);
  118. Vector3 worldCorner0 = transform.TransformPoint(localCorner0);
  119. Vector3 worldCorner1 = transform.TransformPoint(localCorner1);
  120. Vector3 worldCorner2 = transform.TransformPoint(localCorner2);
  121. Vector3 worldCorner3 = transform.TransformPoint(localCorner3);
  122. Vector3 screenCorner0 = targetCamera.WorldToScreenPoint(worldCorner0);
  123. Vector3 screenCorner1 = targetCamera.WorldToScreenPoint(worldCorner1);
  124. Vector3 screenCorner2 = targetCamera.WorldToScreenPoint(worldCorner2);
  125. Vector3 screenCorner3 = targetCamera.WorldToScreenPoint(worldCorner3);
  126. // To avoid perspective distortion when rotated, we project all vertices
  127. // onto a plane parallel to the view frustum near plane.
  128. // Avoids the requirement of 'noperspective' vertex attribute interpolation modifier in shaders.
  129. float averageScreenDepth = (screenCorner0.z + screenCorner1.z + screenCorner2.z + screenCorner3.z) / 4.0f;
  130. screenCorner0.z = screenCorner1.z = screenCorner2.z = screenCorner3.z = averageScreenDepth;
  131. worldCornerNoDistortion0 = targetCamera.ScreenToWorldPoint(screenCorner0);
  132. worldCornerNoDistortion1 = targetCamera.ScreenToWorldPoint(screenCorner1);
  133. worldCornerNoDistortion2 = targetCamera.ScreenToWorldPoint(screenCorner2);
  134. worldCornerNoDistortion3 = targetCamera.ScreenToWorldPoint(screenCorner3);
  135. Vector3 screenSpaceMin, screenSpaceMax;
  136. PrepareTextureMapping(out screenSpaceMin, out screenSpaceMax,
  137. screenCorner0, screenCorner1, screenCorner2, screenCorner3);
  138. PrepareCommandBuffer(targetCamera, screenSpaceMin, screenSpaceMax);
  139. }
  140. protected void PrepareCommandBuffer (Camera targetCamera, Vector3 screenSpaceMin, Vector3 screenSpaceMax) {
  141. commandBuffer.Clear();
  142. commandBuffer.SetRenderTarget(renderTexture);
  143. commandBuffer.ClearRenderTarget(true, true, Color.clear);
  144. commandBuffer.SetProjectionMatrix(targetCamera.projectionMatrix);
  145. commandBuffer.SetViewMatrix(targetCamera.worldToCameraMatrix);
  146. Vector2 targetCameraViewportSize = targetCamera.pixelRect.size;
  147. commandBuffer.SetViewport(new Rect(-screenSpaceMin, targetCameraViewportSize));
  148. }
  149. protected void RenderToRenderTexture () {
  150. meshRenderer.GetPropertyBlock(propertyBlock);
  151. meshRenderer.GetSharedMaterials(materials);
  152. for (int i = 0; i < materials.Count; i++)
  153. commandBuffer.DrawMesh(meshFilter.sharedMesh, transform.localToWorldMatrix,
  154. materials[i], meshRenderer.subMeshStartIndex + i, -1, propertyBlock);
  155. Graphics.ExecuteCommandBuffer(commandBuffer);
  156. }
  157. protected override void AssignMeshAtRenderer () {
  158. quadMeshFilter.mesh = quadMesh;
  159. quadMeshRenderer.sharedMaterial.mainTexture = this.renderTexture;
  160. quadMeshRenderer.sharedMaterial.color = color;
  161. }
  162. #endif
  163. }
  164. }