[c] 4.0 porting, AnimationState

This commit is contained in:
badlogic 2021-04-07 14:36:36 +02:00
parent 3f9577b5ac
commit f74e80ae89
3 changed files with 96 additions and 84 deletions

View File

@ -53,6 +53,7 @@ _SP_ARRAY_DECLARE_TYPE(spTrackEntryArray, spTrackEntry*)
struct spTrackEntry {
spAnimation* animation;
spTrackEntry* previous;
spTrackEntry* next;
spTrackEntry* mixingFrom;
spTrackEntry* mixingTo;
@ -60,6 +61,7 @@ struct spTrackEntry {
int trackIndex;
int /*boolean*/ loop;
int /*boolean*/ holdPrevious;
int /*boolean*/ reverse;
float eventThreshold, attachmentThreshold, drawOrderThreshold;
float animationStart, animationEnd, animationLast, nextAnimationLast;
float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale;
@ -120,6 +122,8 @@ SP_API void spAnimationState_clearListenerNotifications(spAnimationState* self);
SP_API float spTrackEntry_getAnimationTime (spTrackEntry* entry);
SP_API float spTrackEntry_getTrackComplete(spTrackEntry* entry);
/** Use this to dispose static memory before your app exits to appease your memory leak detector*/
SP_API void spAnimationState_disposeStatics ();

View File

@ -216,7 +216,7 @@ struct _spAnimationState {
_spEventQueue* queue;
int* propertyIDs;
spPropertyId* propertyIDs;
int propertyIDsCount;
int propertyIDsCapacity;

View File

@ -63,9 +63,8 @@ spTrackEntry* _spAnimationState_trackEntry (spAnimationState* self, int trackInd
void _spAnimationState_disposeNext (spAnimationState* self, spTrackEntry* entry);
void _spAnimationState_animationsChanged (spAnimationState* self);
float* _spAnimationState_resizeTimelinesRotation(spTrackEntry* entry, int newSize);
int* _spAnimationState_resizeTimelinesFirst(spTrackEntry* entry, int newSize);
void _spAnimationState_ensureCapacityPropertyIDs(spAnimationState* self, int capacity);
int _spAnimationState_addPropertyID(spAnimationState* self, int id);
int _spAnimationState_addPropertyID(spAnimationState* self, spPropertyId id);
void _spTrackEntry_computeHold(spTrackEntry* self, spAnimationState* state);
_spEventQueue* _spEventQueue_create (_spAnimationState* state) {
@ -237,7 +236,7 @@ spAnimationState* spAnimationState_create (spAnimationStateData* data) {
internal->queue = _spEventQueue_create(internal);
internal->events = CALLOC(spEvent*, 128);
internal->propertyIDs = CALLOC(int, 128);
internal->propertyIDs = CALLOC(spPropertyId, 128);
internal->propertyIDsCapacity = 128;
return self;
@ -363,6 +362,8 @@ int spAnimationState_apply (spAnimationState* self, spSkeleton* skeleton) {
spSlot** slots = NULL;
spSlot* slot = NULL;
const char* attachmentName = NULL;
spEvent** applyEvents = NULL;
float applyTime;
if (internal->animationsChanged) _spAnimationState_animationsChanged(self);
@ -382,15 +383,21 @@ int spAnimationState_apply (spAnimationState* self, spSkeleton* skeleton) {
/* Apply current entry. */
animationLast = current->animationLast; animationTime = spTrackEntry_getAnimationTime(current);
timelineCount = current->animation->timelinesCount;
timelines = current->animation->timelines;
timelineCount = current->animation->timelines->size;
applyEvents = internal->events;
applyTime = animationTime;
if (current->reverse) {
applyTime = current->animation->duration - applyTime;
applyEvents = NULL;
}
timelines = current->animation->timelines->items;
if ((i == 0 && mix == 1) || blend == SP_MIX_BLEND_ADD) {
for (ii = 0; ii < timelineCount; ii++) {
timeline = timelines[ii];
if (timeline->type == SP_TIMELINE_ATTACHMENT) {
_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, animationTime, blend, -1);
if (timeline->propertyIds[0] == SP_PROPERTY_ATTACHMENT) {
_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, applyTime, blend, -1);
} else {
spTimeline_apply(timelines[ii], skeleton, animationLast, animationTime, internal->events,
spTimeline_apply(timelines[ii], skeleton, animationLast, applyTime, applyEvents,
&internal->eventsCount, mix, blend, SP_MIX_DIRECTION_IN);
}
}
@ -404,12 +411,12 @@ int spAnimationState_apply (spAnimationState* self, spSkeleton* skeleton) {
for (ii = 0; ii < timelineCount; ii++) {
timeline = timelines[ii];
timelineBlend = timelineMode->items[ii] == SUBSEQUENT ? blend : SP_MIX_BLEND_SETUP;
if (timeline->type == SP_TIMELINE_ROTATE)
_spAnimationState_applyRotateTimeline(self, timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
else if (timeline->type == SP_TIMELINE_ATTACHMENT)
_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, animationTime, timelineBlend, -1);
if (timeline->propertyIds[0] == SP_PROPERTY_ROTATE)
_spAnimationState_applyRotateTimeline(self, timeline, skeleton, applyTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
else if (timeline->propertyIds[0] == SP_PROPERTY_ATTACHMENT)
_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, applyTime, timelineBlend, -1);
else
spTimeline_apply(timeline, skeleton, animationLast, animationTime, internal->events, &internal->eventsCount, mix, timelineBlend, SP_MIX_DIRECTION_IN);
spTimeline_apply(timeline, skeleton, animationLast, applyTime, applyEvents, &internal->eventsCount, mix, timelineBlend, SP_MIX_DIRECTION_IN);
}
}
_spAnimationState_queueEvents(self, current, animationTime);
@ -453,6 +460,7 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* t
float* timelinesRotation;
int i;
spTrackEntry* holdMix;
float applyTime;
spTrackEntry* from = to->mixingFrom;
if (from->mixingFrom) _spAnimationState_applyMixingFrom(self, from, skeleton, blend);
@ -466,18 +474,25 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* t
if (blend != SP_MIX_BLEND_FIRST) blend = from->mixBlend;
}
events = mix < from->eventThreshold ? internal->events : 0;
attachments = mix < from->attachmentThreshold;
drawOrder = mix < from->drawOrderThreshold;
timelineCount = from->animation->timelines->size;
timelines = from->animation->timelines->items;
alphaHold = from->alpha * to->interruptAlpha; alphaMix = alphaHold * (1 - mix);
animationLast = from->animationLast;
animationTime = spTrackEntry_getAnimationTime(from);
timelineCount = from->animation->timelinesCount;
timelines = from->animation->timelines;
alphaHold = from->alpha * to->interruptAlpha; alphaMix = alphaHold * (1 - mix);
applyTime = animationTime;
events = NULL;
if (from->reverse) {
applyTime = from->animation->duration - applyTime;
} else {
if (mix < from->eventThreshold) events = internal->events;
}
if (blend == SP_MIX_BLEND_ADD) {
for (i = 0; i < timelineCount; i++) {
spTimeline *timeline = timelines[i];
spTimeline_apply(timeline, skeleton, animationLast, animationTime, events, &internal->eventsCount, alphaMix, blend, SP_MIX_DIRECTION_OUT);
spTimeline_apply(timeline, skeleton, animationLast, applyTime, events, &internal->eventsCount, alphaMix, blend, SP_MIX_DIRECTION_OUT);
}
} else {
timelineMode = from->timelineMode;
@ -494,7 +509,7 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* t
switch (timelineMode->items[i]) {
case SUBSEQUENT:
if (!drawOrder && timeline->type == SP_TIMELINE_DRAWORDER) continue;
if (!drawOrder && timeline->propertyIds[0] == SP_PROPERTY_DRAWORDER) continue;
timelineBlend = blend;
alpha = alphaMix;
break;
@ -517,15 +532,15 @@ float _spAnimationState_applyMixingFrom (spAnimationState* self, spTrackEntry* t
break;
}
from->totalAlpha += alpha;
if (timeline->type == SP_TIMELINE_ROTATE)
_spAnimationState_applyRotateTimeline(self, timeline, skeleton, animationTime, alpha, timelineBlend,
if (timeline->propertyIds[0] == SP_PROPERTY_ROTATE)
_spAnimationState_applyRotateTimeline(self, timeline, skeleton, applyTime, alpha, timelineBlend,
timelinesRotation, i << 1, firstFrame);
else if (timeline->type == SP_TIMELINE_ATTACHMENT)
_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, animationTime, timelineBlend, attachments);
else if (timeline->propertyIds[0] == SP_PROPERTY_ATTACHMENT)
_spAnimationState_applyAttachmentTimeline(self, timeline, skeleton, applyTime, timelineBlend, attachments);
else {
if (drawOrder && timeline->type == SP_TIMELINE_DRAWORDER && timelineBlend == SP_MIX_BLEND_SETUP)
if (drawOrder && timeline->propertyIds[0] == SP_PROPERTY_DRAWORDER && timelineBlend == SP_MIX_BLEND_SETUP)
direction = SP_MIX_DIRECTION_IN;
spTimeline_apply(timeline, skeleton, animationLast, animationTime, events, &internal->eventsCount,
spTimeline_apply(timeline, skeleton, animationLast, applyTime, events, &internal->eventsCount,
alpha, timelineBlend, direction);
}
}
@ -572,17 +587,12 @@ void _spAnimationState_applyAttachmentTimeline(spAnimationState* self, spTimelin
slot = skeleton->slots[attachmentTimeline->slotIndex];
if (!slot->bone->active) return;
frames = attachmentTimeline->frames;
frames = attachmentTimeline->super.frames->items;
if (time < frames[0]) {
if (blend == SP_MIX_BLEND_SETUP || blend == SP_MIX_BLEND_FIRST)
_spAnimationState_setAttachment(self, skeleton, slot, slot->data->attachmentName, attachments);
}
else {
if (time >= frames[attachmentTimeline->framesCount - 1])
frameIndex = attachmentTimeline->framesCount - 1;
else
frameIndex = binarySearch1(frames, attachmentTimeline->framesCount, time) - 1;
_spAnimationState_setAttachment(self, skeleton, slot, attachmentTimeline->attachmentNames[frameIndex], attachments);
} else {
_spAnimationState_setAttachment(self, skeleton, slot, attachmentTimeline->attachmentNames[binarySearch1(frames, attachmentTimeline->super.frames->size, time)], attachments);
}
/* If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.*/
@ -612,7 +622,7 @@ void _spAnimationState_applyRotateTimeline (spAnimationState* self, spTimeline*
}
rotateTimeline = SUB_CAST(spRotateTimeline, timeline);
frames = rotateTimeline->frames;
frames = rotateTimeline->super.super.frames->items;
bone = skeleton->bones[rotateTimeline->boneIndex];
if (!bone->active) return;
if (time < frames[0]) {
@ -627,21 +637,7 @@ void _spAnimationState_applyRotateTimeline (spAnimationState* self, spTimeline*
}
} else {
r1 = blend == SP_MIX_BLEND_SETUP ? bone->data->rotation : bone->rotation;
if (time >= frames[rotateTimeline->framesCount - ROTATE_ENTRIES]) /* Time is after last frame. */
r2 = bone->data->rotation + frames[rotateTimeline->framesCount + ROTATE_PREV_ROTATION];
else {
/* Interpolate between the previous frame and the current frame. */
frame = _spCurveTimeline_binarySearch(frames, rotateTimeline->framesCount, time, ROTATE_ENTRIES);
prevRotation = frames[frame + ROTATE_PREV_ROTATION];
frameTime = frames[frame];
percent = spCurveTimeline_getCurvePercent(SUPER(rotateTimeline), (frame >> 1) - 1,
1 - (time - frameTime) / (frames[frame + ROTATE_PREV_TIME] - frameTime));
r2 = frames[frame + ROTATE_ROTATION] - prevRotation;
r2 -= (16384 - (int) (16384.499999999996 - r2 / 360)) * 360;
r2 = prevRotation + r2 * percent + bone->data->rotation;
r2 -= (16384 - (int) (16384.499999999996 - r2 / 360)) * 360;
}
r2 = bone->data->rotation + spCurveTimeline1_getCurveValue(&rotateTimeline->super, time);
}
/* Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. */
@ -671,8 +667,7 @@ void _spAnimationState_applyRotateTimeline (spAnimationState* self, spTimeline*
timelinesRotation[i] = total;
}
timelinesRotation[i + 1] = diff;
r1 += total * alpha;
bone->rotation = r1 - (16384 - (int)(16384.499999999996 - r1 / 360)) * 360;
bone->rotation = r1 + total * alpha;
}
void _spAnimationState_queueEvents (spAnimationState* self, spTrackEntry* entry, float animationTime) {
@ -708,6 +703,10 @@ void _spAnimationState_queueEvents (spAnimationState* self, spTrackEntry* entry,
}
}
void spAnimationState_clearNext(spTrackEntry* entry) {
_spAnimationState_disposeTrackEntry(entry);
}
void spAnimationState_clearTracks (spAnimationState* self) {
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
int i, n, oldDrainDisabled;
@ -752,6 +751,7 @@ void _spAnimationState_setCurrent (spAnimationState* self, int index, spTrackEnt
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
spTrackEntry* from = _spAnimationState_expandToIndex(self, index);
self->tracks[index] = current;
current->previous = NULL;
if (from) {
if (interrupt) _spEventQueue_interrupt(internal->queue, from);
@ -823,18 +823,8 @@ spTrackEntry* spAnimationState_addAnimation (spAnimationState* self, int trackIn
_spEventQueue_drain(internal->queue);
} else {
last->next = entry;
if (delay <= 0) {
float duration = last->animationEnd - last->animationStart;
if (duration != 0) {
if (last->loop) {
delay += duration * (1 + (int) (last->trackTime / duration));
} else {
delay += MAX(duration, last->trackTime);
}
delay -= spAnimationStateData_getMix(self->data, last->animation, animation);
} else
delay = last->trackTime;
}
entry->previous = last;
if (delay <= 0) delay += spTrackEntry_getTrackComplete(last) - entry->mixDuration;
}
entry->delay = delay;
@ -850,10 +840,10 @@ spTrackEntry* spAnimationState_setEmptyAnimation(spAnimationState* self, int tra
spTrackEntry* spAnimationState_addEmptyAnimation(spAnimationState* self, int trackIndex, float mixDuration, float delay) {
spTrackEntry* entry;
if (delay <= 0) delay -= mixDuration;
entry = spAnimationState_addAnimation(self, trackIndex, SP_EMPTY_ANIMATION, 0, delay);
entry = spAnimationState_addAnimation(self, trackIndex, SP_EMPTY_ANIMATION, 0, delay <= 0 ? 1 : delay);
entry->mixDuration = mixDuration;
entry->trackEnd = mixDuration;
if (delay <= 0 && entry->previous != NULL) entry->delay = spTrackEntry_getTrackComplete(entry->previous) - entry->mixDuration;
return entry;
}
@ -888,6 +878,9 @@ spTrackEntry* _spAnimationState_trackEntry (spAnimationState* self, int trackInd
entry->animation = animation;
entry->loop = loop;
entry->holdPrevious = 0;
entry->reverse = 0;
entry->previous = 0;
entry->next = 0;
entry->eventThreshold = 0;
entry->attachmentThreshold = 0;
@ -961,15 +954,15 @@ float* _spAnimationState_resizeTimelinesRotation(spTrackEntry* entry, int newSiz
void _spAnimationState_ensureCapacityPropertyIDs(spAnimationState* self, int capacity) {
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
if (internal->propertyIDsCapacity < capacity) {
int *newPropertyIDs = CALLOC(int, capacity << 1);
memcpy(newPropertyIDs, internal->propertyIDs, sizeof(int) * internal->propertyIDsCount);
spPropertyId *newPropertyIDs = CALLOC(spPropertyId, capacity << 1);
memcpy(newPropertyIDs, internal->propertyIDs, sizeof(spPropertyId) * internal->propertyIDsCount);
FREE(internal->propertyIDs);
internal->propertyIDs = newPropertyIDs;
internal->propertyIDsCapacity = capacity << 1;
}
}
int _spAnimationState_addPropertyID(spAnimationState* self, int id) {
int _spAnimationState_addPropertyID(spAnimationState* self, spPropertyId id) {
int i, n;
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
@ -983,6 +976,18 @@ int _spAnimationState_addPropertyID(spAnimationState* self, int id) {
return 1;
}
int _spAnimationState_addPropertyIDs(spAnimationState* self, spPropertyId *ids, int numIds) {
int i, n;
_spAnimationState* internal = SUB_CAST(_spAnimationState, self);
int oldSize = internal->propertyIDsCount;
for (i = 0, n = numIds; i < n; i++) {
_spAnimationState_addPropertyID(self, ids[i]);
}
return internal->propertyIDsCount != oldSize;
}
spTrackEntry* spAnimationState_getCurrent (spAnimationState* self, int trackIndex) {
if (trackIndex >= self->tracksCount) return 0;
return self->tracks[trackIndex];
@ -1002,12 +1007,13 @@ float spTrackEntry_getAnimationTime (spTrackEntry* entry) {
return MIN(entry->trackTime + entry->animationStart, entry->animationEnd);
}
int /*boolean*/ _spTrackEntry_hasTimeline(spTrackEntry* self, int id) {
spTimeline** timelines = self->animation->timelines;
int i, n;
for (i = 0, n = self->animation->timelinesCount; i < n; i++)
if (spTimeline_getPropertyId(timelines[i]) == id) return 1;
return 0;
float spTrackEntry_getTrackComplete(spTrackEntry* entry) {
float duration = entry->animationEnd - entry->animationStart;
if (duration != 0) {
if (entry->loop) return duration * (1 + (int)(entry->trackTime / duration)); /* Completion of next loop. */
if (entry->trackTime < duration) return duration; /* Before duration. */
}
return entry->trackTime; /* Next update. */
}
void _spTrackEntry_computeHold(spTrackEntry* entry, spAnimationState* state) {
@ -1020,16 +1026,17 @@ void _spTrackEntry_computeHold(spTrackEntry* entry, spAnimationState* state) {
int i;
to = entry->mixingTo;
timelines = entry->animation->timelines;
timelinesCount = entry->animation->timelinesCount;
timelines = entry->animation->timelines->items;
timelinesCount = entry->animation->timelines->size;
timelineMode = spIntArray_setSize(entry->timelineMode, timelinesCount)->items;
spTrackEntryArray_clear(entry->timelineHoldMix);
timelineHoldMix = spTrackEntryArray_setSize(entry->timelineHoldMix, timelinesCount)->items;
if (to != 0 && to->holdPrevious) {
for (i = 0; i < timelinesCount; i++) {
int id = spTimeline_getPropertyId(timelines[i]);
timelineMode[i] = _spAnimationState_addPropertyID(state, id) ? HOLD_FIRST : HOLD_SUBSEQUENT;
spPropertyId* ids = timelines[i]->propertyIds;
int numIds = timelines[i]->propertyIdsCount;
timelineMode[i] = _spAnimationState_addPropertyIDs(state, ids, numIds) ? HOLD_FIRST : HOLD_SUBSEQUENT;
}
return;
}
@ -1038,15 +1045,16 @@ void _spTrackEntry_computeHold(spTrackEntry* entry, spAnimationState* state) {
continue_outer:
for (; i < timelinesCount; i++) {
spTimeline* timeline = timelines[i];
int id = spTimeline_getPropertyId(timeline);
if (!_spAnimationState_addPropertyID(state, id))
spPropertyId *ids = timeline->propertyIds;
int numIds = timeline->propertyIdsCount;
if (!_spAnimationState_addPropertyIDs(state, ids, numIds))
timelineMode[i] = SUBSEQUENT;
else if (to == 0 || timeline->type == SP_TIMELINE_ATTACHMENT || timeline->type == SP_TIMELINE_DRAWORDER ||
timeline->type == SP_TIMELINE_EVENT || !_spTrackEntry_hasTimeline(to, id)) {
else if (to == 0 || timeline->propertyIds[0] == SP_PROPERTY_ATTACHMENT || timeline->propertyIds[0] == SP_PROPERTY_DRAWORDER ||
timeline->propertyIds[0] == SP_PROPERTY_EVENT || !spAnimation_hasTimeline(to->animation, ids, numIds)) {
timelineMode[i] = FIRST;
} else {
for (next = to->mixingTo; next != 0; next = next->mixingTo) {
if (_spTrackEntry_hasTimeline(next, id)) continue;
if (spAnimation_hasTimeline(next->animation, ids, numIds)) continue;
if (next->mixDuration > 0) {
timelineMode[i] = HOLD_MIX;
timelineHoldMix[i] = next;