Properly fix double interpolation for IQM models

This commit is contained in:
Ricardo Luís Vaz Silva 2024-09-13 16:56:07 -03:00
parent 1429e22441
commit 79dacdf1b7
6 changed files with 154 additions and 48 deletions

View file

@ -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);

View file

@ -97,6 +97,8 @@ public:
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; }

View file

@ -120,9 +120,13 @@ public:
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();

View file

@ -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 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<ModelAnimFramePrecalculatedIQM>(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<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::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::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, nullptr, animationData, bones, index);
}
else if(std::holds_alternative<ModelAnimFramePrecalculatedIQM>(from))
{
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 ModelAnimFramePrecalculatedIQM* precalculated, const TArray<TRS>* animationData, DBoneComponents* boneComponentData, int index)
{
const TArray<TRS>& animationFrames = animationData ? *animationData : TRSData;
if (Joints.Size() > 0)
@ -619,10 +698,17 @@ const TArray<VSMatrix> IQMModel::CalculateBonesIQM(int frame1, int frame2, float
{
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;

View file

@ -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<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
{
@ -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);

View file

@ -530,12 +530,16 @@ 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)
{
@ -549,24 +553,7 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
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;
}
evaluatedSingle = (smf_flags & MDL_MODELSAREATTACHMENTS) || is_decoupled;
}
mdl->RenderFrame(renderer, tex, modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : -1.f, translation, ssidp, boneData, boneStartingPosition);