Support for StartAnimation,WarpToFinal,BlendToFinal

This commit is contained in:
2023-09-15 15:44:01 -04:00
parent cd3c82f2c4
commit fb65d23230
5 changed files with 124 additions and 78 deletions

Binary file not shown.

View File

@@ -14,19 +14,19 @@ FlxAnimField FlxAnimStepDecoder::ReadField() {
result.Persistent = Decoder.read_bool(); result.Persistent = Decoder.read_bool();
result.Type = (ElxAnimValueType)Decoder.read_uint8(); result.Type = (ElxAnimValueType)Decoder.read_uint8();
switch (result.Type) { switch (result.Type) {
case T_STRING: { case ElxAnimValueType::STRING: {
result.S = Decoder.read_string_view(); result.S = Decoder.read_string_view();
break; break;
} }
case T_NUMBER: { case ElxAnimValueType::NUMBER: {
result.X = Decoder.read_double(); result.X = Decoder.read_double();
break; break;
} }
case T_BOOLEAN: { case ElxAnimValueType::BOOLEAN: {
result.X = Decoder.read_bool() ? 1.0 : 0.0; result.X = Decoder.read_bool() ? 1.0 : 0.0;
break; break;
} }
case T_XYZ: { case ElxAnimValueType::XYZ: {
result.X = Decoder.read_double(); result.X = Decoder.read_double();
result.Y = Decoder.read_double(); result.Y = Decoder.read_double();
result.Z = Decoder.read_double(); result.Z = Decoder.read_double();
@@ -34,7 +34,7 @@ FlxAnimField FlxAnimStepDecoder::ReadField() {
} }
default: { default: {
Decoder.set_at_eof(); Decoder.set_at_eof();
result.Type = T_BOOLEAN; result.Type = ElxAnimValueType::BOOLEAN;
result.X = 0; result.X = 0;
break; break;
} }
@@ -65,16 +65,16 @@ FString FlxAnimStepDecoder::DebugString(const FlxAnimStep& step) {
result.Append(FString(field.Name.size(), (const UTF8CHAR*)field.Name.data())); result.Append(FString(field.Name.size(), (const UTF8CHAR*)field.Name.data()));
result.Append(field.Persistent ? TEXT("=") : TEXT(":")); result.Append(field.Persistent ? TEXT("=") : TEXT(":"));
switch (field.Type) { switch (field.Type) {
case ElxAnimValueType::T_STRING: case ElxAnimValueType::STRING:
result.Append(FString(field.S.size(), (const UTF8CHAR*)field.S.data())); result.Append(FString(field.S.size(), (const UTF8CHAR*)field.S.data()));
break; break;
case ElxAnimValueType::T_NUMBER: case ElxAnimValueType::NUMBER:
result.Appendf(TEXT("%lf"), field.X); result.Appendf(TEXT("%lf"), field.X);
break; break;
case ElxAnimValueType::T_BOOLEAN: case ElxAnimValueType::BOOLEAN:
result.Append((field.X) == 1.0 ? TEXT("true") : TEXT("false")); result.Append((field.X) == 1.0 ? TEXT("true") : TEXT("false"));
break; break;
case ElxAnimValueType::T_XYZ: case ElxAnimValueType::XYZ:
result.Appendf(TEXT("%lf,%lf,%lf"), field.X, field.Y, field.Z); result.Appendf(TEXT("%lf,%lf,%lf"), field.X, field.Y, field.Z);
break; break;
} }
@@ -84,33 +84,12 @@ FString FlxAnimStepDecoder::DebugString(const FlxAnimStep& step) {
} }
//// Our own copy of the animation queue. We only
//// store the hashes, not the steps. The First element
//// of the queue is the oldest item.
////
//TDeque<FlxAnimStoredStep> AQ;
//// The sequence number of the first item in AQ.
////
//int32 FirstSeqno;
//// Map from hash to sequence number.
////
//TMap<uint64, int32> HashToSeqno;
//// The sequence number of the first unstarted animation.
////
//int32 UnstartedSeqno;
//// Array of recently-aborted hash values.
//TArray<uint64> AbortedHashes;
FlxAnimTracker::FlxAnimTracker() { FlxAnimTracker::FlxAnimTracker() {
AQ.Empty(); AQ.Empty();
FirstSeqno = 0; FirstSeqno = 0;
HashToSeqno.Empty(); HashToSeqno.Empty();
UnstartedSeqno = 0; UnstartedSeqno = 0;
PlaybackMode = ElxAnimPlaybackMode::INVALID;
AbortedHashes.Empty(); AbortedHashes.Empty();
} }
@@ -157,18 +136,34 @@ void FlxAnimTracker::Update(std::string_view encqueue) {
} }
} }
// Abort all animations after the most recent matching // Remove all animations after the most recent matching
// record. Animations are aborted in most-recent-first order. // record. If we remove a 'started' animation, add that
// // animation to the list of aborted animations.
int32 nabort = (FirstSeqno + AQ.Num()) - (matchingseqno + 1); //
check((nabort >= 0) && (nabort <= AQ.Num())); int32 nremove = (FirstSeqno + AQ.Num()) - (matchingseqno + 1);
for (int32 i = 0; i < nabort; i++) { check((nremove >= 0) && (nremove <= AQ.Num()));
for (int32 i = 0; i < nremove; i++) {
uint64 lasthash = AQ.Last().Hash; uint64 lasthash = AQ.Last().Hash;
int32 seqno = FirstSeqno + AQ.Num() - 1;
HashToSeqno.Remove(lasthash); HashToSeqno.Remove(lasthash);
AbortedHashes.Emplace(lasthash); if (seqno < UnstartedSeqno) {
AbortedHashes.Emplace(lasthash);
}
AQ.PopLast(); 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;
}
// Transfer the new animations onto the queue. // Transfer the new animations onto the queue.
// //
while (!newsteps.IsEmpty()) { while (!newsteps.IsEmpty()) {
@@ -178,13 +173,6 @@ void FlxAnimTracker::Update(std::string_view encqueue) {
HashToSeqno.Emplace(step.Hash, seqno); HashToSeqno.Emplace(step.Hash, seqno);
} }
// Move back the Unstarted pointer, so that the
// unstarted range includes all the new animations.
//
if (UnstartedSeqno > matchingseqno + 1) {
UnstartedSeqno = matchingseqno + 1;
}
// If there are too many animations in AQ, discard // If there are too many animations in AQ, discard
// any very old ones. // any very old ones.
// //
@@ -194,11 +182,21 @@ void FlxAnimTracker::Update(std::string_view encqueue) {
uint64 hash = AQ.First().Hash; uint64 hash = AQ.First().Hash;
HashToSeqno.Remove(hash); HashToSeqno.Remove(hash);
AQ.PopFirst(); AQ.PopFirst();
FirstSeqno += 1;
} }
FirstSeqno += ndiscard; }
if (UnstartedSeqno < FirstSeqno) {
// 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) {
UnstartedSeqno = FirstSeqno; UnstartedSeqno = FirstSeqno;
} else {
UnstartedSeqno = FirstSeqno + AQ.Num() - 1;
} }
PlaybackMode = ElxAnimPlaybackMode::WARP_TO_FINAL;
} }
} }
@@ -209,21 +207,23 @@ TArray<uint64> FlxAnimTracker::GetAborted() {
return result; return result;
} }
bool FlxAnimTracker::AnyUnstarted() { ElxAnimPlaybackMode FlxAnimTracker::GetNextStep(FlxAnimStoredStep &step) {
int offset = UnstartedSeqno - FirstSeqno; int offset = UnstartedSeqno - FirstSeqno;
return (offset < AQ.Num()); if (offset < AQ.Num()) {
step = AQ[offset];
return PlaybackMode;
} else {
step.Hash = 0;
step.Body = "";
return ElxAnimPlaybackMode::INVALID;
}
} }
FlxAnimStoredStep FlxAnimTracker::GetUnstarted() { void FlxAnimTracker::StartedStep(uint64 hash) {
int offset = UnstartedSeqno - FirstSeqno;
check(offset < AQ.Num());
return AQ[offset];
}
void FlxAnimTracker::Started(uint64 hash) {
int offset = UnstartedSeqno - FirstSeqno; int offset = UnstartedSeqno - FirstSeqno;
check(offset < AQ.Num()); check(offset < AQ.Num());
check(AQ[offset].Hash == hash); check(AQ[offset].Hash == hash);
UnstartedSeqno += 1; UnstartedSeqno += 1;
PlaybackMode = ElxAnimPlaybackMode::START_ANIMATION;
} }

View File

@@ -10,12 +10,37 @@
// //
//////////////////////////////////////////////// ////////////////////////////////////////////////
enum ElxAnimValueType { enum class ElxAnimValueType {
T_STRING, STRING,
T_NUMBER, NUMBER,
T_BOOLEAN, BOOLEAN,
T_XYZ, XYZ,
T_UNINITIALIZED INVALID
};
////////////////////////////////////////////////
//
// Playback Modes
//
// There are three different ways to play an animation:
//
// 1. Start Animation. This is the normal way to play an animation.
//
// 2. Warp to Final. Skip the actual animation, and jump
// instantaneously to the final state of the animation.
//
// 3. Blend to Final. Skip the actual animation, and blend
// smoothly to the final state of the animation. If the current
// state is very far from the final state, then maybe jump instantaneously
// instead.
//
////////////////////////////////////////////////
enum class ElxAnimPlaybackMode {
START_ANIMATION,
WARP_TO_FINAL,
BLEND_TO_FINAL,
INVALID,
}; };
//////////////////////////////////////////////// ////////////////////////////////////////////////
@@ -167,7 +192,12 @@ public:
// //
int32 UnstartedSeqno; int32 UnstartedSeqno;
// Indicates whether the unstarted animation should be played or otherwise.
//
ElxAnimPlaybackMode PlaybackMode;
// Array of recently-aborted hash values. // Array of recently-aborted hash values.
//
TArray<uint64> AbortedHashes; TArray<uint64> AbortedHashes;
public: public:
@@ -191,19 +221,17 @@ public:
// //
TArray<uint64> GetAborted(); TArray<uint64> GetAborted();
// Return true if there are any unstarted animation steps.
//
bool AnyUnstarted();
// Get the next unstarted animation step. // Get the next unstarted animation step.
// //
// You may only call this if AnyUnstarted returns true. // Get the next animation step. Returns the next step and the
// playback mode. If the playback mode is INVALID then there is
// no next step to play
// //
FlxAnimStoredStep GetUnstarted(); ElxAnimPlaybackMode GetNextStep(FlxAnimStoredStep& step);
// Declare that an animation has been started. // Declare that an animation has been started.
// //
// After starting an animation, you should call this. // After starting an animation, you should call this.
// //
void Started(uint64 Hash); void StartedStep(uint64 Hash);
}; };

View File

@@ -127,13 +127,25 @@ void AIntegrationGameModeBase::UpdateTangibles() {
for (uint64 hash : aborted) { for (uint64 hash : aborted) {
IlxTangibleInterface::Execute_AbortAnimation(t->Actor, hash); IlxTangibleInterface::Execute_AbortAnimation(t->Actor, hash);
} }
FlxAnimStoredStep step;
if (t->AnimTracker.AnyUnstarted()) { ElxAnimPlaybackMode mode = t->AnimTracker.GetNextStep(step);
FlxAnimStoredStep step = t->AnimTracker.GetUnstarted(); bool started = false;
bool started = IlxTangibleInterface::Execute_StartAnimation(t->Actor, step.Hash, step.Body.size()); switch (mode) {
if (started) { case ElxAnimPlaybackMode::INVALID:
t->AnimTracker.Started(step.Hash); started = false; // Nothing to do.
} break;
case ElxAnimPlaybackMode::WARP_TO_FINAL:
started = IlxTangibleInterface::Execute_WarpToFinal(t->Actor, step.Hash, step.Body.size());
break;
case ElxAnimPlaybackMode::BLEND_TO_FINAL:
started = IlxTangibleInterface::Execute_BlendToFinal(t->Actor, step.Hash, step.Body.size());
break;
case ElxAnimPlaybackMode::START_ANIMATION:
started = IlxTangibleInterface::Execute_StartAnimation(t->Actor, step.Hash, step.Body.size());
break;
}
if (started) {
t->AnimTracker.StartedStep(step.Hash);
} }
} }
} }

View File

@@ -25,6 +25,12 @@ public:
UFUNCTION(BlueprintImplementableEvent, Category = "Tangible Functionality") UFUNCTION(BlueprintImplementableEvent, Category = "Tangible Functionality")
bool StartAnimation(int64 hash, int StrLen); bool StartAnimation(int64 hash, int StrLen);
UFUNCTION(BlueprintImplementableEvent, Category = "Tangible Functionality")
bool WarpToFinal(int64 hash, int StrLen);
UFUNCTION(BlueprintImplementableEvent, Category = "Tangible Functionality")
bool BlendToFinal(int64 hash, int StrLen);
UFUNCTION(BlueprintImplementableEvent, Category = "Tangible Functionality") UFUNCTION(BlueprintImplementableEvent, Category = "Tangible Functionality")
bool AbortAnimation(int64 hash); bool AbortAnimation(int64 hash);
}; };