SkeletonGraphic.cs 31 KB

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