rework interpolation to allow for precalculated frames

This commit is contained in:
Ricardo Luís Vaz Silva 2024-09-13 15:09:57 -03:00
parent b1318e4b22
commit 1429e22441
9 changed files with 186 additions and 84 deletions

View file

@ -9,6 +9,7 @@
#include "palentry.h"
#include "name.h"
#include "dictionary.h"
#include "bonecomponents.h"
extern bool save_full;
@ -247,7 +248,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FString &sid, FString
FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &sid, NumericValue *def);
FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &mo, struct ModelOverride *def);
FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimModelOverride &mo, struct AnimModelOverride *def);
FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimOverride &ao, struct AnimOverride *def);
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnim &ao, ModelAnim *def);
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnimFrame &ao, ModelAnimFrame *def);
FSerializer& Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p);

View file

@ -4,6 +4,8 @@
#include "TRS.h"
#include "matrix.h"
#include <variant>
class DBoneComponents : public DObject
{
@ -14,3 +16,37 @@ public:
DBoneComponents() = default;
};
struct ModelAnimFrameInterp
{
float inter = -1.0f;
int frame1 = -1;
int frame2 = -1;
};
struct ModelAnimFramePrecalculatedIQM
{
TArray<TRS> precalcBones;
};
enum EModelAnimFlags
{
MODELANIM_NONE = 1 << 0, // no animation
MODELANIM_LOOP = 1 << 1, // animation loops, otherwise it stays on the last frame once it ends
};
struct ModelAnim
{
int firstFrame = 0;
int lastFrame = 0;
int loopFrame = 0;
float framerate = 0;
double startFrame = 0;
int flags = MODELANIM_NONE;
double startTic = 0; // when the current animation started (changing framerates counts as restarting) (or when animation starts if interpolating from previous animation)
double switchOffset = 0; // when the animation was changed -- where to interpolate the switch from
};
static_assert(sizeof(ModelAnim) == sizeof(double) * 6);
using ModelAnimFrame = std::variant<std::nullptr_t, ModelAnimFrameInterp, ModelAnimFramePrecalculatedIQM>;

View file

@ -9,6 +9,8 @@
#include "tarray.h"
#include "name.h"
#include "bonecomponents.h"
class DBoneComponents;
class FModelRenderer;
class FGameTexture;
@ -94,7 +96,8 @@ public:
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) = 0;
virtual float getAspectFactor(float vscale) { return 1.f; }
virtual const TArray<TRS>* AttachAnimationData() { return nullptr; };
virtual const TArray<VSMatrix> CalculateBones(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray<TRS>* animationData, DBoneComponents* bones, int index) { return {}; };
virtual const TArray<VSMatrix> CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, DBoneComponents* bones, int index) { return {}; };
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }

View file

@ -120,7 +120,8 @@ public:
void BuildVertexBuffer(FModelRenderer* renderer) override;
void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override;
const TArray<TRS>* AttachAnimationData() override;
const TArray<VSMatrix> CalculateBones(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray<TRS>* animationData, DBoneComponents* bones, int index) override;
const TArray<VSMatrix> CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray<TRS>* animationData, DBoneComponents* bones, int index);
const TArray<VSMatrix> CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, DBoneComponents* bones, int index) override;
private:
void LoadGeometry();

View file

@ -560,7 +560,29 @@ static TRS InterpolateBone(const TRS &from, const TRS &to, float t, float invt)
return bone;
}
const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray<TRS>* animationData, DBoneComponents* boneComponentData, int index)
#include "printf.h"
const TArray<VSMatrix> IQMModel::CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, DBoneComponents* bones, int index)
{
if(inter <= 0 || !(std::holds_alternative<ModelAnimFrameInterp>(from) || std::holds_alternative<ModelAnimFramePrecalculatedIQM>(from)))
{
return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, animationData, bones, index);
}
else if(std::holds_alternative<ModelAnimFrameInterp>(from))
{
auto &from_interp = std::get<ModelAnimFrameInterp>(from);
Printf("CalculateBones({%d, %d, %f}, {%d, %d, %f}, %f)\n", from_interp.frame1, from_interp.frame2, from_interp.inter, to.frame1, to.frame2, to.inter, inter);
return CalculateBonesIQM(from_interp.frame2, to.frame2, inter, from_interp.frame1, from_interp.inter, to.frame1, to.inter, animationData, bones, index);
}
else if(std::holds_alternative<ModelAnimFramePrecalculatedIQM>(from))
{ //TODO
return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, animationData, bones, index);
}
}
const TArray<VSMatrix> IQMModel::CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray<TRS>* animationData, DBoneComponents* boneComponentData, int index)
{
const TArray<TRS>& animationFrames = animationData ? *animationData : TRSData;
if (Joints.Size() > 0)

View file

@ -700,24 +700,6 @@ enum EViewPosFlags // [MC] Flags for SetViewPos.
VPSF_ORTHOGRAPHIC = 1 << 4, // Use orthographic projection (hardware renderer only).
};
enum EAnimOverrideFlags
{
ANIMOVERRIDE_NONE = 1 << 0, // no animation
ANIMOVERRIDE_LOOP = 1 << 1, // animation loops, otherwise it stays on the last frame once it ends
};
struct AnimOverride
{
int firstFrame;
int lastFrame;
int loopFrame;
double startFrame;
int flags = ANIMOVERRIDE_NONE;
float framerate;
double startTic; // when the current animation started (changing framerates counts as restarting) (or when animation starts if interpolating from previous animation)
double switchOffset; // when the animation was changed -- where to interpolate the switch from
};
struct ModelOverride
{
int modelID;
@ -753,8 +735,8 @@ public:
int overrideFlagsSet;
int overrideFlagsClear;
AnimOverride curAnim;
AnimOverride prevAnim; // used for interpolation when switching anims
ModelAnim curAnim;
ModelAnimFrame prevAnim; // used for interpolation when switching anims
DActorModelData() = default;
virtual void Serialize(FSerializer& arc) override;

View file

@ -5128,7 +5128,8 @@ enum ESetAnimationFlags
SAF_NOOVERRIDE = 1 << 2,
};
extern double getCurrentFrame(const AnimOverride &anim, double tic, bool *looped);
double getCurrentFrame(const ModelAnim &anim, double tic, bool *looped);
void calcFrame(const ModelAnim &anim, double tic, ModelAnimFrameInterp &inter);
void SetAnimationInternal(AActor * self, FName animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags, double ticFrac)
{
@ -5150,7 +5151,7 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
if(animName == NAME_None)
{
self->modelData->curAnim.flags = ANIMOVERRIDE_NONE;
self->modelData->curAnim.flags = MODELANIM_NONE;
return;
}
@ -5165,12 +5166,12 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
int animStart = mdl->FindFirstFrame(animName);
if(animStart == FErr_NotFound)
{
self->modelData->curAnim.flags = ANIMOVERRIDE_NONE;
self->modelData->curAnim.flags = MODELANIM_NONE;
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)
if((flags & SAF_NOOVERRIDE) && self->modelData->curAnim.flags != MODELANIM_NONE && self->modelData->curAnim.firstFrame == animStart)
{
//same animation as current, skip setting it
return;
@ -5184,14 +5185,15 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
if(!(flags & SAF_INSTANT))
{
self->modelData->prevAnim = self->modelData->curAnim;
bool looped = false;
self->modelData->prevAnim.startFrame = getCurrentFrame(self->modelData->prevAnim, tic, &looped);
if(!looped)
if(self->modelData->curAnim.startTic > tic)
{
self->modelData->prevAnim.flags &= ~ANIMOVERRIDE_LOOP;
//TODO
}
else
{
self->modelData->prevAnim = ModelAnimFrameInterp{};
calcFrame(self->modelData->curAnim, tic, std::get<ModelAnimFrameInterp>(self->modelData->prevAnim));
}
}
@ -5206,19 +5208,19 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
if(startFrame >= len)
{
self->modelData->curAnim.flags = ANIMOVERRIDE_NONE;
self->modelData->curAnim.flags = MODELANIM_NONE;
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;
self->modelData->curAnim.flags = MODELANIM_NONE;
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;
self->modelData->curAnim.flags = MODELANIM_NONE;
Printf("frame %d (endFrame) is past the end of animation %s\n", endFrame, animName.GetChars());
return;
}
@ -5227,12 +5229,12 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
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;
self->modelData->curAnim.flags = (flags & SAF_LOOP) ? MODELANIM_LOOP : 0;
self->modelData->curAnim.framerate = (float)framerate;
if(!(flags & SAF_INSTANT))
{
int startTic = floor(tic) + interpolateTics;
int startTic = int(floor(tic)) + interpolateTics;
self->modelData->curAnim.startTic = startTic;
self->modelData->curAnim.switchOffset = startTic - tic;
}
@ -5264,7 +5266,7 @@ void SetAnimationFrameRateInternal(AActor * self, double framerate, double ticFr
EnsureModelData(self);
if(self->modelData->curAnim.flags & ANIMOVERRIDE_NONE) return;
if(self->modelData->curAnim.flags & MODELANIM_NONE) return;
if(framerate < 0)
{

View file

@ -1443,7 +1443,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, AnimModelOverride &amo
return arc;
}
FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimOverride &ao, struct AnimOverride *def)
FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelAnim &ao, struct ModelAnim *def)
{
arc.BeginObject(key);
arc("firstFrame", ao.firstFrame);
@ -1458,6 +1458,64 @@ FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimOverride &a
return arc;
}
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnimFrame &ao, ModelAnimFrame *def)
{
arc.BeginObject(key);
if(arc.isReading())
{
if(arc.HasKey("firstFrame"))
{ // legacy save, clear interpolation
ao = nullptr;
}
else
{
FString type = "nullptr";
arc("type", type);
if(type.Compare("nullptr") == 0)
{
ao = nullptr;
}
else if(type.Compare("interp") == 0)
{
ModelAnimFrameInterp tmp;
arc("inter", tmp.inter);
arc("frame1", tmp.frame1);
arc("frame2", tmp.frame2);
ao = tmp;
}
else if(type.Compare("precalcIQM") == 0)
{
//TODO, unreachable
ao = nullptr;
}
}
}
else // if(arc.isWriting())
{
if(std::holds_alternative<std::nullptr_t>(ao))
{
FString tmp = "nullptr";
arc("type", tmp);
}
else if(std::holds_alternative<ModelAnimFrameInterp>(ao))
{
FString type = "interp";
arc("type", type);
arc("inter", std::get<ModelAnimFrameInterp>(ao).inter);
arc("frame1", std::get<ModelAnimFrameInterp>(ao).frame1);
arc("frame2", std::get<ModelAnimFrameInterp>(ao).frame2);
}
else if(std::holds_alternative<ModelAnimFramePrecalculatedIQM>(ao))
{
//TODO
FString type = "nullptr";
arc("type", type);
}
}
arc.EndObject();
return arc;
}
void DActorModelData::Serialize(FSerializer& arc)
{
Super::Serialize(arc);
@ -3862,7 +3920,7 @@ void AActor::Tick ()
special2++;
}
if(flags9 & MF9_DECOUPLEDANIMATIONS && modelData && !(modelData->curAnim.flags & ANIMOVERRIDE_NONE))
if(flags9 & MF9_DECOUPLEDANIMATIONS && modelData && !(modelData->curAnim.flags & MODELANIM_NONE))
{
modelData->curAnim.startTic += 1;
}
@ -3915,7 +3973,7 @@ void AActor::Tick ()
special2++;
}
if(flags9 & MF9_DECOUPLEDANIMATIONS && modelData && !(modelData->curAnim.flags & ANIMOVERRIDE_NONE))
if(flags9 & MF9_DECOUPLEDANIMATIONS && modelData && !(modelData->curAnim.flags & MODELANIM_NONE))
{
modelData->curAnim.startTic += 1;
}

View file

@ -258,7 +258,7 @@ void RenderHUDModel(FModelRenderer *renderer, DPSprite *psp, FVector3 translatio
renderer->EndDrawHUDModel(playermo->RenderStyle, smf_flags);
}
double getCurrentFrame(const AnimOverride &anim, double tic, bool *looped)
double getCurrentFrame(const ModelAnim &anim, double tic, bool *looped)
{
if(anim.framerate <= 0) return anim.startFrame;
@ -266,7 +266,7 @@ double getCurrentFrame(const AnimOverride &anim, double tic, bool *looped)
double duration = double(anim.lastFrame) - anim.startFrame;
if((anim.flags & ANIMOVERRIDE_LOOP) && frame >= duration)
if((anim.flags & MODELANIM_LOOP) && frame >= duration)
{
if(looped) *looped = true;
frame = frame - duration;
@ -278,26 +278,37 @@ double getCurrentFrame(const AnimOverride &anim, double tic, bool *looped)
}
}
static void fixFrame(const AnimOverride &anim, double frame, double &inter, int &prev, int &next, bool looped)
void calcFrame(const ModelAnim &anim, double tic, ModelAnimFrameInterp &inter)
{
prev = int(floor(frame));
bool looped = false;
double frame = getCurrentFrame(anim, tic, &looped);
inter.frame1 = int(floor(frame));
inter.inter = frame - inter.frame1;
inter.frame2 = int(ceil(frame));
int startFrame = (looped ? anim.startFrame : anim.loopFrame);
if(prev < startFrame) prev = anim.lastFrame;
inter = frame - prev;
next = int(ceil(frame));
if(next > anim.lastFrame) next = startFrame;
if(inter.frame1 < startFrame) inter.frame1 = anim.lastFrame;
if(inter.frame2 > anim.lastFrame) inter.frame2 = startFrame;
}
static void calcFrame(const AnimOverride &anim, double tic, double &inter, int &prev, int &next)
void calcFrames(const ModelAnim &curAnim, double tic, ModelAnimFrameInterp &to, float &inter)
{
bool looped = false;
double frame = getCurrentFrame(anim, tic, &looped);
fixFrame(anim, frame, inter, prev, next, looped);
if(curAnim.startTic > tic)
{
inter = (tic - (curAnim.startTic - curAnim.switchOffset)) / curAnim.switchOffset;
calcFrame(curAnim, curAnim.startTic, to);
}
else
{
inter = -1.0f;
calcFrame(curAnim, tic, to);
}
}
void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, FTranslationID translation, AActor* actor)
@ -308,17 +319,11 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
int smf_flags = smf->getFlags(actor->modelData);
const FSpriteModelFrame * smfNext = nullptr;
double inter = 0.;
double inter_main = -1.f;
double inter_next = -1.f;
float inter = 0.;
bool is_decoupled = (actor->flags9 & MF9_DECOUPLEDANIMATIONS);
int decoupled_main_prev_frame = -1;
int decoupled_next_prev_frame = -1;
int decoupled_main_frame = -1;
int decoupled_next_frame = -1;
ModelAnimFrameInterp decoupled_frame;
// if prev_frame == -1: interpolate(main_frame, next_frame, inter), else: interpolate(interpolate(main_prev_frame, main_frame, inter_main), interpolate(next_prev_frame, next_frame, inter_next), inter)
// 4-way interpolation is needed to interpolate animation switches between animations that aren't 35hz
@ -326,24 +331,15 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
if(is_decoupled)
{
smfNext = smf = &BaseSpriteModelFrames[actor->GetClass()];
if(actor->modelData && !(actor->modelData->curAnim.flags & ANIMOVERRIDE_NONE))
if(actor->modelData && !(actor->modelData->curAnim.flags & MODELANIM_NONE))
{
double tic = actor->Level->totaltime;
if ((ConsoleState == c_up || ConsoleState == c_rising) && (menuactive == MENU_Off || menuactive == MENU_OnNoPause) && !actor->isFrozen())
{
tic += I_GetTimeFrac();
}
if(actor->modelData->curAnim.startTic > tic)
{
inter = (tic - (actor->modelData->curAnim.startTic - actor->modelData->curAnim.switchOffset)) / actor->modelData->curAnim.switchOffset;
fixFrame(actor->modelData->curAnim, actor->modelData->curAnim.startFrame, inter_next, decoupled_next_prev_frame, decoupled_next_frame, false);
fixFrame(actor->modelData->prevAnim, actor->modelData->prevAnim.startFrame, inter_main, decoupled_main_prev_frame, decoupled_main_frame, actor->modelData->prevAnim.flags & ANIMOVERRIDE_LOOP);
}
else
{
calcFrame(actor->modelData->curAnim, tic, inter, decoupled_main_frame, decoupled_next_frame);
}
calcFrames(actor->modelData->curAnim, tic, decoupled_frame, inter);
}
}
else if (gl_interpolate_model_frames && !(smf_flags & MDL_NOINTERPOLATION))
@ -543,14 +539,14 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
if(is_decoupled)
{
if(decoupled_main_frame != -1)
if(decoupled_frame.frame1 != -1)
{
boneData = animation->CalculateBones(decoupled_main_frame, decoupled_next_frame, inter, decoupled_main_prev_frame, inter_main, decoupled_next_prev_frame, inter_next, animationData, actor->boneComponentData, i);
boneData = animation->CalculateBones(actor->modelData->prevAnim, decoupled_frame, inter, animationData, actor->boneComponentData, i);
}
}
else
{
boneData = animation->CalculateBones(modelframe, modelframenext, nextFrame ? inter : -1.f, 0, -1.f, 0, -1.f, animationData, actor->boneComponentData, i);
boneData = animation->CalculateBones(nullptr, {nextFrame ? inter : -1.0f, modelframe, modelframenext}, -1.0f, animationData, actor->boneComponentData, i);
}
boneStartingPosition = renderer->SetupFrame(animation, 0, 0, 0, boneData, -1);
evaluatedSingle = true;
@ -559,14 +555,14 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
{
if(is_decoupled)
{
if(decoupled_main_frame != -1)
if(decoupled_frame.frame1 != -1)
{
boneData = mdl->CalculateBones(decoupled_main_frame, decoupled_next_frame, inter, decoupled_main_prev_frame, inter_main, decoupled_next_prev_frame, inter_next, nullptr, actor->boneComponentData, i);
boneData = mdl->CalculateBones(actor->modelData->prevAnim, decoupled_frame, inter, nullptr, actor->boneComponentData, i);
}
}
else
{
boneData = mdl->CalculateBones(modelframe, modelframenext, nextFrame ? inter : -1.f, 0, -1.f, 0, -1.f, nullptr, actor->boneComponentData, i);
boneData = mdl->CalculateBones(nullptr, {nextFrame ? inter : -1.0f, modelframe, modelframenext}, -1.0f, nullptr, actor->boneComponentData, i);
}
boneStartingPosition = renderer->SetupFrame(mdl, 0, 0, 0, boneData, -1);
evaluatedSingle = true;