From 099c6a8ce44b39b7a6000549aa649a3a2f85c9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Tue, 16 Apr 2024 20:02:27 -0300 Subject: [PATCH] Decoupled Animation fixes and improvements * fixes looping that uses `loopFrame` * adds `endFrame` * adds `SAF_NOOVERRIDE` * fixes crash on SetAnimation if a BaseFrame isn't defined --- src/playsim/actor.h | 2 +- src/playsim/p_actionfunctions.cpp | 45 +++++++++++++++++++-------- src/r_data/models.cpp | 32 +++++++------------ wadsrc/static/zscript/actors/actor.zs | 4 +-- wadsrc/static/zscript/constants.zs | 1 + 5 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index ee87461491c..8e562f12538 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -709,7 +709,7 @@ struct AnimOverride double startFrame; int flags = ANIMOVERRIDE_NONE; float framerate; - double startTic; // when the animation starts if interpolating from previous animation + double startTic; // when the current animation started (changing framerates counts as restarting) (or when animation starts if interpolating from previous animation) double switchTic; // when the animation was changed -- where to interpolate the switch from }; diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 5ebf88ac3e1..aa5489e0e3c 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -5125,11 +5125,10 @@ enum ESetAnimationFlags { SAF_INSTANT = 1 << 0, SAF_LOOP = 1 << 1, - SAF_USEACTORROLL = 1 << 2, - SAF_USEACTORPITCH = 1 << 3, + SAF_NOOVERRIDE = 1 << 2, }; -void SetAnimationInternal(AActor * self, FName animName, double framerate, int startFrame, int loopFrame, int interpolateTics, int flags, double ticFrac) +void SetAnimationInternal(AActor * self, FName animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags, double ticFrac) { if(!self) ThrowAbortException(X_READ_NIL, "In function parameter self"); @@ -5138,6 +5137,11 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s ThrowAbortException(X_OTHER, "Cannot set animation for non-decoupled actors"); } + if(!BaseSpriteModelFrames.CheckKey(self->GetClass())) + { + ThrowAbortException(X_OTHER, "Actor class is missing a MODELDEF definition or a MODELDEF BaseFrame"); + } + if(interpolateTics <= 0) interpolateTics = 1; EnsureModelData(self); @@ -5168,6 +5172,13 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s Printf("Could not find animation %s\n", animName.GetChars()); return; } + + if((flags & SAF_NOOVERRIDE) && self->modelData->curAnim.flags != ANIMOVERRIDE_NONE && self->modelData->curAnim.firstFrame == animStart) + { + //same animation as current, skip setting it + return; + } + int animEnd = mdl->FindLastFrame(animName); if(framerate < 0) @@ -5180,18 +5191,24 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s if(startFrame >= len) { self->modelData->curAnim.flags = ANIMOVERRIDE_NONE; - Printf("frame %d is past the end of animation %s\n", startFrame, animName.GetChars()); + Printf("frame %d (startFrame) is past the end of animation %s\n", startFrame, animName.GetChars()); return; } else if(loopFrame >= len) { self->modelData->curAnim.flags = ANIMOVERRIDE_NONE; - Printf("frame %d is past the end of animation %s\n", startFrame, animName.GetChars()); + Printf("frame %d (loopFrame) is past the end of animation %s\n", startFrame, animName.GetChars()); + return; + } + else if(endFrame >= len) + { + self->modelData->curAnim.flags = ANIMOVERRIDE_NONE; + Printf("frame %d (endFrame) is past the end of animation %s\n", endFrame, animName.GetChars()); return; } self->modelData->curAnim.firstFrame = animStart; - self->modelData->curAnim.lastFrame = animEnd - 1; + self->modelData->curAnim.lastFrame = endFrame < 0 ? animEnd - 1 : animStart + endFrame; self->modelData->curAnim.startFrame = startFrame < 0 ? animStart : animStart + startFrame; self->modelData->curAnim.loopFrame = loopFrame < 0 ? animStart : animStart + loopFrame; self->modelData->curAnim.flags = (flags&SAF_LOOP) ? ANIMOVERRIDE_LOOP : 0; @@ -5208,17 +5225,17 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s } } -void SetAnimationNative(AActor * self, int i_animName, double framerate, int startFrame, int loopFrame, int interpolateTics, int flags) +void SetAnimationNative(AActor * self, int i_animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags) { - SetAnimationInternal(self, FName(ENamedName(i_animName)), framerate, startFrame, loopFrame, interpolateTics, flags, 1); + SetAnimationInternal(self, FName(ENamedName(i_animName)), framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, 1); } -void SetAnimationUINative(AActor * self, int i_animName, double framerate, int startFrame, int loopFrame, int interpolateTics, int flags) +void SetAnimationUINative(AActor * self, int i_animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags) { - SetAnimationInternal(self, FName(ENamedName(i_animName)), framerate, startFrame, loopFrame, interpolateTics, flags, I_GetTimeFrac()); + SetAnimationInternal(self, FName(ENamedName(i_animName)), framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, I_GetTimeFrac()); } -extern double getCurrentFrame(const AnimOverride &anim, double tic); +extern double getCurrentFrame(const AnimOverride &anim, double tic, double *pos = nullptr); void SetAnimationFrameRateInternal(AActor * self, double framerate, double ticFrac) { @@ -5471,10 +5488,11 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimation, SetAnimationNative) PARAM_FLOAT(framerate); PARAM_INT(startFrame); PARAM_INT(loopFrame); + PARAM_INT(endFrame); PARAM_INT(interpolateTics); PARAM_INT(flags); - SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, interpolateTics, flags, 1); + SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, 1); return 0; } @@ -5486,10 +5504,11 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationUI, SetAnimationUINative) PARAM_FLOAT(framerate); PARAM_INT(startFrame); PARAM_INT(loopFrame); + PARAM_INT(endFrame); PARAM_INT(interpolateTics); PARAM_INT(flags); - SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, interpolateTics, flags, I_GetTimeFrac()); + SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, I_GetTimeFrac()); return 0; } diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index d2436fc6789..4e5d3685684 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -261,12 +261,19 @@ double getCurrentFrame(const AnimOverride &anim, double tic) { if(anim.framerate <= 0) return anim.startFrame; - double duration = double(anim.lastFrame - anim.firstFrame) / double(anim.framerate); // duration in seconds - double startPos = double(anim.startFrame - anim.firstFrame) / double(anim.framerate); + double frame = ((tic - anim.startTic) / GameTicRate) * anim.framerate; // position in frames - double pos = startPos + ((tic - anim.startTic) / GameTicRate); // position in seconds + double duration = double(anim.lastFrame) - anim.startFrame; - return (((anim.flags & ANIMOVERRIDE_LOOP) ? fmod(pos, duration) : min(pos, duration)) * anim.framerate) + anim.firstFrame; + if((anim.flags & ANIMOVERRIDE_LOOP) && frame >= duration) + { + frame = frame - duration; + return fmod(frame, anim.lastFrame - anim.loopFrame) + anim.loopFrame; + } + else + { + return min(frame, duration) + anim.startFrame; + } } static void calcFrame(const AnimOverride &anim, double tic, double &inter, int &prev, int &next) @@ -277,22 +284,7 @@ static void calcFrame(const AnimOverride &anim, double tic, double &inter, int & inter = frame - prev; - if(frame > anim.lastFrame) - { - if(anim.flags & ANIMOVERRIDE_LOOP) - { - next = anim.loopFrame + (prev - anim.lastFrame); - } - else - { - inter = 0; - prev = next = anim.lastFrame; - } - } - else - { - next = int(ceil(frame)); - } + next = int(ceil(frame)); } void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, FTranslationID translation, AActor* actor) diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index be9a905f587..72f441b10c1 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -1305,8 +1305,8 @@ class Actor : Thinker native native bool A_AttachLight(Name lightid, int type, Color lightcolor, int radius1, int radius2, int flags = 0, Vector3 ofs = (0,0,0), double param = 0, double spoti = 10, double spoto = 25, double spotp = 0); native bool A_RemoveLight(Name lightid); - native version("4.12") void SetAnimation(Name animName, double framerate = -1, int startFrame = -1, int loopFrame= -1, int interpolateTics = -1, int flags = 0); - native version("4.12") ui void SetAnimationUI(Name animName, double framerate = -1, int startFrame = -1, int loopFrame = -1, int interpolateTics = -1, int flags = 0); + native version("4.12") void SetAnimation(Name animName, double framerate = -1, int startFrame = -1, int loopFrame= -1, int endFrame = -1, int interpolateTics = -1, int flags = 0); + native version("4.12") ui void SetAnimationUI(Name animName, double framerate = -1, int startFrame = -1, int loopFrame = -1, int endFrame = -1, int interpolateTics = -1, int flags = 0); native version("4.12") void SetAnimationFrameRate(double framerate); native version("4.12") ui void SetAnimationFrameRateUI(double framerate); diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index 92120c36df2..e1176c649de 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -375,6 +375,7 @@ enum ESetAnimationFlags { SAF_INSTANT = 1 << 0, SAF_LOOP = 1 << 1, + SAF_NOOVERRIDE = 1 << 2, }; // Change model flags