Overhaul of tangible/blueprint animation interface
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
#include "AnimQueue.h"
|
||||
|
||||
FlxAnimationStep::FlxAnimationStep(uint64 hash, std::string_view body) {
|
||||
Finished = false;
|
||||
Hash = hash;
|
||||
Body.SetNum(body.size());
|
||||
memcpy(Body.GetData(), body.data(), body.size());
|
||||
Blueprint = UlxAnimationStepLibrary::AnimationStepGetString(*this, "bp");
|
||||
}
|
||||
|
||||
static bool ClearProperties(const FString& prefix, UObject* obj) {
|
||||
@@ -85,10 +87,9 @@ FString UlxAnimationStepLibrary::AnimationStepDebugString(const FlxAnimationStep
|
||||
return FlxAnimationStepDecoder::DebugString(step.Hash, body);
|
||||
}
|
||||
|
||||
void UlxAnimationStepLibrary::UnpackAnimationStep(const FlxAnimationStep& step,
|
||||
const FString& prefix, UObject* into) {
|
||||
void UlxAnimationStepLibrary::UnpackAnimationStep(UObject* into, const FlxAnimationStep& step, const FString& prefix) {
|
||||
step.Unpack(prefix, into, true);
|
||||
};
|
||||
}
|
||||
|
||||
static FlxAnimationField FindAnimationField(const FlxAnimationStep& step, const FString& name) {
|
||||
std::string_view body((const char*)(step.Body.GetData()), step.Body.Num());
|
||||
@@ -106,6 +107,10 @@ static FlxAnimationField FindAnimationField(const FlxAnimationStep& step, const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool UlxAnimationStepLibrary::AnimationStepIsIdle(const FlxAnimationStep &step) {
|
||||
return step.Finished;
|
||||
}
|
||||
|
||||
FVector UlxAnimationStepLibrary::AnimationStepGetVector(const FlxAnimationStep& step, const FString& name) {
|
||||
FlxAnimationField field = FindAnimationField(step, name);
|
||||
if (field.Type != ElxAnimValueType::XYZ) return FVector(0, 0, 0);
|
||||
@@ -138,6 +143,13 @@ FlxAnimationStepView FlxAnimQueueDecoder::ReadStep() {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64 FlxAnimQueueDecoder::PeekHash() {
|
||||
std::string_view rest = Decoder.GetRest();
|
||||
uint64 result = Decoder.read_uint64();
|
||||
Decoder.Reset(rest, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
FlxAnimationField FlxAnimationStepDecoder::ReadField() {
|
||||
FlxAnimationField result;
|
||||
result.Name = Decoder.read_string_view();
|
||||
@@ -199,6 +211,11 @@ FString FlxAnimationStepDecoder::DebugString(uint64 hash, std::string_view body)
|
||||
return result;
|
||||
}
|
||||
|
||||
FlxAnimQueueDecoder::FlxAnimQueueDecoder(std::string_view queue) : Decoder(queue) {
|
||||
SizeLimit = Decoder.read_uint8();
|
||||
ActualSize = Decoder.read_uint8();
|
||||
}
|
||||
|
||||
FString FlxAnimQueueDecoder::DebugString(std::string_view queue) {
|
||||
FString result;
|
||||
FlxAnimQueueDecoder decoder(queue);
|
||||
@@ -216,33 +233,50 @@ FlxAnimTracker::FlxAnimTracker() {
|
||||
|
||||
void FlxAnimTracker::Clear() {
|
||||
AQ.Empty();
|
||||
FirstSeqno = 0;
|
||||
HashToSeqno.Empty();
|
||||
UnstartedSeqno = 0;
|
||||
PlaybackMode = ElxAnimationMode::INVALID;
|
||||
AbortedHashes.Empty();
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
void FlxAnimTracker::FinishedAnimation(uint64 hash) {
|
||||
for (int i = 0; i < AQ.Num(); i++) {
|
||||
if (AQ[i].Hash == hash) {
|
||||
AQ[i].Finished = true;
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlxAnimTracker::SkipToEnd() {
|
||||
for (int i = 0; i < AQ.Num(); i++) {
|
||||
if (!AQ[i].Finished) {
|
||||
AQ[i].Finished = true;
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlxAnimTracker::Update(std::string_view encqueue) {
|
||||
// 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.
|
||||
check(!encqueue.empty());
|
||||
|
||||
// If the first hash matches, we don't bother updating at all.
|
||||
//
|
||||
if (encqueue.empty()) {
|
||||
if (AQ.IsEmpty()) {
|
||||
FlxAnimQueueDecoder decoder(encqueue);
|
||||
if (!AQ.IsEmpty()) {
|
||||
if (decoder.PeekHash() == AQ.Last().Hash) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!AQ.IsEmpty()) {
|
||||
FlxStringDecoder qdecoder(encqueue);
|
||||
uint64 hash = qdecoder.read_uint64();
|
||||
if (hash == AQ.Last().Hash) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The Changed flag is set whenever there is any change to
|
||||
// the animation queue of any kind.
|
||||
//
|
||||
Changed = true;
|
||||
|
||||
// Build a map indexing the steps in AQ.
|
||||
//
|
||||
TMap<uint64, int32> HashToIndex;
|
||||
for (int i = 0; i < AQ.Num(); i++) {
|
||||
HashToIndex.Emplace(AQ[i].Hash, i);
|
||||
}
|
||||
|
||||
// Search for a Luprex animation record that matches
|
||||
@@ -252,108 +286,55 @@ void FlxAnimTracker::Update(std::string_view encqueue) {
|
||||
// At the same time, push all non-matching records
|
||||
// after the matching record onto a stack of new records.
|
||||
//
|
||||
FlxAnimQueueDecoder decoder(encqueue);
|
||||
TArray<FlxAnimationStepView> newsteps;
|
||||
int32 matchingseqno = -1;
|
||||
int32 matchingindex = -1;
|
||||
while (!decoder.AtEOF()) {
|
||||
FlxAnimationStepView step = decoder.ReadStep();
|
||||
int32* stepseq = HashToSeqno.Find(step.Hash);
|
||||
if (stepseq == nullptr) {
|
||||
int32* indexp = HashToIndex.Find(step.Hash);
|
||||
if (indexp == nullptr) {
|
||||
newsteps.Emplace(step);
|
||||
} else {
|
||||
matchingseqno = *stepseq;
|
||||
matchingindex = *indexp;
|
||||
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.
|
||||
// record.
|
||||
//
|
||||
int32 nremove = (FirstSeqno + AQ.Num()) - (matchingseqno + 1);
|
||||
int32 nremove = (AQ.Num() - (matchingindex + 1));
|
||||
check((nremove >= 0) && (nremove <= AQ.Num()));
|
||||
for (int32 i = 0; i < nremove; i++) {
|
||||
uint64 lasthash = AQ.Last().Hash;
|
||||
int32 seqno = FirstSeqno + AQ.Num() - 1;
|
||||
HashToSeqno.Remove(lasthash);
|
||||
if (seqno < UnstartedSeqno) {
|
||||
AbortedHashes.Emplace(lasthash);
|
||||
}
|
||||
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 = ElxAnimationMode::BlendToFinal;
|
||||
}
|
||||
|
||||
// Transfer the new animations onto the queue.
|
||||
//
|
||||
while (!newsteps.IsEmpty()) {
|
||||
FlxAnimationStepView step = newsteps.Pop();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// TODO: this is hardwired to keep 10. Instead, we
|
||||
// should keep the number specified in the queue.
|
||||
//
|
||||
if (UnstartedSeqno <= FirstSeqno) {
|
||||
if (AQ.Num() == 0) {
|
||||
UnstartedSeqno = FirstSeqno;
|
||||
} else {
|
||||
UnstartedSeqno = FirstSeqno + AQ.Num() - 1;
|
||||
int limit = 10;
|
||||
int ndiscard = AQ.Num() - limit;
|
||||
if (ndiscard > 0) {
|
||||
for (int i = 0; i < ndiscard; i++) {
|
||||
AQ.PopFirst();
|
||||
}
|
||||
PlaybackMode = ElxAnimationMode::WarpToFinal;
|
||||
}
|
||||
}
|
||||
|
||||
TArray<uint64> FlxAnimTracker::GetAborted() {
|
||||
TArray<uint64> result;
|
||||
result = std::move(AbortedHashes);
|
||||
AbortedHashes.Empty();
|
||||
return result;
|
||||
}
|
||||
|
||||
ElxAnimationMode FlxAnimTracker::GetNextStep(FlxAnimationStep &step) {
|
||||
int offset = UnstartedSeqno - FirstSeqno;
|
||||
if (offset < AQ.Num()) {
|
||||
step = AQ[offset];
|
||||
return PlaybackMode;
|
||||
} else {
|
||||
step.Hash = 0;
|
||||
step.Body.Empty();
|
||||
return ElxAnimationMode::INVALID;
|
||||
FlxAnimationStep FlxAnimTracker::GetCurrentAnimation() {
|
||||
for (int i = 0; i < AQ.Num(); i++) {
|
||||
if (!AQ[i].Finished) {
|
||||
return AQ[i];
|
||||
}
|
||||
}
|
||||
return AQ.Last();
|
||||
}
|
||||
|
||||
void FlxAnimTracker::StartedStep(uint64 hash) {
|
||||
int offset = UnstartedSeqno - FirstSeqno;
|
||||
check(offset < AQ.Num());
|
||||
check(AQ[offset].Hash == hash);
|
||||
UnstartedSeqno += 1;
|
||||
PlaybackMode = ElxAnimationMode::StartAnimation;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user