/****************************************************************************** * 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 #if UNITY_2019_3_OR_NEWER #define SET_VERTICES_HAS_LENGTH_PARAMETER #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 doubleBufferedMesh; protected ExposedList positionBuffer; protected ExposedList colorBuffer; protected ExposedList uvBuffer; protected ExposedList indexBuffer; #if UNITY_EDITOR private void Reset () { if (skeletonRenderer == null) skeletonRenderer = this.GetComponentInParent(); GatherRenderers(); Awake(); if (referenceRenderers.Length > 0) ownRenderer.sharedMaterial = referenceRenderers[0].sharedMaterial; LateUpdate(); } #endif protected void GatherRenderers () { referenceRenderers = this.GetComponentsInChildren(); if (referenceRenderers.Length == 0 || (referenceRenderers.Length == 1 && referenceRenderers[0].gameObject == this.gameObject)) { Transform parent = this.transform.parent; if (parent) referenceRenderers = parent.GetComponentsInChildren(); } referenceRenderers = referenceRenderers.Where( (val, idx) => val.gameObject != this.gameObject && val.enabled).ToArray(); } void Awake () { if (skeletonRenderer == null) skeletonRenderer = this.GetComponentInParent(); if (referenceRenderers == null || referenceRenderers.Length == 0) { GatherRenderers(); } if (renderSeparator == null) { if (skeletonRenderer) renderSeparator = skeletonRenderer.GetComponent(); else renderSeparator = this.GetComponentInParent(); } int count = referenceRenderers.Length; referenceMeshFilters = new MeshFilter[count]; for (int i = 0; i < count; ++i) { referenceMeshFilters[i] = referenceRenderers[i].GetComponent(); } ownRenderer = this.GetComponent(); if (ownRenderer == null) ownRenderer = this.gameObject.AddComponent(); ownMeshFilter = this.GetComponent(); if (ownMeshFilter == null) ownMeshFilter = this.gameObject.AddComponent(); } 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(combinedVertexCount); uvBuffer = new ExposedList(combinedVertexCount); colorBuffer = new ExposedList(combinedVertexCount); indexBuffer = new ExposedList(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(); 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); for (int s = 0, submeshCount = mesh.subMeshCount; s < submeshCount; ++s) { int submeshIndexCount = (int)mesh.GetIndexCount(s); int[] submeshIndices = mesh.GetIndices(s); int[] dstIndices = this.indexBuffer.Items; for (int i = 0; i < submeshIndexCount; ++i) dstIndices[i + combinedI] = submeshIndices[i] + combinedV; combinedI += submeshIndexCount; } combinedV += vertexCount; } Mesh combinedMesh = doubleBufferedMesh.GetNext(); combinedMesh.Clear(); #if SET_VERTICES_HAS_LENGTH_PARAMETER 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); #else // Note: excess already contains zero positions and indices after ExposedList.Resize(). combinedMesh.vertices = this.positionBuffer.Items; combinedMesh.uv = this.uvBuffer.Items; combinedMesh.colors32 = this.colorBuffer.Items; combinedMesh.triangles = this.indexBuffer.Items; #endif 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); } } } } }