SkeletonGraphic.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated January 1, 2020. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2020, 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_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
  30. #define NEW_PREFAB_SYSTEM
  31. #endif
  32. using System.Collections.Generic;
  33. using UnityEngine;
  34. using UnityEngine.UI;
  35. namespace Spine.Unity {
  36. #if NEW_PREFAB_SYSTEM
  37. [ExecuteAlways]
  38. #else
  39. [ExecuteInEditMode]
  40. #endif
  41. [RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent]
  42. [AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")]
  43. [HelpURL("http://esotericsoftware.com/spine-unity#SkeletonGraphic-Component")]
  44. public class SkeletonGraphic : MaskableGraphic, ISkeletonComponent, IAnimationStateComponent, ISkeletonAnimation, IHasSkeletonDataAsset {
  45. #region Inspector
  46. public SkeletonDataAsset skeletonDataAsset;
  47. public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } }
  48. [SpineSkin(dataField:"skeletonDataAsset", defaultAsEmptyString:true)]
  49. public string initialSkinName;
  50. public bool initialFlipX, initialFlipY;
  51. [SpineAnimation(dataField:"skeletonDataAsset")]
  52. public string startingAnimation;
  53. public bool startingLoop;
  54. public float timeScale = 1f;
  55. public bool freeze;
  56. /// <summary>Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.</summary>
  57. public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
  58. protected UpdateMode updateMode = UpdateMode.FullUpdate;
  59. /// <summary>Update mode used when the MeshRenderer becomes invisible
  60. /// (when <c>OnBecameInvisible()</c> is called). Update mode is automatically
  61. /// reset to <c>UpdateMode.FullUpdate</c> when the mesh becomes visible again.</summary>
  62. public UpdateMode updateWhenInvisible = UpdateMode.FullUpdate;
  63. public bool unscaledTime;
  64. public bool allowMultipleCanvasRenderers = false;
  65. public List<CanvasRenderer> canvasRenderers = new List<CanvasRenderer>();
  66. protected List<RawImage> rawImages = new List<RawImage>();
  67. protected int usedRenderersCount = 0;
  68. // Submesh Separation
  69. public const string SeparatorPartGameObjectName = "Part";
  70. /// <summary>Slot names used to populate separatorSlots list when the Skeleton is initialized. Changing this after initialization does nothing.</summary>
  71. [SerializeField] [SpineSlot] protected string[] separatorSlotNames = new string[0];
  72. /// <summary>Slots that determine where the render is split. This is used by components such as SkeletonRenderSeparator so that the skeleton can be rendered by two separate renderers on different GameObjects.</summary>
  73. [System.NonSerialized] public readonly List<Slot> separatorSlots = new List<Slot>();
  74. public bool enableSeparatorSlots = false;
  75. [SerializeField] protected List<Transform> separatorParts = new List<Transform>();
  76. public List<Transform> SeparatorParts { get { return separatorParts; } }
  77. public bool updateSeparatorPartLocation = true;
  78. private bool wasUpdatedAfterInit = true;
  79. private Texture baseTexture = null;
  80. #if UNITY_EDITOR
  81. protected override void OnValidate () {
  82. // This handles Scene View preview.
  83. base.OnValidate ();
  84. if (this.IsValid) {
  85. if (skeletonDataAsset == null) {
  86. Clear();
  87. } else if (skeletonDataAsset.skeletonJSON == null) {
  88. Clear();
  89. } else if (skeletonDataAsset.GetSkeletonData(true) != skeleton.data) {
  90. Clear();
  91. Initialize(true);
  92. if (!allowMultipleCanvasRenderers && (skeletonDataAsset.atlasAssets.Length > 1 || skeletonDataAsset.atlasAssets[0].MaterialCount > 1))
  93. Debug.LogError("Unity UI does not support multiple textures per Renderer. Please enable 'Advanced - Multiple CanvasRenderers' to generate the required CanvasRenderer GameObjects. Otherwise your skeleton will not be rendered correctly.", this);
  94. } else {
  95. if (freeze) return;
  96. if (!string.IsNullOrEmpty(initialSkinName)) {
  97. var skin = skeleton.data.FindSkin(initialSkinName);
  98. if (skin != null) {
  99. if (skin == skeleton.data.defaultSkin)
  100. skeleton.SetSkin((Skin)null);
  101. else
  102. skeleton.SetSkin(skin);
  103. }
  104. }
  105. // Only provide visual feedback to inspector changes in Unity Editor Edit mode.
  106. if (!Application.isPlaying) {
  107. skeleton.ScaleX = this.initialFlipX ? -1 : 1;
  108. skeleton.ScaleY = this.initialFlipY ? -1 : 1;
  109. state.ClearTrack(0);
  110. skeleton.SetToSetupPose();
  111. if (!string.IsNullOrEmpty(startingAnimation)) {
  112. state.SetAnimation(0, startingAnimation, startingLoop);
  113. Update(0f);
  114. }
  115. }
  116. }
  117. } else {
  118. // Under some circumstances (e.g. sometimes on the first import) OnValidate is called
  119. // before SpineEditorUtilities.ImportSpineContent, causing an unnecessary exception.
  120. // The (skeletonDataAsset.skeletonJSON != null) condition serves to prevent this exception.
  121. if (skeletonDataAsset != null && skeletonDataAsset.skeletonJSON != null)
  122. Initialize(true);
  123. }
  124. }
  125. protected override void Reset () {
  126. base.Reset();
  127. if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic"))
  128. Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material.");
  129. }
  130. #endif
  131. #endregion
  132. #region Runtime Instantiation
  133. /// <summary>Create a new GameObject with a SkeletonGraphic component.</summary>
  134. /// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
  135. public static SkeletonGraphic NewSkeletonGraphicGameObject (SkeletonDataAsset skeletonDataAsset, Transform parent, Material material) {
  136. var sg = SkeletonGraphic.AddSkeletonGraphicComponent(new GameObject("New Spine GameObject"), skeletonDataAsset, material);
  137. if (parent != null) sg.transform.SetParent(parent, false);
  138. return sg;
  139. }
  140. /// <summary>Add a SkeletonGraphic component to a GameObject.</summary>
  141. /// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
  142. public static SkeletonGraphic AddSkeletonGraphicComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset, Material material) {
  143. var c = gameObject.AddComponent<SkeletonGraphic>();
  144. if (skeletonDataAsset != null) {
  145. c.material = material;
  146. c.skeletonDataAsset = skeletonDataAsset;
  147. c.Initialize(false);
  148. }
  149. return c;
  150. }
  151. #endregion
  152. #region Overrides
  153. [System.NonSerialized] readonly Dictionary<Texture, Texture> customTextureOverride = new Dictionary<Texture, Texture>();
  154. /// <summary>Use this Dictionary to override a Texture with a different Texture.</summary>
  155. public Dictionary<Texture, Texture> CustomTextureOverride { get { return customTextureOverride; } }
  156. [System.NonSerialized] readonly Dictionary<Texture, Material> customMaterialOverride = new Dictionary<Texture, Material>();
  157. /// <summary>Use this Dictionary to override the Material where the Texture was used at the original atlas.</summary>
  158. public Dictionary<Texture, Material> CustomMaterialOverride { get { return customMaterialOverride; } }
  159. // This is used by the UI system to determine what to put in the MaterialPropertyBlock.
  160. Texture overrideTexture;
  161. public Texture OverrideTexture {
  162. get { return overrideTexture; }
  163. set {
  164. overrideTexture = value;
  165. canvasRenderer.SetTexture(this.mainTexture); // Refresh canvasRenderer's texture. Make sure it handles null.
  166. }
  167. }
  168. #endregion
  169. #region Internals
  170. public override Texture mainTexture {
  171. get {
  172. if (overrideTexture != null) return overrideTexture;
  173. return baseTexture;
  174. }
  175. }
  176. protected override void Awake () {
  177. base.Awake ();
  178. this.onCullStateChanged.AddListener(OnCullStateChanged);
  179. SyncRawImagesWithCanvasRenderers();
  180. if (!this.IsValid) {
  181. #if UNITY_EDITOR
  182. // workaround for special import case of open scene where OnValidate and Awake are
  183. // called in wrong order, before setup of Spine assets.
  184. if (!Application.isPlaying) {
  185. if (this.skeletonDataAsset != null && this.skeletonDataAsset.skeletonJSON == null)
  186. return;
  187. }
  188. #endif
  189. Initialize(false);
  190. Rebuild(CanvasUpdate.PreRender);
  191. }
  192. }
  193. protected override void OnDestroy () {
  194. Clear();
  195. base.OnDestroy();
  196. }
  197. public override void Rebuild (CanvasUpdate update) {
  198. base.Rebuild(update);
  199. if (canvasRenderer.cull) return;
  200. if (update == CanvasUpdate.PreRender) UpdateMesh(keepRendererCount : true);
  201. if (allowMultipleCanvasRenderers) canvasRenderer.Clear();
  202. }
  203. protected override void OnDisable () {
  204. base.OnDisable();
  205. foreach (var canvasRenderer in canvasRenderers) {
  206. canvasRenderer.Clear();
  207. }
  208. }
  209. public virtual void Update () {
  210. #if UNITY_EDITOR
  211. if (!Application.isPlaying) {
  212. Update(0f);
  213. return;
  214. }
  215. #endif
  216. if (freeze) return;
  217. Update(unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime);
  218. }
  219. public virtual void Update (float deltaTime) {
  220. if (!this.IsValid) return;
  221. wasUpdatedAfterInit = true;
  222. if (updateMode < UpdateMode.OnlyAnimationStatus)
  223. return;
  224. UpdateAnimationStatus(deltaTime);
  225. if (updateMode == UpdateMode.OnlyAnimationStatus)
  226. return;
  227. ApplyAnimation();
  228. }
  229. protected void SyncRawImagesWithCanvasRenderers () {
  230. rawImages.Clear();
  231. foreach (var canvasRenderer in canvasRenderers) {
  232. var rawImage = canvasRenderer.GetComponent<RawImage>();
  233. if (rawImage == null) {
  234. rawImage = canvasRenderer.gameObject.AddComponent<RawImage>();
  235. rawImage.maskable = this.maskable;
  236. rawImage.raycastTarget = false;
  237. }
  238. rawImages.Add(rawImage);
  239. }
  240. }
  241. protected void UpdateAnimationStatus (float deltaTime) {
  242. deltaTime *= timeScale;
  243. skeleton.Update(deltaTime);
  244. state.Update(deltaTime);
  245. }
  246. protected void ApplyAnimation () {
  247. if (BeforeApply != null)
  248. BeforeApply(this);
  249. if (updateMode != UpdateMode.OnlyEventTimelines)
  250. state.Apply(skeleton);
  251. else
  252. state.ApplyEventTimelinesOnly(skeleton);
  253. if (UpdateLocal != null)
  254. UpdateLocal(this);
  255. skeleton.UpdateWorldTransform();
  256. if (UpdateWorld != null) {
  257. UpdateWorld(this);
  258. skeleton.UpdateWorldTransform();
  259. }
  260. if (UpdateComplete != null)
  261. UpdateComplete(this);
  262. }
  263. public void LateUpdate () {
  264. // instantiation can happen from Update() after this component, leading to a missing Update() call.
  265. if (!wasUpdatedAfterInit) Update(0);
  266. if (freeze) return;
  267. if (updateMode != UpdateMode.FullUpdate) return;
  268. UpdateMesh();
  269. }
  270. protected void OnCullStateChanged (bool culled) {
  271. if (culled)
  272. OnBecameInvisible();
  273. else
  274. OnBecameVisible();
  275. }
  276. public void OnBecameVisible () {
  277. updateMode = UpdateMode.FullUpdate;
  278. }
  279. public void OnBecameInvisible () {
  280. updateMode = updateWhenInvisible;
  281. }
  282. public void ReapplySeparatorSlotNames () {
  283. if (!IsValid)
  284. return;
  285. separatorSlots.Clear();
  286. for (int i = 0, n = separatorSlotNames.Length; i < n; i++) {
  287. string slotName = separatorSlotNames[i];
  288. if (slotName == "")
  289. continue;
  290. var slot = skeleton.FindSlot(slotName);
  291. if (slot != null) {
  292. separatorSlots.Add(slot);
  293. }
  294. #if UNITY_EDITOR
  295. else
  296. {
  297. Debug.LogWarning(slotName + " is not a slot in " + skeletonDataAsset.skeletonJSON.name);
  298. }
  299. #endif
  300. }
  301. UpdateSeparatorPartParents();
  302. }
  303. #endregion
  304. #region API
  305. protected Skeleton skeleton;
  306. public Skeleton Skeleton {
  307. get {
  308. Initialize(false);
  309. return skeleton;
  310. }
  311. set {
  312. skeleton = value;
  313. }
  314. }
  315. public SkeletonData SkeletonData { get { return skeleton == null ? null : skeleton.data; } }
  316. public bool IsValid { get { return skeleton != null; } }
  317. public delegate void SkeletonRendererDelegate (SkeletonGraphic skeletonGraphic);
  318. /// <summary>OnRebuild is raised after the Skeleton is successfully initialized.</summary>
  319. public event SkeletonRendererDelegate OnRebuild;
  320. /// <summary>OnMeshAndMaterialsUpdated is at the end of LateUpdate after the Mesh and
  321. /// all materials have been updated.</summary>
  322. public event SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
  323. protected Spine.AnimationState state;
  324. public Spine.AnimationState AnimationState {
  325. get {
  326. Initialize(false);
  327. return state;
  328. }
  329. }
  330. [SerializeField] protected Spine.Unity.MeshGenerator meshGenerator = new MeshGenerator();
  331. public Spine.Unity.MeshGenerator MeshGenerator { get { return this.meshGenerator; } }
  332. DoubleBuffered<Spine.Unity.MeshRendererBuffers.SmartMesh> meshBuffers;
  333. SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
  334. readonly ExposedList<Mesh> meshes = new ExposedList<Mesh>();
  335. public Mesh GetLastMesh () {
  336. return meshBuffers.GetCurrent().mesh;
  337. }
  338. public bool MatchRectTransformWithBounds () {
  339. UpdateMesh();
  340. if (!this.allowMultipleCanvasRenderers)
  341. return MatchRectTransformSingleRenderer();
  342. else
  343. return MatchRectTransformMultipleRenderers();
  344. }
  345. protected bool MatchRectTransformSingleRenderer () {
  346. Mesh mesh = this.GetLastMesh();
  347. if (mesh == null) {
  348. return false;
  349. }
  350. if (mesh.vertexCount == 0) {
  351. this.rectTransform.sizeDelta = new Vector2(50f, 50f);
  352. this.rectTransform.pivot = new Vector2(0.5f, 0.5f);
  353. return false;
  354. }
  355. mesh.RecalculateBounds();
  356. SetRectTransformBounds(mesh.bounds);
  357. return true;
  358. }
  359. protected bool MatchRectTransformMultipleRenderers () {
  360. bool anyBoundsAdded = false;
  361. Bounds combinedBounds = new Bounds();
  362. for (int i = 0; i < canvasRenderers.Count; ++i) {
  363. var canvasRenderer = canvasRenderers[i];
  364. if (!canvasRenderer.gameObject.activeSelf)
  365. continue;
  366. Mesh mesh = meshes.Items[i];
  367. if (mesh == null || mesh.vertexCount == 0)
  368. continue;
  369. mesh.RecalculateBounds();
  370. var bounds = mesh.bounds;
  371. if (anyBoundsAdded)
  372. combinedBounds.Encapsulate(bounds);
  373. else {
  374. anyBoundsAdded = true;
  375. combinedBounds = bounds;
  376. }
  377. }
  378. if (!anyBoundsAdded) {
  379. this.rectTransform.sizeDelta = new Vector2(50f, 50f);
  380. this.rectTransform.pivot = new Vector2(0.5f, 0.5f);
  381. return false;
  382. }
  383. SetRectTransformBounds(combinedBounds);
  384. return true;
  385. }
  386. private void SetRectTransformBounds (Bounds combinedBounds) {
  387. var size = combinedBounds.size;
  388. var center = combinedBounds.center;
  389. var p = new Vector2(
  390. 0.5f - (center.x / size.x),
  391. 0.5f - (center.y / size.y)
  392. );
  393. this.rectTransform.sizeDelta = size;
  394. this.rectTransform.pivot = p;
  395. }
  396. public event UpdateBonesDelegate BeforeApply;
  397. public event UpdateBonesDelegate UpdateLocal;
  398. public event UpdateBonesDelegate UpdateWorld;
  399. public event UpdateBonesDelegate UpdateComplete;
  400. /// <summary> Occurs after the vertex data populated every frame, before the vertices are pushed into the mesh.</summary>
  401. public event Spine.Unity.MeshGeneratorDelegate OnPostProcessVertices;
  402. public void Clear () {
  403. skeleton = null;
  404. canvasRenderer.Clear();
  405. for (int i = 0; i < canvasRenderers.Count; ++i)
  406. canvasRenderers[i].Clear();
  407. DestroyMeshes();
  408. DisposeMeshBuffers();
  409. }
  410. public void TrimRenderers () {
  411. var newList = new List<CanvasRenderer>();
  412. foreach (var canvasRenderer in canvasRenderers) {
  413. if (canvasRenderer.gameObject.activeSelf) {
  414. newList.Add(canvasRenderer);
  415. }
  416. else {
  417. if (Application.isEditor && !Application.isPlaying)
  418. DestroyImmediate(canvasRenderer.gameObject);
  419. else
  420. Destroy(canvasRenderer.gameObject);
  421. }
  422. }
  423. canvasRenderers = newList;
  424. SyncRawImagesWithCanvasRenderers();
  425. }
  426. public void Initialize (bool overwrite) {
  427. if (this.IsValid && !overwrite) return;
  428. if (this.skeletonDataAsset == null) return;
  429. var skeletonData = this.skeletonDataAsset.GetSkeletonData(false);
  430. if (skeletonData == null) return;
  431. if (skeletonDataAsset.atlasAssets.Length <= 0 || skeletonDataAsset.atlasAssets[0].MaterialCount <= 0) return;
  432. this.state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
  433. if (state == null) {
  434. Clear();
  435. return;
  436. }
  437. this.skeleton = new Skeleton(skeletonData) {
  438. ScaleX = this.initialFlipX ? -1 : 1,
  439. ScaleY = this.initialFlipY ? -1 : 1
  440. };
  441. InitMeshBuffers();
  442. baseTexture = skeletonDataAsset.atlasAssets[0].PrimaryMaterial.mainTexture;
  443. canvasRenderer.SetTexture(this.mainTexture); // Needed for overwriting initializations.
  444. // Set the initial Skin and Animation
  445. if (!string.IsNullOrEmpty(initialSkinName))
  446. skeleton.SetSkin(initialSkinName);
  447. separatorSlots.Clear();
  448. for (int i = 0; i < separatorSlotNames.Length; i++)
  449. separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i]));
  450. wasUpdatedAfterInit = false;
  451. if (!string.IsNullOrEmpty(startingAnimation)) {
  452. var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(startingAnimation);
  453. if (animationObject != null) {
  454. state.SetAnimation(0, animationObject, startingLoop);
  455. #if UNITY_EDITOR
  456. if (!Application.isPlaying)
  457. Update(0f);
  458. #endif
  459. }
  460. }
  461. if (OnRebuild != null)
  462. OnRebuild(this);
  463. }
  464. public void UpdateMesh (bool keepRendererCount = false) {
  465. if (!this.IsValid) return;
  466. skeleton.SetColor(this.color);
  467. var currentInstructions = this.currentInstructions;
  468. if (!this.allowMultipleCanvasRenderers) {
  469. UpdateMeshSingleCanvasRenderer();
  470. }
  471. else {
  472. UpdateMeshMultipleCanvasRenderers(currentInstructions, keepRendererCount);
  473. }
  474. if (OnMeshAndMaterialsUpdated != null)
  475. OnMeshAndMaterialsUpdated(this);
  476. }
  477. public bool HasMultipleSubmeshInstructions () {
  478. if (!IsValid)
  479. return false;
  480. return MeshGenerator.RequiresMultipleSubmeshesByDrawOrder(skeleton);
  481. }
  482. #endregion
  483. protected void InitMeshBuffers () {
  484. if (meshBuffers != null) {
  485. meshBuffers.GetNext().Clear();
  486. meshBuffers.GetNext().Clear();
  487. }
  488. else {
  489. meshBuffers = new DoubleBuffered<MeshRendererBuffers.SmartMesh>();
  490. }
  491. }
  492. protected void DisposeMeshBuffers () {
  493. if (meshBuffers != null) {
  494. meshBuffers.GetNext().Dispose();
  495. meshBuffers.GetNext().Dispose();
  496. meshBuffers = null;
  497. }
  498. }
  499. protected void UpdateMeshSingleCanvasRenderer () {
  500. if (canvasRenderers.Count > 0)
  501. DisableUnusedCanvasRenderers(usedCount : 0);
  502. var smartMesh = meshBuffers.GetNext();
  503. MeshGenerator.GenerateSingleSubmeshInstruction(currentInstructions, skeleton, null);
  504. bool updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed);
  505. meshGenerator.Begin();
  506. if (currentInstructions.hasActiveClipping && currentInstructions.submeshInstructions.Count > 0) {
  507. meshGenerator.AddSubmesh(currentInstructions.submeshInstructions.Items[0], updateTriangles);
  508. }
  509. else {
  510. meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
  511. }
  512. if (canvas != null) meshGenerator.ScaleVertexData(canvas.referencePixelsPerUnit);
  513. if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
  514. var mesh = smartMesh.mesh;
  515. meshGenerator.FillVertexData(mesh);
  516. if (updateTriangles) meshGenerator.FillTriangles(mesh);
  517. meshGenerator.FillLateVertexData(mesh);
  518. canvasRenderer.SetMesh(mesh);
  519. smartMesh.instructionUsed.Set(currentInstructions);
  520. if (currentInstructions.submeshInstructions.Count > 0) {
  521. var material = currentInstructions.submeshInstructions.Items[0].material;
  522. if (material != null && baseTexture != material.mainTexture) {
  523. baseTexture = material.mainTexture;
  524. if (overrideTexture == null)
  525. canvasRenderer.SetTexture(this.mainTexture);
  526. }
  527. }
  528. //this.UpdateMaterial(); // note: This would allocate memory.
  529. usedRenderersCount = 0;
  530. }
  531. protected void UpdateMeshMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions, bool keepRendererCount) {
  532. MeshGenerator.GenerateSkeletonRendererInstruction(currentInstructions, skeleton, null,
  533. enableSeparatorSlots ? separatorSlots : null,
  534. enableSeparatorSlots ? separatorSlots.Count > 0 : false,
  535. false);
  536. int submeshCount = currentInstructions.submeshInstructions.Count;
  537. if (keepRendererCount && submeshCount != usedRenderersCount)
  538. return;
  539. EnsureCanvasRendererCount(submeshCount);
  540. EnsureMeshesCount(submeshCount);
  541. EnsureSeparatorPartCount();
  542. var c = canvas;
  543. float scale = (c == null) ? 100 : c.referencePixelsPerUnit;
  544. // Generate meshes.
  545. var meshesItems = meshes.Items;
  546. bool useOriginalTextureAndMaterial = (customMaterialOverride.Count == 0 && customTextureOverride.Count == 0);
  547. int separatorSlotGroupIndex = 0;
  548. Transform parent = this.separatorSlots.Count == 0 ? this.transform : this.separatorParts[0];
  549. if (updateSeparatorPartLocation) {
  550. for (int p = 0; p < this.separatorParts.Count; ++p) {
  551. separatorParts[p].position = this.transform.position;
  552. separatorParts[p].rotation = this.transform.rotation;
  553. }
  554. }
  555. int targetSiblingIndex = 0;
  556. for (int i = 0; i < submeshCount; i++) {
  557. var submeshInstructionItem = currentInstructions.submeshInstructions.Items[i];
  558. meshGenerator.Begin();
  559. meshGenerator.AddSubmesh(submeshInstructionItem);
  560. var targetMesh = meshesItems[i];
  561. meshGenerator.ScaleVertexData(scale);
  562. if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
  563. meshGenerator.FillVertexData(targetMesh);
  564. meshGenerator.FillTriangles(targetMesh);
  565. meshGenerator.FillLateVertexData(targetMesh);
  566. var submeshMaterial = submeshInstructionItem.material;
  567. var canvasRenderer = canvasRenderers[i];
  568. if (i >= usedRenderersCount)
  569. canvasRenderer.gameObject.SetActive(true);
  570. canvasRenderer.SetMesh(targetMesh);
  571. canvasRenderer.materialCount = 1;
  572. if (canvasRenderer.transform.parent != parent.transform) {
  573. canvasRenderer.transform.SetParent(parent.transform, false);
  574. canvasRenderer.transform.localPosition = Vector3.zero;
  575. }
  576. canvasRenderer.transform.SetSiblingIndex(targetSiblingIndex++);
  577. if (submeshInstructionItem.forceSeparate) {
  578. targetSiblingIndex = 0;
  579. parent = separatorParts[++separatorSlotGroupIndex];
  580. }
  581. if (useOriginalTextureAndMaterial)
  582. canvasRenderer.SetMaterial(this.materialForRendering, submeshMaterial.mainTexture);
  583. else {
  584. var originalTexture = submeshMaterial.mainTexture;
  585. Material usedMaterial;
  586. Texture usedTexture;
  587. if (!customMaterialOverride.TryGetValue(originalTexture, out usedMaterial))
  588. usedMaterial = material;
  589. if (!customTextureOverride.TryGetValue(originalTexture, out usedTexture))
  590. usedTexture = originalTexture;
  591. canvasRenderer.SetMaterial(usedMaterial, usedTexture);
  592. }
  593. }
  594. DisableUnusedCanvasRenderers(usedCount : submeshCount);
  595. usedRenderersCount = submeshCount;
  596. }
  597. protected void EnsureCanvasRendererCount (int targetCount) {
  598. #if UNITY_EDITOR
  599. RemoveNullCanvasRenderers();
  600. #endif
  601. int currentCount = canvasRenderers.Count;
  602. for (int i = currentCount; i < targetCount; ++i) {
  603. var go = new GameObject(string.Format("Renderer{0}", i), typeof(RectTransform));
  604. go.transform.SetParent(this.transform, false);
  605. go.transform.localPosition = Vector3.zero;
  606. var canvasRenderer = go.AddComponent<CanvasRenderer>();
  607. canvasRenderers.Add(canvasRenderer);
  608. var rawImage = go.AddComponent<RawImage>();
  609. rawImage.maskable = this.maskable;
  610. rawImage.raycastTarget = false;
  611. rawImages.Add(rawImage);
  612. }
  613. }
  614. protected void DisableUnusedCanvasRenderers (int usedCount) {
  615. #if UNITY_EDITOR
  616. RemoveNullCanvasRenderers();
  617. #endif
  618. for (int i = usedCount; i < canvasRenderers.Count; i++) {
  619. canvasRenderers[i].Clear();
  620. canvasRenderers[i].gameObject.SetActive(false);
  621. }
  622. }
  623. #if UNITY_EDITOR
  624. private void RemoveNullCanvasRenderers () {
  625. if (Application.isEditor && !Application.isPlaying) {
  626. for (int i = canvasRenderers.Count - 1; i >= 0; --i) {
  627. if (canvasRenderers[i] == null) {
  628. canvasRenderers.RemoveAt(i);
  629. }
  630. }
  631. }
  632. }
  633. #endif
  634. protected void EnsureMeshesCount (int targetCount) {
  635. int oldCount = meshes.Count;
  636. meshes.EnsureCapacity(targetCount);
  637. for (int i = oldCount; i < targetCount; i++)
  638. meshes.Add(SpineMesh.NewSkeletonMesh());
  639. }
  640. protected void DestroyMeshes () {
  641. foreach (var mesh in meshes) {
  642. #if UNITY_EDITOR
  643. if (Application.isEditor && !Application.isPlaying)
  644. UnityEngine.Object.DestroyImmediate(mesh);
  645. else
  646. UnityEngine.Object.Destroy(mesh);
  647. #else
  648. UnityEngine.Object.Destroy(mesh);
  649. #endif
  650. }
  651. meshes.Clear();
  652. }
  653. protected void EnsureSeparatorPartCount () {
  654. #if UNITY_EDITOR
  655. RemoveNullSeparatorParts();
  656. #endif
  657. int targetCount = separatorSlots.Count + 1;
  658. if (targetCount == 1)
  659. return;
  660. #if UNITY_EDITOR
  661. if (Application.isEditor && !Application.isPlaying) {
  662. for (int i = separatorParts.Count-1; i >= 0; --i) {
  663. if (separatorParts[i] == null) {
  664. separatorParts.RemoveAt(i);
  665. }
  666. }
  667. }
  668. #endif
  669. int currentCount = separatorParts.Count;
  670. for (int i = currentCount; i < targetCount; ++i) {
  671. var go = new GameObject(string.Format("{0}[{1}]", SeparatorPartGameObjectName, i), typeof(RectTransform));
  672. go.transform.SetParent(this.transform, false);
  673. go.transform.localPosition = Vector3.zero;
  674. separatorParts.Add(go.transform);
  675. }
  676. }
  677. protected void UpdateSeparatorPartParents () {
  678. int usedCount = separatorSlots.Count + 1;
  679. if (usedCount == 1) {
  680. usedCount = 0; // placed directly at the SkeletonGraphic parent
  681. for (int i = 0; i < canvasRenderers.Count; ++i) {
  682. var canvasRenderer = canvasRenderers[i];
  683. if (canvasRenderer.transform.parent.name.Contains(SeparatorPartGameObjectName)) {
  684. canvasRenderer.transform.SetParent(this.transform, false);
  685. canvasRenderer.transform.localPosition = Vector3.zero;
  686. }
  687. }
  688. }
  689. for (int i = 0; i < separatorParts.Count; ++i) {
  690. bool isUsed = i < usedCount;
  691. separatorParts[i].gameObject.SetActive(isUsed);
  692. }
  693. }
  694. #if UNITY_EDITOR
  695. private void RemoveNullSeparatorParts () {
  696. if (Application.isEditor && !Application.isPlaying) {
  697. for (int i = separatorParts.Count - 1; i >= 0; --i) {
  698. if (separatorParts[i] == null) {
  699. separatorParts.RemoveAt(i);
  700. }
  701. }
  702. }
  703. }
  704. #endif
  705. }
  706. }