From 1429e224411ec139adfa2906bb0f0ec939f2ce64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Fri, 13 Sep 2024 15:09:57 -0300 Subject: [PATCH] rework interpolation to allow for precalculated frames --- src/common/engine/serializer.h | 4 +- src/common/models/bonecomponents.h | 36 ++++++++++++++ src/common/models/model.h | 5 +- src/common/models/model_iqm.h | 3 +- src/common/models/models_iqm.cpp | 24 +++++++++- src/playsim/actor.h | 22 +-------- src/playsim/p_actionfunctions.cpp | 36 +++++++------- src/playsim/p_mobj.cpp | 64 +++++++++++++++++++++++-- src/r_data/models.cpp | 76 ++++++++++++++---------------- 9 files changed, 186 insertions(+), 84 deletions(-) diff --git a/src/common/engine/serializer.h b/src/common/engine/serializer.h index ae25853d9..99ffb72dc 100644 --- a/src/common/engine/serializer.h +++ b/src/common/engine/serializer.h @@ -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); diff --git a/src/common/models/bonecomponents.h b/src/common/models/bonecomponents.h index 1e65389ea..556b28769 100644 --- a/src/common/models/bonecomponents.h +++ b/src/common/models/bonecomponents.h @@ -4,6 +4,8 @@ #include "TRS.h" #include "matrix.h" +#include + 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 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; diff --git a/src/common/models/model.h b/src/common/models/model.h index 7d5746e33..f25a3df1a 100644 --- a/src/common/models/model.h +++ b/src/common/models/model.h @@ -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* AttachAnimationData() { return nullptr; }; - virtual const TArray CalculateBones(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray* animationData, DBoneComponents* bones, int index) { return {}; }; + + virtual const TArray CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, DBoneComponents* bones, int index) { return {}; }; void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; } IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; } diff --git a/src/common/models/model_iqm.h b/src/common/models/model_iqm.h index 464f5c13a..c0c4916eb 100644 --- a/src/common/models/model_iqm.h +++ b/src/common/models/model_iqm.h @@ -120,7 +120,8 @@ public: void BuildVertexBuffer(FModelRenderer* renderer) override; void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override; const TArray* AttachAnimationData() override; - const TArray CalculateBones(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray* animationData, DBoneComponents* bones, int index) override; + const TArray CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray* animationData, DBoneComponents* bones, int index); + const TArray CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, DBoneComponents* bones, int index) override; private: void LoadGeometry(); diff --git a/src/common/models/models_iqm.cpp b/src/common/models/models_iqm.cpp index 891a19647..047d3226d 100644 --- a/src/common/models/models_iqm.cpp +++ b/src/common/models/models_iqm.cpp @@ -560,7 +560,29 @@ static TRS InterpolateBone(const TRS &from, const TRS &to, float t, float invt) return bone; } -const TArray IQMModel::CalculateBones(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray* animationData, DBoneComponents* boneComponentData, int index) +#include "printf.h" + +const TArray IQMModel::CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, DBoneComponents* bones, int index) +{ + if(inter <= 0 || !(std::holds_alternative(from) || std::holds_alternative(from))) + { + return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, animationData, bones, index); + } + else if(std::holds_alternative(from)) + { + auto &from_interp = std::get(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(from)) + { //TODO + return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, animationData, bones, index); + } +} + +const TArray IQMModel::CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const TArray* animationData, DBoneComponents* boneComponentData, int index) { const TArray& animationFrames = animationData ? *animationData : TRSData; if (Joints.Size() > 0) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 37e9eb645..bbf2eaf98 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -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; diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index bfed28fcb..1b780a194 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -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(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) { diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 442f101f5..e8a6c247a 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -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(ao)) + { + FString tmp = "nullptr"; + arc("type", tmp); + } + else if(std::holds_alternative(ao)) + { + FString type = "interp"; + arc("type", type); + arc("inter", std::get(ao).inter); + arc("frame1", std::get(ao).frame1); + arc("frame2", std::get(ao).frame2); + } + else if(std::holds_alternative(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; } diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index 3c2ed56d7..9a710cd9a 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -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;