Bone Getters Part 2/3, plus fixed warnings for MSVC

* add getters for frame poses

* fix missing joint in GetJointPose

* clean up models_iqm.cpp

* clean up usage of I_GetTimeFrac, split out matrix calculation into its own function

* clean up SetModelBoneRotationInternal

* clean up a few float <-> double and unsigned <-> signed warnings

* fix more warnings

* further clean up warnings

* split mode ObjectToWorldMatrix stuff

* initial work on bone getters, matrix hell

(the matrix/vec3 multiplications are probably wrong af, just gotta add more stuff 'till i can test it)

* clean up matrix math

* GetBone/TransformByBone

* fix GetBoneFramePose

* fix ObjectToWorldMatrix

* fix missing array resize

* raw matrix getters (for use with gutamatics/etc)

* reverse matrix mult order

* replace GetBoneLength/GetBoneDir with GetBoneBaseTRS

* fix GetBonePosition, remove GetBoneWorldMatrix as it's useless

* GetBonePosition

* deduplicate code

* rename GetBonePosition to GetBoneBasePosition to avoid confusion

* GetBoneBaseRotation

* GetBonePosition helper function

* forgot include_offsets
This commit is contained in:
Ricardo Luís Vaz Silva 2025-05-09 17:06:16 -03:00 committed by Nash Muhandes
commit d5f5c71d67
33 changed files with 1065 additions and 220 deletions

View file

@ -307,7 +307,7 @@ bool Wiper_Crossfade::Run(int ticks)
bool Wiper_Crossfade::RunInterpolated(double ticks)
{
Clock += ticks;
Clock += float(ticks);
DrawTexture(twod, startScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, TAG_DONE);
DrawTexture(twod, endScreen, 0, 0, DTA_FlipY, screen->RenderTextureIsFlipped(), DTA_Masked, false, DTA_Alpha, clamp(Clock / 32.f, 0.f, 1.f), TAG_DONE);
return Clock >= 32.;

View file

@ -157,6 +157,7 @@ public:
const char* GetHash() const { return Hash; }
int EntryCount() const { return NumLumps; }
uint32_t EntryCountU() const { return NumLumps; }
int FindEntry(const char* name);
size_t Length(uint32_t entry)

View file

@ -67,13 +67,13 @@ struct BoneOverrideComponent
void Modify(T &value, double tic) const
{
double lerp_amt = interplen > 0.0 ? std::clamp(((tic - switchtic) / interplen), 0.0, 1.0) : 1.0;
float lerp_amt = interplen > 0.0f ? std::clamp(float((tic - switchtic) / interplen), 0.0f, 1.0f) : 1.0f;
if(mode > 0 || (prev_mode > 0 && lerp_amt < 1.0))
{
T from = ModifyValue(value, prev, prev_mode);
T to = ModifyValue(value, cur, mode);
value = Lerp(from, to, lerp_amt, 1.0 - lerp_amt);
value = Lerp(from, to, lerp_amt, 1.0f - lerp_amt);
}
}
@ -126,9 +126,10 @@ struct BoneOverride
struct BoneInfo
{
TArray<TRS> bones_anim_only;
TArray<TRS> bones;
TArray<TRS> bones_with_override;
TArray<VSMatrix> positions;
TArray<VSMatrix> positions_with_override;
};
struct ModelAnim

View file

@ -17,7 +17,9 @@ class FGameTexture;
class IModelVertexBuffer;
class FModel;
class PClass;
class AActor;
struct FSpriteModelFrame;
struct FLevelLocals;
FTextureID LoadSkin(const char* path, const char* fn);
void FlushModels();
@ -59,6 +61,9 @@ public:
unsigned int getFlags(class DActorModelData * defs) const;
friend void InitModels();
friend void ParseModelDefLump(int Lump);
VSMatrix ObjectToWorldMatrix(AActor * actor, float x, float y, float z, double ticFrac);
VSMatrix ObjectToWorldMatrix(FLevelLocals *Level, DVector3 translation, DRotator rotation, DVector2 scaling, unsigned int flags, double tic);
};
@ -92,9 +97,12 @@ public:
virtual int FindJoint(FName name) { return -1; }
virtual int GetJointParent(int joint) { return -1; }
virtual double GetJointLength(int joint) { return 0.0; }
virtual FName GetJointName(int joint) { return NAME_None; }
virtual FVector3 GetJointDir(int joint) { return FVector3(0.0f,0.0f,0.0f); }
virtual FQuaternion GetJointRotation(int joint) { return FQuaternion(0.0f,0.0f,0.0f,1.0f); }
virtual FVector3 GetJointPosition(int joint) { return FVector3(0.0f,0.0f,0.0f); }
virtual TRS GetJointBaseTRS(int joint) { return {}; }
virtual TRS GetJointPose(int joint, int frame) { return {}; }
virtual int NumFrames() { return -1; }
virtual void GetJointChildren(int joint, TArray<int> &out) {}

View file

@ -73,6 +73,9 @@ struct IQMJoint
FVector3 Translate;
FQuaternion Quaternion;
FVector3 Scale;
FVector3 Position;
FQuaternion Rotation;
FVector3 Scaling;
};
struct IQMPose
@ -164,7 +167,7 @@ private:
TArray<VSMatrix> inversebaseframe;
TArray<TRS> TRSData;
public:
int NumJoints() override { return Joints.Size(); }
int NumJoints() override { return Joints.SSize(); }
int FindJoint(FName name) override
{
int *j = NamedJoints.CheckKey(name);
@ -174,12 +177,12 @@ public:
int GetJointParent(int joint) override
{
return (joint >= 0 && joint < Joints.Size()) ? Joints[joint].Parent : -1;
return (joint >= 0 && joint < Joints.SSize()) ? Joints[joint].Parent : -1;
}
FName GetJointName(int joint) override
{
return (joint >= 0 && joint < Joints.Size()) ? Joints[joint].Name : FName(NAME_None);
return (joint >= 0 && joint < Joints.SSize()) ? Joints[joint].Name : FName(NAME_None);
}
void GetRootJoints(TArray<int> &out) override
@ -189,20 +192,34 @@ public:
void GetJointChildren(int joint, TArray<int> &out) override
{
if(joint >= 0 && joint < Joints.Size())
if(joint >= 0 && joint < Joints.SSize())
{
out = Joints[joint].Children;
}
}
double GetJointLength(int joint) override
FQuaternion GetJointRotation(int joint) override
{
return (joint >= 0 && joint < Joints.Size()) ? Joints[joint].Translate.Length() : 0.0;
return (joint >= 0 && joint < Joints.SSize()) ? Joints[joint].Rotation : FQuaternion(0.0f,0.0f,0.0f,1.0f);
}
FVector3 GetJointDir(int joint) override
FVector3 GetJointPosition(int joint) override
{
return (joint >= 0 && joint < Joints.Size()) ? Joints[joint].Translate.Unit() : FVector3(0.0f,0.0f,0.0f);
return (joint >= 0 && joint < Joints.SSize()) ? Joints[joint].Position : FVector3(0.0f,0.0f,0.0f);
}
TRS GetJointBaseTRS(int joint) override
{
return (joint >= 0 && joint < Joints.SSize()) ? TRS{Joints[joint].Translate, Joints[joint].Quaternion, Joints[joint].Scale} : TRS{};
}
TRS GetJointPose(int joint, int frame) override
{
return (joint >= 0 && joint < Joints.SSize() && frame >= 0 && ((frame * Joints.SSize()) + joint) < TRSData.SSize()) ? TRSData[(frame * Joints.SSize()) + joint] : TRS{} ;
}
virtual int NumFrames()
{
return Joints.SSize() > 0 ? (TRSData.SSize() / Joints.SSize()) : 0;
}
};

View file

@ -108,7 +108,7 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
}
reader.SeekTo(ofs_joints);
for (int i = 0; i < Joints.Size(); i++)
for (int i = 0; i < Joints.SSize(); i++)
{
IQMJoint& joint = Joints[i];
@ -126,8 +126,6 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
joint.Translate.Y = reader.ReadFloat();
joint.Translate.Z = reader.ReadFloat();
int len = joint.Translate.Length();
joint.Quaternion.X = reader.ReadFloat();
joint.Quaternion.Y = reader.ReadFloat();
joint.Quaternion.Z = reader.ReadFloat();
@ -139,14 +137,24 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
if(joint.Parent < 0)
{
joint.Rotation = joint.Quaternion;
joint.Scaling = joint.Scale;
joint.Position = joint.Translate.ScaleXYZ(joint.Scaling);
RootJoints.Push(i);
}
else if(joint.Parent >= Joints.Size())
else if(joint.Parent >= i)
{
I_FatalError("Joint child comes before parent in IQM Model");
}
else if(joint.Parent >= Joints.SSize())
{
I_FatalError("Joint parent index out of bounds in IQM Model");
}
else
{
joint.Rotation = (Joints[joint.Parent].Rotation * joint.Quaternion).Unit();
joint.Scaling = joint.Scale.ScaleXYZ(Joints[joint.Parent].Scaling);
joint.Position = (Joints[joint.Parent].Rotation * joint.Translate.ScaleXYZ(joint.Scaling)) + Joints[joint.Parent].Position;
Joints[joint.Parent].Children.Push(i);
}
}
@ -618,11 +626,6 @@ const TArray<VSMatrix>* IQMModel::CalculateBones(const ModelAnimFrame &from, con
}
}
inline void ModifyBone(const BoneOverride& mod, TRS &bone, double time)
{
mod.Modify(bone, time);
}
// explicitly don't pass modelBoneOverrides when precalculating animation for interpolation, as it's applied _after_ animation
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)
{
@ -682,15 +685,16 @@ const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, floa
{
const TArray<TRS>& animationFrames = animationData ? *animationData : TRSData;
TArray<VSMatrix>* outMatrix = out ? &out->positions : &boneData;
TArray<VSMatrix>* outMatrix = out ? &out->positions_with_override : &boneData;
int numbones = Joints.SSize();
outMatrix->Resize(numbones);
if(out)
{
out->bones_anim_only.Resize(numbones);
out->bones.Resize(numbones);
out->bones_with_override.Resize(numbones);
out->positions.Resize(numbones);
}
if(in && in->size() != Joints.Size()) in = nullptr;
@ -701,11 +705,11 @@ const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, floa
frame1 = clamp(frame1, 0, (animationFrames.SSize() - 1) / numbones);
frame2 = clamp(frame2, 0, (animationFrames.SSize() - 1) / numbones);
int offset1 = frame1 * numbones;
int offset2 = frame2 * numbones;
unsigned int offset1 = frame1 * numbones;
unsigned int offset2 = frame2 * numbones;
int offset1_1 = frame1_prev * numbones;
int offset2_1 = frame2_prev * numbones;
unsigned int offset1_1 = frame1_prev * numbones;
unsigned int offset2_1 = frame2_prev * numbones;
float invt = 1.0f - inter;
float invt1 = 1.0f - inter1_prev;
@ -751,7 +755,7 @@ const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, floa
if(out)
{
out->bones_anim_only[i] = bone;
out->bones[i] = bone;
if(in)
{
@ -774,7 +778,6 @@ const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, floa
VSMatrix& result = (*outMatrix)[i];
if (Joints[i].Parent >= 0)
{
assert(Joints[i].Parent < i);
result = (*outMatrix)[Joints[i].Parent];
result.multMatrix(swapYZ);
result.multMatrix(baseframe[Joints[i].Parent]);
@ -788,6 +791,32 @@ const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, floa
result.multMatrix(inversebaseframe[i]);
}
result.multMatrix(swapYZ);
if(out)
{
VSMatrix m;
m.loadIdentity();
m.translate(out->bones[i].translation.X, out->bones[i].translation.Y, out->bones[i].translation.Z);
m.multQuaternion(out->bones[i].rotation);
m.scale(out->bones[i].scaling.X, out->bones[i].scaling.Y, out->bones[i].scaling.Z);
VSMatrix& result = out->positions[i];
if (Joints[i].Parent >= 0)
{
result = out->positions[Joints[i].Parent];
result.multMatrix(swapYZ);
result.multMatrix(baseframe[Joints[i].Parent]);
result.multMatrix(m);
result.multMatrix(inversebaseframe[i]);
}
else
{
result.loadMatrix(swapYZ);
result.multMatrix(m);
result.multMatrix(inversebaseframe[i]);
}
result.multMatrix(swapYZ);
}
}
return &boneData;

View file

@ -680,14 +680,14 @@ int PClass::FindVirtualIndex(FName name, PFunction::Variant *variant, PFunction
auto vproto = Virtuals[i]->Proto;
auto &vflags = Virtuals[i]->ArgFlags;
int n = flags.size();
int n = flags.SSize();
bool flagsOk = true;
for(int i = 0; i < n; i++)
{
int argA = i >= vflags.size() ? 0 : vflags[i];
int argB = i >= flags.size() ? 0 : flags[i];
int argA = i >= vflags.SSize() ? 0 : vflags[i];
int argB = i >= flags.SSize() ? 0 : flags[i];
bool AisRef = argA & (VARF_Out | VARF_Ref);
bool BisRef = argB & (VARF_Out | VARF_Ref);

View file

@ -102,14 +102,14 @@ namespace
float v_MinimumToFill2(uint32_t inwidth, uint32_t inheight)
{
// sx = screen x dimension, sy = same for y
float sx = (float)inwidth * 1.2, sy = (float)inheight;
float sx = (float)inwidth * 1.2f, sy = (float)inheight;
static float lastsx = 0., lastsy = 0., result = 0.;
if (lastsx != sx || lastsy != sy)
{
if (sx <= 0. || sy <= 0.)
return 1.; // prevent x/0 error
// set absolute minimum scale to fill the entire screen but get as close to 640x400 as possible
float ssx = (float)(VID_MIN_UI_WIDTH) / 1.2 / sx, ssy = (float)(VID_MIN_UI_HEIGHT) / sy;
float ssx = (float)(VID_MIN_UI_WIDTH) / 1.2f / sx, ssy = (float)(VID_MIN_UI_HEIGHT) / sy;
result = (ssx < ssy) ? ssy : ssx;
lastsx = sx;
lastsy = sy;
@ -165,7 +165,7 @@ namespace
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 1280; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 800; }, 1.2f, false }, // 4 - 1280x800
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return vid_scale_customwidth; }, [](uint32_t Width, uint32_t Height)->uint32_t { return vid_scale_customheight; }, 1.0f, true }, // 5 - Custom
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return 320; }, [](uint32_t Width, uint32_t Height)->uint32_t { return 200; }, 1.2f, false }, // 6 - 320x200
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillX2(Width, Height) * 1.2; }, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillY2(Width, Height); }, 1.2f, false }, // 7 - Minimum Scale to Fill Entire Screen (1.2)
{ true, [](uint32_t Width, uint32_t Height)->uint32_t { return uint32_t(v_mfillX2(Width, Height) * 1.2); }, [](uint32_t Width, uint32_t Height)->uint32_t { return v_mfillY2(Width, Height); }, 1.2f, false }, // 7 - Minimum Scale to Fill Entire Screen (1.2)
};
bool isOutOfBounds(int x)
{

View file

@ -49,6 +49,7 @@
#endif
#include "m_png.h"
#include "basics.h"
#include "printf.h"
// MACROS ------------------------------------------------------------------
@ -944,7 +945,13 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
y = height;
stream.next_out = buffer.data();
stream.avail_out = buffer.size();
if(buffer.size() > UINT_MAX)
{
I_Error("save png buffer too large");
}
stream.avail_out = (unsigned int) buffer.size();
temprow[0][0] = 0;
#if USE_FILTER_HEURISTIC
@ -1006,12 +1013,24 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
}
while (stream.avail_out == 0)
{
if (!WriteIDAT (file, buffer.data(), buffer.size()))
if(buffer.size() > INT_MAX)
{
I_Error("save png buffer too large");
}
int sz = (int) buffer.size();
if (!WriteIDAT (file, buffer.data(), sz))
{
return false;
}
stream.next_out = buffer.data();
stream.avail_out = buffer.size();
if(buffer.size() > UINT_MAX)
{
I_Error("save png buffer too large");
}
stream.avail_out = (unsigned int) buffer.size();
if (stream.avail_in != 0)
{
err = deflate (&stream, (y == 0) ? Z_FINISH : 0);
@ -1032,12 +1051,23 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
}
if (stream.avail_out == 0)
{
if (!WriteIDAT (file, buffer.data(), buffer.size()))
if(buffer.size() > INT_MAX)
{
I_Error("save png buffer too large");
}
int sz = (int) buffer.size();
if (!WriteIDAT (file, buffer.data(), sz))
{
return false;
}
stream.next_out = buffer.data();
stream.avail_out = buffer.size();
if(buffer.size() > UINT_MAX)
{
I_Error("save png buffer too large");
}
stream.avail_out = (unsigned int) buffer.size();
}
}
@ -1047,7 +1077,15 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height
{
return false;
}
return WriteIDAT (file, buffer.data(), buffer.size() - stream.avail_out);
if((buffer.size() - stream.avail_out) > INT_MAX)
{
I_Error("save png buffer too large");
}
int sz = (int) (buffer.size() - stream.avail_out);
return WriteIDAT (file, buffer.data(), sz);
}
//==========================================================================

View file

@ -144,7 +144,7 @@ class VSMatrix {
static void multMatrix(FLOATTYPE *resMatrix, const FLOATTYPE *aMatrix);
static void setIdentityMatrix(FLOATTYPE *mat, int size = 4);
public:
/// The storage for matrices
FLOATTYPE mMatrix[16];

View file

@ -82,25 +82,15 @@ public:
}
// returns the XY fields as a 2D-vector.
const Vector2& XY() const
Vector2 XY() const
{
return *reinterpret_cast<const Vector2*>(this);
return Vector2(X, Y);
}
Vector2& XY()
// returns the XYZ fields as a 3D-vector.
Vector3 XYZ() const
{
return *reinterpret_cast<Vector2*>(this);
}
// returns the XY fields as a 2D-vector.
const Vector3& XYZ() const
{
return *reinterpret_cast<const Vector3*>(this);
}
Vector3& XYZ()
{
return *reinterpret_cast<Vector3*>(this);
return Vector3(X, Y, Z);
}
@ -320,7 +310,10 @@ public:
auto factor = sinTheta / g_sqrt(lengthSquared);
TQuaternion<vec_t> ret;
ret.W = cosTheta;
ret.XYZ() = factor * axis;
auto xyz = vec_t(factor) * axis;
ret.X = vec_t(xyz.X);
ret.Y = vec_t(xyz.Y);
ret.Z = vec_t(xyz.Z);
return ret;
}
static TQuaternion<vec_t> FromAngles(TAngle<vec_t> yaw, TAngle<vec_t> pitch, TAngle<vec_t> roll)

View file

@ -705,12 +705,16 @@ struct TVector3
return *this;
}
// returns a version with swapped Z/Y
constexpr const TVector3 ToXZY() const
{
return {X, Z, Y};
}
constexpr TVector3 ScaleXYZ (const TVector3 &scaling)
{
return TVector3(X * scaling.X, Y * scaling.Y, Z * scaling.Z);
}
};
template<class vec_t>