Skip to content

Commit

Permalink
rework interpolation to allow for precalculated frames
Browse files Browse the repository at this point in the history
  • Loading branch information
RicardoLuis0 committed Sep 13, 2024
1 parent b1318e4 commit a7b4492
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 82 deletions.
4 changes: 3 additions & 1 deletion src/common/engine/serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "palentry.h"
#include "name.h"
#include "dictionary.h"
#include "bonecomponents.h"

extern bool save_full;

Expand Down Expand Up @@ -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);
Expand Down
36 changes: 36 additions & 0 deletions src/common/models/bonecomponents.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "TRS.h"
#include "matrix.h"

#include <variant>


class DBoneComponents : public DObject
{
Expand All @@ -14,3 +16,37 @@ class DBoneComponents : public DObject

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>;
5 changes: 4 additions & 1 deletion src/common/models/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "tarray.h"
#include "name.h"

#include "bonecomponents.h"

class DBoneComponents;
class FModelRenderer;
class FGameTexture;
Expand Down Expand Up @@ -94,7 +96,8 @@ class FModel
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]; }
Expand Down
3 changes: 2 additions & 1 deletion src/common/models/model_iqm.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ 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> 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();
Expand Down
24 changes: 23 additions & 1 deletion src/common/models/models_iqm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Check warning on line 583 in src/common/models/models_iqm.cpp

View workflow job for this annotation

GitHub Actions / Visual Studio 2019 | Release

'IQMModel::CalculateBones': not all control paths return a value [D:\a\gzdoom\gzdoom\build\src\zdoom.vcxproj]

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)
Expand Down
22 changes: 2 additions & 20 deletions src/playsim/actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -753,8 +735,8 @@ class DActorModelData : public DObject
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;
Expand Down
36 changes: 19 additions & 17 deletions src/playsim/p_actionfunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
}

Expand All @@ -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;
Expand All @@ -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)
{
//TODO
}
else
{
self->modelData->prevAnim.flags &= ~ANIMOVERRIDE_LOOP;
self->modelData->prevAnim = ModelAnimFrameInterp{};

calcFrame(self->modelData->curAnim, tic, std::get<ModelAnimFrameInterp>(self->modelData->prevAnim));
}
}

Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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)
{
Expand Down
64 changes: 61 additions & 3 deletions src/playsim/p_mobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit a7b4492

Please sign in to comment.