Browse Source

[cpp] Port AnimationState deform mixing while attachment timelines mix out. See #1653.

badlogic 5 years ago
parent
commit
6bda0c236b

+ 7 - 2
spine-cpp/spine-cpp/include/spine/AnimationState.h

@@ -36,6 +36,7 @@
 #include <spine/SpineObject.h>
 #include <spine/SpineString.h>
 #include <spine/HasRendererObject.h>
+#include "Slot.h"
 
 #ifdef SPINE_USE_STD_FUNCTION
 #include <functional>
@@ -59,6 +60,7 @@ namespace spine {
 	class AnimationStateData;
 	class Skeleton;
 	class RotateTimeline;
+	class AttachmentTimeline;
 
 #ifdef SPINE_USE_STD_FUNCTION
 	typedef std::function<void (AnimationState* state, EventType type, TrackEntry* entry, Event* event)> AnimationStateListener;
@@ -399,11 +401,14 @@ namespace spine {
 		AnimationStateListener _listener;
 		AnimationStateListenerObject* _listenerObject;
 
+		int _unkeyedState;
+
 		float _timeScale;
 
 		static Animation* getEmptyAnimation();
 
 		static void applyRotateTimeline(RotateTimeline* rotateTimeline, Skeleton& skeleton, float time, float alpha, MixBlend pose, Vector<float>& timelinesRotation, size_t i, bool firstFrame);
+        void applyAttachmentTimeline(AttachmentTimeline* attachmentTimeline, Skeleton& skeleton, float animationTime, MixBlend pose, bool firstFrame);
 
 		/// Returns true when all mixing from entries are complete.
 		bool updateMixingFrom(TrackEntry* to, float delta);
@@ -428,8 +433,8 @@ namespace spine {
 
 		void computeHold(TrackEntry *entry);
 
-		void computeNotLast(TrackEntry *entry);
-	};
+        void setAttachment(Skeleton &skeleton, spine::Slot &slot, const String &attachmentName, bool attachments);
+    };
 }
 
 #endif /* Spine_AnimationState_h */

+ 6 - 3
spine-cpp/spine-cpp/include/spine/AttachmentTimeline.h

@@ -40,6 +40,7 @@
 namespace spine {
 
 	class Skeleton;
+	class Slot;
 	class Event;
 
 	class SP_API AttachmentTimeline : public Timeline {
@@ -60,14 +61,16 @@ namespace spine {
 
 		size_t getSlotIndex();
 		void setSlotIndex(size_t inValue);
-		const Vector<float>& getFrames();
-		const Vector<String>& getAttachmentNames();
+		Vector<float>& getFrames();
+		Vector<String>& getAttachmentNames();
 		size_t getFrameCount();
 	private:
 		size_t _slotIndex;
 		Vector<float> _frames;
 		Vector<String> _attachmentNames;
-	};
+
+        void setAttachment(Skeleton& skeleton, Slot& slot, String* attachmentName);
+    };
 }
 
 #endif /* Spine_AttachmentTimeline_h */

+ 5 - 0
spine-cpp/spine-cpp/include/spine/Slot.h

@@ -102,6 +102,10 @@ public:
 
 	void setAttachment(Attachment *inValue);
 
+	int getAttachmentState();
+
+	void setAttachmentState(int state);
+
 	float getAttachmentTime();
 
 	void setAttachmentTime(float inValue);
@@ -116,6 +120,7 @@ private:
 	Color _darkColor;
 	bool _hasDarkColor;
 	Attachment *_attachment;
+	int _attachmentState;
 	float _attachmentTime;
 	Vector<float> _deform;
 };

+ 64 - 49
spine-cpp/spine-cpp/src/spine/AnimationState.cpp

@@ -43,6 +43,8 @@
 #include <spine/AttachmentTimeline.h>
 #include <spine/DrawOrderTimeline.h>
 #include <spine/EventTimeline.h>
+#include <spine/Slot.h>
+#include <spine/SlotData.h>
 
 #include <float.h>
 
@@ -290,7 +292,9 @@ const int Subsequent = 0;
 const int First = 1;
 const int Hold = 2;
 const int HoldMix = 3;
-const int NotLast = 4;
+
+const int Setup = 1;
+const int Current = 2;
 
 AnimationState::AnimationState(AnimationStateData *data) :
 		_data(data),
@@ -298,6 +302,7 @@ AnimationState::AnimationState(AnimationStateData *data) :
 		_animationsChanged(false),
 		_listener(dummyOnAnimationEventFunc),
 		_listenerObject(NULL),
+		_unkeyedState(0),
 		_timeScale(1) {
 }
 
@@ -418,8 +423,13 @@ bool AnimationState::apply(Skeleton &skeleton) {
 		size_t timelineCount = current._animation->_timelines.size();
 		Vector<Timeline *> &timelines = current._animation->_timelines;
 		if ((i == 0 && mix == 1) || blend == MixBlend_Add) {
-			for (size_t ii = 0; ii < timelineCount; ++ii)
-				timelines[ii]->apply(skeleton, animationLast, animationTime, &_events, mix, blend, MixDirection_In);
+			for (size_t ii = 0; ii < timelineCount; ++ii) {
+                Timeline *timeline = timelines[ii];
+                if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
+                    applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, animationTime, blend, true);
+                else
+                    timeline->apply(skeleton, animationLast, animationTime, &_events, mix, blend, MixDirection_In);
+            }
 		} else {
 			Vector<int> &timelineMode = current._timelineMode;
 
@@ -431,13 +441,12 @@ bool AnimationState::apply(Skeleton &skeleton) {
 				Timeline *timeline = timelines[ii];
 				assert(timeline);
 
-				MixBlend timelineBlend = (timelineMode[ii] & (NotLast - 1)) == Subsequent ? blend : MixBlend_Setup;
+				MixBlend timelineBlend = timelineMode[ii] == Subsequent ? blend : MixBlend_Setup;
 
-				RotateTimeline *rotateTimeline = NULL;
-				if (timeline->getRTTI().isExactly(RotateTimeline::rtti)) rotateTimeline = static_cast<RotateTimeline *>(timeline);
-
-				if (rotateTimeline != NULL)
-					applyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
+				if (timeline->getRTTI().isExactly(RotateTimeline::rtti))
+					applyRotateTimeline(static_cast<RotateTimeline *>(timeline), skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
+				else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))
+                    applyAttachmentTimeline(static_cast<AttachmentTimeline *>(timeline), skeleton, animationTime, timelineBlend, true);
 				else
 					timeline->apply(skeleton, animationLast, animationTime, &_events, mix, timelineBlend, MixDirection_In);
 			}
@@ -449,6 +458,17 @@ bool AnimationState::apply(Skeleton &skeleton) {
 		current._nextTrackLast = current._trackTime;
 	}
 
+    int setupState = _unkeyedState + Setup;
+    Vector<Slot*>& slots = skeleton.getSlots();
+    for (int i = 0, n = slots.size(); i < n; i++) {
+        Slot* slot = slots[i];
+        if (slot->getAttachmentState() == setupState) {
+            const String& attachmentName = slot->getData().getAttachmentName();
+            slot->setAttachment(attachmentName.isEmpty() ? NULL : skeleton.getAttachment(slot->getData().getIndex(), attachmentName));
+        }
+    }
+    _unkeyedState += 2;
+
 	_queue->drain();
 	return applied;
 }
@@ -636,6 +656,28 @@ Animation *AnimationState::getEmptyAnimation() {
 	return &ret;
 }
 
+void AnimationState::applyAttachmentTimeline(AttachmentTimeline* attachmentTimeline, Skeleton& skeleton, float time, MixBlend blend, bool attachments) {
+    Slot* slot = skeleton.getSlots()[attachmentTimeline->getSlotIndex()];
+    if (!slot->getBone().isActive()) return;
+
+    Vector<float>& frames = attachmentTimeline->getFrames();
+    if (time < frames[0]) {
+        if (blend == MixBlend_Setup || blend == MixBlend_First)
+            setAttachment(skeleton, *slot, slot->getData().getAttachmentName(), attachments);
+    } else {
+        int frameIndex = 0;
+        if (time >= frames[attachmentTimeline->getFrames().size() - 1])
+            frameIndex = attachmentTimeline->getFrames().size() - 1;
+        else
+            frameIndex = Animation::binarySearch(frames, time) - 1;
+        setAttachment(skeleton, *slot, attachmentTimeline->getAttachmentNames()[frameIndex], attachments);
+    }
+
+    /* If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.*/
+    if (slot->getAttachmentState() <= _unkeyedState) slot->setAttachmentState(_unkeyedState + Setup);
+}
+
+
 void AnimationState::applyRotateTimeline(RotateTimeline *rotateTimeline, Skeleton &skeleton, float time, float alpha,
 	MixBlend blend, Vector<float> &timelinesRotation, size_t i, bool firstFrame
 ) {
@@ -784,14 +826,10 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle
 			MixDirection direction = MixDirection_Out;
 			MixBlend timelineBlend;
 			float alpha;
-			switch (timelineMode[i] & (NotLast - 1)) {
+			switch (timelineMode[i]) {
 				case Subsequent:
-					timelineBlend = blend;
-					if (!attachments && (timeline->getRTTI().isExactly(AttachmentTimeline::rtti))) {
-						if ((timelineMode[i] & NotLast) == NotLast) continue;
-						timelineBlend = MixBlend_Setup;
-					}
 					if (!drawOrder && (timeline->getRTTI().isExactly(DrawOrderTimeline::rtti))) continue;
+                    timelineBlend = blend;
 					alpha = alphaMix;
 					break;
 				case First:
@@ -811,15 +849,12 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle
 			from->_totalAlpha += alpha;
 			if ((timeline->getRTTI().isExactly(RotateTimeline::rtti))) {
 				applyRotateTimeline((RotateTimeline*)timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame);
-			} else {
-				if (timelineBlend == MixBlend_Setup) {
-					if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) {
-						if (attachments || (timelineMode[i] & NotLast) == NotLast) direction = MixDirection_In;
-					} else if (timeline->getRTTI().isExactly(DrawOrderTimeline::rtti)) {
-						if (drawOrder) direction = MixDirection_In;
-					}
-				}
-				timeline->apply(skeleton, animationLast, animationTime, eventBuffer, alpha, timelineBlend, direction);
+			} else if (timeline->getRTTI().isExactly(AttachmentTimeline::rtti)) {
+                applyAttachmentTimeline(static_cast<AttachmentTimeline*>(timeline), skeleton, animationTime, timelineBlend, attachments);
+            } else {
+			    if (drawOrder && timeline->getRTTI().isExactly(DrawOrderTimeline::rtti) && timelineBlend == MixBlend_Setup)
+			        direction = MixDirection_In;
+			    timeline->apply(skeleton, animationLast, animationTime, eventBuffer, alpha, timelineBlend, direction);
 			}
 		}
 	}
@@ -835,6 +870,11 @@ float AnimationState::applyMixingFrom(TrackEntry *to, Skeleton &skeleton, MixBle
 	return mix;
 }
 
+void AnimationState::setAttachment(Skeleton& skeleton, Slot& slot, const String& attachmentName, bool attachments) {
+    slot.setAttachment(attachmentName.isEmpty() ? NULL : skeleton.getAttachment(slot.getData().getIndex(), attachmentName));
+    if (attachments) slot.setAttachmentState(_unkeyedState + Current);
+}
+
 void AnimationState::queueEvents(TrackEntry *entry, float animationTime) {
 	float animationStart = entry->_animationStart, animationEnd = entry->_animationEnd;
 	float duration = animationEnd - animationStart;
@@ -953,15 +993,6 @@ void AnimationState::animationsChanged() {
 			entry = entry->_mixingTo;
 		} while (entry != NULL);
 	}
-
-	_propertyIDs.clear();
-	for (int i = (int)_tracks.size() - 1; i >= 0; i--) {
-		TrackEntry *entry = _tracks[i];
-		while (entry) {
-			computeNotLast(entry);
-			entry = entry->_mixingFrom;
-		}
-	}
 }
 
 void AnimationState::computeHold(TrackEntry *entry) {
@@ -1013,19 +1044,3 @@ void AnimationState::computeHold(TrackEntry *entry) {
 		}
 	}
 }
-
-void AnimationState::computeNotLast(TrackEntry *entry) {
-	Vector<Timeline *> &timelines = entry->_animation->_timelines;
-	size_t timelinesCount = timelines.size();
-	Vector<int> &timelineMode = entry->_timelineMode;
-
-	for (size_t i = 0; i < timelinesCount; i++) {
-		if (timelines[i]->getRTTI().isExactly(AttachmentTimeline::rtti)) {
-			AttachmentTimeline *timeline = static_cast<AttachmentTimeline *>(timelines[i]);
-			if (!_propertyIDs.containsKey(timeline->getSlotIndex()))
-				_propertyIDs.put(timeline->getSlotIndex(), true);
-			else
-				timelineMode[i] |= NotLast;
-		}
-	}
-}

+ 9 - 7
spine-cpp/spine-cpp/src/spine/AttachmentTimeline.cpp

@@ -57,6 +57,10 @@ AttachmentTimeline::AttachmentTimeline(int frameCount) : Timeline(), _slotIndex(
 	}
 }
 
+void AttachmentTimeline::setAttachment(Skeleton& skeleton, Slot& slot, String* attachmentName) {
+    slot.setAttachment(attachmentName == NULL || attachmentName->isEmpty() ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName));
+}
+
 void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vector<Event *> *pEvents, float alpha,
 	MixBlend blend, MixDirection direction
 ) {
@@ -71,17 +75,15 @@ void AttachmentTimeline::apply(Skeleton &skeleton, float lastTime, float time, V
 	Slot &slot = *slotP;
 	if (!slot._bone.isActive()) return;
 
-	if (direction == MixDirection_Out && blend == MixBlend_Setup) {
-		attachmentName = &slot._data._attachmentName;
-		slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName));
+	if (direction == MixDirection_Out) {
+	    if (blend == MixBlend_Setup) setAttachment(skeleton, slot, &slot._data._attachmentName);
 		return;
 	}
 
 	if (time < _frames[0]) {
 		// Time is before first frame.
 		if (blend == MixBlend_Setup || blend == MixBlend_First) {
-			attachmentName = &slot._data._attachmentName;
-			slot.setAttachment(attachmentName->length() == 0 ? NULL : skeleton.getAttachment(_slotIndex, *attachmentName));
+            setAttachment(skeleton, slot, &slot._data._attachmentName);
 		}
 		return;
 	}
@@ -115,11 +117,11 @@ void AttachmentTimeline::setSlotIndex(size_t inValue) {
 	_slotIndex = inValue;
 }
 
-const Vector<float> &AttachmentTimeline::getFrames() {
+Vector<float> &AttachmentTimeline::getFrames() {
 	return _frames;
 }
 
-const Vector<String> &AttachmentTimeline::getAttachmentNames() {
+Vector<String> &AttachmentTimeline::getAttachmentNames() {
 	return _attachmentNames;
 }
 

+ 7 - 5
spine-cpp/spine-cpp/src/spine/DrawOrderTimeline.cpp

@@ -66,11 +66,13 @@ void DrawOrderTimeline::apply(Skeleton &skeleton, float lastTime, float time, Ve
 
 	Vector<Slot *> &drawOrder = skeleton._drawOrder;
 	Vector<Slot *> &slots = skeleton._slots;
-	if (direction == MixDirection_Out && blend == MixBlend_Setup) {
-		drawOrder.clear();
-		drawOrder.ensureCapacity(slots.size());
-		for (size_t i = 0, n = slots.size(); i < n; ++i)
-			drawOrder.add(slots[i]);
+	if (direction == MixDirection_Out) {
+	    if (blend == MixBlend_Setup) {
+            drawOrder.clear();
+            drawOrder.ensureCapacity(slots.size());
+            for (size_t i = 0, n = slots.size(); i < n; ++i)
+                drawOrder.add(slots[i]);
+        }
 		return;
 	}
 

+ 9 - 0
spine-cpp/spine-cpp/src/spine/Slot.cpp

@@ -48,6 +48,7 @@ Slot::Slot(SlotData &data, Bone &bone) :
 		_darkColor(0, 0, 0, 0),
 		_hasDarkColor(data.hasDarkColor()),
 		_attachment(NULL),
+		_attachmentState(0),
 		_attachmentTime(0) {
 	setToSetupPose();
 }
@@ -102,6 +103,14 @@ void Slot::setAttachment(Attachment *inValue) {
 	_deform.clear();
 }
 
+int Slot::getAttachmentState() {
+    return _attachmentState;
+}
+
+void Slot::setAttachmentState(int state) {
+    _attachmentState = state;
+}
+
 float Slot::getAttachmentTime() {
 	return _skeleton.getTime() - _attachmentTime;
 }