MeshGenerator.cs 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395
  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_2019_3_OR_NEWER
  30. #define MESH_SET_TRIANGLES_PROVIDES_LENGTH_PARAM
  31. #endif
  32. // Not for optimization. Do not disable.
  33. #define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
  34. //#define SPINE_DEBUG
  35. using System;
  36. using System.Collections.Generic;
  37. using UnityEngine;
  38. namespace Spine.Unity {
  39. public delegate void MeshGeneratorDelegate (MeshGeneratorBuffers buffers);
  40. public struct MeshGeneratorBuffers {
  41. /// <summary>The vertex count that will actually be used for the mesh. The Lengths of the buffer arrays may be larger than this number.</summary>
  42. public int vertexCount;
  43. /// <summary> Vertex positions. To be used for UnityEngine.Mesh.vertices.</summary>
  44. public Vector3[] vertexBuffer;
  45. /// <summary> Vertex UVs. To be used for UnityEngine.Mesh.uvs.</summary>
  46. public Vector2[] uvBuffer;
  47. /// <summary> Vertex colors. To be used for UnityEngine.Mesh.colors32.</summary>
  48. public Color32[] colorBuffer;
  49. /// <summary> The Spine rendering component's MeshGenerator. </summary>
  50. public MeshGenerator meshGenerator;
  51. }
  52. /// <summary>Holds several methods to prepare and generate a UnityEngine mesh based on a skeleton. Contains buffers needed to perform the operation, and serializes settings for mesh generation.</summary>
  53. [System.Serializable]
  54. public class MeshGenerator {
  55. public Settings settings = Settings.Default;
  56. [System.Serializable]
  57. public struct Settings {
  58. public bool useClipping;
  59. [Space]
  60. [Range(-0.1f, 0f)] public float zSpacing;
  61. [Space]
  62. [Header("Vertex Data")]
  63. public bool pmaVertexColors;
  64. public bool tintBlack;
  65. [Tooltip("Enable when using Additive blend mode at SkeletonGraphic under a CanvasGroup. " +
  66. "When enabled, Additive alpha value is stored at uv2.g instead of color.a to capture CanvasGroup modifying color.a.")]
  67. public bool canvasGroupTintBlack;
  68. public bool calculateTangents;
  69. public bool addNormals;
  70. public bool immutableTriangles;
  71. static public Settings Default {
  72. get {
  73. return new Settings {
  74. pmaVertexColors = true,
  75. zSpacing = 0f,
  76. useClipping = true,
  77. tintBlack = false,
  78. calculateTangents = false,
  79. //renderMeshes = true,
  80. addNormals = false,
  81. immutableTriangles = false
  82. };
  83. }
  84. }
  85. }
  86. const float BoundsMinDefault = float.PositiveInfinity;
  87. const float BoundsMaxDefault = float.NegativeInfinity;
  88. [NonSerialized] protected readonly ExposedList<Vector3> vertexBuffer = new ExposedList<Vector3>(4);
  89. [NonSerialized] protected readonly ExposedList<Vector2> uvBuffer = new ExposedList<Vector2>(4);
  90. [NonSerialized] protected readonly ExposedList<Color32> colorBuffer = new ExposedList<Color32>(4);
  91. [NonSerialized] protected readonly ExposedList<ExposedList<int>> submeshes = new ExposedList<ExposedList<int>> { new ExposedList<int>(6) }; // start with 1 submesh.
  92. [NonSerialized] Vector2 meshBoundsMin, meshBoundsMax;
  93. [NonSerialized] float meshBoundsThickness;
  94. [NonSerialized] int submeshIndex = 0;
  95. [NonSerialized] SkeletonClipping clipper = new SkeletonClipping();
  96. [NonSerialized] float[] tempVerts = new float[8];
  97. [NonSerialized] int[] regionTriangles = { 0, 1, 2, 2, 3, 0 };
  98. #region Optional Buffers
  99. // These optional buffers are lazy-instantiated when the feature is used.
  100. [NonSerialized] Vector3[] normals;
  101. [NonSerialized] Vector4[] tangents;
  102. [NonSerialized] Vector2[] tempTanBuffer;
  103. [NonSerialized] ExposedList<Vector2> uv2;
  104. [NonSerialized] ExposedList<Vector2> uv3;
  105. #endregion
  106. public int VertexCount { get { return vertexBuffer.Count; } }
  107. public int SubmeshIndexCount (int submeshIndex) { return submeshes.Items[submeshIndex].Count; }
  108. /// <summary>A set of mesh arrays whose values are modifiable by the user. Modify these values before they are passed to the UnityEngine mesh object in order to see the effect.</summary>
  109. public MeshGeneratorBuffers Buffers {
  110. get {
  111. return new MeshGeneratorBuffers {
  112. vertexCount = this.VertexCount,
  113. vertexBuffer = this.vertexBuffer.Items,
  114. uvBuffer = this.uvBuffer.Items,
  115. colorBuffer = this.colorBuffer.Items,
  116. meshGenerator = this
  117. };
  118. }
  119. }
  120. public MeshGenerator () {
  121. submeshes.TrimExcess();
  122. }
  123. #region Step 1 : Generate Instructions
  124. /// <summary>
  125. /// A specialized variant of <see cref="GenerateSkeletonRendererInstruction"/>.
  126. /// Generates renderer instructions using a single submesh, using only a single material and texture.
  127. /// </summary>
  128. /// <param name="instructionOutput">The resulting instructions.</param>
  129. /// <param name="skeleton">The skeleton to generate renderer instructions for.</param>
  130. /// <param name="material">Material to be set at the renderer instruction. When null, the last attachment
  131. /// in the draw order list is assigned as the instruction's material.</param>
  132. public static void GenerateSingleSubmeshInstruction (SkeletonRendererInstruction instructionOutput, Skeleton skeleton, Material material) {
  133. ExposedList<Slot> drawOrder = skeleton.DrawOrder;
  134. int drawOrderCount = drawOrder.Count;
  135. // Clear last state of attachments and submeshes
  136. instructionOutput.Clear(); // submeshInstructions.Clear(); attachments.Clear();
  137. var workingSubmeshInstructions = instructionOutput.submeshInstructions;
  138. #if SPINE_TRIANGLECHECK
  139. instructionOutput.attachments.Resize(drawOrderCount);
  140. var workingAttachmentsItems = instructionOutput.attachments.Items;
  141. int totalRawVertexCount = 0;
  142. #endif
  143. var current = new SubmeshInstruction {
  144. skeleton = skeleton,
  145. preActiveClippingSlotSource = -1,
  146. startSlot = 0,
  147. #if SPINE_TRIANGLECHECK
  148. rawFirstVertexIndex = 0,
  149. #endif
  150. material = material,
  151. forceSeparate = false,
  152. endSlot = drawOrderCount
  153. };
  154. #if SPINE_TRIANGLECHECK
  155. object rendererObject = null;
  156. bool skeletonHasClipping = false;
  157. var drawOrderItems = drawOrder.Items;
  158. for (int i = 0; i < drawOrderCount; i++) {
  159. Slot slot = drawOrderItems[i];
  160. if (!slot.Bone.Active) {
  161. workingAttachmentsItems[i] = null;
  162. continue;
  163. }
  164. if (slot.Data.BlendMode == BlendMode.Additive) current.hasPMAAdditiveSlot = true;
  165. Attachment attachment = slot.Attachment;
  166. workingAttachmentsItems[i] = attachment;
  167. int attachmentTriangleCount;
  168. int attachmentVertexCount;
  169. var regionAttachment = attachment as RegionAttachment;
  170. if (regionAttachment != null) {
  171. rendererObject = regionAttachment.RendererObject;
  172. attachmentVertexCount = 4;
  173. attachmentTriangleCount = 6;
  174. } else {
  175. var meshAttachment = attachment as MeshAttachment;
  176. if (meshAttachment != null) {
  177. rendererObject = meshAttachment.RendererObject;
  178. attachmentVertexCount = meshAttachment.WorldVerticesLength >> 1;
  179. attachmentTriangleCount = meshAttachment.Triangles.Length;
  180. } else {
  181. var clippingAttachment = attachment as ClippingAttachment;
  182. if (clippingAttachment != null) {
  183. current.hasClipping = true;
  184. skeletonHasClipping = true;
  185. }
  186. attachmentVertexCount = 0;
  187. attachmentTriangleCount = 0;
  188. }
  189. }
  190. current.rawTriangleCount += attachmentTriangleCount;
  191. current.rawVertexCount += attachmentVertexCount;
  192. totalRawVertexCount += attachmentVertexCount;
  193. }
  194. #if !SPINE_TK2D
  195. if (material == null && rendererObject != null)
  196. current.material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
  197. #else
  198. if (material == null && rendererObject != null)
  199. current.material = (rendererObject is Material) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
  200. #endif
  201. instructionOutput.hasActiveClipping = skeletonHasClipping;
  202. instructionOutput.rawVertexCount = totalRawVertexCount;
  203. #endif
  204. if (totalRawVertexCount > 0) {
  205. workingSubmeshInstructions.Resize(1);
  206. workingSubmeshInstructions.Items[0] = current;
  207. } else {
  208. workingSubmeshInstructions.Resize(0);
  209. }
  210. }
  211. public static bool RequiresMultipleSubmeshesByDrawOrder (Skeleton skeleton) {
  212. #if SPINE_TK2D
  213. return false;
  214. #endif
  215. ExposedList<Slot> drawOrder = skeleton.DrawOrder;
  216. int drawOrderCount = drawOrder.Count;
  217. var drawOrderItems = drawOrder.Items;
  218. Material lastRendererMaterial = null;
  219. for (int i = 0; i < drawOrderCount; i++) {
  220. Slot slot = drawOrderItems[i];
  221. if (!slot.Bone.Active) continue;
  222. Attachment attachment = slot.Attachment;
  223. var rendererAttachment = attachment as IHasRendererObject;
  224. if (rendererAttachment != null) {
  225. AtlasRegion atlasRegion = (AtlasRegion)rendererAttachment.RendererObject;
  226. Material material = (Material)atlasRegion.page.rendererObject;
  227. if (lastRendererMaterial != material) {
  228. if (lastRendererMaterial != null)
  229. return true;
  230. else
  231. lastRendererMaterial = material;
  232. }
  233. }
  234. }
  235. return false;
  236. }
  237. public static void GenerateSkeletonRendererInstruction (SkeletonRendererInstruction instructionOutput, Skeleton skeleton, Dictionary<Slot, Material> customSlotMaterials, List<Slot> separatorSlots, bool generateMeshOverride, bool immutableTriangles = false) {
  238. // if (skeleton == null) throw new ArgumentNullException("skeleton");
  239. // if (instructionOutput == null) throw new ArgumentNullException("instructionOutput");
  240. ExposedList<Slot> drawOrder = skeleton.DrawOrder;
  241. int drawOrderCount = drawOrder.Count;
  242. // Clear last state of attachments and submeshes
  243. instructionOutput.Clear(); // submeshInstructions.Clear(); attachments.Clear();
  244. var workingSubmeshInstructions = instructionOutput.submeshInstructions;
  245. #if SPINE_TRIANGLECHECK
  246. instructionOutput.attachments.Resize(drawOrderCount);
  247. var workingAttachmentsItems = instructionOutput.attachments.Items;
  248. int totalRawVertexCount = 0;
  249. bool skeletonHasClipping = false;
  250. #endif
  251. var current = new SubmeshInstruction {
  252. skeleton = skeleton,
  253. preActiveClippingSlotSource = -1
  254. };
  255. #if !SPINE_TK2D
  256. bool isCustomSlotMaterialsPopulated = customSlotMaterials != null && customSlotMaterials.Count > 0;
  257. #endif
  258. int separatorCount = separatorSlots == null ? 0 : separatorSlots.Count;
  259. bool hasSeparators = separatorCount > 0;
  260. int clippingAttachmentSource = -1;
  261. int lastPreActiveClipping = -1; // The index of the last slot that had an active ClippingAttachment.
  262. SlotData clippingEndSlot = null;
  263. int submeshIndex = 0;
  264. var drawOrderItems = drawOrder.Items;
  265. for (int i = 0; i < drawOrderCount; i++) {
  266. Slot slot = drawOrderItems[i];
  267. if (!slot.Bone.Active) {
  268. workingAttachmentsItems[i] = null;
  269. continue;
  270. }
  271. if (slot.Data.BlendMode == BlendMode.Additive) current.hasPMAAdditiveSlot = true;
  272. Attachment attachment = slot.Attachment;
  273. #if SPINE_TRIANGLECHECK
  274. workingAttachmentsItems[i] = attachment;
  275. int attachmentVertexCount = 0, attachmentTriangleCount = 0;
  276. #endif
  277. object rendererObject = null; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
  278. bool noRender = false; // Using this allows empty slots as separators, and keeps separated parts more stable despite slots being reordered
  279. var regionAttachment = attachment as RegionAttachment;
  280. if (regionAttachment != null) {
  281. rendererObject = regionAttachment.RendererObject;
  282. #if SPINE_TRIANGLECHECK
  283. attachmentVertexCount = 4;
  284. attachmentTriangleCount = 6;
  285. #endif
  286. } else {
  287. var meshAttachment = attachment as MeshAttachment;
  288. if (meshAttachment != null) {
  289. rendererObject = meshAttachment.RendererObject;
  290. #if SPINE_TRIANGLECHECK
  291. attachmentVertexCount = meshAttachment.WorldVerticesLength >> 1;
  292. attachmentTriangleCount = meshAttachment.Triangles.Length;
  293. #endif
  294. } else {
  295. #if SPINE_TRIANGLECHECK
  296. var clippingAttachment = attachment as ClippingAttachment;
  297. if (clippingAttachment != null) {
  298. clippingEndSlot = clippingAttachment.EndSlot;
  299. clippingAttachmentSource = i;
  300. current.hasClipping = true;
  301. skeletonHasClipping = true;
  302. }
  303. #endif
  304. noRender = true;
  305. }
  306. }
  307. // Create a new SubmeshInstruction when material changes. (or when forced to separate by a submeshSeparator)
  308. // Slot with a separator/new material will become the starting slot of the next new instruction.
  309. if (hasSeparators) { //current.forceSeparate = hasSeparators && separatorSlots.Contains(slot);
  310. current.forceSeparate = false;
  311. for (int s = 0; s < separatorCount; s++) {
  312. if (Slot.ReferenceEquals(slot, separatorSlots[s])) {
  313. current.forceSeparate = true;
  314. break;
  315. }
  316. }
  317. }
  318. if (noRender) {
  319. if (current.forceSeparate && generateMeshOverride) { // && current.rawVertexCount > 0) {
  320. { // Add
  321. current.endSlot = i;
  322. current.preActiveClippingSlotSource = lastPreActiveClipping;
  323. workingSubmeshInstructions.Resize(submeshIndex + 1);
  324. workingSubmeshInstructions.Items[submeshIndex] = current;
  325. submeshIndex++;
  326. }
  327. current.startSlot = i;
  328. lastPreActiveClipping = clippingAttachmentSource;
  329. #if SPINE_TRIANGLECHECK
  330. current.rawTriangleCount = 0;
  331. current.rawVertexCount = 0;
  332. current.rawFirstVertexIndex = totalRawVertexCount;
  333. current.hasClipping = clippingAttachmentSource >= 0;
  334. #endif
  335. }
  336. } else {
  337. #if !SPINE_TK2D
  338. Material material;
  339. if (isCustomSlotMaterialsPopulated) {
  340. if (!customSlotMaterials.TryGetValue(slot, out material))
  341. material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
  342. } else {
  343. material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
  344. }
  345. #else
  346. Material material = (rendererObject is Material) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
  347. #endif
  348. if (current.forceSeparate || (current.rawVertexCount > 0 && !System.Object.ReferenceEquals(current.material, material))) { // Material changed. Add the previous submesh.
  349. { // Add
  350. current.endSlot = i;
  351. current.preActiveClippingSlotSource = lastPreActiveClipping;
  352. workingSubmeshInstructions.Resize(submeshIndex + 1);
  353. workingSubmeshInstructions.Items[submeshIndex] = current;
  354. submeshIndex++;
  355. }
  356. current.startSlot = i;
  357. lastPreActiveClipping = clippingAttachmentSource;
  358. #if SPINE_TRIANGLECHECK
  359. current.rawTriangleCount = 0;
  360. current.rawVertexCount = 0;
  361. current.rawFirstVertexIndex = totalRawVertexCount;
  362. current.hasClipping = clippingAttachmentSource >= 0;
  363. #endif
  364. }
  365. // Update state for the next Attachment.
  366. current.material = material;
  367. #if SPINE_TRIANGLECHECK
  368. current.rawTriangleCount += attachmentTriangleCount;
  369. current.rawVertexCount += attachmentVertexCount;
  370. current.rawFirstVertexIndex = totalRawVertexCount;
  371. totalRawVertexCount += attachmentVertexCount;
  372. #endif
  373. }
  374. if (clippingEndSlot != null && slot.Data == clippingEndSlot && i != clippingAttachmentSource) {
  375. clippingEndSlot = null;
  376. clippingAttachmentSource = -1;
  377. }
  378. }
  379. if (current.rawVertexCount > 0) {
  380. { // Add last or only submesh.
  381. current.endSlot = drawOrderCount;
  382. current.preActiveClippingSlotSource = lastPreActiveClipping;
  383. current.forceSeparate = false;
  384. workingSubmeshInstructions.Resize(submeshIndex + 1);
  385. workingSubmeshInstructions.Items[submeshIndex] = current;
  386. //submeshIndex++;
  387. }
  388. }
  389. #if SPINE_TRIANGLECHECK
  390. instructionOutput.hasActiveClipping = skeletonHasClipping;
  391. instructionOutput.rawVertexCount = totalRawVertexCount;
  392. #endif
  393. instructionOutput.immutableTriangles = immutableTriangles;
  394. }
  395. public static void TryReplaceMaterials (ExposedList<SubmeshInstruction> workingSubmeshInstructions, Dictionary<Material, Material> customMaterialOverride) {
  396. // Material overrides are done here so they can be applied per submesh instead of per slot
  397. // but they will still be passed through the GenerateMeshOverride delegate,
  398. // and will still go through the normal material match check step in STEP 3.
  399. var wsii = workingSubmeshInstructions.Items;
  400. for (int i = 0; i < workingSubmeshInstructions.Count; i++) {
  401. var m = wsii[i].material;
  402. Material mo;
  403. if (customMaterialOverride.TryGetValue(m, out mo))
  404. wsii[i].material = mo;
  405. }
  406. }
  407. #endregion
  408. #region Step 2 : Populate vertex data and triangle index buffers.
  409. public void Begin () {
  410. vertexBuffer.Clear(false);
  411. colorBuffer.Clear(false);
  412. uvBuffer.Clear(false);
  413. clipper.ClipEnd();
  414. {
  415. meshBoundsMin.x = BoundsMinDefault;
  416. meshBoundsMin.y = BoundsMinDefault;
  417. meshBoundsMax.x = BoundsMaxDefault;
  418. meshBoundsMax.y = BoundsMaxDefault;
  419. meshBoundsThickness = 0f;
  420. }
  421. submeshIndex = 0;
  422. submeshes.Count = 1;
  423. //submeshes.Items[0].Clear(false);
  424. }
  425. public void AddSubmesh (SubmeshInstruction instruction, bool updateTriangles = true) {
  426. var settings = this.settings;
  427. int newSubmeshCount = submeshIndex + 1;
  428. if (submeshes.Items.Length < newSubmeshCount)
  429. submeshes.Resize(newSubmeshCount);
  430. submeshes.Count = newSubmeshCount;
  431. var submesh = submeshes.Items[submeshIndex];
  432. if (submesh == null)
  433. submeshes.Items[submeshIndex] = submesh = new ExposedList<int>();
  434. submesh.Clear(false);
  435. var skeleton = instruction.skeleton;
  436. var drawOrderItems = skeleton.DrawOrder.Items;
  437. Color32 color = default(Color32);
  438. float skeletonA = skeleton.A, skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B;
  439. Vector2 meshBoundsMin = this.meshBoundsMin, meshBoundsMax = this.meshBoundsMax;
  440. // Settings
  441. float zSpacing = settings.zSpacing;
  442. bool pmaVertexColors = settings.pmaVertexColors;
  443. bool tintBlack = settings.tintBlack;
  444. #if SPINE_TRIANGLECHECK
  445. bool useClipping = settings.useClipping && instruction.hasClipping;
  446. #else
  447. bool useClipping = settings.useClipping;
  448. #endif
  449. bool canvasGroupTintBlack = settings.tintBlack && settings.canvasGroupTintBlack;
  450. if (useClipping) {
  451. if (instruction.preActiveClippingSlotSource >= 0) {
  452. var slot = drawOrderItems[instruction.preActiveClippingSlotSource];
  453. clipper.ClipStart(slot, slot.Attachment as ClippingAttachment);
  454. }
  455. }
  456. for (int slotIndex = instruction.startSlot; slotIndex < instruction.endSlot; slotIndex++) {
  457. var slot = drawOrderItems[slotIndex];
  458. if (!slot.Bone.Active) {
  459. clipper.ClipEnd(slot);
  460. continue;
  461. }
  462. var attachment = slot.Attachment;
  463. float z = zSpacing * slotIndex;
  464. var workingVerts = this.tempVerts;
  465. float[] uvs;
  466. int[] attachmentTriangleIndices;
  467. int attachmentVertexCount;
  468. int attachmentIndexCount;
  469. Color c = default(Color);
  470. // Identify and prepare values.
  471. var region = attachment as RegionAttachment;
  472. if (region != null) {
  473. region.ComputeWorldVertices(slot.Bone, workingVerts, 0);
  474. uvs = region.UVs;
  475. attachmentTriangleIndices = regionTriangles;
  476. c.r = region.R; c.g = region.G; c.b = region.B; c.a = region.A;
  477. attachmentVertexCount = 4;
  478. attachmentIndexCount = 6;
  479. } else {
  480. var mesh = attachment as MeshAttachment;
  481. if (mesh != null) {
  482. int meshVerticesLength = mesh.WorldVerticesLength;
  483. if (workingVerts.Length < meshVerticesLength) {
  484. workingVerts = new float[meshVerticesLength];
  485. this.tempVerts = workingVerts;
  486. }
  487. mesh.ComputeWorldVertices(slot, 0, meshVerticesLength, workingVerts, 0); //meshAttachment.ComputeWorldVertices(slot, tempVerts);
  488. uvs = mesh.UVs;
  489. attachmentTriangleIndices = mesh.Triangles;
  490. c.r = mesh.R; c.g = mesh.G; c.b = mesh.B; c.a = mesh.A;
  491. attachmentVertexCount = meshVerticesLength >> 1; // meshVertexCount / 2;
  492. attachmentIndexCount = mesh.Triangles.Length;
  493. } else {
  494. if (useClipping) {
  495. var clippingAttachment = attachment as ClippingAttachment;
  496. if (clippingAttachment != null) {
  497. clipper.ClipStart(slot, clippingAttachment);
  498. continue;
  499. }
  500. }
  501. // If not any renderable attachment.
  502. clipper.ClipEnd(slot);
  503. continue;
  504. }
  505. }
  506. float tintBlackAlpha = 1.0f;
  507. if (pmaVertexColors) {
  508. float colorA = skeletonA * slot.A * c.a;
  509. color.a = (byte)(colorA * 255);
  510. color.r = (byte)(skeletonR * slot.R * c.r * color.a);
  511. color.g = (byte)(skeletonG * slot.G * c.g * color.a);
  512. color.b = (byte)(skeletonB * slot.B * c.b * color.a);
  513. if (slot.Data.BlendMode == BlendMode.Additive) {
  514. if (canvasGroupTintBlack)
  515. tintBlackAlpha = 0;
  516. else
  517. color.a = 0;
  518. } else if (canvasGroupTintBlack) { // other blend modes
  519. tintBlackAlpha = colorA;
  520. }
  521. } else {
  522. color.a = (byte)(skeletonA * slot.A * c.a * 255);
  523. color.r = (byte)(skeletonR * slot.R * c.r * 255);
  524. color.g = (byte)(skeletonG * slot.G * c.g * 255);
  525. color.b = (byte)(skeletonB * slot.B * c.b * 255);
  526. }
  527. if (useClipping && clipper.IsClipping) {
  528. clipper.ClipTriangles(workingVerts, attachmentVertexCount << 1, attachmentTriangleIndices, attachmentIndexCount, uvs);
  529. workingVerts = clipper.ClippedVertices.Items;
  530. attachmentVertexCount = clipper.ClippedVertices.Count >> 1;
  531. attachmentTriangleIndices = clipper.ClippedTriangles.Items;
  532. attachmentIndexCount = clipper.ClippedTriangles.Count;
  533. uvs = clipper.ClippedUVs.Items;
  534. }
  535. // Actually add slot/attachment data into buffers.
  536. if (attachmentVertexCount != 0 && attachmentIndexCount != 0) {
  537. if (tintBlack) {
  538. float r2 = slot.R2;
  539. float g2 = slot.G2;
  540. float b2 = slot.B2;
  541. if (pmaVertexColors) {
  542. float alpha = skeletonA * slot.A * c.a;
  543. r2 *= alpha;
  544. g2 *= alpha;
  545. b2 *= alpha;
  546. }
  547. AddAttachmentTintBlack(r2, g2, b2, tintBlackAlpha, attachmentVertexCount);
  548. }
  549. //AddAttachment(workingVerts, uvs, color, attachmentTriangleIndices, attachmentVertexCount, attachmentIndexCount, ref meshBoundsMin, ref meshBoundsMax, z);
  550. int ovc = vertexBuffer.Count;
  551. // Add data to vertex buffers
  552. {
  553. int newVertexCount = ovc + attachmentVertexCount;
  554. int oldArraySize = vertexBuffer.Items.Length;
  555. if (newVertexCount > oldArraySize) {
  556. int newArraySize = (int)(oldArraySize * 1.3f);
  557. if (newArraySize < newVertexCount) newArraySize = newVertexCount;
  558. Array.Resize(ref vertexBuffer.Items, newArraySize);
  559. Array.Resize(ref uvBuffer.Items, newArraySize);
  560. Array.Resize(ref colorBuffer.Items, newArraySize);
  561. }
  562. vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = newVertexCount;
  563. }
  564. var vbi = vertexBuffer.Items;
  565. var ubi = uvBuffer.Items;
  566. var cbi = colorBuffer.Items;
  567. if (ovc == 0) {
  568. for (int i = 0; i < attachmentVertexCount; i++) {
  569. int vi = ovc + i;
  570. int i2 = i << 1; // i * 2
  571. float x = workingVerts[i2];
  572. float y = workingVerts[i2 + 1];
  573. vbi[vi].x = x;
  574. vbi[vi].y = y;
  575. vbi[vi].z = z;
  576. ubi[vi].x = uvs[i2];
  577. ubi[vi].y = uvs[i2 + 1];
  578. cbi[vi] = color;
  579. // Calculate bounds.
  580. if (x < meshBoundsMin.x) meshBoundsMin.x = x;
  581. if (x > meshBoundsMax.x) meshBoundsMax.x = x;
  582. if (y < meshBoundsMin.y) meshBoundsMin.y = y;
  583. if (y > meshBoundsMax.y) meshBoundsMax.y = y;
  584. }
  585. } else {
  586. for (int i = 0; i < attachmentVertexCount; i++) {
  587. int vi = ovc + i;
  588. int i2 = i << 1; // i * 2
  589. float x = workingVerts[i2];
  590. float y = workingVerts[i2 + 1];
  591. vbi[vi].x = x;
  592. vbi[vi].y = y;
  593. vbi[vi].z = z;
  594. ubi[vi].x = uvs[i2];
  595. ubi[vi].y = uvs[i2 + 1];
  596. cbi[vi] = color;
  597. // Calculate bounds.
  598. if (x < meshBoundsMin.x) meshBoundsMin.x = x;
  599. else if (x > meshBoundsMax.x) meshBoundsMax.x = x;
  600. if (y < meshBoundsMin.y) meshBoundsMin.y = y;
  601. else if (y > meshBoundsMax.y) meshBoundsMax.y = y;
  602. }
  603. }
  604. // Add data to triangle buffer
  605. if (updateTriangles) {
  606. int oldTriangleCount = submesh.Count;
  607. { //submesh.Resize(oldTriangleCount + attachmentIndexCount);
  608. int newTriangleCount = oldTriangleCount + attachmentIndexCount;
  609. if (newTriangleCount > submesh.Items.Length) Array.Resize(ref submesh.Items, newTriangleCount);
  610. submesh.Count = newTriangleCount;
  611. }
  612. var submeshItems = submesh.Items;
  613. for (int i = 0; i < attachmentIndexCount; i++)
  614. submeshItems[oldTriangleCount + i] = attachmentTriangleIndices[i] + ovc;
  615. }
  616. }
  617. clipper.ClipEnd(slot);
  618. }
  619. clipper.ClipEnd();
  620. this.meshBoundsMin = meshBoundsMin;
  621. this.meshBoundsMax = meshBoundsMax;
  622. meshBoundsThickness = instruction.endSlot * zSpacing;
  623. // Trim or zero submesh triangles.
  624. var currentSubmeshItems = submesh.Items;
  625. for (int i = submesh.Count, n = currentSubmeshItems.Length; i < n; i++)
  626. currentSubmeshItems[i] = 0;
  627. submeshIndex++; // Next AddSubmesh will use a new submeshIndex value.
  628. }
  629. public void BuildMesh (SkeletonRendererInstruction instruction, bool updateTriangles) {
  630. var wsii = instruction.submeshInstructions.Items;
  631. for (int i = 0, n = instruction.submeshInstructions.Count; i < n; i++)
  632. this.AddSubmesh(wsii[i], updateTriangles);
  633. }
  634. // Use this faster method when no clipping is involved.
  635. public void BuildMeshWithArrays (SkeletonRendererInstruction instruction, bool updateTriangles) {
  636. var settings = this.settings;
  637. bool canvasGroupTintBlack = settings.tintBlack && settings.canvasGroupTintBlack;
  638. int totalVertexCount = instruction.rawVertexCount;
  639. // Add data to vertex buffers
  640. {
  641. if (totalVertexCount > vertexBuffer.Items.Length) { // Manual ExposedList.Resize()
  642. Array.Resize(ref vertexBuffer.Items, totalVertexCount);
  643. Array.Resize(ref uvBuffer.Items, totalVertexCount);
  644. Array.Resize(ref colorBuffer.Items, totalVertexCount);
  645. }
  646. vertexBuffer.Count = uvBuffer.Count = colorBuffer.Count = totalVertexCount;
  647. }
  648. // Populate Verts
  649. Color32 color = default(Color32);
  650. int vertexIndex = 0;
  651. var tempVerts = this.tempVerts;
  652. Vector2 bmin = this.meshBoundsMin;
  653. Vector2 bmax = this.meshBoundsMax;
  654. var vbi = vertexBuffer.Items;
  655. var ubi = uvBuffer.Items;
  656. var cbi = colorBuffer.Items;
  657. int lastSlotIndex = 0;
  658. // drawOrder[endSlot] is excluded
  659. for (int si = 0, n = instruction.submeshInstructions.Count; si < n; si++) {
  660. var submesh = instruction.submeshInstructions.Items[si];
  661. var skeleton = submesh.skeleton;
  662. var drawOrderItems = skeleton.DrawOrder.Items;
  663. float a = skeleton.A, r = skeleton.R, g = skeleton.G, b = skeleton.B;
  664. int endSlot = submesh.endSlot;
  665. int startSlot = submesh.startSlot;
  666. lastSlotIndex = endSlot;
  667. if (settings.tintBlack) {
  668. Vector2 rg, b2;
  669. int vi = vertexIndex;
  670. b2.y = 1f;
  671. {
  672. if (uv2 == null) {
  673. uv2 = new ExposedList<Vector2>();
  674. uv3 = new ExposedList<Vector2>();
  675. }
  676. if (totalVertexCount > uv2.Items.Length) { // Manual ExposedList.Resize()
  677. Array.Resize(ref uv2.Items, totalVertexCount);
  678. Array.Resize(ref uv3.Items, totalVertexCount);
  679. }
  680. uv2.Count = uv3.Count = totalVertexCount;
  681. }
  682. var uv2i = uv2.Items;
  683. var uv3i = uv3.Items;
  684. for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
  685. var slot = drawOrderItems[slotIndex];
  686. if (!slot.Bone.Active) continue;
  687. var attachment = slot.Attachment;
  688. rg.x = slot.R2; //r
  689. rg.y = slot.G2; //g
  690. b2.x = slot.B2; //b
  691. b2.y = 1.0f;
  692. var regionAttachment = attachment as RegionAttachment;
  693. if (regionAttachment != null) {
  694. if (settings.pmaVertexColors) {
  695. float alpha = a * slot.A * regionAttachment.A;
  696. rg.x *= alpha;
  697. rg.y *= alpha;
  698. b2.x *= alpha;
  699. b2.y = slot.Data.BlendMode == BlendMode.Additive ? 0 : alpha;
  700. }
  701. uv2i[vi] = rg; uv2i[vi + 1] = rg; uv2i[vi + 2] = rg; uv2i[vi + 3] = rg;
  702. uv3i[vi] = b2; uv3i[vi + 1] = b2; uv3i[vi + 2] = b2; uv3i[vi + 3] = b2;
  703. vi += 4;
  704. } else { //} if (settings.renderMeshes) {
  705. var meshAttachment = attachment as MeshAttachment;
  706. if (meshAttachment != null) {
  707. if (settings.pmaVertexColors) {
  708. float alpha = a * slot.A * meshAttachment.A;
  709. rg.x *= alpha;
  710. rg.y *= alpha;
  711. b2.x *= alpha;
  712. b2.y = slot.Data.BlendMode == BlendMode.Additive ? 0 : alpha;
  713. }
  714. int meshVertexCount = meshAttachment.WorldVerticesLength;
  715. for (int iii = 0; iii < meshVertexCount; iii += 2) {
  716. uv2i[vi] = rg;
  717. uv3i[vi] = b2;
  718. vi++;
  719. }
  720. }
  721. }
  722. }
  723. }
  724. for (int slotIndex = startSlot; slotIndex < endSlot; slotIndex++) {
  725. var slot = drawOrderItems[slotIndex];
  726. if (!slot.Bone.Active) continue;
  727. var attachment = slot.Attachment;
  728. float z = slotIndex * settings.zSpacing;
  729. var regionAttachment = attachment as RegionAttachment;
  730. if (regionAttachment != null) {
  731. regionAttachment.ComputeWorldVertices(slot.Bone, tempVerts, 0);
  732. float x1 = tempVerts[RegionAttachment.BLX], y1 = tempVerts[RegionAttachment.BLY];
  733. float x2 = tempVerts[RegionAttachment.ULX], y2 = tempVerts[RegionAttachment.ULY];
  734. float x3 = tempVerts[RegionAttachment.URX], y3 = tempVerts[RegionAttachment.URY];
  735. float x4 = tempVerts[RegionAttachment.BRX], y4 = tempVerts[RegionAttachment.BRY];
  736. vbi[vertexIndex].x = x1; vbi[vertexIndex].y = y1; vbi[vertexIndex].z = z;
  737. vbi[vertexIndex + 1].x = x4; vbi[vertexIndex + 1].y = y4; vbi[vertexIndex + 1].z = z;
  738. vbi[vertexIndex + 2].x = x2; vbi[vertexIndex + 2].y = y2; vbi[vertexIndex + 2].z = z;
  739. vbi[vertexIndex + 3].x = x3; vbi[vertexIndex + 3].y = y3; vbi[vertexIndex + 3].z = z;
  740. if (settings.pmaVertexColors) {
  741. color.a = (byte)(a * slot.A * regionAttachment.A * 255);
  742. color.r = (byte)(r * slot.R * regionAttachment.R * color.a);
  743. color.g = (byte)(g * slot.G * regionAttachment.G * color.a);
  744. color.b = (byte)(b * slot.B * regionAttachment.B * color.a);
  745. if (slot.Data.BlendMode == BlendMode.Additive && !canvasGroupTintBlack) color.a = 0;
  746. } else {
  747. color.a = (byte)(a * slot.A * regionAttachment.A * 255);
  748. color.r = (byte)(r * slot.R * regionAttachment.R * 255);
  749. color.g = (byte)(g * slot.G * regionAttachment.G * 255);
  750. color.b = (byte)(b * slot.B * regionAttachment.B * 255);
  751. }
  752. cbi[vertexIndex] = color; cbi[vertexIndex + 1] = color; cbi[vertexIndex + 2] = color; cbi[vertexIndex + 3] = color;
  753. float[] regionUVs = regionAttachment.UVs;
  754. ubi[vertexIndex].x = regionUVs[RegionAttachment.BLX]; ubi[vertexIndex].y = regionUVs[RegionAttachment.BLY];
  755. ubi[vertexIndex + 1].x = regionUVs[RegionAttachment.BRX]; ubi[vertexIndex + 1].y = regionUVs[RegionAttachment.BRY];
  756. ubi[vertexIndex + 2].x = regionUVs[RegionAttachment.ULX]; ubi[vertexIndex + 2].y = regionUVs[RegionAttachment.ULY];
  757. ubi[vertexIndex + 3].x = regionUVs[RegionAttachment.URX]; ubi[vertexIndex + 3].y = regionUVs[RegionAttachment.URY];
  758. if (x1 < bmin.x) bmin.x = x1; // Potential first attachment bounds initialization. Initial min should not block initial max. Same for Y below.
  759. if (x1 > bmax.x) bmax.x = x1;
  760. if (x2 < bmin.x) bmin.x = x2;
  761. else if (x2 > bmax.x) bmax.x = x2;
  762. if (x3 < bmin.x) bmin.x = x3;
  763. else if (x3 > bmax.x) bmax.x = x3;
  764. if (x4 < bmin.x) bmin.x = x4;
  765. else if (x4 > bmax.x) bmax.x = x4;
  766. if (y1 < bmin.y) bmin.y = y1;
  767. if (y1 > bmax.y) bmax.y = y1;
  768. if (y2 < bmin.y) bmin.y = y2;
  769. else if (y2 > bmax.y) bmax.y = y2;
  770. if (y3 < bmin.y) bmin.y = y3;
  771. else if (y3 > bmax.y) bmax.y = y3;
  772. if (y4 < bmin.y) bmin.y = y4;
  773. else if (y4 > bmax.y) bmax.y = y4;
  774. vertexIndex += 4;
  775. } else { //if (settings.renderMeshes) {
  776. var meshAttachment = attachment as MeshAttachment;
  777. if (meshAttachment != null) {
  778. int meshVertexCount = meshAttachment.WorldVerticesLength;
  779. if (tempVerts.Length < meshVertexCount) this.tempVerts = tempVerts = new float[meshVertexCount];
  780. meshAttachment.ComputeWorldVertices(slot, tempVerts);
  781. if (settings.pmaVertexColors) {
  782. color.a = (byte)(a * slot.A * meshAttachment.A * 255);
  783. color.r = (byte)(r * slot.R * meshAttachment.R * color.a);
  784. color.g = (byte)(g * slot.G * meshAttachment.G * color.a);
  785. color.b = (byte)(b * slot.B * meshAttachment.B * color.a);
  786. if (slot.Data.BlendMode == BlendMode.Additive && !canvasGroupTintBlack) color.a = 0;
  787. } else {
  788. color.a = (byte)(a * slot.A * meshAttachment.A * 255);
  789. color.r = (byte)(r * slot.R * meshAttachment.R * 255);
  790. color.g = (byte)(g * slot.G * meshAttachment.G * 255);
  791. color.b = (byte)(b * slot.B * meshAttachment.B * 255);
  792. }
  793. float[] attachmentUVs = meshAttachment.UVs;
  794. // Potential first attachment bounds initialization. See conditions in RegionAttachment logic.
  795. if (vertexIndex == 0) {
  796. // Initial min should not block initial max.
  797. // vi == vertexIndex does not always mean the bounds are fresh. It could be a submesh. Do not nuke old values by omitting the check.
  798. // Should know that this is the first attachment in the submesh. slotIndex == startSlot could be an empty slot.
  799. float fx = tempVerts[0], fy = tempVerts[1];
  800. if (fx < bmin.x) bmin.x = fx;
  801. if (fx > bmax.x) bmax.x = fx;
  802. if (fy < bmin.y) bmin.y = fy;
  803. if (fy > bmax.y) bmax.y = fy;
  804. }
  805. for (int iii = 0; iii < meshVertexCount; iii += 2) {
  806. float x = tempVerts[iii], y = tempVerts[iii + 1];
  807. vbi[vertexIndex].x = x; vbi[vertexIndex].y = y; vbi[vertexIndex].z = z;
  808. cbi[vertexIndex] = color; ubi[vertexIndex].x = attachmentUVs[iii]; ubi[vertexIndex].y = attachmentUVs[iii + 1];
  809. if (x < bmin.x) bmin.x = x;
  810. else if (x > bmax.x) bmax.x = x;
  811. if (y < bmin.y) bmin.y = y;
  812. else if (y > bmax.y) bmax.y = y;
  813. vertexIndex++;
  814. }
  815. }
  816. }
  817. }
  818. }
  819. this.meshBoundsMin = bmin;
  820. this.meshBoundsMax = bmax;
  821. this.meshBoundsThickness = lastSlotIndex * settings.zSpacing;
  822. int submeshInstructionCount = instruction.submeshInstructions.Count;
  823. submeshes.Count = submeshInstructionCount;
  824. // Add triangles
  825. if (updateTriangles) {
  826. // Match submesh buffers count with submeshInstruction count.
  827. if (this.submeshes.Items.Length < submeshInstructionCount) {
  828. this.submeshes.Resize(submeshInstructionCount);
  829. for (int i = 0, n = submeshInstructionCount; i < n; i++) {
  830. var submeshBuffer = this.submeshes.Items[i];
  831. if (submeshBuffer == null)
  832. this.submeshes.Items[i] = new ExposedList<int>();
  833. else
  834. submeshBuffer.Clear(false);
  835. }
  836. }
  837. var submeshInstructionsItems = instruction.submeshInstructions.Items; // This relies on the resize above.
  838. // Fill the buffers.
  839. int attachmentFirstVertex = 0;
  840. for (int smbi = 0; smbi < submeshInstructionCount; smbi++) {
  841. var submeshInstruction = submeshInstructionsItems[smbi];
  842. var currentSubmeshBuffer = this.submeshes.Items[smbi];
  843. { //submesh.Resize(submesh.rawTriangleCount);
  844. int newTriangleCount = submeshInstruction.rawTriangleCount;
  845. if (newTriangleCount > currentSubmeshBuffer.Items.Length)
  846. Array.Resize(ref currentSubmeshBuffer.Items, newTriangleCount);
  847. else if (newTriangleCount < currentSubmeshBuffer.Items.Length) {
  848. // Zero the extra.
  849. var sbi = currentSubmeshBuffer.Items;
  850. for (int ei = newTriangleCount, nn = sbi.Length; ei < nn; ei++)
  851. sbi[ei] = 0;
  852. }
  853. currentSubmeshBuffer.Count = newTriangleCount;
  854. }
  855. var tris = currentSubmeshBuffer.Items;
  856. int triangleIndex = 0;
  857. var skeleton = submeshInstruction.skeleton;
  858. var drawOrderItems = skeleton.DrawOrder.Items;
  859. for (int slotIndex = submeshInstruction.startSlot, endSlot = submeshInstruction.endSlot; slotIndex < endSlot; slotIndex++) {
  860. var slot = drawOrderItems[slotIndex];
  861. if (!slot.Bone.Active) continue;
  862. var attachment = drawOrderItems[slotIndex].Attachment;
  863. if (attachment is RegionAttachment) {
  864. tris[triangleIndex] = attachmentFirstVertex;
  865. tris[triangleIndex + 1] = attachmentFirstVertex + 2;
  866. tris[triangleIndex + 2] = attachmentFirstVertex + 1;
  867. tris[triangleIndex + 3] = attachmentFirstVertex + 2;
  868. tris[triangleIndex + 4] = attachmentFirstVertex + 3;
  869. tris[triangleIndex + 5] = attachmentFirstVertex + 1;
  870. triangleIndex += 6;
  871. attachmentFirstVertex += 4;
  872. continue;
  873. }
  874. var meshAttachment = attachment as MeshAttachment;
  875. if (meshAttachment != null) {
  876. int[] attachmentTriangles = meshAttachment.Triangles;
  877. for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii++, triangleIndex++)
  878. tris[triangleIndex] = attachmentFirstVertex + attachmentTriangles[ii];
  879. attachmentFirstVertex += meshAttachment.WorldVerticesLength >> 1; // length/2;
  880. }
  881. }
  882. }
  883. }
  884. }
  885. public void ScaleVertexData (float scale) {
  886. var vbi = vertexBuffer.Items;
  887. for (int i = 0, n = vertexBuffer.Count; i < n; i++) {
  888. vbi[i] *= scale; // vbi[i].x *= scale; vbi[i].y *= scale;
  889. }
  890. meshBoundsMin *= scale;
  891. meshBoundsMax *= scale;
  892. meshBoundsThickness *= scale;
  893. }
  894. public Bounds GetMeshBounds () {
  895. if (float.IsInfinity(meshBoundsMin.x)) { // meshBoundsMin.x == BoundsMinDefault // == doesn't work on float Infinity constants.
  896. return new Bounds();
  897. } else {
  898. //mesh.bounds = ArraysMeshGenerator.ToBounds(meshBoundsMin, meshBoundsMax);
  899. float halfWidth = (meshBoundsMax.x - meshBoundsMin.x) * 0.5f;
  900. float halfHeight = (meshBoundsMax.y - meshBoundsMin.y) * 0.5f;
  901. return new Bounds {
  902. center = new Vector3(meshBoundsMin.x + halfWidth, meshBoundsMin.y + halfHeight),
  903. extents = new Vector3(halfWidth, halfHeight, meshBoundsThickness * 0.5f)
  904. };
  905. }
  906. }
  907. void AddAttachmentTintBlack (float r2, float g2, float b2, float a, int vertexCount) {
  908. var rg = new Vector2(r2, g2);
  909. var bo = new Vector2(b2, a);
  910. int ovc = vertexBuffer.Count;
  911. int newVertexCount = ovc + vertexCount;
  912. {
  913. if (uv2 == null) {
  914. uv2 = new ExposedList<Vector2>();
  915. uv3 = new ExposedList<Vector2>();
  916. }
  917. if (newVertexCount > uv2.Items.Length) { // Manual ExposedList.Resize()
  918. Array.Resize(ref uv2.Items, newVertexCount);
  919. Array.Resize(ref uv3.Items, newVertexCount);
  920. }
  921. uv2.Count = uv3.Count = newVertexCount;
  922. }
  923. var uv2i = uv2.Items;
  924. var uv3i = uv3.Items;
  925. for (int i = 0; i < vertexCount; i++) {
  926. uv2i[ovc + i] = rg;
  927. uv3i[ovc + i] = bo;
  928. }
  929. }
  930. #endregion
  931. #region Step 3 : Transfer vertex and triangle data to UnityEngine.Mesh
  932. public void FillVertexData (Mesh mesh) {
  933. var vbi = vertexBuffer.Items;
  934. var ubi = uvBuffer.Items;
  935. var cbi = colorBuffer.Items;
  936. int vbiLength = vbi.Length;
  937. // Zero the extra.
  938. {
  939. int listCount = vertexBuffer.Count;
  940. var vector3zero = Vector3.zero;
  941. for (int i = listCount; i < vbiLength; i++)
  942. vbi[i] = vector3zero;
  943. }
  944. // Set the vertex buffer.
  945. {
  946. mesh.vertices = vbi;
  947. mesh.uv = ubi;
  948. mesh.colors32 = cbi;
  949. mesh.bounds = GetMeshBounds();
  950. }
  951. {
  952. if (settings.addNormals) {
  953. int oldLength = 0;
  954. if (normals == null)
  955. normals = new Vector3[vbiLength];
  956. else
  957. oldLength = normals.Length;
  958. if (oldLength != vbiLength) {
  959. Array.Resize(ref this.normals, vbiLength);
  960. var localNormals = this.normals;
  961. for (int i = oldLength; i < vbiLength; i++) localNormals[i] = Vector3.back;
  962. }
  963. mesh.normals = this.normals;
  964. }
  965. if (settings.tintBlack) {
  966. if (uv2 != null) {
  967. // Sometimes, the vertex buffer becomes smaller. We need to trim the size of the tint black buffers to match.
  968. if (vbiLength != uv2.Items.Length) {
  969. Array.Resize(ref uv2.Items, vbiLength);
  970. Array.Resize(ref uv3.Items, vbiLength);
  971. uv2.Count = uv3.Count = vbiLength;
  972. }
  973. mesh.uv2 = this.uv2.Items;
  974. mesh.uv3 = this.uv3.Items;
  975. }
  976. }
  977. }
  978. }
  979. public void FillLateVertexData (Mesh mesh) {
  980. if (settings.calculateTangents) {
  981. int vertexCount = this.vertexBuffer.Count;
  982. var sbi = submeshes.Items;
  983. int submeshCount = submeshes.Count;
  984. var vbi = vertexBuffer.Items;
  985. var ubi = uvBuffer.Items;
  986. MeshGenerator.SolveTangents2DEnsureSize(ref this.tangents, ref this.tempTanBuffer, vertexCount, vbi.Length);
  987. for (int i = 0; i < submeshCount; i++) {
  988. var submesh = sbi[i].Items;
  989. int triangleCount = sbi[i].Count;
  990. MeshGenerator.SolveTangents2DTriangles(this.tempTanBuffer, submesh, triangleCount, vbi, ubi, vertexCount);
  991. }
  992. MeshGenerator.SolveTangents2DBuffer(this.tangents, this.tempTanBuffer, vertexCount);
  993. mesh.tangents = this.tangents;
  994. }
  995. }
  996. public void FillTriangles (Mesh mesh) {
  997. int submeshCount = submeshes.Count;
  998. var submeshesItems = submeshes.Items;
  999. mesh.subMeshCount = submeshCount;
  1000. for (int i = 0; i < submeshCount; i++)
  1001. #if MESH_SET_TRIANGLES_PROVIDES_LENGTH_PARAM
  1002. mesh.SetTriangles(submeshesItems[i].Items, 0, submeshesItems[i].Count, i, false);
  1003. #else
  1004. mesh.SetTriangles(submeshesItems[i].Items, i, false);
  1005. #endif
  1006. }
  1007. #endregion
  1008. public void EnsureVertexCapacity (int minimumVertexCount, bool inlcudeTintBlack = false, bool includeTangents = false, bool includeNormals = false) {
  1009. if (minimumVertexCount > vertexBuffer.Items.Length) {
  1010. Array.Resize(ref vertexBuffer.Items, minimumVertexCount);
  1011. Array.Resize(ref uvBuffer.Items, minimumVertexCount);
  1012. Array.Resize(ref colorBuffer.Items, minimumVertexCount);
  1013. if (inlcudeTintBlack) {
  1014. if (uv2 == null) {
  1015. uv2 = new ExposedList<Vector2>(minimumVertexCount);
  1016. uv3 = new ExposedList<Vector2>(minimumVertexCount);
  1017. }
  1018. uv2.Resize(minimumVertexCount);
  1019. uv3.Resize(minimumVertexCount);
  1020. }
  1021. if (includeNormals) {
  1022. if (normals == null)
  1023. normals = new Vector3[minimumVertexCount];
  1024. else
  1025. Array.Resize(ref normals, minimumVertexCount);
  1026. }
  1027. if (includeTangents) {
  1028. if (tangents == null)
  1029. tangents = new Vector4[minimumVertexCount];
  1030. else
  1031. Array.Resize(ref tangents, minimumVertexCount);
  1032. }
  1033. }
  1034. }
  1035. /// <summary>Trims internal buffers to reduce the resulting mesh data stream size.</summary>
  1036. public void TrimExcess () {
  1037. vertexBuffer.TrimExcess();
  1038. uvBuffer.TrimExcess();
  1039. colorBuffer.TrimExcess();
  1040. if (uv2 != null) uv2.TrimExcess();
  1041. if (uv3 != null) uv3.TrimExcess();
  1042. int vbiLength = vertexBuffer.Items.Length;
  1043. if (normals != null) Array.Resize(ref normals, vbiLength);
  1044. if (tangents != null) Array.Resize(ref tangents, vbiLength);
  1045. }
  1046. #region TangentSolver2D
  1047. // Thanks to contributions from forum user ToddRivers
  1048. /// <summary>Step 1 of solving tangents. Ensure you have buffers of the correct size.</summary>
  1049. /// <param name="tangentBuffer">Eventual Vector4[] tangent buffer to assign to Mesh.tangents.</param>
  1050. /// <param name="tempTanBuffer">Temporary Vector2 buffer for calculating directions.</param>
  1051. /// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
  1052. internal static void SolveTangents2DEnsureSize (ref Vector4[] tangentBuffer, ref Vector2[] tempTanBuffer, int vertexCount, int vertexBufferLength) {
  1053. if (tangentBuffer == null || tangentBuffer.Length != vertexBufferLength)
  1054. tangentBuffer = new Vector4[vertexBufferLength];
  1055. if (tempTanBuffer == null || tempTanBuffer.Length < vertexCount * 2)
  1056. tempTanBuffer = new Vector2[vertexCount * 2]; // two arrays in one.
  1057. }
  1058. /// <summary>Step 2 of solving tangents. Fills (part of) a temporary tangent-solution buffer based on the vertices and uvs defined by a submesh's triangle buffer. Only needs to be called once for single-submesh meshes.</summary>
  1059. /// <param name="tempTanBuffer">A temporary Vector3[] for calculating tangents.</param>
  1060. /// <param name="vertices">The mesh's current vertex position buffer.</param>
  1061. /// <param name="triangles">The mesh's current triangles buffer.</param>
  1062. /// <param name="uvs">The mesh's current uvs buffer.</param>
  1063. /// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
  1064. /// <param name = "triangleCount">The number of triangle indexes in the triangle array to be used.</param>
  1065. internal static void SolveTangents2DTriangles (Vector2[] tempTanBuffer, int[] triangles, int triangleCount, Vector3[] vertices, Vector2[] uvs, int vertexCount) {
  1066. Vector2 sdir;
  1067. Vector2 tdir;
  1068. for (int t = 0; t < triangleCount; t += 3) {
  1069. int i1 = triangles[t + 0];
  1070. int i2 = triangles[t + 1];
  1071. int i3 = triangles[t + 2];
  1072. Vector3 v1 = vertices[i1];
  1073. Vector3 v2 = vertices[i2];
  1074. Vector3 v3 = vertices[i3];
  1075. Vector2 w1 = uvs[i1];
  1076. Vector2 w2 = uvs[i2];
  1077. Vector2 w3 = uvs[i3];
  1078. float x1 = v2.x - v1.x;
  1079. float x2 = v3.x - v1.x;
  1080. float y1 = v2.y - v1.y;
  1081. float y2 = v3.y - v1.y;
  1082. float s1 = w2.x - w1.x;
  1083. float s2 = w3.x - w1.x;
  1084. float t1 = w2.y - w1.y;
  1085. float t2 = w3.y - w1.y;
  1086. float div = s1 * t2 - s2 * t1;
  1087. float r = (div == 0f) ? 0f : 1f / div;
  1088. sdir.x = (t2 * x1 - t1 * x2) * r;
  1089. sdir.y = (t2 * y1 - t1 * y2) * r;
  1090. tempTanBuffer[i1] = tempTanBuffer[i2] = tempTanBuffer[i3] = sdir;
  1091. tdir.x = (s1 * x2 - s2 * x1) * r;
  1092. tdir.y = (s1 * y2 - s2 * y1) * r;
  1093. tempTanBuffer[vertexCount + i1] = tempTanBuffer[vertexCount + i2] = tempTanBuffer[vertexCount + i3] = tdir;
  1094. }
  1095. }
  1096. /// <summary>Step 3 of solving tangents. Fills a Vector4[] tangents array according to values calculated in step 2.</summary>
  1097. /// <param name="tangents">A Vector4[] that will eventually be used to set Mesh.tangents</param>
  1098. /// <param name="tempTanBuffer">A temporary Vector3[] for calculating tangents.</param>
  1099. /// <param name="vertexCount">Number of vertices that require tangents (or the size of the vertex array)</param>
  1100. internal static void SolveTangents2DBuffer (Vector4[] tangents, Vector2[] tempTanBuffer, int vertexCount) {
  1101. Vector4 tangent;
  1102. tangent.z = 0;
  1103. for (int i = 0; i < vertexCount; ++i) {
  1104. Vector2 t = tempTanBuffer[i];
  1105. // t.Normalize() (aggressively inlined). Even better if offloaded to GPU via vertex shader.
  1106. float magnitude = Mathf.Sqrt(t.x * t.x + t.y * t.y);
  1107. if (magnitude > 1E-05) {
  1108. float reciprocalMagnitude = 1f / magnitude;
  1109. t.x *= reciprocalMagnitude;
  1110. t.y *= reciprocalMagnitude;
  1111. }
  1112. Vector2 t2 = tempTanBuffer[vertexCount + i];
  1113. tangent.x = t.x;
  1114. tangent.y = t.y;
  1115. //tangent.z = 0;
  1116. tangent.w = (t.y * t2.x > t.x * t2.y) ? 1 : -1; // 2D direction calculation. Used for binormals.
  1117. tangents[i] = tangent;
  1118. }
  1119. }
  1120. #endregion
  1121. #region AttachmentRendering
  1122. static List<Vector3> AttachmentVerts = new List<Vector3>();
  1123. static List<Vector2> AttachmentUVs = new List<Vector2>();
  1124. static List<Color32> AttachmentColors32 = new List<Color32>();
  1125. static List<int> AttachmentIndices = new List<int>();
  1126. /// <summary>Fills mesh vertex data to render a RegionAttachment.</summary>
  1127. public static void FillMeshLocal (Mesh mesh, RegionAttachment regionAttachment) {
  1128. if (mesh == null) return;
  1129. if (regionAttachment == null) return;
  1130. AttachmentVerts.Clear();
  1131. var offsets = regionAttachment.Offset;
  1132. AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.BLX], offsets[RegionAttachment.BLY]));
  1133. AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.ULX], offsets[RegionAttachment.ULY]));
  1134. AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.URX], offsets[RegionAttachment.URY]));
  1135. AttachmentVerts.Add(new Vector3(offsets[RegionAttachment.BRX], offsets[RegionAttachment.BRY]));
  1136. AttachmentUVs.Clear();
  1137. var uvs = regionAttachment.UVs;
  1138. AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.ULX], uvs[RegionAttachment.ULY]));
  1139. AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.URX], uvs[RegionAttachment.URY]));
  1140. AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.BRX], uvs[RegionAttachment.BRY]));
  1141. AttachmentUVs.Add(new Vector2(uvs[RegionAttachment.BLX], uvs[RegionAttachment.BLY]));
  1142. AttachmentColors32.Clear();
  1143. Color32 c = (Color32)(new Color(regionAttachment.R, regionAttachment.G, regionAttachment.B, regionAttachment.A));
  1144. for (int i = 0; i < 4; i++)
  1145. AttachmentColors32.Add(c);
  1146. AttachmentIndices.Clear();
  1147. AttachmentIndices.AddRange(new[] { 0, 2, 1, 0, 3, 2 });
  1148. mesh.Clear();
  1149. mesh.name = regionAttachment.Name;
  1150. mesh.SetVertices(AttachmentVerts);
  1151. mesh.SetUVs(0, AttachmentUVs);
  1152. mesh.SetColors(AttachmentColors32);
  1153. mesh.SetTriangles(AttachmentIndices, 0);
  1154. mesh.RecalculateBounds();
  1155. AttachmentVerts.Clear();
  1156. AttachmentUVs.Clear();
  1157. AttachmentColors32.Clear();
  1158. AttachmentIndices.Clear();
  1159. }
  1160. public static void FillMeshLocal (Mesh mesh, MeshAttachment meshAttachment, SkeletonData skeletonData) {
  1161. if (mesh == null) return;
  1162. if (meshAttachment == null) return;
  1163. int vertexCount = meshAttachment.WorldVerticesLength / 2;
  1164. AttachmentVerts.Clear();
  1165. if (meshAttachment.IsWeighted()) {
  1166. int count = meshAttachment.WorldVerticesLength;
  1167. int[] meshAttachmentBones = meshAttachment.Bones;
  1168. int v = 0;
  1169. float[] vertices = meshAttachment.Vertices;
  1170. for (int w = 0, b = 0; w < count; w += 2) {
  1171. float wx = 0, wy = 0;
  1172. int n = meshAttachmentBones[v++];
  1173. n += v;
  1174. for (; v < n; v++, b += 3) {
  1175. BoneMatrix bm = BoneMatrix.CalculateSetupWorld(skeletonData.Bones.Items[meshAttachmentBones[v]]);
  1176. float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
  1177. wx += (vx * bm.a + vy * bm.b + bm.x) * weight;
  1178. wy += (vx * bm.c + vy * bm.d + bm.y) * weight;
  1179. }
  1180. AttachmentVerts.Add(new Vector3(wx, wy));
  1181. }
  1182. } else {
  1183. var localVerts = meshAttachment.Vertices;
  1184. Vector3 pos = default(Vector3);
  1185. for (int i = 0; i < vertexCount; i++) {
  1186. int ii = i * 2;
  1187. pos.x = localVerts[ii];
  1188. pos.y = localVerts[ii + 1];
  1189. AttachmentVerts.Add(pos);
  1190. }
  1191. }
  1192. var uvs = meshAttachment.UVs;
  1193. Vector2 uv = default(Vector2);
  1194. Color32 c = (Color32)(new Color(meshAttachment.R, meshAttachment.G, meshAttachment.B, meshAttachment.A));
  1195. AttachmentUVs.Clear();
  1196. AttachmentColors32.Clear();
  1197. for (int i = 0; i < vertexCount; i++) {
  1198. int ii = i * 2;
  1199. uv.x = uvs[ii];
  1200. uv.y = uvs[ii + 1];
  1201. AttachmentUVs.Add(uv);
  1202. AttachmentColors32.Add(c);
  1203. }
  1204. AttachmentIndices.Clear();
  1205. AttachmentIndices.AddRange(meshAttachment.Triangles);
  1206. mesh.Clear();
  1207. mesh.name = meshAttachment.Name;
  1208. mesh.SetVertices(AttachmentVerts);
  1209. mesh.SetUVs(0, AttachmentUVs);
  1210. mesh.SetColors(AttachmentColors32);
  1211. mesh.SetTriangles(AttachmentIndices, 0);
  1212. mesh.RecalculateBounds();
  1213. AttachmentVerts.Clear();
  1214. AttachmentUVs.Clear();
  1215. AttachmentColors32.Clear();
  1216. AttachmentIndices.Clear();
  1217. }
  1218. #endregion
  1219. }
  1220. }