RegionAttachment.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. using System;
  30. namespace Spine {
  31. /// <summary>Attachment that displays a texture region.</summary>
  32. public class RegionAttachment : Attachment, IHasTextureRegion {
  33. public const int BLX = 0, BLY = 1;
  34. public const int ULX = 2, ULY = 3;
  35. public const int URX = 4, URY = 5;
  36. public const int BRX = 6, BRY = 7;
  37. internal TextureRegion region;
  38. internal float x, y, rotation, scaleX = 1, scaleY = 1, width, height;
  39. internal float[] offset = new float[8], uvs = new float[8];
  40. internal float r = 1, g = 1, b = 1, a = 1;
  41. internal Sequence sequence;
  42. public float X { get { return x; } set { x = value; } }
  43. public float Y { get { return y; } set { y = value; } }
  44. public float Rotation { get { return rotation; } set { rotation = value; } }
  45. public float ScaleX { get { return scaleX; } set { scaleX = value; } }
  46. public float ScaleY { get { return scaleY; } set { scaleY = value; } }
  47. public float Width { get { return width; } set { width = value; } }
  48. public float Height { get { return height; } set { height = value; } }
  49. public float R { get { return r; } set { r = value; } }
  50. public float G { get { return g; } set { g = value; } }
  51. public float B { get { return b; } set { b = value; } }
  52. public float A { get { return a; } set { a = value; } }
  53. public string Path { get; set; }
  54. public TextureRegion Region { get { return region; } set { region = value; } }
  55. /// <summary>For each of the 4 vertices, a pair of <code>x,y</code> values that is the local position of the vertex.</summary>
  56. /// <seealso cref="UpdateRegion"/>
  57. public float[] Offset { get { return offset; } }
  58. public float[] UVs { get { return uvs; } }
  59. public Sequence Sequence { get { return sequence; } set { sequence = value; } }
  60. public RegionAttachment (string name)
  61. : base(name) {
  62. }
  63. /// <summary>Copy constructor.</summary>
  64. public RegionAttachment (RegionAttachment other)
  65. : base(other) {
  66. region = other.region;
  67. Path = other.Path;
  68. x = other.x;
  69. y = other.y;
  70. scaleX = other.scaleX;
  71. scaleY = other.scaleY;
  72. rotation = other.rotation;
  73. width = other.width;
  74. height = other.height;
  75. Array.Copy(other.uvs, 0, uvs, 0, 8);
  76. Array.Copy(other.offset, 0, offset, 0, 8);
  77. r = other.r;
  78. g = other.g;
  79. b = other.b;
  80. a = other.a;
  81. sequence = other.sequence == null ? null : new Sequence(other.sequence);
  82. }
  83. /// <summary>Calculates the <see cref="Offset"/> and <see cref="UVs"/> using the region and the attachment's transform. Must be called if the
  84. /// region, the region's properties, or the transform are changed.</summary>
  85. public void UpdateRegion () {
  86. float[] uvs = this.uvs;
  87. if (region == null) {
  88. uvs[BLX] = 0;
  89. uvs[BLY] = 0;
  90. uvs[ULX] = 0;
  91. uvs[ULY] = 1;
  92. uvs[URX] = 1;
  93. uvs[URY] = 1;
  94. uvs[BRX] = 1;
  95. uvs[BRY] = 0;
  96. return;
  97. }
  98. float width = Width, height = Height;
  99. float localX2 = width / 2;
  100. float localY2 = height / 2;
  101. float localX = -localX2;
  102. float localY = -localY2;
  103. bool rotated = false;
  104. if (region is AtlasRegion) {
  105. AtlasRegion region = (AtlasRegion)this.region;
  106. localX += region.offsetX / region.originalWidth * width;
  107. localY += region.offsetY / region.originalHeight * height;
  108. if (region.degrees == 90) {
  109. rotated = true;
  110. localX2 -= (region.originalWidth - region.offsetX - region.packedHeight) / region.originalWidth * width;
  111. localY2 -= (region.originalHeight - region.offsetY - region.packedWidth) / region.originalHeight * height;
  112. } else {
  113. localX2 -= (region.originalWidth - region.offsetX - region.packedWidth) / region.originalWidth * width;
  114. localY2 -= (region.originalHeight - region.offsetY - region.packedHeight) / region.originalHeight * height;
  115. }
  116. }
  117. float scaleX = ScaleX, scaleY = ScaleY;
  118. localX *= scaleX;
  119. localY *= scaleY;
  120. localX2 *= scaleX;
  121. localY2 *= scaleY;
  122. float r = Rotation * MathUtils.DegRad, cos = (float)Math.Cos(r), sin = (float)Math.Sin(r);
  123. float x = X, y = Y;
  124. float localXCos = localX * cos + x;
  125. float localXSin = localX * sin;
  126. float localYCos = localY * cos + y;
  127. float localYSin = localY * sin;
  128. float localX2Cos = localX2 * cos + x;
  129. float localX2Sin = localX2 * sin;
  130. float localY2Cos = localY2 * cos + y;
  131. float localY2Sin = localY2 * sin;
  132. float[] offset = this.offset;
  133. offset[BLX] = localXCos - localYSin;
  134. offset[BLY] = localYCos + localXSin;
  135. offset[ULX] = localXCos - localY2Sin;
  136. offset[ULY] = localY2Cos + localXSin;
  137. offset[URX] = localX2Cos - localY2Sin;
  138. offset[URY] = localY2Cos + localX2Sin;
  139. offset[BRX] = localX2Cos - localYSin;
  140. offset[BRY] = localYCos + localX2Sin;
  141. if (rotated) {
  142. uvs[BLX] = region.u2;
  143. uvs[BLY] = region.v;
  144. uvs[ULX] = region.u2;
  145. uvs[ULY] = region.v2;
  146. uvs[URX] = region.u;
  147. uvs[URY] = region.v2;
  148. uvs[BRX] = region.u;
  149. uvs[BRY] = region.v;
  150. } else {
  151. uvs[BLX] = region.u2;
  152. uvs[BLY] = region.v2;
  153. uvs[ULX] = region.u;
  154. uvs[ULY] = region.v2;
  155. uvs[URX] = region.u;
  156. uvs[URY] = region.v;
  157. uvs[BRX] = region.u2;
  158. uvs[BRY] = region.v;
  159. }
  160. }
  161. /// <summary>
  162. /// Transforms the attachment's four vertices to world coordinates. If the attachment has a <see cref="Sequence"/> the region may
  163. /// be changed.</summary>
  164. /// <param name="bone">The parent bone.</param>
  165. /// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to offset + 8.</param>
  166. /// <param name="offset">The worldVertices index to begin writing values.</param>
  167. /// <param name="stride">The number of worldVertices entries between the value pairs written.</param>
  168. public void ComputeWorldVertices (Slot slot, float[] worldVertices, int offset, int stride = 2) {
  169. if (sequence != null) sequence.Apply(slot, this);
  170. float[] vertexOffset = this.offset;
  171. Bone bone = slot.Bone;
  172. float bwx = bone.worldX, bwy = bone.worldY;
  173. float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
  174. float offsetX, offsetY;
  175. // Vertex order is different from RegionAttachment.java
  176. offsetX = vertexOffset[BRX]; // 0
  177. offsetY = vertexOffset[BRY]; // 1
  178. worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl
  179. worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
  180. offset += stride;
  181. offsetX = vertexOffset[BLX]; // 2
  182. offsetY = vertexOffset[BLY]; // 3
  183. worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul
  184. worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
  185. offset += stride;
  186. offsetX = vertexOffset[ULX]; // 4
  187. offsetY = vertexOffset[ULY]; // 5
  188. worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur
  189. worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
  190. offset += stride;
  191. offsetX = vertexOffset[URX]; // 6
  192. offsetY = vertexOffset[URY]; // 7
  193. worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br
  194. worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
  195. //offset += stride;
  196. }
  197. public override Attachment Copy () {
  198. return new RegionAttachment(this);
  199. }
  200. }
  201. }