MeshGenerator.cs 59 KB

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