SkeletonGraphic.cs 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated July 28, 2023. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2023, 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 or
  13. * 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 THE
  27. * 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. #define SPINE_OPTIONAL_ON_DEMAND_LOADING
  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. /// <summary>Own color to replace <c>Graphic.m_Color</c>.</summary>
  56. [UnityEngine.Serialization.FormerlySerializedAs("m_Color")]
  57. [SerializeField] protected Color m_SkeletonColor = Color.white;
  58. /// <summary>Sets the color of the skeleton. Does not call <see cref="Rebuild"/> and <see cref="UpdateMesh"/>
  59. /// unnecessarily as <c>Graphic.color</c> would otherwise do.</summary>
  60. override public Color color { get { return m_SkeletonColor; } set { m_SkeletonColor = value; } }
  61. [SpineSkin(dataField: "skeletonDataAsset", defaultAsEmptyString: true)]
  62. public string initialSkinName;
  63. public bool initialFlipX, initialFlipY;
  64. [SpineAnimation(dataField: "skeletonDataAsset")]
  65. public string startingAnimation;
  66. public bool startingLoop;
  67. public float timeScale = 1f;
  68. public bool freeze;
  69. protected float meshScale = 1f;
  70. protected Vector2 meshOffset = Vector2.zero;
  71. public float MeshScale { get { return meshScale; } }
  72. public Vector2 MeshOffset { get { return meshOffset; } }
  73. public enum LayoutMode {
  74. None = 0,
  75. WidthControlsHeight,
  76. HeightControlsWidth,
  77. FitInParent,
  78. EnvelopeParent
  79. }
  80. public LayoutMode layoutScaleMode = LayoutMode.None;
  81. [SerializeField] protected Vector2 referenceSize = Vector2.one;
  82. /// <summary>Offset relative to the pivot position, before potential layout scale is applied.</summary>
  83. [SerializeField] protected Vector2 pivotOffset = Vector2.zero;
  84. [SerializeField] protected float referenceScale = 1f;
  85. [SerializeField] protected float layoutScale = 1f;
  86. #if UNITY_EDITOR
  87. protected LayoutMode previousLayoutScaleMode = LayoutMode.None;
  88. [SerializeField] protected Vector2 rectTransformSize = Vector2.zero;
  89. [SerializeField] protected bool editReferenceRect = false;
  90. protected bool previousEditReferenceRect = false;
  91. public bool EditReferenceRect { get { return editReferenceRect; } set { editReferenceRect = value; } }
  92. public Vector2 RectTransformSize { get { return rectTransformSize; } }
  93. #else
  94. protected const bool EditReferenceRect = false;
  95. #endif
  96. /// <summary>Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.</summary>
  97. public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
  98. protected UpdateMode updateMode = UpdateMode.FullUpdate;
  99. /// <summary>Update mode used when the MeshRenderer becomes invisible
  100. /// (when <c>OnBecameInvisible()</c> is called). Update mode is automatically
  101. /// reset to <c>UpdateMode.FullUpdate</c> when the mesh becomes visible again.</summary>
  102. public UpdateMode updateWhenInvisible = UpdateMode.FullUpdate;
  103. public bool allowMultipleCanvasRenderers = false;
  104. public List<CanvasRenderer> canvasRenderers = new List<CanvasRenderer>();
  105. protected List<SkeletonSubmeshGraphic> submeshGraphics = new List<SkeletonSubmeshGraphic>();
  106. protected int usedRenderersCount = 0;
  107. // Submesh Separation
  108. public const string SeparatorPartGameObjectName = "Part";
  109. /// <summary>Slot names used to populate separatorSlots list when the Skeleton is initialized. Changing this after initialization does nothing.</summary>
  110. [SerializeField] [SpineSlot] protected string[] separatorSlotNames = new string[0];
  111. /// <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>
  112. [System.NonSerialized] public readonly List<Slot> separatorSlots = new List<Slot>();
  113. public bool enableSeparatorSlots = false;
  114. [SerializeField] protected List<Transform> separatorParts = new List<Transform>();
  115. public List<Transform> SeparatorParts { get { return separatorParts; } }
  116. public bool updateSeparatorPartLocation = true;
  117. public bool updateSeparatorPartScale = false;
  118. private bool wasUpdatedAfterInit = true;
  119. private Texture baseTexture = null;
  120. #if UNITY_EDITOR
  121. protected override void OnValidate () {
  122. // This handles Scene View preview.
  123. base.OnValidate();
  124. if (this.IsValid) {
  125. if (skeletonDataAsset == null) {
  126. Clear();
  127. } else if (skeletonDataAsset.skeletonJSON == null) {
  128. Clear();
  129. } else if (skeletonDataAsset.GetSkeletonData(true) != skeleton.Data) {
  130. Clear();
  131. Initialize(true);
  132. if (!allowMultipleCanvasRenderers && (skeletonDataAsset.atlasAssets.Length > 1 || skeletonDataAsset.atlasAssets[0].MaterialCount > 1))
  133. 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);
  134. } else {
  135. if (freeze) return;
  136. if (!Application.isPlaying) {
  137. Initialize(true);
  138. return;
  139. }
  140. if (!string.IsNullOrEmpty(initialSkinName)) {
  141. Skin skin = skeleton.Data.FindSkin(initialSkinName);
  142. if (skin != null) {
  143. if (skin == skeleton.Data.DefaultSkin)
  144. skeleton.SetSkin((Skin)null);
  145. else
  146. skeleton.SetSkin(skin);
  147. }
  148. }
  149. }
  150. } else {
  151. // Under some circumstances (e.g. sometimes on the first import) OnValidate is called
  152. // before SpineEditorUtilities.ImportSpineContent, causing an unnecessary exception.
  153. // The (skeletonDataAsset.skeletonJSON != null) condition serves to prevent this exception.
  154. if (skeletonDataAsset != null && skeletonDataAsset.skeletonJSON != null)
  155. Initialize(true);
  156. }
  157. }
  158. protected override void Reset () {
  159. base.Reset();
  160. if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic"))
  161. Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material.");
  162. }
  163. #endif
  164. #endregion
  165. #region Runtime Instantiation
  166. /// <summary>Create a new GameObject with a SkeletonGraphic component.</summary>
  167. /// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
  168. public static SkeletonGraphic NewSkeletonGraphicGameObject (SkeletonDataAsset skeletonDataAsset, Transform parent, Material material) {
  169. SkeletonGraphic sg = SkeletonGraphic.AddSkeletonGraphicComponent(new GameObject("New Spine GameObject"), skeletonDataAsset, material);
  170. if (parent != null) sg.transform.SetParent(parent, false);
  171. return sg;
  172. }
  173. /// <summary>Add a SkeletonGraphic component to a GameObject.</summary>
  174. /// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
  175. public static SkeletonGraphic AddSkeletonGraphicComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset, Material material) {
  176. SkeletonGraphic skeletonGraphic = gameObject.AddComponent<SkeletonGraphic>();
  177. if (skeletonDataAsset != null) {
  178. skeletonGraphic.material = material;
  179. skeletonGraphic.skeletonDataAsset = skeletonDataAsset;
  180. skeletonGraphic.Initialize(false);
  181. }
  182. #if HAS_CULL_TRANSPARENT_MESH
  183. CanvasRenderer canvasRenderer = gameObject.GetComponent<CanvasRenderer>();
  184. if (canvasRenderer) canvasRenderer.cullTransparentMesh = false;
  185. #endif
  186. return skeletonGraphic;
  187. }
  188. #endregion
  189. #region Overrides
  190. // API for taking over rendering.
  191. /// <summary>When true, no meshes and materials are assigned at CanvasRenderers if the used override
  192. /// AssignMeshOverrideSingleRenderer or AssignMeshOverrideMultipleRenderers is non-null.</summary>
  193. public bool disableMeshAssignmentOnOverride = true;
  194. /// <summary>Delegate type for overriding mesh and material assignment,
  195. /// used when <c>allowMultipleCanvasRenderers</c> is false.</summary>
  196. /// <param name="mesh">Mesh normally assigned at the main CanvasRenderer.</param>
  197. /// <param name="graphicMaterial">Material normally assigned at the main CanvasRenderer.</param>
  198. /// <param name="texture">Texture normally assigned at the main CanvasRenderer.</param>
  199. public delegate void MeshAssignmentDelegateSingle (Mesh mesh, Material graphicMaterial, Texture texture);
  200. /// <param name="meshCount">Number of meshes. Don't use <c>meshes.Length</c> as this might be higher
  201. /// due to pre-allocated entries.</param>
  202. /// <param name="meshes">Mesh array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
  203. /// <param name="graphicMaterials">Material array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
  204. /// <param name="textures">Texture array where each element is normally assigned to one of the <c>canvasRenderers</c>.</param>
  205. public delegate void MeshAssignmentDelegateMultiple (int meshCount, Mesh[] meshes, Material[] graphicMaterials, Texture[] textures);
  206. event MeshAssignmentDelegateSingle assignMeshOverrideSingle;
  207. event MeshAssignmentDelegateMultiple assignMeshOverrideMultiple;
  208. /// <summary>Allows separate code to take over mesh and material assignment for this SkeletonGraphic component.
  209. /// Used when <c>allowMultipleCanvasRenderers</c> is false.</summary>
  210. public event MeshAssignmentDelegateSingle AssignMeshOverrideSingleRenderer {
  211. add {
  212. assignMeshOverrideSingle += value;
  213. if (disableMeshAssignmentOnOverride && assignMeshOverrideSingle != null) {
  214. Initialize(false);
  215. }
  216. }
  217. remove {
  218. assignMeshOverrideSingle -= value;
  219. if (disableMeshAssignmentOnOverride && assignMeshOverrideSingle == null) {
  220. Initialize(false);
  221. }
  222. }
  223. }
  224. /// <summary>Allows separate code to take over mesh and material assignment for this SkeletonGraphic component.
  225. /// Used when <c>allowMultipleCanvasRenderers</c> is true.</summary>
  226. public event MeshAssignmentDelegateMultiple AssignMeshOverrideMultipleRenderers {
  227. add {
  228. assignMeshOverrideMultiple += value;
  229. if (disableMeshAssignmentOnOverride && assignMeshOverrideMultiple != null) {
  230. Initialize(false);
  231. }
  232. }
  233. remove {
  234. assignMeshOverrideMultiple -= value;
  235. if (disableMeshAssignmentOnOverride && assignMeshOverrideMultiple == null) {
  236. Initialize(false);
  237. }
  238. }
  239. }
  240. [System.NonSerialized] readonly Dictionary<Texture, Texture> customTextureOverride = new Dictionary<Texture, Texture>();
  241. /// <summary>Use this Dictionary to override a Texture with a different Texture.</summary>
  242. public Dictionary<Texture, Texture> CustomTextureOverride { get { return customTextureOverride; } }
  243. [System.NonSerialized] readonly Dictionary<Texture, Material> customMaterialOverride = new Dictionary<Texture, Material>();
  244. /// <summary>Use this Dictionary to override the Material where the Texture was used at the original atlas.</summary>
  245. public Dictionary<Texture, Material> CustomMaterialOverride { get { return customMaterialOverride; } }
  246. // This is used by the UI system to determine what to put in the MaterialPropertyBlock.
  247. Texture overrideTexture;
  248. public Texture OverrideTexture {
  249. get { return overrideTexture; }
  250. set {
  251. overrideTexture = value;
  252. canvasRenderer.SetTexture(this.mainTexture); // Refresh canvasRenderer's texture. Make sure it handles null.
  253. }
  254. }
  255. #endregion
  256. #region Internals
  257. public override Texture mainTexture {
  258. get {
  259. if (overrideTexture != null) return overrideTexture;
  260. return baseTexture;
  261. }
  262. }
  263. protected override void Awake () {
  264. base.Awake();
  265. this.onCullStateChanged.AddListener(OnCullStateChanged);
  266. SyncSubmeshGraphicsWithCanvasRenderers();
  267. if (!this.IsValid) {
  268. #if UNITY_EDITOR
  269. // workaround for special import case of open scene where OnValidate and Awake are
  270. // called in wrong order, before setup of Spine assets.
  271. if (!Application.isPlaying) {
  272. if (this.skeletonDataAsset != null && this.skeletonDataAsset.skeletonJSON == null)
  273. return;
  274. }
  275. #endif
  276. Initialize(false);
  277. if (this.IsValid) Rebuild(CanvasUpdate.PreRender);
  278. }
  279. #if UNITY_EDITOR
  280. InitLayoutScaleParameters();
  281. #endif
  282. }
  283. protected override void OnDestroy () {
  284. Clear();
  285. base.OnDestroy();
  286. }
  287. public override void Rebuild (CanvasUpdate update) {
  288. base.Rebuild(update);
  289. if (!this.IsValid) return;
  290. if (canvasRenderer.cull) return;
  291. if (update == CanvasUpdate.PreRender) {
  292. PrepareInstructionsAndRenderers(isInRebuild: true);
  293. UpdateMeshToInstructions();
  294. }
  295. if (allowMultipleCanvasRenderers) canvasRenderer.Clear();
  296. }
  297. protected override void OnDisable () {
  298. base.OnDisable();
  299. foreach (CanvasRenderer canvasRenderer in canvasRenderers) {
  300. canvasRenderer.Clear();
  301. }
  302. }
  303. public virtual void Update () {
  304. #if UNITY_EDITOR
  305. UpdateReferenceRectSizes();
  306. if (!Application.isPlaying) {
  307. Update(0f);
  308. return;
  309. }
  310. #endif
  311. if (freeze || updateTiming != UpdateTiming.InUpdate) return;
  312. Update(unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime);
  313. }
  314. virtual protected void FixedUpdate () {
  315. if (freeze || updateTiming != UpdateTiming.InFixedUpdate) return;
  316. Update(unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime);
  317. }
  318. public virtual void Update (float deltaTime) {
  319. if (!this.IsValid) return;
  320. wasUpdatedAfterInit = true;
  321. if (updateMode < UpdateMode.OnlyAnimationStatus)
  322. return;
  323. UpdateAnimationStatus(deltaTime);
  324. if (updateMode == UpdateMode.OnlyAnimationStatus)
  325. return;
  326. ApplyAnimation();
  327. }
  328. protected void SyncSubmeshGraphicsWithCanvasRenderers () {
  329. submeshGraphics.Clear();
  330. #if UNITY_EDITOR
  331. if (!Application.isPlaying)
  332. DestroyOldRawImages();
  333. #endif
  334. foreach (CanvasRenderer canvasRenderer in canvasRenderers) {
  335. SkeletonSubmeshGraphic submeshGraphic = canvasRenderer.GetComponent<SkeletonSubmeshGraphic>();
  336. if (submeshGraphic == null) {
  337. submeshGraphic = canvasRenderer.gameObject.AddComponent<SkeletonSubmeshGraphic>();
  338. submeshGraphic.maskable = this.maskable;
  339. submeshGraphic.raycastTarget = false;
  340. }
  341. submeshGraphics.Add(submeshGraphic);
  342. }
  343. }
  344. protected void UpdateAnimationStatus (float deltaTime) {
  345. deltaTime *= timeScale;
  346. state.Update(deltaTime);
  347. skeleton.Update(deltaTime);
  348. ApplyTransformMovementToPhysics();
  349. if (updateMode == UpdateMode.OnlyAnimationStatus) {
  350. state.ApplyEventTimelinesOnly(skeleton, issueEvents: false);
  351. return;
  352. }
  353. }
  354. public virtual void ApplyTransformMovementToPhysics () {
  355. if (Application.isPlaying) {
  356. if (physicsPositionInheritanceFactor != Vector2.zero) {
  357. Vector2 position = GetPhysicsTransformPosition();
  358. Vector2 positionDelta = (position - lastPosition) / meshScale;
  359. positionDelta = transform.InverseTransformVector(positionDelta);
  360. if (physicsMovementRelativeTo != null) {
  361. positionDelta = physicsMovementRelativeTo.TransformVector(positionDelta);
  362. }
  363. positionDelta.x *= physicsPositionInheritanceFactor.x;
  364. positionDelta.y *= physicsPositionInheritanceFactor.y;
  365. skeleton.PhysicsTranslate(positionDelta.x, positionDelta.y);
  366. lastPosition = position;
  367. }
  368. if (physicsRotationInheritanceFactor != 0f) {
  369. float rotation = GetPhysicsTransformRotation();
  370. skeleton.PhysicsRotate(0, 0, physicsRotationInheritanceFactor * (rotation - lastRotation));
  371. lastRotation = rotation;
  372. }
  373. }
  374. }
  375. protected Vector2 GetPhysicsTransformPosition () {
  376. if (physicsMovementRelativeTo == null) {
  377. return transform.position;
  378. } else {
  379. if (physicsMovementRelativeTo == transform.parent)
  380. return transform.localPosition;
  381. else
  382. return physicsMovementRelativeTo.InverseTransformPoint(transform.position);
  383. }
  384. }
  385. protected float GetPhysicsTransformRotation () {
  386. if (physicsMovementRelativeTo == null) {
  387. return this.transform.rotation.eulerAngles.z;
  388. } else {
  389. if (physicsMovementRelativeTo == this.transform.parent)
  390. return this.transform.localRotation.eulerAngles.z;
  391. else {
  392. Quaternion relative = Quaternion.Inverse(physicsMovementRelativeTo.rotation) * this.transform.rotation;
  393. return relative.eulerAngles.z;
  394. }
  395. }
  396. }
  397. public virtual void ApplyAnimation () {
  398. if (BeforeApply != null)
  399. BeforeApply(this);
  400. if (updateMode != UpdateMode.OnlyEventTimelines)
  401. state.Apply(skeleton);
  402. else
  403. state.ApplyEventTimelinesOnly(skeleton, issueEvents: true);
  404. AfterAnimationApplied();
  405. }
  406. public virtual void AfterAnimationApplied () {
  407. if (UpdateLocal != null)
  408. UpdateLocal(this);
  409. if (UpdateWorld == null) {
  410. UpdateWorldTransform(Skeleton.Physics.Update);
  411. } else {
  412. UpdateWorldTransform(Skeleton.Physics.Pose);
  413. UpdateWorld(this);
  414. UpdateWorldTransform(Skeleton.Physics.Update);
  415. }
  416. if (UpdateComplete != null)
  417. UpdateComplete(this);
  418. }
  419. protected void UpdateWorldTransform (Skeleton.Physics physics) {
  420. skeleton.UpdateWorldTransform(physics);
  421. }
  422. public void LateUpdate () {
  423. if (!this.IsValid) return;
  424. // instantiation can happen from Update() after this component, leading to a missing Update() call.
  425. if (!wasUpdatedAfterInit) Update(0);
  426. if (freeze) return;
  427. if (updateMode != UpdateMode.FullUpdate) return;
  428. if (updateTiming == UpdateTiming.InLateUpdate)
  429. Update(unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime);
  430. UpdateMesh();
  431. }
  432. protected void OnCullStateChanged (bool culled) {
  433. if (culled)
  434. OnBecameInvisible();
  435. else
  436. OnBecameVisible();
  437. }
  438. public void OnBecameVisible () {
  439. updateMode = UpdateMode.FullUpdate;
  440. }
  441. public void OnBecameInvisible () {
  442. updateMode = updateWhenInvisible;
  443. }
  444. public void ReapplySeparatorSlotNames () {
  445. if (!IsValid)
  446. return;
  447. separatorSlots.Clear();
  448. for (int i = 0, n = separatorSlotNames.Length; i < n; i++) {
  449. string slotName = separatorSlotNames[i];
  450. if (slotName == "")
  451. continue;
  452. Slot slot = skeleton.FindSlot(slotName);
  453. if (slot != null) {
  454. separatorSlots.Add(slot);
  455. }
  456. #if UNITY_EDITOR
  457. else {
  458. Debug.LogWarning(slotName + " is not a slot in " + skeletonDataAsset.skeletonJSON.name);
  459. }
  460. #endif
  461. }
  462. UpdateSeparatorPartParents();
  463. }
  464. #endregion
  465. #region API
  466. protected Skeleton skeleton;
  467. public Skeleton Skeleton {
  468. get {
  469. Initialize(false);
  470. return skeleton;
  471. }
  472. set {
  473. skeleton = value;
  474. }
  475. }
  476. public SkeletonData SkeletonData {
  477. get {
  478. Initialize(false);
  479. return skeleton == null ? null : skeleton.Data;
  480. }
  481. }
  482. public bool IsValid { get { return skeleton != null; } }
  483. public delegate void SkeletonRendererDelegate (SkeletonGraphic skeletonGraphic);
  484. public delegate void InstructionDelegate (SkeletonRendererInstruction instruction);
  485. /// <summary>OnRebuild is raised after the Skeleton is successfully initialized.</summary>
  486. public event SkeletonRendererDelegate OnRebuild;
  487. /// <summary>OnInstructionsPrepared is raised at the end of <c>LateUpdate</c> after render instructions
  488. /// are done, target renderers are prepared, and the mesh is ready to be generated.</summary>
  489. public event InstructionDelegate OnInstructionsPrepared;
  490. /// <summary>OnMeshAndMaterialsUpdated is raised at the end of <c>Rebuild</c> after the Mesh and
  491. /// all materials have been updated. Note that some Unity API calls are not permitted to be issued from
  492. /// <c>Rebuild</c>, so you may want to subscribe to <see cref="OnInstructionsPrepared"/> instead
  493. /// from where you can issue such preparation calls.</summary>
  494. public event SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
  495. protected Spine.AnimationState state;
  496. public Spine.AnimationState AnimationState {
  497. get {
  498. Initialize(false);
  499. return state;
  500. }
  501. }
  502. /// <seealso cref="PhysicsPositionInheritanceFactor"/>
  503. [SerializeField] protected Vector2 physicsPositionInheritanceFactor = Vector2.one;
  504. /// <seealso cref="PhysicsRotationInheritanceFactor"/>
  505. [SerializeField] protected float physicsRotationInheritanceFactor = 1.0f;
  506. /// <summary>Reference transform relative to which physics movement will be calculated, or null to use world location.</summary>
  507. [SerializeField] protected Transform physicsMovementRelativeTo = null;
  508. /// <summary>Used for applying Transform translation to skeleton PhysicsConstraints.</summary>
  509. protected Vector2 lastPosition;
  510. /// <summary>Used for applying Transform rotation to skeleton PhysicsConstraints.</summary>
  511. protected float lastRotation;
  512. /// <summary>When set to non-zero, Transform position movement in X and Y direction
  513. /// is applied to skeleton PhysicsConstraints, multiplied by this scale factor.
  514. /// Typical values are <c>Vector2.one</c> to apply XY movement 1:1,
  515. /// <c>Vector2(2f, 2f)</c> to apply movement with double intensity,
  516. /// <c>Vector2(1f, 0f)</c> to apply only horizontal movement, or
  517. /// <c>Vector2.zero</c> to not apply any Transform position movement at all.</summary>
  518. public Vector2 PhysicsPositionInheritanceFactor {
  519. get {
  520. return physicsPositionInheritanceFactor;
  521. }
  522. set {
  523. if (physicsPositionInheritanceFactor == Vector2.zero && value != Vector2.zero) ResetLastPosition();
  524. physicsPositionInheritanceFactor = value;
  525. }
  526. }
  527. /// <summary>When set to non-zero, Transform rotation movement is applied to skeleton PhysicsConstraints,
  528. /// multiplied by this scale factor. Typical values are <c>1</c> to apply movement 1:1,
  529. /// <c>2</c> to apply movement with double intensity, or
  530. /// <c>0</c> to not apply any Transform rotation movement at all.</summary>
  531. public float PhysicsRotationInheritanceFactor {
  532. get {
  533. return physicsRotationInheritanceFactor;
  534. }
  535. set {
  536. if (physicsRotationInheritanceFactor == 0f && value != 0f) ResetLastRotation();
  537. physicsRotationInheritanceFactor = value;
  538. }
  539. }
  540. /// <summary>Reference transform relative to which physics movement will be calculated, or null to use world location.</summary>
  541. public Transform PhysicsMovementRelativeTo {
  542. get {
  543. return physicsMovementRelativeTo;
  544. }
  545. set {
  546. physicsMovementRelativeTo = value;
  547. if (physicsPositionInheritanceFactor != Vector2.zero) ResetLastPosition();
  548. if (physicsRotationInheritanceFactor != 0f) ResetLastRotation();
  549. }
  550. }
  551. public void ResetLastPosition () {
  552. lastPosition = GetPhysicsTransformPosition();
  553. }
  554. public void ResetLastRotation () {
  555. lastRotation = GetPhysicsTransformRotation();
  556. }
  557. public void ResetLastPositionAndRotation () {
  558. lastPosition = GetPhysicsTransformPosition();
  559. lastRotation = GetPhysicsTransformRotation();
  560. }
  561. [SerializeField] protected Spine.Unity.MeshGenerator meshGenerator = new MeshGenerator();
  562. public Spine.Unity.MeshGenerator MeshGenerator { get { return this.meshGenerator; } }
  563. DoubleBuffered<Spine.Unity.MeshRendererBuffers.SmartMesh> meshBuffers;
  564. SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
  565. readonly ExposedList<Mesh> meshes = new ExposedList<Mesh>();
  566. readonly ExposedList<Material> usedMaterials = new ExposedList<Material>();
  567. readonly ExposedList<Texture> usedTextures = new ExposedList<Texture>();
  568. /// <summary>Returns the <see cref="SkeletonClipping"/> used by this renderer for use with e.g.
  569. /// <see cref="Skeleton.GetBounds(out float, out float, out float, out float, ref float[], SkeletonClipping)"/>
  570. /// </summary>
  571. public SkeletonClipping SkeletonClipping { get { return meshGenerator.SkeletonClipping; } }
  572. public ExposedList<Mesh> MeshesMultipleCanvasRenderers { get { return meshes; } }
  573. public ExposedList<Material> MaterialsMultipleCanvasRenderers { get { return usedMaterials; } }
  574. public ExposedList<Texture> TexturesMultipleCanvasRenderers { get { return usedTextures; } }
  575. public Mesh GetLastMesh () {
  576. return meshBuffers.GetCurrent().mesh;
  577. }
  578. public bool MatchRectTransformWithBounds () {
  579. if (!wasUpdatedAfterInit) Update(0);
  580. UpdateMesh();
  581. if (!this.allowMultipleCanvasRenderers)
  582. return MatchRectTransformSingleRenderer();
  583. else
  584. return MatchRectTransformMultipleRenderers();
  585. }
  586. protected bool MatchRectTransformSingleRenderer () {
  587. Mesh mesh = this.GetLastMesh();
  588. if (mesh == null) {
  589. return false;
  590. }
  591. if (mesh.vertexCount == 0 || mesh.bounds.size == Vector3.zero) {
  592. this.rectTransform.sizeDelta = new Vector2(50f, 50f);
  593. this.rectTransform.pivot = new Vector2(0.5f, 0.5f);
  594. return false;
  595. }
  596. mesh.RecalculateBounds();
  597. SetRectTransformBounds(mesh.bounds);
  598. return true;
  599. }
  600. protected bool MatchRectTransformMultipleRenderers () {
  601. bool anyBoundsAdded = false;
  602. Bounds combinedBounds = new Bounds();
  603. for (int i = 0; i < canvasRenderers.Count; ++i) {
  604. CanvasRenderer canvasRenderer = canvasRenderers[i];
  605. if (!canvasRenderer.gameObject.activeSelf)
  606. continue;
  607. Mesh mesh = meshes.Items[i];
  608. if (mesh == null || mesh.vertexCount == 0)
  609. continue;
  610. mesh.RecalculateBounds();
  611. Bounds bounds = mesh.bounds;
  612. if (anyBoundsAdded)
  613. combinedBounds.Encapsulate(bounds);
  614. else {
  615. anyBoundsAdded = true;
  616. combinedBounds = bounds;
  617. }
  618. }
  619. if (!anyBoundsAdded || combinedBounds.size == Vector3.zero) {
  620. this.rectTransform.sizeDelta = new Vector2(50f, 50f);
  621. this.rectTransform.pivot = new Vector2(0.5f, 0.5f);
  622. return false;
  623. }
  624. SetRectTransformBounds(combinedBounds);
  625. return true;
  626. }
  627. private void SetRectTransformBounds (Bounds combinedBounds) {
  628. Vector3 size = combinedBounds.size;
  629. Vector3 center = combinedBounds.center;
  630. Vector2 p = new Vector2(
  631. 0.5f - (center.x / size.x),
  632. 0.5f - (center.y / size.y)
  633. );
  634. SetRectTransformSize(this, size);
  635. this.rectTransform.pivot = p;
  636. foreach (Transform separatorPart in separatorParts) {
  637. RectTransform separatorTransform = separatorPart.GetComponent<RectTransform>();
  638. if (separatorTransform) {
  639. SetRectTransformSize(separatorTransform, size);
  640. separatorTransform.pivot = p;
  641. }
  642. }
  643. foreach (SkeletonSubmeshGraphic submeshGraphic in submeshGraphics) {
  644. SetRectTransformSize(submeshGraphic, size);
  645. submeshGraphic.rectTransform.pivot = p;
  646. }
  647. this.referenceSize = size;
  648. referenceScale = referenceScale * layoutScale;
  649. layoutScale = 1f;
  650. }
  651. public static void SetRectTransformSize (Graphic target, Vector2 size) {
  652. SetRectTransformSize(target.rectTransform, size);
  653. }
  654. public static void SetRectTransformSize (RectTransform targetRectTransform, Vector2 size) {
  655. Vector2 parentSize = Vector2.zero;
  656. if (targetRectTransform.parent != null) {
  657. RectTransform parentTransform = targetRectTransform.parent.GetComponent<RectTransform>();
  658. if (parentTransform)
  659. parentSize = parentTransform.rect.size;
  660. }
  661. Vector2 anchorAreaSize = Vector2.Scale(targetRectTransform.anchorMax - targetRectTransform.anchorMin, parentSize);
  662. targetRectTransform.sizeDelta = size - anchorAreaSize;
  663. }
  664. /// <summary>OnAnimationRebuild is raised after the SkeletonAnimation component is successfully initialized.</summary>
  665. public event ISkeletonAnimationDelegate OnAnimationRebuild;
  666. public event UpdateBonesDelegate BeforeApply;
  667. public event UpdateBonesDelegate UpdateLocal;
  668. public event UpdateBonesDelegate UpdateWorld;
  669. public event UpdateBonesDelegate UpdateComplete;
  670. [SerializeField] protected UpdateTiming updateTiming = UpdateTiming.InUpdate;
  671. public UpdateTiming UpdateTiming { get { return updateTiming; } set { updateTiming = value; } }
  672. [SerializeField] protected bool unscaledTime;
  673. public bool UnscaledTime { get { return unscaledTime; } set { unscaledTime = value; } }
  674. /// <summary> Occurs after the vertex data populated every frame, before the vertices are pushed into the mesh.</summary>
  675. public event Spine.Unity.MeshGeneratorDelegate OnPostProcessVertices;
  676. public void Clear () {
  677. skeleton = null;
  678. canvasRenderer.Clear();
  679. for (int i = 0; i < canvasRenderers.Count; ++i)
  680. canvasRenderers[i].Clear();
  681. DestroyMeshes();
  682. usedMaterials.Clear();
  683. usedTextures.Clear();
  684. DisposeMeshBuffers();
  685. }
  686. public void TrimRenderers () {
  687. List<CanvasRenderer> newList = new List<CanvasRenderer>();
  688. foreach (CanvasRenderer canvasRenderer in canvasRenderers) {
  689. if (canvasRenderer.gameObject.activeSelf) {
  690. newList.Add(canvasRenderer);
  691. } else {
  692. if (Application.isEditor && !Application.isPlaying)
  693. DestroyImmediate(canvasRenderer.gameObject);
  694. else
  695. Destroy(canvasRenderer.gameObject);
  696. }
  697. }
  698. canvasRenderers = newList;
  699. SyncSubmeshGraphicsWithCanvasRenderers();
  700. }
  701. public void Initialize (bool overwrite) {
  702. if (this.IsValid && !overwrite) return;
  703. #if UNITY_EDITOR
  704. if (BuildUtilities.IsInSkeletonAssetBuildPreProcessing)
  705. return;
  706. #endif
  707. if (this.skeletonDataAsset == null) return;
  708. SkeletonData skeletonData = this.skeletonDataAsset.GetSkeletonData(false);
  709. if (skeletonData == null) return;
  710. if (skeletonDataAsset.atlasAssets.Length <= 0 || skeletonDataAsset.atlasAssets[0].MaterialCount <= 0) return;
  711. this.skeleton = new Skeleton(skeletonData) {
  712. ScaleX = this.initialFlipX ? -1 : 1,
  713. ScaleY = this.initialFlipY ? -1 : 1
  714. };
  715. InitMeshBuffers();
  716. baseTexture = skeletonDataAsset.atlasAssets[0].PrimaryMaterial.mainTexture;
  717. canvasRenderer.SetTexture(this.mainTexture); // Needed for overwriting initializations.
  718. ResetLastPositionAndRotation();
  719. // Set the initial Skin and Animation
  720. if (!string.IsNullOrEmpty(initialSkinName))
  721. skeleton.SetSkin(initialSkinName);
  722. separatorSlots.Clear();
  723. for (int i = 0; i < separatorSlotNames.Length; i++)
  724. separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i]));
  725. if (OnRebuild != null)
  726. OnRebuild(this);
  727. wasUpdatedAfterInit = false;
  728. this.state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
  729. if (state == null) {
  730. Clear();
  731. return;
  732. }
  733. if (!string.IsNullOrEmpty(startingAnimation)) {
  734. Spine.Animation animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(startingAnimation);
  735. if (animationObject != null) {
  736. state.SetAnimation(0, animationObject, startingLoop);
  737. #if UNITY_EDITOR
  738. if (!Application.isPlaying)
  739. Update(0f);
  740. #endif
  741. }
  742. }
  743. if (OnAnimationRebuild != null)
  744. OnAnimationRebuild(this);
  745. }
  746. public void PrepareInstructionsAndRenderers (bool isInRebuild = false) {
  747. if (!this.allowMultipleCanvasRenderers) {
  748. MeshGenerator.GenerateSingleSubmeshInstruction(currentInstructions, skeleton, null);
  749. if (canvasRenderers.Count > 0)
  750. DisableUnusedCanvasRenderers(usedCount: 0, isInRebuild: isInRebuild);
  751. usedRenderersCount = 0;
  752. } else {
  753. MeshGenerator.GenerateSkeletonRendererInstruction(currentInstructions, skeleton, null,
  754. enableSeparatorSlots ? separatorSlots : null,
  755. enableSeparatorSlots ? separatorSlots.Count > 0 : false,
  756. false);
  757. int submeshCount = currentInstructions.submeshInstructions.Count;
  758. EnsureCanvasRendererCount(submeshCount);
  759. EnsureMeshesCount(submeshCount);
  760. EnsureUsedTexturesAndMaterialsCount(submeshCount);
  761. EnsureSeparatorPartCount();
  762. PrepareRendererGameObjects(currentInstructions, isInRebuild);
  763. }
  764. if (OnInstructionsPrepared != null)
  765. OnInstructionsPrepared(this.currentInstructions);
  766. }
  767. public void UpdateMesh () {
  768. PrepareInstructionsAndRenderers();
  769. UpdateMeshToInstructions();
  770. }
  771. public void UpdateMeshToInstructions () {
  772. if (!this.IsValid || currentInstructions.rawVertexCount < 0) return;
  773. skeleton.SetColor(this.color);
  774. if (!this.allowMultipleCanvasRenderers) {
  775. UpdateMeshSingleCanvasRenderer(currentInstructions);
  776. } else {
  777. UpdateMaterialsMultipleCanvasRenderers(currentInstructions);
  778. UpdateMeshMultipleCanvasRenderers(currentInstructions);
  779. }
  780. if (OnMeshAndMaterialsUpdated != null)
  781. OnMeshAndMaterialsUpdated(this);
  782. }
  783. public bool HasMultipleSubmeshInstructions () {
  784. if (!IsValid)
  785. return false;
  786. return MeshGenerator.RequiresMultipleSubmeshesByDrawOrder(skeleton);
  787. }
  788. #endregion
  789. protected void InitMeshBuffers () {
  790. if (meshBuffers != null) {
  791. meshBuffers.GetNext().Clear();
  792. meshBuffers.GetNext().Clear();
  793. } else {
  794. meshBuffers = new DoubleBuffered<MeshRendererBuffers.SmartMesh>();
  795. }
  796. }
  797. protected void DisposeMeshBuffers () {
  798. if (meshBuffers != null) {
  799. meshBuffers.GetNext().Dispose();
  800. meshBuffers.GetNext().Dispose();
  801. meshBuffers = null;
  802. }
  803. }
  804. protected void UpdateMeshSingleCanvasRenderer (SkeletonRendererInstruction currentInstructions) {
  805. MeshRendererBuffers.SmartMesh smartMesh = meshBuffers.GetNext();
  806. bool updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed);
  807. meshGenerator.Begin();
  808. bool useAddSubmesh = currentInstructions.hasActiveClipping && currentInstructions.submeshInstructions.Count > 0;
  809. if (useAddSubmesh) {
  810. meshGenerator.AddSubmesh(currentInstructions.submeshInstructions.Items[0], updateTriangles);
  811. } else {
  812. meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
  813. }
  814. meshScale = (canvas == null) ? 100 : canvas.referencePixelsPerUnit;
  815. if (layoutScaleMode != LayoutMode.None) {
  816. meshScale *= referenceScale;
  817. layoutScale = GetLayoutScale(layoutScaleMode);
  818. if (!EditReferenceRect) {
  819. meshScale *= layoutScale;
  820. }
  821. meshOffset = pivotOffset * layoutScale;
  822. } else {
  823. meshOffset = pivotOffset;
  824. }
  825. if (meshOffset == Vector2.zero)
  826. meshGenerator.ScaleVertexData(meshScale);
  827. else
  828. meshGenerator.ScaleAndOffsetVertexData(meshScale, meshOffset);
  829. if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
  830. Mesh mesh = smartMesh.mesh;
  831. meshGenerator.FillVertexData(mesh);
  832. if (updateTriangles) meshGenerator.FillTriangles(mesh);
  833. meshGenerator.FillLateVertexData(mesh);
  834. smartMesh.instructionUsed.Set(currentInstructions);
  835. if (assignMeshOverrideSingle != null)
  836. assignMeshOverrideSingle(mesh, this.canvasRenderer.GetMaterial(), this.mainTexture);
  837. bool assignAtCanvasRenderer = (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride);
  838. if (assignAtCanvasRenderer)
  839. canvasRenderer.SetMesh(mesh);
  840. else
  841. canvasRenderer.SetMesh(null);
  842. bool assignTexture = false;
  843. if (currentInstructions.submeshInstructions.Count > 0) {
  844. Material material = currentInstructions.submeshInstructions.Items[0].material;
  845. if (material != null && baseTexture != material.mainTexture) {
  846. baseTexture = material.mainTexture;
  847. if (overrideTexture == null && assignAtCanvasRenderer)
  848. assignTexture = true;
  849. }
  850. }
  851. #if SPINE_OPTIONAL_ON_DEMAND_LOADING
  852. if (Application.isPlaying)
  853. HandleOnDemandLoading();
  854. #endif
  855. if (assignTexture)
  856. canvasRenderer.SetTexture(this.mainTexture);
  857. }
  858. protected void UpdateMaterialsMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions) {
  859. int submeshCount = currentInstructions.submeshInstructions.Count;
  860. bool useOriginalTextureAndMaterial = (customMaterialOverride.Count == 0 && customTextureOverride.Count == 0);
  861. BlendModeMaterials blendModeMaterials = skeletonDataAsset.blendModeMaterials;
  862. bool hasBlendModeMaterials = blendModeMaterials.RequiresBlendModeMaterials;
  863. bool pmaVertexColors = meshGenerator.settings.pmaVertexColors;
  864. Material[] usedMaterialItems = usedMaterials.Items;
  865. Texture[] usedTextureItems = usedTextures.Items;
  866. for (int i = 0; i < submeshCount; i++) {
  867. SubmeshInstruction submeshInstructionItem = currentInstructions.submeshInstructions.Items[i];
  868. Material submeshMaterial = submeshInstructionItem.material;
  869. if (useOriginalTextureAndMaterial) {
  870. if (submeshMaterial == null) {
  871. usedMaterialItems[i] = null;
  872. usedTextureItems[i] = null;
  873. continue;
  874. }
  875. usedTextureItems[i] = submeshMaterial.mainTexture;
  876. if (!hasBlendModeMaterials) {
  877. usedMaterialItems[i] = this.materialForRendering;
  878. } else {
  879. BlendMode blendMode = blendModeMaterials.BlendModeForMaterial(submeshMaterial);
  880. Material usedMaterial = this.materialForRendering;
  881. if (blendMode == BlendMode.Additive && !pmaVertexColors && additiveMaterial) {
  882. usedMaterial = additiveMaterial;
  883. } else if (blendMode == BlendMode.Multiply && multiplyMaterial)
  884. usedMaterial = multiplyMaterial;
  885. else if (blendMode == BlendMode.Screen && screenMaterial)
  886. usedMaterial = screenMaterial;
  887. usedMaterialItems[i] = submeshGraphics[i].GetModifiedMaterial(usedMaterial);
  888. }
  889. } else {
  890. Texture originalTexture = submeshMaterial.mainTexture;
  891. Material usedMaterial;
  892. Texture usedTexture;
  893. if (!customMaterialOverride.TryGetValue(originalTexture, out usedMaterial))
  894. usedMaterial = material;
  895. if (!customTextureOverride.TryGetValue(originalTexture, out usedTexture))
  896. usedTexture = originalTexture;
  897. usedMaterialItems[i] = submeshGraphics[i].GetModifiedMaterial(usedMaterial);
  898. usedTextureItems[i] = usedTexture;
  899. }
  900. }
  901. }
  902. protected void UpdateMeshMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions) {
  903. meshScale = (canvas == null) ? 100 : canvas.referencePixelsPerUnit;
  904. if (layoutScaleMode != LayoutMode.None) {
  905. meshScale *= referenceScale;
  906. layoutScale = GetLayoutScale(layoutScaleMode);
  907. if (!EditReferenceRect) {
  908. meshScale *= layoutScale;
  909. }
  910. meshOffset = pivotOffset * layoutScale;
  911. } else {
  912. meshOffset = pivotOffset;
  913. }
  914. // Generate meshes.
  915. int submeshCount = currentInstructions.submeshInstructions.Count;
  916. Mesh[] meshesItems = meshes.Items;
  917. bool useOriginalTextureAndMaterial = (customMaterialOverride.Count == 0 && customTextureOverride.Count == 0);
  918. BlendModeMaterials blendModeMaterials = skeletonDataAsset.blendModeMaterials;
  919. bool hasBlendModeMaterials = blendModeMaterials.RequiresBlendModeMaterials;
  920. #if HAS_CULL_TRANSPARENT_MESH
  921. bool mainCullTransparentMesh = this.canvasRenderer.cullTransparentMesh;
  922. #endif
  923. bool pmaVertexColors = meshGenerator.settings.pmaVertexColors;
  924. Material[] usedMaterialItems = usedMaterials.Items;
  925. Texture[] usedTextureItems = usedTextures.Items;
  926. for (int i = 0; i < submeshCount; i++) {
  927. SubmeshInstruction submeshInstructionItem = currentInstructions.submeshInstructions.Items[i];
  928. meshGenerator.Begin();
  929. meshGenerator.AddSubmesh(submeshInstructionItem);
  930. Mesh targetMesh = meshesItems[i];
  931. if (meshOffset == Vector2.zero)
  932. meshGenerator.ScaleVertexData(meshScale);
  933. else
  934. meshGenerator.ScaleAndOffsetVertexData(meshScale, meshOffset);
  935. if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
  936. meshGenerator.FillVertexData(targetMesh);
  937. meshGenerator.FillTriangles(targetMesh);
  938. meshGenerator.FillLateVertexData(targetMesh);
  939. CanvasRenderer canvasRenderer = canvasRenderers[i];
  940. if (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride)
  941. canvasRenderer.SetMesh(targetMesh);
  942. else
  943. canvasRenderer.SetMesh(null);
  944. SkeletonSubmeshGraphic submeshGraphic = submeshGraphics[i];
  945. if (useOriginalTextureAndMaterial && hasBlendModeMaterials) {
  946. bool allowCullTransparentMesh = true;
  947. BlendMode materialBlendMode = blendModeMaterials.BlendModeForMaterial(usedMaterialItems[i]);
  948. if ((materialBlendMode == BlendMode.Normal && submeshInstructionItem.hasPMAAdditiveSlot) ||
  949. (materialBlendMode == BlendMode.Additive && pmaVertexColors)) {
  950. allowCullTransparentMesh = false;
  951. }
  952. #if HAS_CULL_TRANSPARENT_MESH
  953. canvasRenderer.cullTransparentMesh = allowCullTransparentMesh ?
  954. mainCullTransparentMesh : false;
  955. #endif
  956. }
  957. canvasRenderer.materialCount = 1;
  958. }
  959. #if SPINE_OPTIONAL_ON_DEMAND_LOADING
  960. if (Application.isPlaying)
  961. HandleOnDemandLoading();
  962. #endif
  963. bool assignAtCanvasRenderer = (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride);
  964. if (assignAtCanvasRenderer) {
  965. for (int i = 0; i < submeshCount; i++) {
  966. CanvasRenderer canvasRenderer = canvasRenderers[i];
  967. canvasRenderer.SetMaterial(usedMaterialItems[i], usedTextureItems[i]);
  968. }
  969. }
  970. if (assignMeshOverrideMultiple != null)
  971. assignMeshOverrideMultiple(submeshCount, meshesItems, usedMaterialItems, usedTextureItems);
  972. }
  973. #if SPINE_OPTIONAL_ON_DEMAND_LOADING
  974. void HandleOnDemandLoading () {
  975. foreach (AtlasAssetBase atlasAsset in skeletonDataAsset.atlasAssets) {
  976. if (atlasAsset.TextureLoadingMode != AtlasAssetBase.LoadingMode.Normal) {
  977. atlasAsset.BeginCustomTextureLoading();
  978. if (!this.allowMultipleCanvasRenderers) {
  979. Texture loadedTexture = null;
  980. atlasAsset.RequireTextureLoaded(this.mainTexture, ref loadedTexture, null);
  981. if (loadedTexture)
  982. this.baseTexture = loadedTexture;
  983. } else {
  984. Texture[] textureItems = usedTextures.Items;
  985. for (int i = 0, count = usedTextures.Count; i < count; ++i) {
  986. Texture loadedTexture = null;
  987. atlasAsset.RequireTextureLoaded(textureItems[i], ref loadedTexture, null);
  988. if (loadedTexture)
  989. usedTextures.Items[i] = loadedTexture;
  990. }
  991. }
  992. atlasAsset.EndCustomTextureLoading();
  993. }
  994. }
  995. }
  996. #endif
  997. protected void EnsureCanvasRendererCount (int targetCount) {
  998. #if UNITY_EDITOR
  999. RemoveNullCanvasRenderers();
  1000. #endif
  1001. int currentCount = canvasRenderers.Count;
  1002. for (int i = currentCount; i < targetCount; ++i) {
  1003. GameObject go = new GameObject(string.Format("Renderer{0}", i), typeof(RectTransform));
  1004. go.transform.SetParent(this.transform, false);
  1005. go.transform.localPosition = Vector3.zero;
  1006. CanvasRenderer canvasRenderer = go.AddComponent<CanvasRenderer>();
  1007. canvasRenderers.Add(canvasRenderer);
  1008. SkeletonSubmeshGraphic submeshGraphic = go.AddComponent<SkeletonSubmeshGraphic>();
  1009. submeshGraphic.maskable = this.maskable;
  1010. submeshGraphic.raycastTarget = false;
  1011. submeshGraphic.rectTransform.pivot = rectTransform.pivot;
  1012. submeshGraphic.rectTransform.anchorMin = Vector2.zero;
  1013. submeshGraphic.rectTransform.anchorMax = Vector2.one;
  1014. submeshGraphic.rectTransform.sizeDelta = Vector2.zero;
  1015. submeshGraphics.Add(submeshGraphic);
  1016. }
  1017. }
  1018. protected void PrepareRendererGameObjects (SkeletonRendererInstruction currentInstructions,
  1019. bool isInRebuild = false) {
  1020. int submeshCount = currentInstructions.submeshInstructions.Count;
  1021. DisableUnusedCanvasRenderers(usedCount: submeshCount, isInRebuild: isInRebuild);
  1022. Transform parent = this.separatorParts.Count == 0 ? this.transform : this.separatorParts[0];
  1023. if (updateSeparatorPartLocation) {
  1024. for (int p = 0; p < this.separatorParts.Count; ++p) {
  1025. Transform separatorPart = separatorParts[p];
  1026. if (separatorPart == null) continue;
  1027. separatorPart.position = this.transform.position;
  1028. separatorPart.rotation = this.transform.rotation;
  1029. }
  1030. }
  1031. if (updateSeparatorPartScale) {
  1032. Vector3 targetScale = this.transform.lossyScale;
  1033. for (int p = 0; p < this.separatorParts.Count; ++p) {
  1034. Transform separatorPart = separatorParts[p];
  1035. if (separatorPart == null) continue;
  1036. Transform partParent = separatorPart.parent;
  1037. Vector3 parentScale = partParent == null ? Vector3.one : partParent.lossyScale;
  1038. separatorPart.localScale = new Vector3(
  1039. parentScale.x == 0f ? 1f : targetScale.x / parentScale.x,
  1040. parentScale.y == 0f ? 1f : targetScale.y / parentScale.y,
  1041. parentScale.z == 0f ? 1f : targetScale.z / parentScale.z);
  1042. }
  1043. }
  1044. int separatorSlotGroupIndex = 0;
  1045. int targetSiblingIndex = 0;
  1046. for (int i = 0; i < submeshCount; i++) {
  1047. CanvasRenderer canvasRenderer = canvasRenderers[i];
  1048. if (canvasRenderer != null) {
  1049. if (i >= usedRenderersCount)
  1050. canvasRenderer.gameObject.SetActive(true);
  1051. if (canvasRenderer.transform.parent != parent.transform && !isInRebuild)
  1052. canvasRenderer.transform.SetParent(parent.transform, false);
  1053. canvasRenderer.transform.SetSiblingIndex(targetSiblingIndex++);
  1054. }
  1055. SkeletonSubmeshGraphic submeshGraphic = submeshGraphics[i];
  1056. if (submeshGraphic != null) {
  1057. RectTransform dstTransform = submeshGraphic.rectTransform;
  1058. dstTransform.localPosition = Vector3.zero;
  1059. dstTransform.pivot = rectTransform.pivot;
  1060. dstTransform.anchorMin = Vector2.zero;
  1061. dstTransform.anchorMax = Vector2.one;
  1062. dstTransform.sizeDelta = Vector2.zero;
  1063. }
  1064. SubmeshInstruction submeshInstructionItem = currentInstructions.submeshInstructions.Items[i];
  1065. if (submeshInstructionItem.forceSeparate) {
  1066. targetSiblingIndex = 0;
  1067. parent = separatorParts[++separatorSlotGroupIndex];
  1068. }
  1069. }
  1070. usedRenderersCount = submeshCount;
  1071. }
  1072. protected void DisableUnusedCanvasRenderers (int usedCount, bool isInRebuild = false) {
  1073. #if UNITY_EDITOR
  1074. RemoveNullCanvasRenderers();
  1075. #endif
  1076. for (int i = usedCount; i < canvasRenderers.Count; i++) {
  1077. canvasRenderers[i].Clear();
  1078. if (!isInRebuild) // rebuild does not allow disabling Graphic and thus removing it from rebuild list.
  1079. canvasRenderers[i].gameObject.SetActive(false);
  1080. }
  1081. }
  1082. #if UNITY_EDITOR
  1083. private void RemoveNullCanvasRenderers () {
  1084. if (Application.isEditor && !Application.isPlaying) {
  1085. for (int i = canvasRenderers.Count - 1; i >= 0; --i) {
  1086. if (canvasRenderers[i] == null) {
  1087. canvasRenderers.RemoveAt(i);
  1088. submeshGraphics.RemoveAt(i);
  1089. }
  1090. }
  1091. }
  1092. }
  1093. private void DestroyOldRawImages () {
  1094. foreach (CanvasRenderer canvasRenderer in canvasRenderers) {
  1095. RawImage oldRawImage = canvasRenderer.GetComponent<RawImage>();
  1096. if (oldRawImage != null) {
  1097. DestroyImmediate(oldRawImage);
  1098. }
  1099. }
  1100. }
  1101. #endif
  1102. protected void EnsureMeshesCount (int targetCount) {
  1103. int oldCount = meshes.Count;
  1104. meshes.EnsureCapacity(targetCount);
  1105. for (int i = oldCount; i < targetCount; i++)
  1106. meshes.Add(SpineMesh.NewSkeletonMesh());
  1107. }
  1108. protected void EnsureUsedTexturesAndMaterialsCount (int targetCount) {
  1109. int oldCount = usedMaterials.Count;
  1110. usedMaterials.EnsureCapacity(targetCount);
  1111. usedTextures.EnsureCapacity(targetCount);
  1112. for (int i = oldCount; i < targetCount; i++) {
  1113. usedMaterials.Add(null);
  1114. usedTextures.Add(null);
  1115. }
  1116. }
  1117. protected void DestroyMeshes () {
  1118. foreach (Mesh mesh in meshes) {
  1119. #if UNITY_EDITOR
  1120. if (Application.isEditor && !Application.isPlaying)
  1121. UnityEngine.Object.DestroyImmediate(mesh);
  1122. else
  1123. UnityEngine.Object.Destroy(mesh);
  1124. #else
  1125. UnityEngine.Object.Destroy(mesh);
  1126. #endif
  1127. }
  1128. meshes.Clear();
  1129. }
  1130. protected void EnsureSeparatorPartCount () {
  1131. #if UNITY_EDITOR
  1132. RemoveNullSeparatorParts();
  1133. #endif
  1134. int targetCount = separatorSlots.Count + 1;
  1135. if (targetCount == 1)
  1136. return;
  1137. #if UNITY_EDITOR
  1138. if (Application.isEditor && !Application.isPlaying) {
  1139. for (int i = separatorParts.Count - 1; i >= 0; --i) {
  1140. if (separatorParts[i] == null) {
  1141. separatorParts.RemoveAt(i);
  1142. }
  1143. }
  1144. }
  1145. #endif
  1146. int currentCount = separatorParts.Count;
  1147. for (int i = currentCount; i < targetCount; ++i) {
  1148. GameObject go = new GameObject(string.Format("{0}[{1}]", SeparatorPartGameObjectName, i), typeof(RectTransform));
  1149. go.transform.SetParent(this.transform, false);
  1150. RectTransform dstTransform = go.transform.GetComponent<RectTransform>();
  1151. dstTransform.localPosition = Vector3.zero;
  1152. dstTransform.pivot = rectTransform.pivot;
  1153. dstTransform.anchorMin = Vector2.zero;
  1154. dstTransform.anchorMax = Vector2.one;
  1155. dstTransform.sizeDelta = Vector2.zero;
  1156. separatorParts.Add(go.transform);
  1157. }
  1158. }
  1159. protected void UpdateSeparatorPartParents () {
  1160. int usedCount = separatorSlots.Count + 1;
  1161. if (usedCount == 1) {
  1162. usedCount = 0; // placed directly at the SkeletonGraphic parent
  1163. for (int i = 0; i < canvasRenderers.Count; ++i) {
  1164. CanvasRenderer canvasRenderer = canvasRenderers[i];
  1165. if (canvasRenderer.transform.parent.name.Contains(SeparatorPartGameObjectName)) {
  1166. canvasRenderer.transform.SetParent(this.transform, false);
  1167. canvasRenderer.transform.localPosition = Vector3.zero;
  1168. }
  1169. }
  1170. }
  1171. for (int i = 0; i < separatorParts.Count; ++i) {
  1172. bool isUsed = i < usedCount;
  1173. separatorParts[i].gameObject.SetActive(isUsed);
  1174. }
  1175. }
  1176. #if UNITY_EDITOR
  1177. private void RemoveNullSeparatorParts () {
  1178. if (Application.isEditor && !Application.isPlaying) {
  1179. for (int i = separatorParts.Count - 1; i >= 0; --i) {
  1180. if (separatorParts[i] == null) {
  1181. separatorParts.RemoveAt(i);
  1182. }
  1183. }
  1184. }
  1185. }
  1186. protected void InitLayoutScaleParameters () {
  1187. previousLayoutScaleMode = layoutScaleMode;
  1188. }
  1189. protected void UpdateReferenceRectSizes () {
  1190. if (rectTransformSize == Vector2.zero)
  1191. rectTransformSize = GetCurrentRectSize();
  1192. HandleChangedEditReferenceRect();
  1193. if (layoutScaleMode != previousLayoutScaleMode) {
  1194. if (layoutScaleMode != LayoutMode.None) {
  1195. SetRectTransformSize(this, rectTransformSize);
  1196. } else {
  1197. rectTransformSize = referenceSize / referenceScale;
  1198. referenceScale = 1f;
  1199. SetRectTransformSize(this, rectTransformSize);
  1200. }
  1201. }
  1202. if (editReferenceRect || layoutScaleMode == LayoutMode.None)
  1203. referenceSize = GetCurrentRectSize();
  1204. previousLayoutScaleMode = layoutScaleMode;
  1205. }
  1206. protected void HandleChangedEditReferenceRect () {
  1207. if (editReferenceRect == previousEditReferenceRect) return;
  1208. previousEditReferenceRect = editReferenceRect;
  1209. if (editReferenceRect) {
  1210. rectTransformSize = GetCurrentRectSize();
  1211. ResetRectToReferenceRectSize();
  1212. } else {
  1213. SetRectTransformSize(this, rectTransformSize);
  1214. }
  1215. }
  1216. public void ResetRectToReferenceRectSize () {
  1217. referenceScale = referenceScale * GetLayoutScale(previousLayoutScaleMode);
  1218. float referenceAspect = referenceSize.x / referenceSize.y;
  1219. Vector2 newSize = GetCurrentRectSize();
  1220. LayoutMode mode = GetEffectiveLayoutMode(previousLayoutScaleMode);
  1221. if (mode == LayoutMode.WidthControlsHeight)
  1222. newSize.y = newSize.x / referenceAspect;
  1223. else if (mode == LayoutMode.HeightControlsWidth)
  1224. newSize.x = newSize.y * referenceAspect;
  1225. SetRectTransformSize(this, newSize);
  1226. }
  1227. public Vector2 GetReferenceRectSize () {
  1228. return referenceSize * GetLayoutScale(layoutScaleMode);
  1229. }
  1230. public Vector2 GetPivotOffset () {
  1231. return pivotOffset;
  1232. }
  1233. public Vector2 GetScaledPivotOffset () {
  1234. return pivotOffset * GetLayoutScale(layoutScaleMode);
  1235. }
  1236. public void SetScaledPivotOffset (Vector2 pivotOffsetScaled) {
  1237. pivotOffset = pivotOffsetScaled / GetLayoutScale(layoutScaleMode);
  1238. }
  1239. #endif
  1240. protected float GetLayoutScale (LayoutMode mode) {
  1241. Vector2 currentSize = GetCurrentRectSize();
  1242. mode = GetEffectiveLayoutMode(mode);
  1243. if (mode == LayoutMode.WidthControlsHeight) {
  1244. return currentSize.x / referenceSize.x;
  1245. } else if (mode == LayoutMode.HeightControlsWidth) {
  1246. return currentSize.y / referenceSize.y;
  1247. }
  1248. return 1f;
  1249. }
  1250. /// <summary>
  1251. /// <c>LayoutMode FitInParent</c> and <c>EnvelopeParent</c> actually result in
  1252. /// <c>HeightControlsWidth</c> or <c>WidthControlsHeight</c> depending on the actual vs reference aspect ratio.
  1253. /// This method returns the respective <c>LayoutMode</c> of the two for any given input <c>mode</c>.
  1254. /// </summary>
  1255. protected LayoutMode GetEffectiveLayoutMode (LayoutMode mode) {
  1256. Vector2 currentSize = GetCurrentRectSize();
  1257. float referenceAspect = referenceSize.x / referenceSize.y;
  1258. float frameAspect = currentSize.x / currentSize.y;
  1259. if (mode == LayoutMode.FitInParent)
  1260. mode = frameAspect > referenceAspect ? LayoutMode.HeightControlsWidth : LayoutMode.WidthControlsHeight;
  1261. else if (mode == LayoutMode.EnvelopeParent)
  1262. mode = frameAspect > referenceAspect ? LayoutMode.WidthControlsHeight : LayoutMode.HeightControlsWidth;
  1263. return mode;
  1264. }
  1265. private Vector2 GetCurrentRectSize () {
  1266. return this.rectTransform.rect.size;
  1267. }
  1268. }
  1269. }