|
@@ -0,0 +1,253 @@
|
|
|
+/******************************************************************************
|
|
|
+ * Spine Runtimes License Agreement
|
|
|
+ * Last updated July 28, 2023. Replaces all prior versions.
|
|
|
+ *
|
|
|
+ * Copyright (c) 2013-2023, 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.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using UnityEngine;
|
|
|
+
|
|
|
+namespace Spine.Unity.Examples {
|
|
|
+
|
|
|
+#if NEW_PREFAB_SYSTEM
|
|
|
+ [ExecuteAlways]
|
|
|
+#else
|
|
|
+ [ExecuteInEditMode]
|
|
|
+#endif
|
|
|
+ public class RenderCombinedMesh : MonoBehaviour {
|
|
|
+ public SkeletonRenderer skeletonRenderer;
|
|
|
+ public SkeletonRenderSeparator renderSeparator;
|
|
|
+ public MeshRenderer[] referenceRenderers;
|
|
|
+
|
|
|
+ bool updateViaSkeletonCallback = false;
|
|
|
+ MeshFilter[] referenceMeshFilters;
|
|
|
+ MeshRenderer ownRenderer;
|
|
|
+ MeshFilter ownMeshFilter;
|
|
|
+
|
|
|
+ protected DoubleBuffered<Mesh> doubleBufferedMesh;
|
|
|
+ protected ExposedList<Vector3> positionBuffer;
|
|
|
+ protected ExposedList<Color32> colorBuffer;
|
|
|
+ protected ExposedList<Vector2> uvBuffer;
|
|
|
+ protected ExposedList<int> indexBuffer;
|
|
|
+
|
|
|
+#if UNITY_EDITOR
|
|
|
+ private void Reset () {
|
|
|
+ if (skeletonRenderer == null)
|
|
|
+ skeletonRenderer = this.GetComponentInParent<SkeletonRenderer>();
|
|
|
+ GatherRenderers();
|
|
|
+
|
|
|
+ Awake();
|
|
|
+ if (referenceRenderers.Length > 0)
|
|
|
+ ownRenderer.sharedMaterial = referenceRenderers[0].sharedMaterial;
|
|
|
+
|
|
|
+ LateUpdate();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ protected void GatherRenderers () {
|
|
|
+ referenceRenderers = this.GetComponentsInChildren<MeshRenderer>();
|
|
|
+ if (referenceRenderers.Length == 0 ||
|
|
|
+ (referenceRenderers.Length == 1 && referenceRenderers[0].gameObject == this.gameObject)) {
|
|
|
+ Transform parent = this.transform.parent;
|
|
|
+ if (parent)
|
|
|
+ referenceRenderers = parent.GetComponentsInChildren<MeshRenderer>();
|
|
|
+ }
|
|
|
+ referenceRenderers = referenceRenderers.Where(
|
|
|
+ (val, idx) => val.gameObject != this.gameObject && val.enabled).ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ void Awake () {
|
|
|
+ if (skeletonRenderer == null)
|
|
|
+ skeletonRenderer = this.GetComponentInParent<SkeletonRenderer>();
|
|
|
+ if (referenceRenderers == null || referenceRenderers.Length == 0) {
|
|
|
+ GatherRenderers();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (renderSeparator == null) {
|
|
|
+ if (skeletonRenderer)
|
|
|
+ renderSeparator = skeletonRenderer.GetComponent<SkeletonRenderSeparator>();
|
|
|
+ else
|
|
|
+ renderSeparator = this.GetComponentInParent<SkeletonRenderSeparator>();
|
|
|
+ }
|
|
|
+
|
|
|
+ int count = referenceRenderers.Length;
|
|
|
+ referenceMeshFilters = new MeshFilter[count];
|
|
|
+ for (int i = 0; i < count; ++i) {
|
|
|
+ referenceMeshFilters[i] = referenceRenderers[i].GetComponent<MeshFilter>();
|
|
|
+ }
|
|
|
+
|
|
|
+ ownRenderer = this.GetComponent<MeshRenderer>();
|
|
|
+ if (ownRenderer == null)
|
|
|
+ ownRenderer = this.gameObject.AddComponent<MeshRenderer>();
|
|
|
+ ownMeshFilter = this.GetComponent<MeshFilter>();
|
|
|
+ if (ownMeshFilter == null)
|
|
|
+ ownMeshFilter = this.gameObject.AddComponent<MeshFilter>();
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnEnable () {
|
|
|
+#if UNITY_EDITOR
|
|
|
+ if (Application.isPlaying)
|
|
|
+ Awake();
|
|
|
+#endif
|
|
|
+ if (skeletonRenderer) {
|
|
|
+ skeletonRenderer.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
|
|
+ skeletonRenderer.OnMeshAndMaterialsUpdated += UpdateOnCallback;
|
|
|
+ updateViaSkeletonCallback = true;
|
|
|
+ }
|
|
|
+ if (renderSeparator) {
|
|
|
+ renderSeparator.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
|
|
+ renderSeparator.OnMeshAndMaterialsUpdated += UpdateOnCallback;
|
|
|
+ updateViaSkeletonCallback = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnDisable () {
|
|
|
+ if (skeletonRenderer)
|
|
|
+ skeletonRenderer.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
|
|
+ if (renderSeparator)
|
|
|
+ renderSeparator.OnMeshAndMaterialsUpdated -= UpdateOnCallback;
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnDestroy () {
|
|
|
+ for (int i = 0; i < 2; ++i) {
|
|
|
+ Mesh mesh = doubleBufferedMesh.GetNext();
|
|
|
+#if UNITY_EDITOR
|
|
|
+ if (Application.isEditor && !Application.isPlaying)
|
|
|
+ UnityEngine.Object.DestroyImmediate(mesh);
|
|
|
+ else
|
|
|
+ UnityEngine.Object.Destroy(mesh);
|
|
|
+#else
|
|
|
+ UnityEngine.Object.Destroy(mesh);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void LateUpdate () {
|
|
|
+#if UNITY_EDITOR
|
|
|
+ if (!Application.isPlaying) {
|
|
|
+ UpdateMesh();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (updateViaSkeletonCallback)
|
|
|
+ return;
|
|
|
+ UpdateMesh();
|
|
|
+ }
|
|
|
+
|
|
|
+ void UpdateOnCallback (SkeletonRenderer r) {
|
|
|
+ UpdateMesh();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void EnsureBufferSizes (int combinedVertexCount, int combinedIndexCount) {
|
|
|
+ if (positionBuffer == null) {
|
|
|
+ positionBuffer = new ExposedList<Vector3>(combinedVertexCount);
|
|
|
+ uvBuffer = new ExposedList<Vector2>(combinedVertexCount);
|
|
|
+ colorBuffer = new ExposedList<Color32>(combinedVertexCount);
|
|
|
+ indexBuffer = new ExposedList<int>(combinedIndexCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (positionBuffer.Count < combinedVertexCount) {
|
|
|
+ positionBuffer.Resize(combinedVertexCount);
|
|
|
+ uvBuffer.Resize(combinedVertexCount);
|
|
|
+ colorBuffer.Resize(combinedVertexCount);
|
|
|
+ }
|
|
|
+ if (indexBuffer.Count < combinedIndexCount) {
|
|
|
+ indexBuffer.Resize(combinedIndexCount);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void InitMesh () {
|
|
|
+ if (doubleBufferedMesh == null) {
|
|
|
+ doubleBufferedMesh = new DoubleBuffered<Mesh>();
|
|
|
+ for (int i = 0; i < 2; ++i) {
|
|
|
+ Mesh combinedMesh = doubleBufferedMesh.GetNext();
|
|
|
+ combinedMesh.MarkDynamic();
|
|
|
+ combinedMesh.name = "RenderCombinedMesh" + i;
|
|
|
+ combinedMesh.subMeshCount = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void UpdateMesh () {
|
|
|
+ InitMesh();
|
|
|
+ int combinedVertexCount = 0;
|
|
|
+ int combinedIndexCount = 0;
|
|
|
+ GetCombinedMeshInfo(ref combinedVertexCount, ref combinedIndexCount);
|
|
|
+
|
|
|
+ EnsureBufferSizes(combinedVertexCount, combinedIndexCount);
|
|
|
+
|
|
|
+ int combinedV = 0;
|
|
|
+ int combinedI = 0;
|
|
|
+ for (int r = 0, rendererCount = referenceMeshFilters.Length; r < rendererCount; ++r) {
|
|
|
+ MeshFilter meshFilter = referenceMeshFilters[r];
|
|
|
+ Mesh mesh = meshFilter.sharedMesh;
|
|
|
+ if (mesh == null) continue;
|
|
|
+
|
|
|
+ int vertexCount = mesh.vertexCount;
|
|
|
+ Vector3[] positions = mesh.vertices;
|
|
|
+ Vector2[] uvs = mesh.uv;
|
|
|
+ Color32[] colors = mesh.colors32;
|
|
|
+
|
|
|
+ System.Array.Copy(positions, 0, this.positionBuffer.Items, combinedV, vertexCount);
|
|
|
+ System.Array.Copy(uvs, 0, this.uvBuffer.Items, combinedV, vertexCount);
|
|
|
+ System.Array.Copy(colors, 0, this.colorBuffer.Items, combinedV, vertexCount);
|
|
|
+ combinedV += vertexCount;
|
|
|
+
|
|
|
+ for (int s = 0, submeshCount = mesh.subMeshCount; s < submeshCount; ++s) {
|
|
|
+ int submeshIndexCount = (int)mesh.GetIndexCount(s);
|
|
|
+ int[] submeshIndices = mesh.GetIndices(s);
|
|
|
+ System.Array.Copy(submeshIndices, 0, this.indexBuffer.Items, combinedI, submeshIndexCount);
|
|
|
+ combinedI += submeshIndexCount;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Mesh combinedMesh = doubleBufferedMesh.GetNext();
|
|
|
+ combinedMesh.SetVertices(this.positionBuffer.Items, 0, this.positionBuffer.Count);
|
|
|
+ combinedMesh.SetUVs(0, this.uvBuffer.Items, 0, this.uvBuffer.Count);
|
|
|
+ combinedMesh.SetColors(this.colorBuffer.Items, 0, this.colorBuffer.Count);
|
|
|
+ combinedMesh.SetTriangles(this.indexBuffer.Items, 0, this.indexBuffer.Count, 0);
|
|
|
+ ownMeshFilter.sharedMesh = combinedMesh;
|
|
|
+ }
|
|
|
+
|
|
|
+ void GetCombinedMeshInfo (ref int vertexCount, ref int indexCount) {
|
|
|
+ for (int r = 0, rendererCount = referenceMeshFilters.Length; r < rendererCount; ++r) {
|
|
|
+ MeshFilter meshFilter = referenceMeshFilters[r];
|
|
|
+ Mesh mesh = meshFilter.sharedMesh;
|
|
|
+ if (mesh == null) continue;
|
|
|
+
|
|
|
+ vertexCount += mesh.vertexCount;
|
|
|
+ for (int s = 0, submeshCount = mesh.subMeshCount; s < submeshCount; ++s) {
|
|
|
+ indexCount += (int)mesh.GetIndexCount(s);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|