Skip to content

Commit

Permalink
Properly fix double interpolation for IQM models
Browse files Browse the repository at this point in the history
  • Loading branch information
RicardoLuis0 committed Sep 13, 2024
1 parent 1429e22 commit 79dacdf
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 47 deletions.
4 changes: 4 additions & 0 deletions src/common/models/bonecomponents.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@ struct ModelAnim
static_assert(sizeof(ModelAnim) == sizeof(double) * 6);

using ModelAnimFrame = std::variant<std::nullptr_t, ModelAnimFrameInterp, ModelAnimFramePrecalculatedIQM>;

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);
2 changes: 2 additions & 0 deletions src/common/models/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ class FModel
virtual float getAspectFactor(float vscale) { return 1.f; }
virtual const TArray<TRS>* AttachAnimationData() { return nullptr; };

virtual ModelAnimFrame PrecalculateFrame(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, DBoneComponents* bones, int index) { return nullptr; };

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; }
Expand Down
6 changes: 5 additions & 1 deletion src/common/models/model_iqm.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,13 @@ class IQMModel : public FModel
void BuildVertexBuffer(FModelRenderer* renderer) override;
void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override;
const TArray<TRS>* AttachAnimationData() 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);

ModelAnimFrame PrecalculateFrame(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, DBoneComponents* bones, int index) override;
const TArray<VSMatrix> CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* 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<TRS>* animationData, DBoneComponents* bones, int index);
const TArray<VSMatrix> CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray<TRS>* animationData, DBoneComponents* bones, int index);

private:
void LoadGeometry();
void UnloadGeometry();
Expand Down
106 changes: 96 additions & 10 deletions src/common/models/models_iqm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,27 +562,106 @@ static TRS InterpolateBone(const TRS &from, const TRS &to, float t, float invt)

#include "printf.h"

const TArray<VSMatrix> IQMModel::CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, DBoneComponents* bones, int index)

ModelAnimFrame IQMModel::PrecalculateFrame(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)))
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<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 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<ModelAnimFramePrecalculatedIQM>(from))
{
return CalculateFrameIQM(0, to.frame2, inter, 0, -1.f, to.frame1, to.inter, &std::get<ModelAnimFramePrecalculatedIQM>(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<VSMatrix> IQMModel::CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* 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<ModelAnimFrameInterp>(from))
{
auto &from_interp = std::get<ModelAnimFrameInterp>(from);

return CalculateBonesIQM(from_interp.frame2, to.frame2, inter, from_interp.frame1, from_interp.inter, to.frame1, to.inter, animationData, bones, index);
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<ModelAnimFramePrecalculatedIQM>(from))
{ //TODO
return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, animationData, bones, index);
{
return CalculateBonesIQM(0, to.frame2, inter, 0, -1.f, to.frame1, to.inter, &std::get<ModelAnimFramePrecalculatedIQM>(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<TRS>* animationData, DBoneComponents* boneComponentData, int index)
{
ModelAnimFramePrecalculatedIQM out;
const TArray<TRS>& 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<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<VSMatrix> 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<TRS>* animationData, DBoneComponents* boneComponentData, int index)
{
const TArray<TRS>& animationFrames = animationData ? *animationData : TRSData;
if (Joints.Size() > 0)
Expand Down Expand Up @@ -619,9 +698,16 @@ const TArray<VSMatrix> IQMModel::CalculateBonesIQM(int frame1, int frame2, float
{
TRS prev;

if(frame1 >= 0 && (frame1_prev >= 0 || inter1_prev < 0))
if(precalculated)
{
prev = precalculated->precalcBones[i];
}
else
{
prev = inter1_prev <= 0 ? animationFrames[offset1 + i] : InterpolateBone(animationFrames[offset1_1 + i], animationFrames[offset1 + i], inter1_prev, invt1);
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;
Expand Down
43 changes: 33 additions & 10 deletions src/playsim/p_actionfunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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<TRS>* 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
{
Expand All @@ -5196,6 +5215,10 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s
calcFrame(self->modelData->curAnim, tic, std::get<ModelAnimFrameInterp>(self->modelData->prevAnim));
}
}
else
{
self->modelData->prevAnim = nullptr;
}

int animEnd = mdl->FindLastFrame(animName);

Expand Down
39 changes: 13 additions & 26 deletions src/r_data/models.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<TRS>* animationData = nullptr;

if (animationid >= 0)
{
FModel* animation = Models[animationid];
animation = Models[animationid];
const TArray<TRS>* animationData = animation->AttachAnimationData();
}

if(is_decoupled)
{
if(decoupled_frame.frame1 != -1)
{
boneData = animation->CalculateBones(actor->modelData->prevAnim, decoupled_frame, inter, animationData, actor->boneComponentData, i);
}
}
else
if(is_decoupled)
{
if(decoupled_frame.frame1 != -1)
{
boneData = animation->CalculateBones(nullptr, {nextFrame ? inter : -1.0f, modelframe, modelframenext}, -1.0f, animationData, actor->boneComponentData, i);
boneData = animation->CalculateBones(actor->modelData->prevAnim, decoupled_frame, inter, 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);
Expand Down

0 comments on commit 79dacdf

Please sign in to comment.