Browse Source

Merge branch '3.7-beta' into 3.7-beta-cpp

badlogic 7 years ago
parent
commit
7c398be3b6
100 changed files with 323 additions and 93 deletions
  1. 6 0
      CHANGELOG.md
  2. 0 0
      examples/alien/export/alien-ess.json
  3. BIN
      examples/alien/export/alien-ess.skel
  4. 0 0
      examples/alien/export/alien-pro.json
  5. BIN
      examples/alien/export/alien-pro.skel
  6. 0 0
      examples/coin/export/coin-pro.json
  7. BIN
      examples/coin/export/coin-pro.skel
  8. 0 0
      examples/dragon/export/dragon-ess.json
  9. BIN
      examples/dragon/export/dragon-ess.skel
  10. 7 7
      examples/export/runtimes.sh
  11. 0 0
      examples/goblins/export/goblins-ess.json
  12. BIN
      examples/goblins/export/goblins-ess.skel
  13. 0 0
      examples/goblins/export/goblins-pro.json
  14. BIN
      examples/goblins/export/goblins-pro.skel
  15. 0 0
      examples/hero/export/hero-ess.json
  16. BIN
      examples/hero/export/hero-ess.skel
  17. 0 0
      examples/hero/export/hero-pro.json
  18. BIN
      examples/hero/export/hero-pro.skel
  19. 0 0
      examples/owl/export/owl-pro.json
  20. BIN
      examples/owl/export/owl-pro.skel
  21. 0 0
      examples/powerup/export/powerup-ess.json
  22. BIN
      examples/powerup/export/powerup-ess.skel
  23. 0 0
      examples/powerup/export/powerup-pro.json
  24. BIN
      examples/powerup/export/powerup-pro.skel
  25. 0 0
      examples/raptor/export/raptor-pro.json
  26. BIN
      examples/raptor/export/raptor-pro.skel
  27. 0 0
      examples/speedy/export/speedy-ess.json
  28. BIN
      examples/speedy/export/speedy-ess.skel
  29. 0 0
      examples/spineboy/export/spineboy-ess.json
  30. BIN
      examples/spineboy/export/spineboy-ess.skel
  31. 0 0
      examples/spineboy/export/spineboy-pro.json
  32. BIN
      examples/spineboy/export/spineboy-pro.skel
  33. BIN
      examples/spineboy/spineboy-pro.spine
  34. 0 0
      examples/spinosaurus/export/spinosaurus-ess.json
  35. BIN
      examples/spinosaurus/export/spinosaurus-ess.skel
  36. 0 0
      examples/stretchyman-stretchy-ik/export/stretchyman-stretchy-ik.json
  37. BIN
      examples/stretchyman-stretchy-ik/export/stretchyman-stretchy-ik.skel
  38. 0 0
      examples/stretchyman/export/stretchyman-pro.json
  39. BIN
      examples/stretchyman/export/stretchyman-pro.skel
  40. 0 0
      examples/tank/export/tank-pro.json
  41. BIN
      examples/tank/export/tank-pro.skel
  42. 0 0
      examples/vine/export/vine-pro.json
  43. BIN
      examples/vine/export/vine-pro.skel
  44. BIN
      spine-as3/spine-as3-example/lib/spine-as3.swc
  45. 0 0
      spine-as3/spine-as3-example/src/spineboy-ess.json
  46. 2 0
      spine-as3/spine-as3/src/spine/Event.as
  47. 4 2
      spine-as3/spine-as3/src/spine/EventData.as
  48. 9 0
      spine-as3/spine-as3/src/spine/SkeletonJson.as
  49. 5 1
      spine-c/spine-c/include/spine/Event.h
  50. 5 1
      spine-c/spine-c/include/spine/EventData.h
  51. 8 0
      spine-c/spine-c/src/spine/SkeletonBinary.c
  52. 11 0
      spine-c/spine-c/src/spine/SkeletonJson.c
  53. 0 0
      spine-cocos2d-objc/Resources/coin-pro.json
  54. 0 0
      spine-cocos2d-objc/Resources/goblins-pro.json
  55. 0 0
      spine-cocos2d-objc/Resources/raptor-pro.json
  56. 0 0
      spine-cocos2d-objc/Resources/spineboy-ess.json
  57. 0 0
      spine-cocos2d-objc/Resources/tank-pro.json
  58. BIN
      spine-cocos2dx/example/Resources/common/coin-pro.skel
  59. 0 0
      spine-cocos2dx/example/Resources/common/goblins-pro.json
  60. 0 0
      spine-cocos2dx/example/Resources/common/raptor-pro.json
  61. 0 0
      spine-cocos2dx/example/Resources/common/spineboy-ess.json
  62. BIN
      spine-cocos2dx/example/Resources/common/tank-pro.skel
  63. 0 0
      spine-corona/data/coin-pro.json
  64. 0 0
      spine-corona/data/goblins-pro.json
  65. 0 0
      spine-corona/data/owl-pro.json
  66. 0 0
      spine-corona/data/raptor-pro.json
  67. 0 0
      spine-corona/data/spineboy-ess.json
  68. 0 0
      spine-corona/data/spineboy-pro.json
  69. 0 0
      spine-corona/data/stretchyman-pro.json
  70. 0 0
      spine-corona/data/stretchyman-stretchy-ik.json
  71. 0 0
      spine-corona/data/tank-pro.json
  72. 0 0
      spine-corona/data/vine-pro.json
  73. 2 2
      spine-corona/main.lua
  74. 10 0
      spine-cpp/spine-cpp/include/spine/Event.h
  75. 15 0
      spine-cpp/spine-cpp/include/spine/EventData.h
  76. 20 1
      spine-cpp/spine-cpp/src/spine/Event.cpp
  77. 31 3
      spine-cpp/spine-cpp/src/spine/EventData.cpp
  78. 10 1
      spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp
  79. 10 0
      spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp
  80. 91 65
      spine-csharp/src/AnimationState.cs
  81. 5 0
      spine-csharp/src/Event.cs
  82. 3 0
      spine-csharp/src/EventData.cs
  83. 13 4
      spine-csharp/src/SkeletonBinary.cs
  84. 14 5
      spine-csharp/src/SkeletonJson.cs
  85. 0 0
      spine-libgdx/spine-libgdx-tests/assets/coin/coin-pro.json
  86. BIN
      spine-libgdx/spine-libgdx-tests/assets/coin/coin-pro.skel
  87. 0 0
      spine-libgdx/spine-libgdx-tests/assets/goblins/goblins-ess.json
  88. BIN
      spine-libgdx/spine-libgdx-tests/assets/goblins/goblins-ess.skel
  89. 0 0
      spine-libgdx/spine-libgdx-tests/assets/goblins/goblins-pro.json
  90. BIN
      spine-libgdx/spine-libgdx-tests/assets/goblins/goblins-pro.skel
  91. 0 0
      spine-libgdx/spine-libgdx-tests/assets/raptor/raptor-pro.json
  92. BIN
      spine-libgdx/spine-libgdx-tests/assets/raptor/raptor-pro.skel
  93. 0 0
      spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-ess.json
  94. BIN
      spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-ess.skel
  95. 0 0
      spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-pro.json
  96. BIN
      spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-pro.skel
  97. 0 1
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java
  98. 17 0
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Event.java
  99. 17 0
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/EventData.java
  100. 8 0
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

+ 6 - 0
CHANGELOG.md

@@ -8,6 +8,7 @@
 * **Additions**
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry#setMixBlend(MixBlend.add)` on each track. To specify the blend percentage, set `TrackEntry#alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Support for stretchy IK
+  * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
 
 ### Starling
 * Added support for vertex effects. See `RaptorExample.as`
@@ -40,6 +41,7 @@
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `spTrackEntry->mixBlend = SP_MIXBLEND_ADD)` on each track. To specify the blend percentage, set `spTrackEntry->alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Optimized attachment lookup to give a 40x speed-up. See https://github.com/EsotericSoftware/spine-runtimes/commit/cab81276263890b65d07fa2329ace16db1e365ff
   * Support for stretchy IK
+  * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
 
 ### Cocos2d-Objc
 * Added vertex effect support to modify vertices of skeletons on the CPU. See `RaptorExample.m`.
@@ -97,6 +99,7 @@
 * **Additions**
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry#MixBlend = MixBlend.add` on each track. To specify the blend percentage, set `TrackEntry#Alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Support for stretchy IK
+  * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
 
 ### Unity
 * **Runtime and Editor, and Assembly Definition** Files and folders have been reorganized into "Runtime" and "Editor". Each of these have an `.asmdef` file that defines these separately as their own assembly in Unity. For projects not using assembly definition, you may delete the `.asmdef` files. These assembly definitions will be ignored by older versions of Unity that don't support it.
@@ -137,6 +140,7 @@
   * Added convenience method to add all attachments from one skin to another, see https://github.com/EsotericSoftware/spine-runtimes/commit/a0b7bb6c445efdfac12b0cdee2057afa3eff3ead
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry#setMixBlend(MixBlend.add)` on each track. To specify the blend percentage, set `TrackEntry#alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Support for stretchy IK
+  * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
 
 ### libGDX
 * Added `VertexEffect` interface, instances of which can be set on `SkeletonRenderer`. Allows to modify vertices before submitting them to GPU. See `SwirlEffect`, `JitterEffect` and `VertexEffectTest`.
@@ -154,6 +158,7 @@
   * Added `JitterEffect` and `SwirlEffect` and support for vertex effects in Corona and Love
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry:setMixBlend(MixBlend.add)` on each track. To specify the blend percentage, set `TrackEntry.alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Support for stretchy IK
+  * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
 
 ### Love2D
 * Added support for vertex effects. Set an implementation like "JitterEffect" on `Skeleton.vertexEffect`. See `main.lua` for an example.
@@ -170,6 +175,7 @@
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry#setMixBlend(MixBlend.add)` on each track. To specify the blend percentage, set `TrackEntry#alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion. See https://github.com/EsotericSoftware/spine-runtimes/blob/f045d221836fa56191ccda73dd42ae884d4731b8/spine-ts/webgl/tests/test-additive-animation-blending.html for an example.
   * Added work-around for iOS WebKit JIT bug, see https://github.com/EsotericSoftware/spine-runtimes/commit/c28bbebf804980f55cdd773fed9ff145e0e7e76c
   * Support for stretchy IK
+  * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
 
 ### WebGL backend
 * Added `VertexEffect` interface, instances of which can be set on `SkeletonRenderer`. Allows to modify vertices before submitting them to GPU. See `SwirlEffect`, `JitterEffect`, and the example which allows to set effects.

File diff suppressed because it is too large
+ 0 - 0
examples/alien/export/alien-ess.json


BIN
examples/alien/export/alien-ess.skel


File diff suppressed because it is too large
+ 0 - 0
examples/alien/export/alien-pro.json


BIN
examples/alien/export/alien-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/coin/export/coin-pro.json


BIN
examples/coin/export/coin-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/dragon/export/dragon-ess.json


BIN
examples/dragon/export/dragon-ess.skel


+ 7 - 7
examples/export/runtimes.sh

@@ -90,7 +90,7 @@ cp -f ../raptor/export/raptor-pro.json ../../spine-corona/data
 cp -f ../raptor/export/raptor.atlas ../../spine-corona/data
 cp -f ../raptor/export/raptor.png ../../spine-corona/data
 
-cp -f ../spineboy/export/spineboy-ess.json ../../spine-corona/data
+cp -f ../spineboy/export/spineboy-pro.json ../../spine-corona/data
 cp -f ../spineboy/export/spineboy.atlas ../../spine-corona/data
 cp -f ../spineboy/export/spineboy.png ../../spine-corona/data
 
@@ -126,7 +126,7 @@ cp -f ../raptor/export/raptor-pro.json ../../spine-love/data
 cp -f ../raptor/export/raptor.atlas ../../spine-love/data
 cp -f ../raptor/export/raptor.png ../../spine-love/data
 
-cp -f ../spineboy/export/spineboy-ess.json ../../spine-love/data
+cp -f ../spineboy/export/spineboy-pro.json ../../spine-love/data
 cp -f ../spineboy/export/spineboy.atlas ../../spine-love/data
 cp -f ../spineboy/export/spineboy.png ../../spine-love/data
 
@@ -161,8 +161,8 @@ cp -f ../raptor/export/raptor-pro.skel ../../spine-sfml/c/data/
 cp -f ../raptor/export/raptor.atlas ../../spine-sfml/c/data/
 cp -f ../raptor/export/raptor.png ../../spine-sfml/c/data/
 
-cp -f ../spineboy/export/spineboy-ess.json ../../spine-sfml/c/data/
-cp -f ../spineboy/export/spineboy-ess.skel ../../spine-sfml/c/data/
+cp -f ../spineboy/export/spineboy-pro.json ../../spine-sfml/c/data/
+cp -f ../spineboy/export/spineboy-pro.skel ../../spine-sfml/c/data/
 cp -f ../spineboy/export/spineboy.atlas ../../spine-sfml/c/data/
 cp -f ../spineboy/export/spineboy.png ../../spine-sfml/c/data/
 
@@ -206,8 +206,8 @@ cp -f ../raptor/export/raptor-pro.skel ../../spine-sfml/cpp/data/
 cp -f ../raptor/export/raptor.atlas ../../spine-sfml/cpp/data/
 cp -f ../raptor/export/raptor.png ../../spine-sfml/cpp/data/
 
-cp -f ../spineboy/export/spineboy-ess.json ../../spine-sfml/cpp/data/
-cp -f ../spineboy/export/spineboy-ess.skel ../../spine-sfml/cpp/data/
+cp -f ../spineboy/export/spineboy-pro.json ../../spine-sfml/cpp/data/
+cp -f ../spineboy/export/spineboy-pro.skel ../../spine-sfml/cpp/data/
 cp -f ../spineboy/export/spineboy.atlas ../../spine-sfml/cpp/data/
 cp -f ../spineboy/export/spineboy.png ../../spine-sfml/cpp/data/
 
@@ -248,7 +248,7 @@ cp -f ../raptor/export/raptor-pro.json ../../spine-starling/spine-starling-examp
 cp -f ../raptor/export/raptor.atlas ../../spine-starling/spine-starling-example/src/
 cp -f ../raptor/export/raptor.png ../../spine-starling/spine-starling-example/src/
 
-cp -f ../spineboy/export/spineboy-ess.json ../../spine-starling/spine-starling-example/src/
+cp -f ../spineboy/export/spineboy-pro.json ../../spine-starling/spine-starling-example/src/
 cp -f ../spineboy/export/spineboy.atlas ../../spine-starling/spine-starling-example/src/
 cp -f ../spineboy/export/spineboy.png ../../spine-starling/spine-starling-example/src/
 

File diff suppressed because it is too large
+ 0 - 0
examples/goblins/export/goblins-ess.json


BIN
examples/goblins/export/goblins-ess.skel


File diff suppressed because it is too large
+ 0 - 0
examples/goblins/export/goblins-pro.json


BIN
examples/goblins/export/goblins-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/hero/export/hero-ess.json


BIN
examples/hero/export/hero-ess.skel


File diff suppressed because it is too large
+ 0 - 0
examples/hero/export/hero-pro.json


BIN
examples/hero/export/hero-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/owl/export/owl-pro.json


BIN
examples/owl/export/owl-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/powerup/export/powerup-ess.json


BIN
examples/powerup/export/powerup-ess.skel


File diff suppressed because it is too large
+ 0 - 0
examples/powerup/export/powerup-pro.json


BIN
examples/powerup/export/powerup-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/raptor/export/raptor-pro.json


BIN
examples/raptor/export/raptor-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/speedy/export/speedy-ess.json


BIN
examples/speedy/export/speedy-ess.skel


File diff suppressed because it is too large
+ 0 - 0
examples/spineboy/export/spineboy-ess.json


BIN
examples/spineboy/export/spineboy-ess.skel


File diff suppressed because it is too large
+ 0 - 0
examples/spineboy/export/spineboy-pro.json


BIN
examples/spineboy/export/spineboy-pro.skel


BIN
examples/spineboy/spineboy-pro.spine


File diff suppressed because it is too large
+ 0 - 0
examples/spinosaurus/export/spinosaurus-ess.json


BIN
examples/spinosaurus/export/spinosaurus-ess.skel


File diff suppressed because it is too large
+ 0 - 0
examples/stretchyman-stretchy-ik/export/stretchyman-stretchy-ik.json


BIN
examples/stretchyman-stretchy-ik/export/stretchyman-stretchy-ik.skel


File diff suppressed because it is too large
+ 0 - 0
examples/stretchyman/export/stretchyman-pro.json


BIN
examples/stretchyman/export/stretchyman-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/tank/export/tank-pro.json


BIN
examples/tank/export/tank-pro.skel


File diff suppressed because it is too large
+ 0 - 0
examples/vine/export/vine-pro.json


BIN
examples/vine/export/vine-pro.skel


BIN
spine-as3/spine-as3-example/lib/spine-as3.swc


File diff suppressed because it is too large
+ 0 - 0
spine-as3/spine-as3-example/src/spineboy-ess.json


+ 2 - 0
spine-as3/spine-as3/src/spine/Event.as

@@ -35,6 +35,8 @@ package spine {
 		public var intValue : int;
 		public var floatValue : Number;
 		public var stringValue : String;
+		public var volume: Number = 1;
+		public var balance: Number = 0;
 
 		public function Event(time : Number, data : EventData) {
 			if (data == null) throw new ArgumentError("data cannot be null.");

+ 4 - 2
spine-as3/spine-as3/src/spine/EventData.as

@@ -31,10 +31,12 @@
 package spine {
 	public class EventData {
 		internal var _name : String;
-		public var intValue : int;
-		;
+		public var intValue : int;		
 		public var floatValue : Number;
 		public var stringValue : String;
+		public var audioPath: String;
+		public var volume: Number = 1;
+		public var balance: Number = 0;
 
 		public function EventData(name : String) {
 			if (name == null) throw new ArgumentError("name cannot be null.");

+ 9 - 0
spine-as3/spine-as3/src/spine/SkeletonJson.as

@@ -266,6 +266,11 @@ package spine {
 					eventData.intValue = eventMap["int"] || 0;
 					eventData.floatValue = eventMap["float"] || 0;
 					eventData.stringValue = eventMap["string"] || "";
+					eventData.audioPath = eventMap["audio"] || null;
+					if (eventData.audioPath != null) {
+						eventData.volume = eventMap["volume"] || 1;
+						eventData.balance = eventMap["balance"] || 0;
+					}
 					skeletonData.events.push(eventData);
 				}
 			}
@@ -714,6 +719,10 @@ package spine {
 					event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue;
 					event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue;
 					event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue;
+					if (eventData.audioPath != null) {
+						event.volume = eventMap.hasOwnProperty("volume") ? eventMap["volume"] : 1;
+						event.balance = eventMap.hasOwnProperty("balance") ? eventMap["balance"] : 0;
+					}
 					eventTimeline.setFrame(frameIndex++, event);
 				}
 				timelines[timelines.length] = eventTimeline;

+ 5 - 1
spine-c/spine-c/include/spine/Event.h

@@ -44,6 +44,8 @@ typedef struct spEvent {
 	int intValue;
 	float floatValue;
 	const char* stringValue;
+	float volume;
+	float balance;
 
 #ifdef __cplusplus
 	spEvent() :
@@ -51,7 +53,9 @@ typedef struct spEvent {
 		time(0),
 		intValue(0),
 		floatValue(0),
-		stringValue(0) {
+		stringValue(0),
+		volume(0),
+		balance(0) {
 	}
 #endif
 } spEvent;

+ 5 - 1
spine-c/spine-c/include/spine/EventData.h

@@ -43,6 +43,8 @@ typedef struct spEventData {
 	float floatValue;
 	const char* stringValue;
 	const char* audioPath;
+	float volume;
+	float balance;
 
 #ifdef __cplusplus
 	spEventData() :
@@ -50,7 +52,9 @@ typedef struct spEventData {
 		intValue(0),
 		floatValue(0),
 		stringValue(0),
-		audioPath(0) {
+		audioPath(0),
+		volume(0),
+		balance(0) {
 	}
 #endif
 } spEventData;

+ 8 - 0
spine-c/spine-c/src/spine/SkeletonBinary.c

@@ -592,6 +592,10 @@ static spAnimation* _spSkeletonBinary_readAnimation (spSkeletonBinary* self, con
 				event->stringValue = readString(input);
 			else
 				MALLOC_STR(event->stringValue, eventData->stringValue);
+			if (eventData->audioPath) {
+				event->volume = readFloat(input);
+				event->balance = readFloat(input);
+			}
 			spEventTimeline_setFrame(timeline, i, event);
 		}
 		spTimelineArray_add(timelines, (spTimeline*)timeline);
@@ -1077,6 +1081,10 @@ spSkeletonData* spSkeletonBinary_readSkeletonData (spSkeletonBinary* self, const
 		eventData->floatValue = readFloat(input);
 		eventData->stringValue = readString(input);
 		eventData->audioPath = readString(input);
+		if (eventData->audioPath) {
+			eventData->volume = readFloat(input);
+			eventData->balance = readFloat(input);
+		}
 		skeletonData->events[i] = eventData;
 	}
 

+ 11 - 0
spine-c/spine-c/src/spine/SkeletonJson.c

@@ -502,6 +502,10 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r
 			event->floatValue = Json_getFloat(valueMap, "float", eventData->floatValue);
 			stringValue = Json_getString(valueMap, "string", eventData->stringValue);
 			if (stringValue) MALLOC_STR(event->stringValue, stringValue);
+			if (eventData->audioPath) {
+				event->volume = Json_getFloat(valueMap, "volume", 1);
+				event->balance = Json_getFloat(valueMap, "volume", 0);
+			}
 			spEventTimeline_setFrame(timeline, frameIndex, event);
 		}
 		animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);
@@ -1081,6 +1085,7 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 	if (events) {
 		Json *eventMap;
 		const char* stringValue;
+		const char* audioPath;
 		skeletonData->eventsCount = events->size;
 		skeletonData->events = MALLOC(spEventData*, events->size);
 		for (eventMap = events->child, i = 0; eventMap; eventMap = eventMap->next, ++i) {
@@ -1089,6 +1094,12 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha
 			eventData->floatValue = Json_getFloat(eventMap, "float", 0);
 			stringValue = Json_getString(eventMap, "string", 0);
 			if (stringValue) MALLOC_STR(eventData->stringValue, stringValue);
+			audioPath = Json_getString(eventMap, "audio", 0);
+			if (audioPath) {
+				MALLOC_STR(eventData->audioPath, audioPath);
+				eventData->volume = Json_getFloat(eventMap, "volume", 1);
+				eventData->balance = Json_getFloat(eventMap, "balance", 0);
+			}
 			skeletonData->events[i] = eventData;
 		}
 	}

File diff suppressed because it is too large
+ 0 - 0
spine-cocos2d-objc/Resources/coin-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-cocos2d-objc/Resources/goblins-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-cocos2d-objc/Resources/raptor-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-cocos2d-objc/Resources/spineboy-ess.json


File diff suppressed because it is too large
+ 0 - 0
spine-cocos2d-objc/Resources/tank-pro.json


BIN
spine-cocos2dx/example/Resources/common/coin-pro.skel


File diff suppressed because it is too large
+ 0 - 0
spine-cocos2dx/example/Resources/common/goblins-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-cocos2dx/example/Resources/common/raptor-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-cocos2dx/example/Resources/common/spineboy-ess.json


BIN
spine-cocos2dx/example/Resources/common/tank-pro.skel


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/coin-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/goblins-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/owl-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/raptor-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/spineboy-ess.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/spineboy-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/stretchyman-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/stretchyman-stretchy-ik.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/tank-pro.json


File diff suppressed because it is too large
+ 0 - 0
spine-corona/data/vine-pro.json


+ 2 - 2
spine-corona/main.lua

@@ -60,7 +60,7 @@ function loadSkeleton(atlasFile, jsonFile, x, y, scale, animation, skin)
 		print(entry.trackIndex.." dispose: "..entry.animation.name)
 	end
 	animationState.onEvent = function (entry, event)
-		print(entry.trackIndex.." event: "..entry.animation.name..", "..event.data.name..", "..event.intValue..", "..event.floatValue..", '"..(event.stringValue or "").."'")
+		print(entry.trackIndex.." event: "..entry.animation.name..", "..event.data.name..", "..event.intValue..", "..event.floatValue..", '"..(event.stringValue or "").."'" .. ", " .. event.volume .. ", " .. event.balance)
 	end
 	
   if atlasFile == "spineboy.atlas" then
@@ -81,9 +81,9 @@ function loadSkeleton(atlasFile, jsonFile, x, y, scale, animation, skin)
 	return { skeleton = skeleton, state = animationState }
 end
 
+table.insert(skeletons, loadSkeleton("spineboy.atlas", "spineboy-pro.json", 240, 300, 0.4, "walk"))
 table.insert(skeletons, loadSkeleton("stretchyman.atlas", "stretchyman-stretchy-ik.json", 40, 300, 0.5, "sneak"))
 table.insert(skeletons, loadSkeleton("coin.atlas", "coin-pro.json", 240, 300, 0.4, "rotate"))
-table.insert(skeletons, loadSkeleton("spineboy.atlas", "spineboy-ess.json", 240, 300, 0.4, "walk"))
 table.insert(skeletons, loadSkeleton("raptor.atlas", "raptor-pro.json", 200, 300, 0.25, "walk"))
 table.insert(skeletons, loadSkeleton("goblins.atlas", "goblins-pro.json", 240, 300, 0.8, "walk", "goblin"))
 table.insert(skeletons, loadSkeleton("stretchyman.atlas", "stretchyman-pro.json", 40, 300, 0.5, "sneak"))

+ 10 - 0
spine-cpp/spine-cpp/include/spine/Event.h

@@ -65,12 +65,22 @@ public:
 
 	void setStringValue(const String &inValue);
 
+	float getVolume();
+
+	void setVolume(float inValue);
+
+	float getBalance();
+
+	void setBalance(float inValue);
+
 private:
 	const EventData &_data;
 	const float _time;
 	int _intValue;
 	float _floatValue;
 	String _stringValue;
+	float _volume;
+	float _balance;
 };
 }
 

+ 15 - 0
spine-cpp/spine-cpp/include/spine/EventData.h

@@ -61,11 +61,26 @@ public:
 
 	void setStringValue(const String &inValue);
 
+	const String &getAudioPath();
+
+	void setAudioPath(const String &inValue);
+
+	float getVolume();
+
+	void setVolume(float inValue);
+
+	float getBalance();
+
+	void setBalance(float inValue);
+
 private:
 	const String _name;
 	int _intValue;
 	float _floatValue;
 	String _stringValue;
+	String _audioPath;
+	float _volume;
+	float _balance;
 };
 }
 

+ 20 - 1
spine-cpp/spine-cpp/src/spine/Event.cpp

@@ -41,7 +41,9 @@ spine::Event::Event(float time, const spine::EventData &data) :
 		_time(time),
 		_intValue(0),
 		_floatValue(0),
-		_stringValue() {
+		_stringValue(),
+		_volume(1),
+		_balance(0) {
 }
 
 const spine::EventData &spine::Event::getData() {
@@ -75,3 +77,20 @@ const spine::String &spine::Event::getStringValue() {
 void spine::Event::setStringValue(const spine::String &inValue) {
 	_stringValue = inValue;
 }
+
+
+float Event::getVolume() {
+	return _volume;
+}
+
+void Event::setVolume(float inValue) {
+	_volume = inValue;
+}
+
+float Event::getBalance() {
+	return _balance;
+}
+
+void Event::setBalance(float inValue) {
+	_balance = inValue;
+}

+ 31 - 3
spine-cpp/spine-cpp/src/spine/EventData.cpp

@@ -38,9 +38,12 @@
 
 spine::EventData::EventData(const spine::String &name) :
 		_name(name),
-		_intValue((int)0),
-		_floatValue(0.0f)
-		 {
+		_intValue(0),
+		_floatValue(0),
+		_stringValue(),
+		_audioPath(),
+		_volume(1),
+		_balance(0) {
 	assert(_name.length() > 0);
 }
 
@@ -72,3 +75,28 @@ const spine::String &spine::EventData::getStringValue() {
 void spine::EventData::setStringValue(const spine::String &inValue) {
 	this->_stringValue = inValue;
 }
+
+const String &EventData::getAudioPath() {
+	return _audioPath;
+}
+
+void EventData::setAudioPath(const String &inValue) {
+	_audioPath = inValue;
+}
+
+
+float EventData::getVolume() {
+	return _volume;
+}
+
+void EventData::setVolume(float inValue) {
+	_volume = inValue;
+}
+
+float EventData::getBalance() {
+	return _balance;
+}
+
+void EventData::setBalance(float inValue) {
+	_balance = inValue;
+}

+ 10 - 1
spine-cpp/spine-cpp/src/spine/SkeletonBinary.cpp

@@ -315,7 +315,11 @@ SkeletonData *SkeletonBinary::readSkeletonData(const unsigned char *binary, cons
 		eventData->_intValue = readVarint(input, false);
 		eventData->_floatValue = readFloat(input);
 		eventData->_stringValue.own(readString(input));
-		String(readString(input), true); // skip audio path
+		eventData->_audioPath.own(readString(input)); // skip audio path
+		if (!eventData->_audioPath.isEmpty()) {
+			eventData->_volume = readFloat(input);
+			eventData->_balance = readFloat(input);
+		}
 		skeletonData->_events[i] = eventData;
 	}
 
@@ -1016,6 +1020,11 @@ Animation *SkeletonBinary::readAnimation(const String &name, DataInput *input, S
 			if (freeString) {
 				SpineExtension::free(event_stringValue, __FILE__, __LINE__);
 			}
+
+			if (!eventData->_audioPath.isEmpty()) {
+				event->_volume = readFloat(input);
+				event->_balance = readFloat(input);
+			}
 			timeline->setFrame(i, event);
 		}
 

+ 10 - 0
spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp

@@ -678,6 +678,12 @@ SkeletonData *SkeletonJson::readSkeletonData(const char *json) {
 			eventData->_floatValue = Json::getFloat(eventMap, "float", 0);
 			const char *stringValue = Json::getString(eventMap, "string", 0);
 			eventData->_stringValue = stringValue;
+			const char *audioPath = Json::getString(eventMap, "audio", 0);
+			eventData->_audioPath = audioPath;
+			if (audioPath) {
+				eventData->_volume = Json::getFloat(eventMap, "volume", 1);
+				eventData->_balance = Json::getFloat(eventMap, "balance", 0);
+			}
 			skeletonData->_events[i] = eventData;
 		}
 	}
@@ -1175,6 +1181,10 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) {
 			event->_intValue = Json::getInt(valueMap, "int", eventData->_intValue);
 			event->_floatValue = Json::getFloat(valueMap, "float", eventData->_floatValue);
 			event->_stringValue = Json::getString(valueMap, "string", eventData->_stringValue.buffer());
+			if (!eventData->_audioPath.isEmpty()) {
+				event->_volume = Json::getFloat(valueMap, "volume", 1);
+				event->_balance = Json::getFloat(valueMap, "balance", 0);
+			}
 			timeline->setFrame(frameIndex, event);
 		}
 		timelines.add(timeline);

+ 91 - 65
spine-csharp/src/AnimationState.cs

@@ -34,17 +34,16 @@ using System.Collections.Generic;
 namespace Spine {
 	public class AnimationState {
 		static readonly Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 0);
-		internal const int Subsequent = 0, First = 1, Dip = 2, DipMix = 3;
+		internal const int Subsequent = 0, First = 1, Hold = 2, HoldMix = 3;
 
 		private AnimationStateData data;
 
-		Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
+		private readonly Pool<TrackEntry> trackEntryPool = new Pool<TrackEntry>();
 		private readonly ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
 		private readonly ExposedList<Event> events = new ExposedList<Event>();
 		private readonly EventQueue queue; // Initialized by constructor.
 
 		private readonly HashSet<int> propertyIDs = new HashSet<int>();
-		private readonly ExposedList<TrackEntry> mixingTo = new ExposedList<TrackEntry>();
 		private bool animationsChanged;
 
 		private float timeScale = 1;
@@ -119,6 +118,7 @@ namespace Spine {
 					// End mixing from entries once all have completed.
 					var from = current.mixingFrom;
 					current.mixingFrom = null;
+					if (from != null) from.mixingTo = null;
 					while (from != null) {
 						queue.End(from);
 						from = from.mixingFrom;
@@ -146,6 +146,7 @@ namespace Spine {
 				// Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
 				if (from.totalAlpha == 0 || to.mixDuration == 0) {
 					to.mixingFrom = from.mixingFrom;
+					if (from.mixingFrom != null) from.mixingFrom.mixingTo = to;
 					to.interruptAlpha = from.interruptAlpha;
 					queue.End(from);
 				}
@@ -187,11 +188,11 @@ namespace Spine {
 				int timelineCount = current.animation.timelines.Count;
 				var timelines = current.animation.timelines;
 				var timelinesItems = timelines.Items;
-				if (mix == 1 || blend == MixBlend.Add) {
+				if (i == 0 && (mix == 1 || blend == MixBlend.Add)) {
 					for (int ii = 0; ii < timelineCount; ii++)
 						timelinesItems[ii].Apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.In);
 				} else {
-					var timelineData = current.timelineData.Items;
+					var timelineMode = current.timelineMode.Items;
 
 					bool firstFrame = current.timelinesRotation.Count == 0;
 					if (firstFrame) current.timelinesRotation.EnsureCapacity(timelines.Count << 1);
@@ -199,7 +200,7 @@ namespace Spine {
 
 					for (int ii = 0; ii < timelineCount; ii++) {
 						Timeline timeline = timelinesItems[ii];
-						MixBlend timelineBlend = timelineData[ii] >= AnimationState.Subsequent ? blend : MixBlend.Setup;
+						MixBlend timelineBlend = timelineMode[ii] >= AnimationState.Subsequent ? blend : MixBlend.Setup;
 						var rotateTimeline = timeline as RotateTimeline;
 						if (rotateTimeline != null)
 							ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
@@ -237,14 +238,14 @@ namespace Spine {
 			var timelines = from.animation.timelines;
 			int timelineCount = timelines.Count;
 			var timelinesItems = timelines.Items;
-			float alphaDip = from.alpha * to.interruptAlpha, alphaMix = alphaDip * (1 - mix);
+			float alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
 
 			if (blend == MixBlend.Add) {
 				for (int i = 0; i < timelineCount; i++)
 					(timelinesItems[i]).Apply(skeleton, animationLast, animationTime, eventBuffer, alphaMix, blend, MixDirection.Out);
 			} else {
-				var timelineData = from.timelineData.Items;
-				var timelineDipMix = from.timelineDipMix.Items;
+				var timelineMode = from.timelineMode.Items;
+				var timelineHoldMix = from.timelineHoldMix.Items;
 
 				bool firstFrame = from.timelinesRotation.Count == 0;
 				if (firstFrame)	from.timelinesRotation.Resize(timelines.Count << 1); // from.timelinesRotation.setSize
@@ -255,7 +256,7 @@ namespace Spine {
 					Timeline timeline = timelinesItems[i];
 					MixBlend timelineBlend;
 					float alpha;
-					switch (timelineData[i]) {
+					switch (timelineMode[i]) {
 						case AnimationState.Subsequent:
 							if (!attachments && timeline is AttachmentTimeline)
 								continue;
@@ -268,14 +269,14 @@ namespace Spine {
 							timelineBlend = MixBlend.Setup;
 							alpha = alphaMix;
 							break;
-						case AnimationState.Dip:
+						case AnimationState.Hold:
 							timelineBlend = MixBlend.Setup;
-							alpha = alphaDip;
+							alpha = alphaHold;
 							break;
 						default:
 							timelineBlend = MixBlend.Setup;
-							TrackEntry dipMix = timelineDipMix[i];
-							alpha = alphaDip * Math.Max(0, 1 - dipMix.mixTime / dipMix.mixDuration);
+							TrackEntry holdMix = timelineHoldMix[i];
+							alpha = alphaHold * Math.Max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
 							break;
 					}
 					from.totalAlpha += alpha;
@@ -428,6 +429,7 @@ namespace Spine {
 				if (from == null) break;
 				queue.End(from);
 				entry.mixingFrom = null;
+				entry.mixingTo = null;
 				entry = from;
 			}
 
@@ -444,6 +446,7 @@ namespace Spine {
 			if (from != null) {
 				if (interrupt) queue.Interrupt(from);
 				current.mixingFrom = from;
+				current.mixingTo = current;
 				current.mixTime = 0;
 
 				// Store interrupted mix percentage.
@@ -599,6 +602,7 @@ namespace Spine {
 			entry.trackIndex = trackIndex;
 			entry.animation = animation;
 			entry.loop = loop;
+			entry.holdPrevious = false;
 
 			entry.eventThreshold = 0;
 			entry.attachmentThreshold = 0;
@@ -636,17 +640,71 @@ namespace Spine {
 		private void AnimationsChanged () {
 			animationsChanged = false;
 
-			var propertyIDs = this.propertyIDs;
-			propertyIDs.Clear();
-			var mixingTo = this.mixingTo;
+			this.propertyIDs.Clear();
 
 			var tracksItems = tracks.Items;
 			for (int i = 0, n = tracks.Count; i < n; i++) {
 				var entry = tracksItems[i];
-				if (entry != null && (i == 0 || entry.mixBlend != MixBlend.Add)) entry.SetTimelineData(null, mixingTo, propertyIDs);
+				if (entry == null) continue;
+				// Move to last entry, then iterate in reverse (the order animations are applied).
+				while (entry.mixingFrom != null)
+					entry = entry.mixingFrom;
+
+				do {
+					if (entry.mixingTo == null || entry.mixBlend != MixBlend.Add) SetTimelineModes(entry);
+					entry = entry.mixingTo;
+				} while (entry != null);
+
+			}
+		}
+
+		private void SetTimelineModes (TrackEntry entry) {
+			var to = entry.mixingTo;
+			var timelines = entry.animation.timelines.Items;
+			int timelinesCount = entry.animation.timelines.Count;
+			var timelineMode = entry.timelineMode.Resize(timelinesCount).Items; //timelineMode.setSize(timelinesCount);
+			entry.timelineHoldMix.Clear();
+			var timelineHoldMix = entry.timelineHoldMix.Resize(timelinesCount).Items; //timelineHoldMix.setSize(timelinesCount);
+			var propertyIDs = this.propertyIDs;
+
+			if (to != null && to.holdPrevious) {
+				for (int i = 0; i < timelinesCount; i++) {
+					propertyIDs.Add(timelines[i].PropertyId);
+					timelineMode[i] = AnimationState.Hold;
+				}
+				return;
+			}
+
+			// outer:
+			for (int i = 0; i < timelinesCount; i++) {
+				int id = timelines[i].PropertyId;
+				if (!propertyIDs.Add(id))
+					timelineMode[i] = AnimationState.Subsequent;
+				else if (to == null || !HasTimeline(to, id))
+					timelineMode[i] = AnimationState.First;
+				else {
+					for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
+						if (HasTimeline(next, id)) continue;
+						if (next.mixDuration > 0) {
+							timelineMode[i] = AnimationState.HoldMix;
+							timelineHoldMix[i] = next;
+							goto continue_outer; // continue outer;
+						}
+						break;
+					}
+					timelineMode[i] = AnimationState.Hold;
+				}
+				continue_outer:	{}
 			}
 		}
 
+		static bool HasTimeline (TrackEntry entry, int id) {
+			var timelines = entry.animation.timelines.Items;
+			for (int i = 0, n = entry.animation.timelines.Count; i < n; i++)
+				if (timelines[i].PropertyId == id) return true;
+			return false;
+		}
+
 		/// <returns>The track entry for the animation currently playing on the track, or null if no animation is currently playing.</returns>
 		public TrackEntry GetCurrent (int trackIndex) {
 			return (trackIndex >= tracks.Count) ? null : tracks.Items[trackIndex];
@@ -675,17 +733,17 @@ namespace Spine {
 	public class TrackEntry : Pool<TrackEntry>.IPoolable {
 		internal Animation animation;
 
-		internal TrackEntry next, mixingFrom;
+		internal TrackEntry next, mixingFrom, mixingTo;
 		internal int trackIndex;
 
-		internal bool loop;
+		internal bool loop, holdPrevious;
 		internal float eventThreshold, attachmentThreshold, drawOrderThreshold;
 		internal float animationStart, animationEnd, animationLast, nextAnimationLast;
 		internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
 		internal float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha;
 		internal MixBlend mixBlend = MixBlend.Replace;
-		internal readonly ExposedList<int> timelineData = new ExposedList<int>();
-		internal readonly ExposedList<TrackEntry> timelineDipMix = new ExposedList<TrackEntry>();
+		internal readonly ExposedList<int> timelineMode = new ExposedList<int>();
+		internal readonly ExposedList<TrackEntry> timelineHoldMix = new ExposedList<TrackEntry>();
 		internal readonly ExposedList<float> timelinesRotation = new ExposedList<float>();
 
 		// IPoolable.Reset()
@@ -693,8 +751,8 @@ namespace Spine {
 			next = null;
 			mixingFrom = null;
 			animation = null;
-			timelineData.Clear();
-			timelineDipMix.Clear();
+			timelineMode.Clear();
+			timelineHoldMix.Clear();
 			timelinesRotation.Clear();
 
 			Start = null;
@@ -705,47 +763,6 @@ namespace Spine {
 			Event = null;
 		}
 
-		/// <summary>Sets the timeline data.</summary>
-		/// <param name="to">May be null.</param>
-		internal TrackEntry SetTimelineData (TrackEntry to, ExposedList<TrackEntry> mixingToArray, HashSet<int> propertyIDs) {
-			if (to != null) mixingToArray.Add(to);
-			var lastEntry = mixingFrom != null ? mixingFrom.SetTimelineData(this, mixingToArray, propertyIDs) : this;
-			if (to != null) mixingToArray.Pop();
-
-			var mixingTo = mixingToArray.Items;
-			int mixingToLast = mixingToArray.Count - 1;
-			var timelines = animation.timelines.Items;
-			int timelinesCount = animation.timelines.Count;
-			var timelineDataItems = timelineData.Resize(timelinesCount).Items; // timelineData.setSize(timelinesCount);
-			timelineDipMix.Clear();
-			var timelineDipMixItems = timelineDipMix.Resize(timelinesCount).Items; //timelineDipMix.setSize(timelinesCount);
-
-			// outer:
-			for (int i = 0; i < timelinesCount; i++) {
-				int id = timelines[i].PropertyId;
-				if (!propertyIDs.Add(id)) {
-					timelineDataItems[i] = AnimationState.Subsequent;
-				} else if (to == null || !to.HasTimeline(id)) {
-					timelineDataItems[i] = AnimationState.First;
-				} else {
-					for (int ii = mixingToLast; ii >= 0; ii--) {
-						var entry = mixingTo[ii];
-						if (!entry.HasTimeline(id)) {
-							if (entry.mixDuration > 0) {
-								timelineDataItems[i] = AnimationState.DipMix;
-								timelineDipMixItems[i] = entry;
-								goto continue_outer; // continue outer;
-							}
-							break;
-						}
-					}
-					timelineDataItems[i] = AnimationState.Dip;
-				}
-				continue_outer: {}
-			}
-			return lastEntry;
-		}
-
 		bool HasTimeline (int id) {
 			var timelines = animation.timelines.Items;
 			for (int i = 0, n = animation.timelines.Count; i < n; i++)
@@ -903,6 +920,15 @@ namespace Spine {
 		/// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a linked list.</summary>
 		public TrackEntry MixingFrom { get { return mixingFrom; } }
 
+		/// <summary>
+		/// If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead of being mixed out.
+		/// 
+		/// When mixing between animations that key the same property, if a lower track also keys that property then the value will briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0% while the second animation mixes from 0% to 100%. Setting HoldPrevious to true applies the first animation at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which keys the property, only when a higher track also keys the property.
+		/// 
+		/// Snapping will occur if HoldPrevious is true and this animation does not key all the same properties as the previous animation.
+		/// </summary>
+		public bool HoldPrevious { get { return holdPrevious; } set { holdPrevious = value; } }
+
 		public event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete;
 		public event AnimationState.TrackEntryEventDelegate Event;
 		internal void OnStart () { if (Start != null) Start(this); }

+ 5 - 0
spine-csharp/src/Event.cs

@@ -38,6 +38,8 @@ namespace Spine {
 		internal int intValue;
 		internal float floatValue;
 		internal string stringValue;
+		internal float volume;
+		internal  float balance;
 
 		public EventData Data { get { return data; } }
 		/// <summary>The animation time this event was keyed.</summary>
@@ -47,6 +49,9 @@ namespace Spine {
 		public float Float { get { return floatValue; } set { floatValue = value; } }
 		public string String { get { return stringValue; } set { stringValue = value; } }
 
+		public float Volume { get { return volume; } set { volume = value; } }
+		public float Balance { get { return balance; } set { balance = value; } }
+
 		public Event (float time, EventData data) {
 			if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
 			this.time = time;

+ 3 - 0
spine-csharp/src/EventData.cs

@@ -40,7 +40,10 @@ namespace Spine {
 		public int Int { get; set; }
 		public float Float { get; set; }
 		public string @String { get; set; }
+
 		public string AudioPath { get; set; }
+		public float Volume { get; set; }
+		public float Balance { get; set; }
 
 		public EventData (string name) {
 			if (name == null) throw new ArgumentNullException("name", "name cannot be null.");

+ 13 - 4
spine-csharp/src/SkeletonBinary.cs

@@ -288,6 +288,10 @@ namespace Spine {
 				data.Float = ReadFloat(input);
 				data.String = ReadString(input);
 				data.AudioPath = ReadString(input);
+				if (data.AudioPath != null) {
+					data.Volume = ReadFloat(input);
+					data.Balance = ReadFloat(input);
+				}
 				skeletonData.events.Add(data);
 			}
 
@@ -804,10 +808,15 @@ namespace Spine {
 				for (int i = 0; i < eventCount; i++) {
 					float time = ReadFloat(input);
 					EventData eventData = skeletonData.events.Items[ReadVarint(input, true)];
-					Event e = new Event(time, eventData);
-					e.Int = ReadVarint(input, false);
-					e.Float = ReadFloat(input);
-					e.String = ReadBoolean(input) ? ReadString(input) : eventData.String;
+					Event e = new Event(time, eventData) {
+						Int = ReadVarint(input, false),
+						Float = ReadFloat(input),
+						String = ReadBoolean(input) ? ReadString(input) : eventData.String
+					};
+					if (e.data.AudioPath != null) {
+						e.volume = ReadFloat(input);
+						e.balance = ReadFloat(input);
+					}
 					timeline.SetFrame(i, e);
 				}
 				timelines.Add(timeline);

+ 14 - 5
spine-csharp/src/SkeletonJson.cs

@@ -296,7 +296,11 @@ namespace Spine {
 					data.Int = GetInt(entryMap, "int", 0);
 					data.Float = GetFloat(entryMap, "float", 0);
 					data.String = GetString(entryMap, "string", string.Empty);
-					data.AudioPath = GetString(entryMap, "audio", string.Empty);
+					data.AudioPath = GetString(entryMap, "audio", null);
+					if (data.AudioPath != null) {
+						data.Volume = GetFloat(entryMap, "volume", 1);
+						data.Balance = GetFloat(entryMap, "balance", 0);
+					}
 					skeletonData.events.Add(data);
 				}
 			}
@@ -779,10 +783,15 @@ namespace Spine {
 				foreach (Dictionary<string, Object> eventMap in eventsMap) {
 					EventData eventData = skeletonData.FindEvent((string)eventMap["name"]);
 					if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]);
-					var e = new Event((float)eventMap["time"], eventData);
-					e.Int = GetInt(eventMap, "int", eventData.Int);
-					e.Float = GetFloat(eventMap, "float", eventData.Float);
-					e.String = GetString(eventMap, "string", eventData.String);
+					var e = new Event((float)eventMap["time"], eventData) {
+						intValue = GetInt(eventMap, "int", eventData.Int),
+						floatValue = GetFloat(eventMap, "float", eventData.Float),
+						stringValue = GetString(eventMap, "string", eventData.String)
+					};
+					if (e.data.AudioPath != null) {
+						e.volume = GetFloat(eventMap, "volume", eventData.Volume);
+						e.balance = GetFloat(eventMap, "balance", eventData.Balance);
+					}
 					timeline.SetFrame(frameIndex++, e);
 				}
 				timelines.Add(timeline);

File diff suppressed because it is too large
+ 0 - 0
spine-libgdx/spine-libgdx-tests/assets/coin/coin-pro.json


BIN
spine-libgdx/spine-libgdx-tests/assets/coin/coin-pro.skel


File diff suppressed because it is too large
+ 0 - 0
spine-libgdx/spine-libgdx-tests/assets/goblins/goblins-ess.json


BIN
spine-libgdx/spine-libgdx-tests/assets/goblins/goblins-ess.skel


File diff suppressed because it is too large
+ 0 - 0
spine-libgdx/spine-libgdx-tests/assets/goblins/goblins-pro.json


BIN
spine-libgdx/spine-libgdx-tests/assets/goblins/goblins-pro.skel


File diff suppressed because it is too large
+ 0 - 0
spine-libgdx/spine-libgdx-tests/assets/raptor/raptor-pro.json


BIN
spine-libgdx/spine-libgdx-tests/assets/raptor/raptor-pro.skel


File diff suppressed because it is too large
+ 0 - 0
spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-ess.json


BIN
spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-ess.skel


File diff suppressed because it is too large
+ 0 - 0
spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-pro.json


BIN
spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-pro.skel


+ 0 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java

@@ -666,7 +666,6 @@ public class AnimationState {
 	private void animationsChanged () {
 		animationsChanged = false;
 
-		IntSet propertyIDs = this.propertyIDs;
 		propertyIDs.clear(2048);
 
 		for (int i = 0, n = tracks.size; i < n; i++) {

+ 17 - 0
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Event.java

@@ -44,6 +44,7 @@ public class Event {
 	int intValue;
 	float floatValue;
 	String stringValue;
+	float volume, balance;
 	final float time;
 
 	public Event (float time, EventData data) {
@@ -76,6 +77,22 @@ public class Event {
 		this.stringValue = stringValue;
 	}
 
+	public float getVolume () {
+		return volume;
+	}
+
+	public void setVolume (float volume) {
+		this.volume = volume;
+	}
+
+	public float getBalance () {
+		return balance;
+	}
+
+	public void setBalance (float balance) {
+		this.balance = balance;
+	}
+
 	/** The animation time this event was keyed. */
 	public float getTime () {
 		return time;

+ 17 - 0
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/EventData.java

@@ -38,6 +38,7 @@ public class EventData {
 	int intValue;
 	float floatValue;
 	String stringValue, audioPath;
+	float volume, balance;
 
 	public EventData (String name) {
 		if (name == null) throw new IllegalArgumentException("name cannot be null.");
@@ -76,6 +77,22 @@ public class EventData {
 		this.audioPath = audioPath;
 	}
 
+	public float getVolume () {
+		return volume;
+	}
+
+	public void setVolume (float volume) {
+		this.volume = volume;
+	}
+
+	public float getBalance () {
+		return balance;
+	}
+
+	public void setBalance (float balance) {
+		this.balance = balance;
+	}
+
 	/** The name of the event, which is unique within the skeleton. */
 	public String getName () {
 		return name;

+ 8 - 0
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

@@ -310,6 +310,10 @@ public class SkeletonBinary {
 				data.floatValue = input.readFloat();
 				data.stringValue = input.readString();
 				data.audioPath = input.readString();
+				if (data.audioPath != null) {
+					data.volume = input.readFloat();
+					data.balance = input.readFloat();
+				}
 				skeletonData.events.add(data);
 			}
 
@@ -821,6 +825,10 @@ public class SkeletonBinary {
 					event.intValue = input.readInt(false);
 					event.floatValue = input.readFloat();
 					event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue;
+					if (event.getData().audioPath != null) {
+						event.volume = input.readFloat();
+						event.balance = input.readFloat();
+					}
 					timeline.setFrame(i, event);
 				}
 				timelines.add(timeline);

Some files were not shown because too many files changed in this diff