From 79dacdf1b73e10147f6a2c629dbc13b410ee0f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Fri, 13 Sep 2024 16:56:07 -0300 Subject: [PATCH] Properly fix double interpolation for IQM models --- src/common/models/bonecomponents.h | 4 ++ src/common/models/model.h | 2 + src/common/models/model_iqm.h | 6 +- src/common/models/models_iqm.cpp | 108 ++++++++++++++++++++++++++--- src/playsim/p_actionfunctions.cpp | 43 +++++++++--- src/r_data/models.cpp | 39 ++++------- 6 files changed, 154 insertions(+), 48 deletions(-) diff --git a/src/common/models/bonecomponents.h b/src/common/models/bonecomponents.h index 556b28769..313b0832e 100644 --- a/src/common/models/bonecomponents.h +++ b/src/common/models/bonecomponents.h @@ -50,3 +50,7 @@ struct ModelAnim static_assert(sizeof(ModelAnim) == sizeof(double) * 6); using ModelAnimFrame = std::variant; + +double getCurrentFrame(const ModelAnim &anim, double tic, bool *looped); +void calcFrame(const ModelAnim &anim, double tic, ModelAnimFrameInterp &inter); +void calcFrames(const ModelAnim &curAnim, double tic, ModelAnimFrameInterp &to, float &inter); diff --git a/src/common/models/model.h b/src/common/models/model.h index f25a3df1a..2be1f54cc 100644 --- a/src/common/models/model.h +++ b/src/common/models/model.h @@ -97,6 +97,8 @@ public: virtual float getAspectFactor(float vscale) { return 1.f; } virtual const TArray* AttachAnimationData() { return nullptr; }; + virtual ModelAnimFrame PrecalculateFrame(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, DBoneComponents* bones, int index) { return nullptr; }; + 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; } diff --git a/src/common/models/model_iqm.h b/src/common/models/model_iqm.h index c0c4916eb..c16e8372a 100644 --- a/src/common/models/model_iqm.h +++ b/src/common/models/model_iqm.h @@ -120,9 +120,13 @@ public: void BuildVertexBuffer(FModelRenderer* renderer) override; void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override; const TArray* AttachAnimationData() 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); + + ModelAnimFrame PrecalculateFrame(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, DBoneComponents* bones, int index) override; const TArray CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, DBoneComponents* bones, int index) override; + ModelAnimFramePrecalculatedIQM CalculateFrameIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray* animationData, DBoneComponents* bones, int index); + const TArray CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray* animationData, DBoneComponents* bones, int index); + private: void LoadGeometry(); void UnloadGeometry(); diff --git a/src/common/models/models_iqm.cpp b/src/common/models/models_iqm.cpp index 047d3226d..1384debce 100644 --- a/src/common/models/models_iqm.cpp +++ b/src/common/models/models_iqm.cpp @@ -562,27 +562,106 @@ static TRS InterpolateBone(const TRS &from, const TRS &to, float t, float invt) #include "printf.h" -const TArray IQMModel::CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, DBoneComponents* bones, int index) + +ModelAnimFrame IQMModel::PrecalculateFrame(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))) + if(inter <= 0) { - return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, animationData, bones, index); + return CalculateFrameIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, nullptr, 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); + return CalculateFrameIQM(from_interp.frame2, to.frame2, inter, from_interp.frame1, from_interp.inter, to.frame1, to.inter, nullptr, 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); + { + return CalculateFrameIQM(0, to.frame2, inter, 0, -1.f, to.frame1, to.inter, &std::get(from), animationData, bones, index); + } + else + { + return CalculateFrameIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, nullptr, 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 IQMModel::CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, DBoneComponents* bones, int index) +{ + if(inter <= 0) + { + return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, nullptr, animationData, bones, index); + } + else if(std::holds_alternative(from)) + { + auto &from_interp = std::get(from); + + return CalculateBonesIQM(from_interp.frame2, to.frame2, inter, from_interp.frame1, from_interp.inter, to.frame1, to.inter, nullptr, animationData, bones, index); + } + else if(std::holds_alternative(from)) + { + return CalculateBonesIQM(0, to.frame2, inter, 0, -1.f, to.frame1, to.inter, &std::get(from), animationData, bones, index); + } + else + { + return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, nullptr, animationData, bones, index); + } +} + +ModelAnimFramePrecalculatedIQM IQMModel::CalculateFrameIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray* animationData, DBoneComponents* boneComponentData, int index) +{ + ModelAnimFramePrecalculatedIQM out; + const TArray& animationFrames = animationData ? *animationData : TRSData; + + out.precalcBones.Resize(Joints.Size()); + + if (Joints.Size() > 0) + { + int numbones = Joints.SSize(); + + int offset1 = frame1 * numbones; + int offset2 = frame2 * numbones; + + int offset1_1 = frame1_prev * numbones; + int offset2_1 = frame2_prev * numbones; + + float invt = 1.0f - inter; + float invt1 = 1.0f - inter1_prev; + float invt2 = 1.0f - inter2_prev; + + for (int i = 0; i < numbones; i++) + { + TRS prev; + + if(precalculated) + { + prev = precalculated->precalcBones[i]; + } + else + { + if(frame1 >= 0 && (frame1_prev >= 0 || inter1_prev < 0)) + { + prev = inter1_prev <= 0 ? animationFrames[offset1 + i] : InterpolateBone(animationFrames[offset1_1 + i], animationFrames[offset1 + i], inter1_prev, invt1); + } + } + + TRS next; + + if(frame2 >= 0 && (frame2_prev >= 0 || inter2_prev < 0)) + { + next = inter2_prev <= 0 ? animationFrames[offset2 + i] : InterpolateBone(animationFrames[offset2_1 + i], animationFrames[offset2 + i], inter2_prev, invt2); + } + + if(frame1 >= 0 || inter < 0) + { + out.precalcBones[i] = inter < 0 ? animationFrames[offset1 + i] : InterpolateBone(prev, next , inter, invt); + } + } + } + + return out; +} + +const TArray IQMModel::CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray* animationData, DBoneComponents* boneComponentData, int index) { const TArray& animationFrames = animationData ? *animationData : TRSData; if (Joints.Size() > 0) @@ -619,9 +698,16 @@ const TArray IQMModel::CalculateBonesIQM(int frame1, int frame2, float { TRS prev; - if(frame1 >= 0 && (frame1_prev >= 0 || inter1_prev < 0)) + if(precalculated) { - prev = inter1_prev <= 0 ? animationFrames[offset1 + i] : InterpolateBone(animationFrames[offset1_1 + i], animationFrames[offset1 + i], inter1_prev, invt1); + prev = precalculated->precalcBones[i]; + } + else + { + if(frame1 >= 0 && (frame1_prev >= 0 || inter1_prev < 0)) + { + prev = inter1_prev <= 0 ? animationFrames[offset1 + i] : InterpolateBone(animationFrames[offset1_1 + i], animationFrames[offset1 + i], inter1_prev, invt1); + } } TRS next; diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 1b780a194..ea7090c1d 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -5128,9 +5128,6 @@ enum ESetAnimationFlags SAF_NOOVERRIDE = 1 << 2, }; -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) { if(!self) ThrowAbortException(X_READ_NIL, "In function parameter self"); @@ -5177,17 +5174,39 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s return; } - - if(self->modelData->curAnim.startTic > tic) - { // force instant switch if interpolating - flags |= SAF_INSTANT; - } - if(!(flags & SAF_INSTANT)) { if(self->modelData->curAnim.startTic > tic) { - //TODO + ModelAnimFrameInterp to; + float inter; + + calcFrames(self->modelData->curAnim, tic, to, inter); + + const TArray* animationData = nullptr; + + int animationid = -1; + + const FSpriteModelFrame * smf = &BaseSpriteModelFrames[self->GetClass()]; + + if (self->modelData->animationIDs.Size() > 0 && self->modelData->animationIDs[0] >= 0) + { + animationid = self->modelData->animationIDs[0]; + } + else if(smf->modelsAmount > 0) + { + animationid = smf->animationIDs[0]; + } + + FModel* animation = mdl; + + if (animationid >= 0) + { + animation = Models[animationid]; + animationData = animation->AttachAnimationData(); + } + + self->modelData->prevAnim = animation->PrecalculateFrame(self->modelData->prevAnim, to, inter, animationData, self->boneComponentData, 0); } else { @@ -5196,6 +5215,10 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s calcFrame(self->modelData->curAnim, tic, std::get(self->modelData->prevAnim)); } } + else + { + self->modelData->prevAnim = nullptr; + } int animEnd = mdl->FindLastFrame(animName); diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index 9a710cd9a..41166a0e8 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -530,43 +530,30 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr } // [RL0] while per-model animations aren't done, DECOUPLEDANIMATIONS does the same as MODELSAREATTACHMENTS - if ((!(smf_flags & MDL_MODELSAREATTACHMENTS) && !is_decoupled) || !evaluatedSingle) + if(!evaluatedSingle) { + FModel* animation = mdl; + const TArray* animationData = nullptr; + if (animationid >= 0) { - FModel* animation = Models[animationid]; + animation = Models[animationid]; const TArray* animationData = animation->AttachAnimationData(); + } - if(is_decoupled) + if(is_decoupled) + { + if(decoupled_frame.frame1 != -1) { - if(decoupled_frame.frame1 != -1) - { - boneData = animation->CalculateBones(actor->modelData->prevAnim, decoupled_frame, inter, animationData, actor->boneComponentData, i); - } + boneData = animation->CalculateBones(actor->modelData->prevAnim, decoupled_frame, inter, animationData, actor->boneComponentData, i); } - else - { - 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; } else { - if(is_decoupled) - { - if(decoupled_frame.frame1 != -1) - { - boneData = mdl->CalculateBones(actor->modelData->prevAnim, decoupled_frame, inter, nullptr, actor->boneComponentData, i); - } - } - else - { - 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; + 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 = (smf_flags & MDL_MODELSAREATTACHMENTS) || is_decoupled; } mdl->RenderFrame(renderer, tex, modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : -1.f, translation, ssidp, boneData, boneStartingPosition);