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
This commit is contained in:
Ricardo Luís Vaz Silva 2024-04-16 20:02:27 -03:00
parent f2072cec95
commit 7c93cfa97b
5 changed files with 47 additions and 35 deletions

View file

@ -709,7 +709,7 @@ struct AnimOverride
double startFrame; double startFrame;
int flags = ANIMOVERRIDE_NONE; int flags = ANIMOVERRIDE_NONE;
float framerate; 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 double switchTic; // when the animation was changed -- where to interpolate the switch from
}; };

View file

@ -5125,11 +5125,10 @@ enum ESetAnimationFlags
{ {
SAF_INSTANT = 1 << 0, SAF_INSTANT = 1 << 0,
SAF_LOOP = 1 << 1, SAF_LOOP = 1 << 1,
SAF_USEACTORROLL = 1 << 2, SAF_NOOVERRIDE = 1 << 2,
SAF_USEACTORPITCH = 1 << 3,
}; };
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"); 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"); 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; if(interpolateTics <= 0) interpolateTics = 1;
EnsureModelData(self); 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()); Printf("Could not find animation %s\n", animName.GetChars());
return; 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); int animEnd = mdl->FindLastFrame(animName);
if(framerate < 0) if(framerate < 0)
@ -5180,18 +5191,24 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
if(startFrame >= len) if(startFrame >= len)
{ {
self->modelData->curAnim.flags = ANIMOVERRIDE_NONE; 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; return;
} }
else if(loopFrame >= len) else if(loopFrame >= len)
{ {
self->modelData->curAnim.flags = ANIMOVERRIDE_NONE; 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; return;
} }
self->modelData->curAnim.firstFrame = animStart; 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.startFrame = startFrame < 0 ? animStart : animStart + startFrame;
self->modelData->curAnim.loopFrame = loopFrame < 0 ? animStart : animStart + loopFrame; self->modelData->curAnim.loopFrame = loopFrame < 0 ? animStart : animStart + loopFrame;
self->modelData->curAnim.flags = (flags&SAF_LOOP) ? ANIMOVERRIDE_LOOP : 0; self->modelData->curAnim.flags = (flags&SAF_LOOP) ? ANIMOVERRIDE_LOOP : 0;
@ -5208,14 +5225,14 @@ 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);
@ -5471,10 +5488,11 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimation, SetAnimationNative)
PARAM_FLOAT(framerate); PARAM_FLOAT(framerate);
PARAM_INT(startFrame); PARAM_INT(startFrame);
PARAM_INT(loopFrame); PARAM_INT(loopFrame);
PARAM_INT(endFrame);
PARAM_INT(interpolateTics); PARAM_INT(interpolateTics);
PARAM_INT(flags); PARAM_INT(flags);
SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, interpolateTics, flags, 1); SetAnimationInternal(self, animName, framerate, startFrame, loopFrame, endFrame, interpolateTics, flags, 1);
return 0; return 0;
} }
@ -5486,10 +5504,11 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationUI, SetAnimationUINative)
PARAM_FLOAT(framerate); PARAM_FLOAT(framerate);
PARAM_INT(startFrame); PARAM_INT(startFrame);
PARAM_INT(loopFrame); PARAM_INT(loopFrame);
PARAM_INT(endFrame);
PARAM_INT(interpolateTics); PARAM_INT(interpolateTics);
PARAM_INT(flags); 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; return 0;
} }

View file

@ -261,12 +261,19 @@ double getCurrentFrame(const AnimOverride &anim, double tic)
{ {
if(anim.framerate <= 0) return anim.startFrame; if(anim.framerate <= 0) return anim.startFrame;
double duration = double(anim.lastFrame - anim.firstFrame) / double(anim.framerate); // duration in seconds double frame = ((tic - anim.startTic) / GameTicRate) * anim.framerate; // position in frames
double startPos = double(anim.startFrame - anim.firstFrame) / double(anim.framerate);
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) 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; inter = frame - prev;
if(frame > anim.lastFrame) next = int(ceil(frame));
{
if(anim.flags & ANIMOVERRIDE_LOOP)
{
next = anim.loopFrame + (prev - anim.lastFrame);
}
else
{
inter = 0;
prev = next = anim.lastFrame;
}
}
else
{
next = int(ceil(frame));
}
} }
void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, FTranslationID translation, AActor* actor) void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, FTranslationID translation, AActor* actor)

View file

@ -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_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 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") 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 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") void SetAnimationFrameRate(double framerate);
native version("4.12") ui void SetAnimationFrameRateUI(double framerate); native version("4.12") ui void SetAnimationFrameRateUI(double framerate);

View file

@ -375,6 +375,7 @@ enum ESetAnimationFlags
{ {
SAF_INSTANT = 1 << 0, SAF_INSTANT = 1 << 0,
SAF_LOOP = 1 << 1, SAF_LOOP = 1 << 1,
SAF_NOOVERRIDE = 1 << 2,
}; };
// Change model flags // Change model flags