Files
integration/Source/Integration/AnimQueue.cpp

230 lines
5.9 KiB
C++
Raw Normal View History

2023-09-08 05:38:09 -04:00
#include "AnimQueue.h"
2023-09-15 13:28:18 -04:00
FlxAnimStep FlxAnimQueueDecoder::ReadStep() {
FlxAnimStep result;
2023-09-08 05:38:09 -04:00
result.Hash = Decoder.read_uint64();
result.Body = Decoder.read_string_view();
return result;
}
2023-09-15 13:28:18 -04:00
FlxAnimField FlxAnimStepDecoder::ReadField() {
FlxAnimField result;
2023-09-08 05:38:09 -04:00
result.Name = Decoder.read_string_view();
result.Persistent = Decoder.read_bool();
2023-09-15 13:28:18 -04:00
result.Type = (ElxAnimValueType)Decoder.read_uint8();
2023-09-08 05:38:09 -04:00
switch (result.Type) {
case ElxAnimValueType::STRING: {
2023-09-08 05:38:09 -04:00
result.S = Decoder.read_string_view();
break;
}
case ElxAnimValueType::NUMBER: {
2023-09-08 05:38:09 -04:00
result.X = Decoder.read_double();
break;
}
case ElxAnimValueType::BOOLEAN: {
2023-09-08 05:38:09 -04:00
result.X = Decoder.read_bool() ? 1.0 : 0.0;
break;
}
case ElxAnimValueType::XYZ: {
2023-09-08 05:38:09 -04:00
result.X = Decoder.read_double();
result.Y = Decoder.read_double();
result.Z = Decoder.read_double();
break;
}
default: {
Decoder.set_at_eof();
result.Type = ElxAnimValueType::BOOLEAN;
2023-09-08 05:38:09 -04:00
result.X = 0;
break;
}
}
return result;
}
2023-09-15 13:28:18 -04:00
FString FlxAnimQueueDecoder::DebugString(std::string_view queue) {
FString result;
2023-09-15 13:28:18 -04:00
FlxAnimQueueDecoder decoder(queue);
while (!decoder.AtEOF()) {
2023-09-15 13:28:18 -04:00
FlxAnimStep step = decoder.ReadStep();
FString stepdebug = FlxAnimStepDecoder::DebugString(step);
result.Appendf(TEXT("%s\n"), *stepdebug);
}
return result;
2023-09-08 05:38:09 -04:00
}
2023-09-15 13:28:18 -04:00
FString FlxAnimStepDecoder::DebugString(const FlxAnimStep& step) {
FString result;
2023-09-15 13:28:18 -04:00
FlxAnimStepDecoder decoder(step);
bool first = true;
while (!decoder.AtEOF()) {
2023-09-15 13:28:18 -04:00
FlxAnimField field = decoder.ReadField();
if (!first) {
result.Append(TEXT(" "));
}
result.Append(FString(field.Name.size(), (const UTF8CHAR*)field.Name.data()));
result.Append(field.Persistent ? TEXT("=") : TEXT(":"));
switch (field.Type) {
case ElxAnimValueType::STRING:
result.Append(FString(field.S.size(), (const UTF8CHAR*)field.S.data()));
break;
case ElxAnimValueType::NUMBER:
result.Appendf(TEXT("%lf"), field.X);
break;
case ElxAnimValueType::BOOLEAN:
result.Append((field.X) == 1.0 ? TEXT("true") : TEXT("false"));
break;
case ElxAnimValueType::XYZ:
result.Appendf(TEXT("%lf,%lf,%lf"), field.X, field.Y, field.Z);
break;
}
first = false;
}
return result;
2023-09-08 05:38:09 -04:00
}
2023-09-15 00:01:41 -04:00
2023-09-15 13:28:18 -04:00
FlxAnimTracker::FlxAnimTracker() {
2023-09-15 00:01:41 -04:00
AQ.Empty();
FirstSeqno = 0;
HashToSeqno.Empty();
UnstartedSeqno = 0;
PlaybackMode = ElxAnimPlaybackMode::INVALID;
2023-09-15 00:01:41 -04:00
AbortedHashes.Empty();
}
2023-09-15 13:28:18 -04:00
void FlxAnimTracker::Update(std::string_view encqueue) {
2023-09-15 00:01:41 -04:00
// Check for an exact match. If the most recent entry
// in encqueue has the same hash as the most recent
// entry in AQ, then that's an exact match. Or,
// if both are empty, that's also an exact match.
// In case of exact match, abort early, there's no
// further work needed.
//
if (encqueue.empty()) {
if (AQ.IsEmpty()) {
return;
}
} else {
if (!AQ.IsEmpty()) {
2023-09-15 13:28:18 -04:00
FlxStringDecoder qdecoder(encqueue);
2023-09-15 00:01:41 -04:00
uint64 hash = qdecoder.read_uint64();
if (hash == AQ.Last().Hash) {
return;
}
}
}
// Search for a Luprex animation record that matches
// something that we already have. Yields the sequence
// number of the most recent matching record.
//
// At the same time, push all non-matching records
// after the matching record onto a stack of new records.
//
2023-09-15 13:28:18 -04:00
FlxAnimQueueDecoder decoder(encqueue);
TArray<FlxAnimStep> newsteps;
2023-09-15 00:01:41 -04:00
int32 matchingseqno = -1;
while (!decoder.AtEOF()) {
2023-09-15 13:28:18 -04:00
FlxAnimStep step = decoder.ReadStep();
2023-09-15 00:01:41 -04:00
int32* stepseq = HashToSeqno.Find(step.Hash);
if (stepseq == nullptr) {
newsteps.Emplace(step);
} else {
matchingseqno = *stepseq;
break;
}
}
// Remove all animations after the most recent matching
// record. If we remove a 'started' animation, add that
// animation to the list of aborted animations.
//
int32 nremove = (FirstSeqno + AQ.Num()) - (matchingseqno + 1);
check((nremove >= 0) && (nremove <= AQ.Num()));
for (int32 i = 0; i < nremove; i++) {
2023-09-15 00:01:41 -04:00
uint64 lasthash = AQ.Last().Hash;
int32 seqno = FirstSeqno + AQ.Num() - 1;
2023-09-15 00:01:41 -04:00
HashToSeqno.Remove(lasthash);
if (seqno < UnstartedSeqno) {
AbortedHashes.Emplace(lasthash);
}
2023-09-15 00:01:41 -04:00
AQ.PopLast();
}
// If we aborted a 'started' animation, we have to fix
// up the Unstarted animation pointer.
//
// Note: this could leave the Unstarted pointer at
// seqno less than FirstSeqno. We will fix that state
// of affairs up later.
//
if (UnstartedSeqno > (FirstSeqno + AQ.Num())) {
UnstartedSeqno = matchingseqno;
PlaybackMode = ElxAnimPlaybackMode::BLEND_TO_FINAL;
}
2023-09-15 00:01:41 -04:00
// Transfer the new animations onto the queue.
//
while (!newsteps.IsEmpty()) {
2023-09-15 13:28:18 -04:00
FlxAnimStep step = newsteps.Pop();
2023-09-15 00:01:41 -04:00
int32 seqno = FirstSeqno + AQ.Num();
AQ.EmplaceLast(step.Hash, step.Body);
HashToSeqno.Emplace(step.Hash, seqno);
}
// If there are too many animations in AQ, discard
// any very old ones.
//
if (AQ.Num() > 10) {
int ndiscard = AQ.Num() - 10;
for (int i = 0; i < ndiscard; i++) {
uint64 hash = AQ.First().Hash;
HashToSeqno.Remove(hash);
AQ.PopFirst();
FirstSeqno += 1;
2023-09-15 00:01:41 -04:00
}
}
// If UnstartedSeqno is before the live range,
// then the whole queue has to be replayed. Don't
// do that: just fast skip to the end.
//
if (UnstartedSeqno <= FirstSeqno) {
if (AQ.Num() == 0) {
2023-09-15 00:01:41 -04:00
UnstartedSeqno = FirstSeqno;
} else {
UnstartedSeqno = FirstSeqno + AQ.Num() - 1;
2023-09-15 00:01:41 -04:00
}
PlaybackMode = ElxAnimPlaybackMode::WARP_TO_FINAL;
2023-09-15 00:01:41 -04:00
}
}
2023-09-15 13:28:18 -04:00
TArray<uint64> FlxAnimTracker::GetAborted() {
2023-09-15 00:01:41 -04:00
TArray<uint64> result;
result = std::move(AbortedHashes);
AbortedHashes.Empty();
return result;
}
ElxAnimPlaybackMode FlxAnimTracker::GetNextStep(FlxAnimStoredStep &step) {
2023-09-15 00:01:41 -04:00
int offset = UnstartedSeqno - FirstSeqno;
if (offset < AQ.Num()) {
step = AQ[offset];
return PlaybackMode;
} else {
step.Hash = 0;
step.Body = "";
return ElxAnimPlaybackMode::INVALID;
}
2023-09-15 00:01:41 -04:00
}
void FlxAnimTracker::StartedStep(uint64 hash) {
2023-09-15 00:01:41 -04:00
int offset = UnstartedSeqno - FirstSeqno;
check(offset < AQ.Num());
check(AQ[offset].Hash == hash);
UnstartedSeqno += 1;
PlaybackMode = ElxAnimPlaybackMode::START_ANIMATION;
2023-09-15 00:01:41 -04:00
}