MeshGenerator.cs 50 KB

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