Decoupled IQM Model Animations

This commit is contained in:
Ricardo Luís Vaz Silva 2023-11-20 18:54:49 -03:00 committed by Rachael Alexanderson
commit 5d5e7ca042
14 changed files with 515 additions and 75 deletions

View file

@ -240,7 +240,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d
FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def);
FSerializer &Serialize(FSerializer &arc, const char *key, FString &sid, FString *def);
FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &sid, NumericValue *def);
FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &sid, struct ModelOverride *def);
FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &mo, struct ModelOverride *def);
FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimOverride &ao, struct AnimOverride *def);
FSerializer& Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p);

View file

@ -47,6 +47,7 @@
TArray<FString> savedModelFiles;
TDeletingArray<FModel*> Models;
TArray<FSpriteModelFrame> SpriteModelFrames;
TMap<void*, FSpriteModelFrame> BaseSpriteModelFrames;
/////////////////////////////////////////////////////////////////////////////

View file

@ -6,6 +6,8 @@
#include "matrix.h"
#include "palettecontainer.h"
#include "TRS.h"
#include "tarray.h"
#include "name.h"
class DBoneComponents;
class FModelRenderer;
@ -19,6 +21,7 @@ void FlushModels();
extern TArray<FString> savedModelFiles;
extern TDeletingArray<FModel*> Models;
extern TArray<FSpriteModelFrame> SpriteModelFrames;
extern TMap<void*, FSpriteModelFrame> BaseSpriteModelFrames;
#define MD3_MAX_SURFACES 32
#define MIN_MODELS 4
@ -72,13 +75,20 @@ public:
virtual ~FModel();
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0;
virtual int FindFrame(const char * name, bool nodefault = false) = 0;
// [RL0] these are used for decoupled iqm animations
virtual int FindFirstFrame(FName name) { return FErr_NotFound; }
virtual int FindLastFrame(FName name) { return FErr_NotFound; }
virtual double FindFramerate(FName name) { return FErr_NotFound; }
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, FTranslationID translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData, int boneStartPosition) = 0;
virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0;
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, double inter, const TArray<TRS>* animationData, DBoneComponents* bones, int index) { return {}; };
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 {}; };
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }
@ -86,7 +96,8 @@ public:
bool hasSurfaces = false;
FString mFileName;
FSpriteModelFrame *baseFrame;
private:
IModelVertexBuffer *mVBuf[NumModelRendererTypes];
};

View file

@ -6,6 +6,7 @@
#include "matrix.h"
#include "common/rendering/i_modelvertexbuffer.h"
#include "m_swap.h"
#include "name.h"
class DBoneComponents;
@ -112,11 +113,14 @@ public:
bool Load(const char* fn, int lumpnum, const char* buffer, int length) override;
int FindFrame(const char* name, bool nodefault) override;
int FindFirstFrame(FName name) override;
int FindLastFrame(FName name) override;
double FindFramerate(FName name) override;
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, FTranslationID translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData, int boneStartPosition) override;
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, double inter, const TArray<TRS>* animationData, DBoneComponents* bones, int index) 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;
private:
void LoadGeometry();
@ -129,6 +133,9 @@ private:
void LoadBlendWeights(IQMFileReader& reader, const IQMVertexArray& vertexArray);
int mLumpNum = -1;
TMap<FName, int> NamedAnimations;
TArray<IQMMesh> Meshes;
TArray<IQMTriangle> Triangles;
TArray<IQMAdjacency> Adjacency;

View file

@ -137,9 +137,12 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
}
reader.SeekTo(ofs_anims);
for (IQMAnim& anim : Anims)
for(int i = 0; i < Anims.Size(); i++)
{
IQMAnim& anim = Anims[i];
anim.Name = reader.ReadName(text);
NamedAnimations.Insert(anim.Name, i);
anim.FirstFrame = reader.ReadUInt32();
anim.NumFrames = reader.ReadUInt32();
anim.Framerate = reader.ReadFloat();
@ -445,6 +448,27 @@ int IQMModel::FindFrame(const char* name, bool nodefault)
return FErr_NotFound;
}
int IQMModel::FindFirstFrame(FName name)
{
int * nam = NamedAnimations.CheckKey(name);
if(nam) return Anims[*nam].FirstFrame;
return FErr_NotFound;
}
int IQMModel::FindLastFrame(FName name)
{
int * nam = NamedAnimations.CheckKey(name);
if(nam) return Anims[*nam].FirstFrame + Anims[*nam].NumFrames;
return FErr_NotFound;
}
double IQMModel::FindFramerate(FName name)
{
int * nam = NamedAnimations.CheckKey(name);
if(nam) return Anims[*nam].Framerate;
return FErr_NotFound;
}
void IQMModel::RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame1, int frame2, double inter, FTranslationID translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData, int boneStartPosition)
{
renderer->SetupFrame(this, 0, 0, NumVertices, boneData, boneStartPosition);
@ -517,7 +541,26 @@ const TArray<TRS>* IQMModel::AttachAnimationData()
return &TRSData;
}
const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray<TRS>* animationData, DBoneComponents* boneComponentData, int index)
static TRS InterpolateBone(const TRS &from, const TRS &to, float t, float invt)
{
TRS bone;
bone.translation = from.translation * invt + to.translation * t;
bone.rotation = from.rotation * invt;
if ((bone.rotation | to.rotation * t) < 0)
{
bone.rotation.X *= -1; bone.rotation.Y *= -1; bone.rotation.Z *= -1; bone.rotation.W *= -1;
}
bone.rotation += to.rotation * t;
bone.rotation.MakeUnit();
bone.scaling = from.scaling * invt + to.scaling * t;
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)
{
const TArray<TRS>& animationFrames = animationData ? *animationData : TRSData;
if (Joints.Size() > 0)
@ -534,8 +577,13 @@ const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double i
int offset1 = frame1 * numbones;
int offset2 = frame2 * numbones;
float t = (float)inter;
float invt = 1.0f - t;
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;
float swapYZ[16] = { 0.0f };
swapYZ[0 + 0 * 4] = 1.0f;
@ -547,19 +595,26 @@ const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double i
TArray<bool> modifiedBone(numbones, true);
for (int i = 0; i < numbones; i++)
{
TRS bone;
TRS from = animationFrames[offset1 + i];
TRS to = animationFrames[offset2 + i];
TRS prev;
bone.translation = from.translation * invt + to.translation * t;
bone.rotation = from.rotation * invt;
if ((bone.rotation | to.rotation * t) < 0)
if(frame1 >= 0 && (frame1_prev >= 0 || inter1_prev < 0))
{
bone.rotation.X *= -1; bone.rotation.Y *= -1; bone.rotation.Z *= -1; bone.rotation.W *= -1;
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);
}
TRS bone;
if(frame1 >= 0 || inter < 0)
{
bone = inter < 0 ? animationFrames[offset1 + i] : InterpolateBone(prev, next , inter, invt);
}
bone.rotation += to.rotation * t;
bone.rotation.MakeUnit();
bone.scaling = from.scaling * invt + to.scaling * t;
if (Joints[i].Parent >= 0 && modifiedBone[Joints[i].Parent])
{

View file

@ -28,6 +28,7 @@ class FTextureID
public:
FTextureID() = default;
FTextureID(nullptr_t) : texnum(0) {}
bool isNull() const { return texnum == 0; }
bool isValid() const { return texnum > 0; }
bool Exists() const { return texnum >= 0; }