Bladeren bron

[unity] Added `RenderExistingMeshGraphic` sample component similar to `RenderExistingMesh`.

Harald Csaszar 5 maanden geleden
bovenliggende
commit
a0721dbf4b

+ 5 - 0
CHANGELOG.md

@@ -170,6 +170,11 @@
   - `Universal Render Pipeline/Spine/Skeleton Lit` shader now supports [Adaptive Probe Volumes (APV)](https://docs.unity3d.com/6000.0/Documentation/Manual/urp/probevolumes-concept.html) introduced in Unity 6. The shader also provides a new material property `APV per Pixel` to either calculate APV lighting contribution per pixel (the default) or per vertex.
   - `Universal Render Pipeline/Spine/Sprite` shader now also supports [Adaptive Probe Volumes (APV)](https://docs.unity3d.com/6000.0/Documentation/Manual/urp/probevolumes-concept.html) introduced in Unity 6. APV lighting contribution is automatically calculated per pixel.
   - All Spine Outline shaders, including the URP outline shaders, now provide an additional parameter `Fill`. Enable it to also fill the opaque area inside the outline with the outline color. Prevents a semi-transparent gap between outline and skeleton. Defaults to `disabled` to maintain existing behaviour.
+  - Added example component `RenderExistingMeshGraphic` (similar to `RenderExistingMesh`) to render a `SkeletonGraphic` mesh again with different materials. This might be required by e.g. URP and SkeletonGraphic outline shaders skipping additional render passes. To add a second outline variant of your SkeletonGraphic:
+    1. Add a GameObject at the same hierarchy level as the reference SkeletonGraphic and move it before the reference SkeletonGraphic to render behind.
+    2. Add a `RenderExistingMeshGraphic` component.
+    3. In the `RenderExistingMeshGraphic` component Inspector at `Reference Skeleton Graphic` assign the original `SkeletonGraphic` object.
+    4. At `Replacement Material` assign e.g. the included _SkeletonGraphicDefaultOutline_ material to replace all materials with this material. Alternatively, if `Multiple CanvasRenderers` is enabled at the reference SkeletonGraphic, you can add entries to the `Replacement Materials` list and at each entry assign the original SkeletonGraphic material (e.g. _SkeletonGraphicDefault_) to be replaced and the respective `Replacement Material` (e.g. _SkeletonGraphicDefaultOutline_).
 
 - **Breaking changes**
 

+ 214 - 0
spine-unity/Assets/Spine Examples/Scripts/Sample Components/RenderExistingMeshGraphic.cs

@@ -0,0 +1,214 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated July 28, 2023. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2025, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software or
+ * otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
+ * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
+ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
+#define NEW_PREFAB_SYSTEM
+#endif
+
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace Spine.Unity.Examples {
+	using MaterialReplacement = RenderExistingMesh.MaterialReplacement;
+
+#if NEW_PREFAB_SYSTEM
+	[ExecuteAlways]
+#else
+	[ExecuteInEditMode]
+#endif
+	public class RenderExistingMeshGraphic : MonoBehaviour {
+		public SkeletonGraphic referenceSkeletonGraphic;
+		public Material replacementMaterial;
+
+		public MaterialReplacement[] replacementMaterials = new MaterialReplacement[0];
+
+		SkeletonSubmeshGraphic ownGraphic;
+		public List<SkeletonSubmeshGraphic> ownSubmeshGraphics;
+
+#if UNITY_EDITOR
+		private void Reset () {
+			Awake();
+			LateUpdate();
+		}
+#endif
+
+		void Awake () {
+			// subscribe to OnMeshAndMaterialsUpdated
+			if (referenceSkeletonGraphic) {
+				referenceSkeletonGraphic.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
+				referenceSkeletonGraphic.OnMeshAndMaterialsUpdated += UpdateOnCallback;
+			}
+
+			ownGraphic = this.GetComponent<SkeletonSubmeshGraphic>();
+			if (referenceSkeletonGraphic) {
+				if (referenceSkeletonGraphic.allowMultipleCanvasRenderers)
+					EnsureCanvasRendererCount(referenceSkeletonGraphic.canvasRenderers.Count);
+				else
+					SetupSubmeshGraphic();
+			}
+		}
+
+		protected void OnDisable () {
+			if (referenceSkeletonGraphic) {
+				referenceSkeletonGraphic.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
+			}
+		}
+
+		protected void OnEnable () {
+#if UNITY_EDITOR
+			// handle disabled scene reload
+			if (Application.isPlaying) {
+				Awake();
+				return;
+			}
+#endif
+			if (referenceSkeletonGraphic) {
+				referenceSkeletonGraphic.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
+				referenceSkeletonGraphic.OnMeshAndMaterialsUpdated += UpdateOnCallback;
+			}
+		}
+
+		void SetupSubmeshGraphic () {
+			if (ownGraphic == null)
+				ownGraphic = this.gameObject.AddComponent<SkeletonSubmeshGraphic>();
+
+			ownGraphic.maskable = referenceSkeletonGraphic.maskable;
+			ownGraphic.canvasRenderer.cullTransparentMesh = referenceSkeletonGraphic.canvasRenderer.cullTransparentMesh;
+			ownGraphic.canvasRenderer.SetMaterial(replacementMaterial, referenceSkeletonGraphic.mainTexture);
+		}
+
+		protected void EnsureCanvasRendererCount (int targetCount) {
+			if (ownSubmeshGraphics == null)
+				ownSubmeshGraphics = new List<SkeletonSubmeshGraphic>();
+
+			bool cullTransparentMesh = referenceSkeletonGraphic.canvasRenderer.cullTransparentMesh;
+			Vector2 pivot = referenceSkeletonGraphic.rectTransform.pivot;
+
+			int currentCount = ownSubmeshGraphics.Count;
+			for (int i = currentCount; i < targetCount; ++i) {
+				GameObject go = new GameObject(string.Format("Renderer{0}", i), typeof(RectTransform));
+				go.transform.SetParent(this.transform, false);
+				go.transform.localPosition = Vector3.zero;
+				CanvasRenderer canvasRenderer = go.AddComponent<CanvasRenderer>();
+				canvasRenderer.cullTransparentMesh = cullTransparentMesh;
+				SkeletonSubmeshGraphic submeshGraphic = go.AddComponent<SkeletonSubmeshGraphic>();
+				ownSubmeshGraphics.Add(submeshGraphic);
+				submeshGraphic.maskable = referenceSkeletonGraphic.maskable;
+				submeshGraphic.raycastTarget = false;
+				submeshGraphic.rectTransform.pivot = pivot;
+				submeshGraphic.rectTransform.anchorMin = Vector2.zero;
+				submeshGraphic.rectTransform.anchorMax = Vector2.one;
+				submeshGraphic.rectTransform.sizeDelta = Vector2.zero;
+			}
+		}
+
+		protected void UpdateCanvasRenderers () {
+			Mesh[] referenceMeshes = referenceSkeletonGraphic.MeshesMultipleCanvasRenderers.Items;
+			Material[] referenceMaterials = referenceSkeletonGraphic.MaterialsMultipleCanvasRenderers.Items;
+			Texture[] referenceTextures = referenceSkeletonGraphic.TexturesMultipleCanvasRenderers.Items;
+
+			int end = Math.Min(ownSubmeshGraphics.Count, referenceSkeletonGraphic.TexturesMultipleCanvasRenderers.Count);
+
+			for (int i = 0; i < end; i++) {
+				SkeletonSubmeshGraphic submeshGraphic = ownSubmeshGraphics[i];
+				CanvasRenderer reference = referenceSkeletonGraphic.canvasRenderers[i];
+
+				if (reference.gameObject.activeInHierarchy) {
+					Material usedMaterial = replacementMaterial != null ?
+						replacementMaterial : GetReplacementMaterialFor(referenceMaterials[i]);
+					if (usedMaterial == null)
+						usedMaterial = referenceMaterials[i];
+					usedMaterial = referenceSkeletonGraphic.GetModifiedMaterial(usedMaterial);
+					submeshGraphic.canvasRenderer.SetMaterial(usedMaterial, referenceTextures[i]);
+					submeshGraphic.canvasRenderer.SetMesh(referenceMeshes[i]);
+					submeshGraphic.gameObject.SetActive(true);
+				} else {
+					submeshGraphic.canvasRenderer.Clear();
+					submeshGraphic.gameObject.SetActive(false);
+				}
+			}
+		}
+
+		protected void DisableCanvasRenderers () {
+			for (int i = 0; i < ownSubmeshGraphics.Count; i++) {
+				SkeletonSubmeshGraphic submeshGraphic = ownSubmeshGraphics[i];
+				submeshGraphic.canvasRenderer.Clear();
+				submeshGraphic.gameObject.SetActive(false);
+			}
+		}
+
+		protected Material GetReplacementMaterialFor (Material originalMaterial) {
+			for (int i = 0; i < replacementMaterials.Length; ++i) {
+				MaterialReplacement entry = replacementMaterials[i];
+				if (entry.originalMaterial != null && entry.originalMaterial.shader == originalMaterial.shader)
+					return entry.replacementMaterial;
+			}
+			return null;
+		}
+
+#if UNITY_EDITOR
+		void LateUpdate () {
+			if (!Application.isPlaying) {
+				UpdateMesh();
+			}
+		}
+#endif
+
+		void UpdateOnCallback (SkeletonGraphic g) {
+			UpdateMesh();
+		}
+
+		void UpdateMesh () {
+			if (!referenceSkeletonGraphic) return;
+
+			if (referenceSkeletonGraphic.allowMultipleCanvasRenderers) {
+				EnsureCanvasRendererCount(referenceSkeletonGraphic.canvasRenderers.Count);
+				UpdateCanvasRenderers();
+				if (ownGraphic)
+					ownGraphic.canvasRenderer.Clear();
+			} else {
+				if (ownGraphic == null)
+					ownGraphic = this.gameObject.AddComponent<SkeletonSubmeshGraphic>();
+
+				DisableCanvasRenderers();
+
+				Material referenceMaterial = referenceSkeletonGraphic.materialForRendering;
+				Material usedMaterial = replacementMaterial != null ? replacementMaterial : GetReplacementMaterialFor(referenceMaterial);
+				if (usedMaterial == null)
+					usedMaterial = referenceMaterial;
+				usedMaterial = referenceSkeletonGraphic.GetModifiedMaterial(usedMaterial);
+				ownGraphic.canvasRenderer.SetMaterial(usedMaterial, referenceSkeletonGraphic.mainTexture);
+				Mesh mesh = referenceSkeletonGraphic.GetLastMesh();
+				ownGraphic.canvasRenderer.SetMesh(mesh);
+			}
+		}
+	}
+}

+ 2 - 0
spine-unity/Assets/Spine Examples/Scripts/Sample Components/RenderExistingMeshGraphic.cs.meta

@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: ff6ce4ce6b9336a479c6bf5af81fa80a

+ 1 - 1
spine-unity/Assets/Spine Examples/package.json

@@ -2,7 +2,7 @@
   "name": "com.esotericsoftware.spine.spine-unity-examples",
   "displayName": "spine-unity Runtime Examples",
   "description": "This plugin provides example scenes and scripts for the spine-unity runtime.",
-  "version": "4.2.37",
+  "version": "4.2.38",
   "unity": "2018.3",
   "author": {
     "name": "Esoteric Software",