MeshGenerator.cs 51 KB

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