源战役客户端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

541 line
21 KiB

  1. /******************************************************************************
  2. * Spine Runtimes Software License v2.5
  3. *
  4. * Copyright (c) 2013-2016, Esoteric Software
  5. * All rights reserved.
  6. *
  7. * You are granted a perpetual, non-exclusive, non-sublicensable, and
  8. * non-transferable license to use, install, execute, and perform the Spine
  9. * Runtimes software and derivative works solely for personal or internal
  10. * use. Without the written permission of Esoteric Software (see Section 2 of
  11. * the Spine Software License Agreement), you may not (a) modify, translate,
  12. * adapt, or develop new applications using the Spine Runtimes or otherwise
  13. * create derivative works or improvements of the Spine Runtimes or (b) remove,
  14. * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
  15. * or other intellectual property or proprietary rights notices on or in the
  16. * Software, including any copy thereof. Redistributions in binary or source
  17. * form must include this license and terms.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
  20. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  22. * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
  25. * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  26. * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. * POSSIBILITY OF SUCH DAMAGE.
  29. *****************************************************************************/
  30. using System;
  31. using System.Collections.Generic;
  32. namespace Spine {
  33. public class Skeleton {
  34. internal SkeletonData data;
  35. internal ExposedList<Bone> bones;
  36. internal ExposedList<Slot> slots;
  37. internal ExposedList<Slot> drawOrder;
  38. internal ExposedList<IkConstraint> ikConstraints;
  39. internal ExposedList<TransformConstraint> transformConstraints;
  40. internal ExposedList<PathConstraint> pathConstraints;
  41. internal ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
  42. internal ExposedList<Bone> updateCacheReset = new ExposedList<Bone>();
  43. internal Skin skin;
  44. internal float r = 1, g = 1, b = 1, a = 1;
  45. internal float time;
  46. internal bool flipX, flipY;
  47. internal float x, y;
  48. public SkeletonData Data { get { return data; } }
  49. public ExposedList<Bone> Bones { get { return bones; } }
  50. public ExposedList<IUpdatable> UpdateCacheList { get { return updateCache; } }
  51. public ExposedList<Slot> Slots { get { return slots; } }
  52. public ExposedList<Slot> DrawOrder { get { return drawOrder; } }
  53. public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } }
  54. public ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } }
  55. public ExposedList<TransformConstraint> TransformConstraints { get { return transformConstraints; } }
  56. public Skin Skin { get { return skin; } set { skin = value; } }
  57. public float R { get { return r; } set { r = value; } }
  58. public float G { get { return g; } set { g = value; } }
  59. public float B { get { return b; } set { b = value; } }
  60. public float A { get { return a; } set { a = value; } }
  61. public float Time { get { return time; } set { time = value; } }
  62. public float X { get { return x; } set { x = value; } }
  63. public float Y { get { return y; } set { y = value; } }
  64. public bool FlipX { get { return flipX; } set { flipX = value; } }
  65. public bool FlipY { get { return flipY; } set { flipY = value; } }
  66. public Bone RootBone {
  67. get { return bones.Count == 0 ? null : bones.Items[0]; }
  68. }
  69. public Skeleton (SkeletonData data) {
  70. if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
  71. this.data = data;
  72. bones = new ExposedList<Bone>(data.bones.Count);
  73. foreach (BoneData boneData in data.bones) {
  74. Bone bone;
  75. if (boneData.parent == null) {
  76. bone = new Bone(boneData, this, null);
  77. } else {
  78. Bone parent = bones.Items[boneData.parent.index];
  79. bone = new Bone(boneData, this, parent);
  80. parent.children.Add(bone);
  81. }
  82. bones.Add(bone);
  83. }
  84. slots = new ExposedList<Slot>(data.slots.Count);
  85. drawOrder = new ExposedList<Slot>(data.slots.Count);
  86. foreach (SlotData slotData in data.slots) {
  87. Bone bone = bones.Items[slotData.boneData.index];
  88. Slot slot = new Slot(slotData, bone);
  89. slots.Add(slot);
  90. drawOrder.Add(slot);
  91. }
  92. ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
  93. foreach (IkConstraintData ikConstraintData in data.ikConstraints)
  94. ikConstraints.Add(new IkConstraint(ikConstraintData, this));
  95. transformConstraints = new ExposedList<TransformConstraint>(data.transformConstraints.Count);
  96. foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
  97. transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
  98. pathConstraints = new ExposedList<PathConstraint> (data.pathConstraints.Count);
  99. foreach (PathConstraintData pathConstraintData in data.pathConstraints)
  100. pathConstraints.Add(new PathConstraint(pathConstraintData, this));
  101. UpdateCache();
  102. UpdateWorldTransform();
  103. }
  104. /// <summary>Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added
  105. /// or removed.</summary>
  106. public void UpdateCache () {
  107. ExposedList<IUpdatable> updateCache = this.updateCache;
  108. updateCache.Clear();
  109. this.updateCacheReset.Clear();
  110. ExposedList<Bone> bones = this.bones;
  111. for (int i = 0, n = bones.Count; i < n; i++)
  112. bones.Items[i].sorted = false;
  113. ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
  114. var transformConstraints = this.transformConstraints;
  115. var pathConstraints = this.pathConstraints;
  116. int ikCount = IkConstraints.Count, transformCount = transformConstraints.Count, pathCount = pathConstraints.Count;
  117. int constraintCount = ikCount + transformCount + pathCount;
  118. //outer:
  119. for (int i = 0; i < constraintCount; i++) {
  120. for (int ii = 0; ii < ikCount; ii++) {
  121. IkConstraint constraint = ikConstraints.Items[ii];
  122. if (constraint.data.order == i) {
  123. SortIkConstraint(constraint);
  124. goto continue_outer; //continue outer;
  125. }
  126. }
  127. for (int ii = 0; ii < transformCount; ii++) {
  128. TransformConstraint constraint = transformConstraints.Items[ii];
  129. if (constraint.data.order == i) {
  130. SortTransformConstraint(constraint);
  131. goto continue_outer; //continue outer;
  132. }
  133. }
  134. for (int ii = 0; ii < pathCount; ii++) {
  135. PathConstraint constraint = pathConstraints.Items[ii];
  136. if (constraint.data.order == i) {
  137. SortPathConstraint(constraint);
  138. goto continue_outer; //continue outer;
  139. }
  140. }
  141. continue_outer: {}
  142. }
  143. for (int i = 0, n = bones.Count; i < n; i++)
  144. SortBone(bones.Items[i]);
  145. }
  146. private void SortIkConstraint (IkConstraint constraint) {
  147. Bone target = constraint.target;
  148. SortBone(target);
  149. var constrained = constraint.bones;
  150. Bone parent = constrained.Items[0];
  151. SortBone(parent);
  152. if (constrained.Count > 1) {
  153. Bone child = constrained.Items[constrained.Count - 1];
  154. if (!updateCache.Contains(child))
  155. updateCacheReset.Add(child);
  156. }
  157. updateCache.Add(constraint);
  158. SortReset(parent.children);
  159. constrained.Items[constrained.Count - 1].sorted = true;
  160. }
  161. private void SortPathConstraint (PathConstraint constraint) {
  162. Slot slot = constraint.target;
  163. int slotIndex = slot.data.index;
  164. Bone slotBone = slot.bone;
  165. if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone);
  166. if (data.defaultSkin != null && data.defaultSkin != skin)
  167. SortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
  168. for (int ii = 0, nn = data.skins.Count; ii < nn; ii++)
  169. SortPathConstraintAttachment(data.skins.Items[ii], slotIndex, slotBone);
  170. Attachment attachment = slot.attachment;
  171. if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone);
  172. var constrained = constraint.bones;
  173. int boneCount = constrained.Count;
  174. for (int i = 0; i < boneCount; i++)
  175. SortBone(constrained.Items[i]);
  176. updateCache.Add(constraint);
  177. for (int i = 0; i < boneCount; i++)
  178. SortReset(constrained.Items[i].children);
  179. for (int i = 0; i < boneCount; i++)
  180. constrained.Items[i].sorted = true;
  181. }
  182. private void SortTransformConstraint (TransformConstraint constraint) {
  183. SortBone(constraint.target);
  184. var constrained = constraint.bones;
  185. int boneCount = constrained.Count;
  186. if (constraint.data.local) {
  187. for (int i = 0; i < boneCount; i++) {
  188. Bone child = constrained.Items[i];
  189. SortBone(child.parent);
  190. if (!updateCache.Contains(child)) updateCacheReset.Add(child);
  191. }
  192. } else {
  193. for (int i = 0; i < boneCount; i++)
  194. SortBone(constrained.Items[i]);
  195. }
  196. updateCache.Add(constraint);
  197. for (int i = 0; i < boneCount; i++)
  198. SortReset(constrained.Items[i].children);
  199. for (int i = 0; i < boneCount; i++)
  200. constrained.Items[i].sorted = true;
  201. }
  202. private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
  203. foreach (var entry in skin.Attachments)
  204. if (entry.Key.slotIndex == slotIndex) SortPathConstraintAttachment(entry.Value, slotBone);
  205. }
  206. private void SortPathConstraintAttachment (Attachment attachment, Bone slotBone) {
  207. if (!(attachment is PathAttachment)) return;
  208. int[] pathBones = ((PathAttachment)attachment).bones;
  209. if (pathBones == null)
  210. SortBone(slotBone);
  211. else {
  212. var bones = this.bones;
  213. for (int i = 0, n = pathBones.Length; i < n;) {
  214. int nn = pathBones[i++];
  215. nn += i;
  216. while (i < nn)
  217. SortBone(bones.Items[pathBones[i++]]);
  218. }
  219. }
  220. }
  221. private void SortBone (Bone bone) {
  222. if (bone.sorted) return;
  223. Bone parent = bone.parent;
  224. if (parent != null) SortBone(parent);
  225. bone.sorted = true;
  226. updateCache.Add(bone);
  227. }
  228. private static void SortReset (ExposedList<Bone> bones) {
  229. var bonesItems = bones.Items;
  230. for (int i = 0, n = bones.Count; i < n; i++) {
  231. Bone bone = bonesItems[i];
  232. if (bone.sorted) SortReset(bone.children);
  233. bone.sorted = false;
  234. }
  235. }
  236. /// <summary>Updates the world transform for each bone and applies constraints.</summary>
  237. public void UpdateWorldTransform () {
  238. var updateCacheReset = this.updateCacheReset;
  239. var updateCacheResetItems = updateCacheReset.Items;
  240. for (int i = 0, n = updateCacheReset.Count; i < n; i++) {
  241. Bone bone = updateCacheResetItems[i];
  242. bone.ax = bone.x;
  243. bone.ay = bone.y;
  244. bone.arotation = bone.rotation;
  245. bone.ascaleX = bone.scaleX;
  246. bone.ascaleY = bone.scaleY;
  247. bone.ashearX = bone.shearX;
  248. bone.ashearY = bone.shearY;
  249. bone.appliedValid = true;
  250. }
  251. var updateItems = this.updateCache.Items;
  252. for (int i = 0, n = updateCache.Count; i < n; i++)
  253. updateItems[i].Update();
  254. }
  255. /// <summary>Sets the bones, constraints, and slots to their setup pose values.</summary>
  256. public void SetToSetupPose () {
  257. SetBonesToSetupPose();
  258. SetSlotsToSetupPose();
  259. }
  260. /// <summary>Sets the bones and constraints to their setup pose values.</summary>
  261. public void SetBonesToSetupPose () {
  262. var bonesItems = this.bones.Items;
  263. for (int i = 0, n = bones.Count; i < n; i++)
  264. bonesItems[i].SetToSetupPose();
  265. var ikConstraintsItems = this.ikConstraints.Items;
  266. for (int i = 0, n = ikConstraints.Count; i < n; i++) {
  267. IkConstraint constraint = ikConstraintsItems[i];
  268. constraint.bendDirection = constraint.data.bendDirection;
  269. constraint.mix = constraint.data.mix;
  270. }
  271. var transformConstraintsItems = this.transformConstraints.Items;
  272. for (int i = 0, n = transformConstraints.Count; i < n; i++) {
  273. TransformConstraint constraint = transformConstraintsItems[i];
  274. TransformConstraintData constraintData = constraint.data;
  275. constraint.rotateMix = constraintData.rotateMix;
  276. constraint.translateMix = constraintData.translateMix;
  277. constraint.scaleMix = constraintData.scaleMix;
  278. constraint.shearMix = constraintData.shearMix;
  279. }
  280. var pathConstraintItems = this.pathConstraints.Items;
  281. for (int i = 0, n = pathConstraints.Count; i < n; i++) {
  282. PathConstraint constraint = pathConstraintItems[i];
  283. PathConstraintData constraintData = constraint.data;
  284. constraint.position = constraintData.position;
  285. constraint.spacing = constraintData.spacing;
  286. constraint.rotateMix = constraintData.rotateMix;
  287. constraint.translateMix = constraintData.translateMix;
  288. }
  289. }
  290. public void SetSlotsToSetupPose () {
  291. var slots = this.slots;
  292. var slotsItems = slots.Items;
  293. drawOrder.Clear();
  294. for (int i = 0, n = slots.Count; i < n; i++)
  295. drawOrder.Add(slotsItems[i]);
  296. for (int i = 0, n = slots.Count; i < n; i++)
  297. slotsItems[i].SetToSetupPose();
  298. }
  299. /// <returns>May be null.</returns>
  300. public Bone FindBone (string boneName) {
  301. if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
  302. var bones = this.bones;
  303. var bonesItems = bones.Items;
  304. for (int i = 0, n = bones.Count; i < n; i++) {
  305. Bone bone = bonesItems[i];
  306. if (bone.data.name == boneName) return bone;
  307. }
  308. return null;
  309. }
  310. /// <returns>-1 if the bone was not found.</returns>
  311. public int FindBoneIndex (string boneName) {
  312. if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
  313. var bones = this.bones;
  314. var bonesItems = bones.Items;
  315. for (int i = 0, n = bones.Count; i < n; i++)
  316. if (bonesItems[i].data.name == boneName) return i;
  317. return -1;
  318. }
  319. /// <returns>May be null.</returns>
  320. public Slot FindSlot (string slotName) {
  321. if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
  322. var slots = this.slots;
  323. var slotsItems = slots.Items;
  324. for (int i = 0, n = slots.Count; i < n; i++) {
  325. Slot slot = slotsItems[i];
  326. if (slot.data.name == slotName) return slot;
  327. }
  328. return null;
  329. }
  330. /// <returns>-1 if the bone was not found.</returns>
  331. public int FindSlotIndex (string slotName) {
  332. if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
  333. var slots = this.slots;
  334. var slotsItems = slots.Items;
  335. for (int i = 0, n = slots.Count; i < n; i++)
  336. if (slotsItems[i].data.name.Equals(slotName)) return i;
  337. return -1;
  338. }
  339. /// <summary>Sets a skin by name (see SetSkin).</summary>
  340. public void SetSkin (string skinName) {
  341. Skin foundSkin = data.FindSkin(skinName);
  342. if (foundSkin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName");
  343. SetSkin(foundSkin);
  344. }
  345. /// <summary>
  346. /// <para>Attachments from the new skin are attached if the corresponding attachment from the old skin was attached.
  347. /// If there was no old skin, each slot's setup mode attachment is attached from the new skin.</para>
  348. /// <para>After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling
  349. /// <see cref="Skeleton.SetSlotsToSetupPose()"/>.
  350. /// Also, often <see cref="AnimationState.Apply(Skeleton)"/> is called before the next time the
  351. /// skeleton is rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin.</para>
  352. /// </summary>
  353. /// <param name="newSkin">May be null.</param>
  354. public void SetSkin (Skin newSkin) {
  355. if (newSkin != null) {
  356. if (skin != null)
  357. newSkin.AttachAll(this, skin);
  358. else {
  359. ExposedList<Slot> slots = this.slots;
  360. for (int i = 0, n = slots.Count; i < n; i++) {
  361. Slot slot = slots.Items[i];
  362. string name = slot.data.attachmentName;
  363. if (name != null) {
  364. Attachment attachment = newSkin.GetAttachment(i, name);
  365. if (attachment != null) slot.Attachment = attachment;
  366. }
  367. }
  368. }
  369. }
  370. skin = newSkin;
  371. }
  372. /// <returns>May be null.</returns>
  373. public Attachment GetAttachment (string slotName, string attachmentName) {
  374. return GetAttachment(data.FindSlotIndex(slotName), attachmentName);
  375. }
  376. /// <returns>May be null.</returns>
  377. public Attachment GetAttachment (int slotIndex, string attachmentName) {
  378. if (attachmentName == null) throw new ArgumentNullException("attachmentName", "attachmentName cannot be null.");
  379. if (skin != null) {
  380. Attachment attachment = skin.GetAttachment(slotIndex, attachmentName);
  381. if (attachment != null) return attachment;
  382. }
  383. return data.defaultSkin != null ? data.defaultSkin.GetAttachment(slotIndex, attachmentName) : null;
  384. }
  385. /// <param name="attachmentName">May be null.</param>
  386. public void SetAttachment (string slotName, string attachmentName) {
  387. if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
  388. ExposedList<Slot> slots = this.slots;
  389. for (int i = 0, n = slots.Count; i < n; i++) {
  390. Slot slot = slots.Items[i];
  391. if (slot.data.name == slotName) {
  392. Attachment attachment = null;
  393. if (attachmentName != null) {
  394. attachment = GetAttachment(i, attachmentName);
  395. if (attachment == null) throw new Exception("Attachment not found: " + attachmentName + ", for slot: " + slotName);
  396. }
  397. slot.Attachment = attachment;
  398. return;
  399. }
  400. }
  401. throw new Exception("Slot not found: " + slotName);
  402. }
  403. /// <returns>May be null.</returns>
  404. public IkConstraint FindIkConstraint (string constraintName) {
  405. if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
  406. ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
  407. for (int i = 0, n = ikConstraints.Count; i < n; i++) {
  408. IkConstraint ikConstraint = ikConstraints.Items[i];
  409. if (ikConstraint.data.name == constraintName) return ikConstraint;
  410. }
  411. return null;
  412. }
  413. /// <returns>May be null.</returns>
  414. public TransformConstraint FindTransformConstraint (string constraintName) {
  415. if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
  416. ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
  417. for (int i = 0, n = transformConstraints.Count; i < n; i++) {
  418. TransformConstraint transformConstraint = transformConstraints.Items[i];
  419. if (transformConstraint.data.name == constraintName) return transformConstraint;
  420. }
  421. return null;
  422. }
  423. /// <returns>May be null.</returns>
  424. public PathConstraint FindPathConstraint (string constraintName) {
  425. if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
  426. ExposedList<PathConstraint> pathConstraints = this.pathConstraints;
  427. for (int i = 0, n = pathConstraints.Count; i < n; i++) {
  428. PathConstraint constraint = pathConstraints.Items[i];
  429. if (constraint.data.name.Equals(constraintName)) return constraint;
  430. }
  431. return null;
  432. }
  433. public void Update (float delta) {
  434. time += delta;
  435. }
  436. /// <summary>Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.</summary>
  437. /// <param name="x">The horizontal distance between the skeleton origin and the left side of the AABB.</param>
  438. /// <param name="y">The vertical distance between the skeleton origin and the bottom side of the AABB.</param>
  439. /// <param name="width">The width of the AABB</param>
  440. /// <param name="height">The height of the AABB.</param>
  441. /// <param name="vertexBuffer">Reference to hold a float[]. May be a null reference. This method will assign it a new float[] with the appropriate size as needed.</param>
  442. public void GetBounds (out float x, out float y, out float width, out float height, ref float[] vertexBuffer) {
  443. float[] temp = vertexBuffer;
  444. temp = temp ?? new float[8];
  445. var drawOrderItems = this.drawOrder.Items;
  446. float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
  447. for (int i = 0, n = this.drawOrder.Count; i < n; i++) {
  448. Slot slot = drawOrderItems[i];
  449. int verticesLength = 0;
  450. float[] vertices = null;
  451. Attachment attachment = slot.attachment;
  452. var regionAttachment = attachment as RegionAttachment;
  453. if (regionAttachment != null) {
  454. verticesLength = 8;
  455. vertices = temp;
  456. if (vertices.Length < 8) vertices = temp = new float[8];
  457. regionAttachment.ComputeWorldVertices(slot.bone, temp, 0);
  458. } else {
  459. var meshAttachment = attachment as MeshAttachment;
  460. if (meshAttachment != null) {
  461. MeshAttachment mesh = meshAttachment;
  462. verticesLength = mesh.WorldVerticesLength;
  463. vertices = temp;
  464. if (vertices.Length < verticesLength) vertices = temp = new float[verticesLength];
  465. mesh.ComputeWorldVertices(slot, 0, verticesLength, temp, 0);
  466. }
  467. }
  468. if (vertices != null) {
  469. for (int ii = 0; ii < verticesLength; ii += 2) {
  470. float vx = vertices[ii], vy = vertices[ii + 1];
  471. minX = Math.Min(minX, vx);
  472. minY = Math.Min(minY, vy);
  473. maxX = Math.Max(maxX, vx);
  474. maxY = Math.Max(maxY, vy);
  475. }
  476. }
  477. }
  478. x = minX;
  479. y = minY;
  480. width = maxX - minX;
  481. height = maxY - minY;
  482. vertexBuffer = temp;
  483. }
  484. }
  485. }