IQM Refactor start

- Refactored IQM and calculateBones to process TRS at runtime which resolves some of the faulty animations with large rotations. Will also make bone manipulations much easier to do
This commit is contained in:
Shiny Metagross 2022-10-26 11:30:59 -07:00 committed by Christoph Oelckers
commit 3f3cc5bbc3
7 changed files with 140 additions and 96 deletions

View file

@ -5,6 +5,7 @@
#include "texturemanager.h"
#include "modelrenderer.h"
#include "engineerrors.h"
#include "r_utility.h"
IQMModel::IQMModel()
{
@ -168,18 +169,10 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
{
baseframe[i] = m;
inversebaseframe[i] = invm;
}
}
}
// Swap YZ axis as we did that with the vertices down in LoadGeometry.
// This is an unfortunate side effect of the coordinate system in the gzdoom model rendering system
float swapYZ[16] = { 0.0f };
swapYZ[0 + 0 * 4] = 1.0f;
swapYZ[1 + 2 * 4] = 1.0f;
swapYZ[2 + 1 * 4] = 1.0f;
swapYZ[3 + 3 * 4] = 1.0f;
FrameTransforms.Resize(num_frames * num_poses);
TRSData.Resize(num_frames * num_poses);
reader.SeekTo(ofs_frames);
for (uint32_t i = 0; i < num_frames; i++)
{
@ -204,39 +197,15 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
scale.Y = p.ChannelOffset[8]; if (p.ChannelMask & 0x100) scale.Y += reader.ReadUInt16() * p.ChannelScale[8];
scale.Z = p.ChannelOffset[9]; if (p.ChannelMask & 0x200) scale.Z += reader.ReadUInt16() * p.ChannelScale[9];
TRSData[i * num_poses + j].translation = translate;
TRSData[i * num_poses + j].rotation = quaternion;
TRSData[i * num_poses + j].scaling = scale;
VSMatrix m;
m.loadIdentity();
m.translate(translate.X, translate.Y, translate.Z);
m.multQuaternion(quaternion);
m.scale(scale.X, scale.Y, scale.Z);
// Concatenate each pose with the inverse base pose to avoid doing this at animation time.
// If the joint has a parent, then it needs to be pre-concatenated with its parent's base pose.
// Thus it all negates at animation time like so:
// (parentPose * parentInverseBasePose) * (parentBasePose * childPose * childInverseBasePose) =>
// parentPose * (parentInverseBasePose * parentBasePose) * childPose * childInverseBasePose =>
// parentPose * childPose * childInverseBasePose
VSMatrix& result = FrameTransforms[i * num_poses + j];
if (p.Parent >= 0)
{
result = baseframe[p.Parent];
result.multMatrix(m);
result.multMatrix(inversebaseframe[j]);
}
else
{
result = m;
result.multMatrix(inversebaseframe[j]);
}
}
for (uint32_t j = 0; j < num_poses; j++)
{
VSMatrix m;
m.loadMatrix(swapYZ);
m.multMatrix(FrameTransforms[i * num_poses + j]);
m.multMatrix(swapYZ);
FrameTransforms[i * num_poses + j] = m;
}
}
@ -244,7 +213,6 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
if (num_frames <= 0)
{
num_frames = 1;
FrameTransforms.Resize(num_joints);
for (uint32_t j = 0; j < num_joints; j++)
{
@ -270,28 +238,6 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
m.translate(translate.X, translate.Y, translate.Z);
m.multQuaternion(quaternion);
m.scale(scale.X, scale.Y, scale.Z);
VSMatrix& result = FrameTransforms[j];
if (Joints[j].Parent >= 0)
{
result = baseframe[Joints[j].Parent];
result.multMatrix(m);
result.multMatrix(inversebaseframe[j]);
}
else
{
result = m;
result.multMatrix(inversebaseframe[j]);
}
}
for (uint32_t j = 0; j < num_joints; j++)
{
VSMatrix m;
m.loadMatrix(swapYZ);
m.multMatrix(FrameTransforms[j]);
m.multMatrix(swapYZ);
FrameTransforms[j] = m;
}
}
@ -372,8 +318,8 @@ void IQMModel::LoadPosition(IQMFileReader& reader, const IQMVertexArray& vertexA
for (FModelVertex& v : Vertices)
{
v.x = reader.ReadFloat();
v.z = reader.ReadFloat();
v.y = reader.ReadFloat();
v.z = reader.ReadFloat();
v.lu = lu;
v.lv = lv;
@ -412,7 +358,7 @@ void IQMModel::LoadNormal(IQMFileReader& reader, const IQMVertexArray& vertexArr
float y = reader.ReadFloat();
float z = reader.ReadFloat();
v.SetNormal(x, z, y);
v.SetNormal(x, y, z);
}
}
else
@ -511,24 +457,25 @@ void IQMModel::RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int fra
{
meshSkin = TexMan.GetGameTexture(surfaceskinids[i], true);
}
else if (!Meshes[i].Skin.isValid())
else if (Meshes[i].Skin.isValid())
{
meshSkin = TexMan.GetGameTexture(Meshes[i].Skin, true);
}
else
{
continue;
}
else
{
meshSkin = TexMan.GetGameTexture(Meshes[i].Skin, true);
}
if (!meshSkin) continue;
}
if (meshSkin != lastSkin)
if (meshSkin->isValid())
{
renderer->SetMaterial(meshSkin, false, translation);
lastSkin = meshSkin;
if (meshSkin != lastSkin)
{
renderer->SetMaterial(meshSkin, false, translation);
lastSkin = meshSkin;
}
renderer->DrawElements(Meshes[i].NumTriangles * 3, Meshes[i].FirstTriangle * 3 * sizeof(unsigned int));
}
renderer->DrawElements(Meshes[i].NumTriangles * 3, Meshes[i].FirstTriangle * 3 * sizeof(unsigned int));
}
}
@ -562,17 +509,26 @@ void IQMModel::AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids)
}
}
const TArray<VSMatrix>* IQMModel::AttachAnimationData()
const TArray<TRS>* IQMModel::AttachAnimationData()
{
return &FrameTransforms;
return &TRSData;
}
const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData)
const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray<TRS>& animationData, AActor* actor)
{
const TArray<VSMatrix>& animationFrames = &animationData ? animationData : FrameTransforms;
const TArray<TRS>& animationFrames = &animationData ? animationData : TRSData;
int numbones = Joints.Size();
if (actor->boneComponentData == nullptr)
{
auto ptr = Create<DBoneComponents>();
ptr->trscomponents.Resize(numbones);
ptr->trsmatrix.Resize(numbones);
actor->boneComponentData = ptr;
GC::WriteBarrier(actor, ptr);
}
frame1 = clamp(frame1, 0, ((int)animationFrames.Size() - 1) / numbones);
frame2 = clamp(frame2, 0, ((int)animationFrames.Size() - 1) / numbones);
@ -584,27 +540,49 @@ const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double i
TArray<VSMatrix> bones(numbones, true);
for (int i = 0; i < numbones; i++)
{
const float* from = animationFrames[offset1 + i].get();
const float* to = animationFrames[offset2 + i].get();
// Interpolate bone between the two frames
float bone[16];
for (int j = 0; j < 16; j++)
TRS bone;
bone.translation = animationFrames[offset1 + i].translation * invt + animationFrames[offset2 + i].translation * t;
bone.rotation = animationFrames[offset1 + i].rotation * invt;
if ((bone.rotation | animationFrames[offset2 + i].rotation * t) < 0)
{
bone[j] = from[j] * invt + to[j] * t;
bone.rotation.X *= -1; bone.rotation.Y *= -1; bone.rotation.Z *= -1; bone.rotation.W *= -1;
}
bone.rotation += animationFrames[offset2 + i].rotation * t;
bone.rotation.MakeUnit();
bone.scaling = animationFrames[offset1 + i].scaling * invt + animationFrames[offset2 + i].scaling * t;
// Apply parent bone
if (Joints[i].Parent >= 0)
if (actor->boneComponentData->trscomponents[i].Equals(bone))
{
bones[i] = bones[Joints[i].Parent];
bones[i].multMatrix(bone);
bones[i] = actor->boneComponentData->trsmatrix[i];
continue;
}
else
{
bones[i].loadMatrix(bone);
actor->boneComponentData->trscomponents[i] = bone;
}
VSMatrix m;
m.loadIdentity();
m.translate(bone.translation.X, bone.translation.Y, bone.translation.Z);
m.multQuaternion(bone.rotation);
m.scale(bone.scaling.X, bone.scaling.Y, bone.scaling.Z);
VSMatrix& result = bones[i];
if (Joints[i].Parent >= 0)
{
result = bones[Joints[i].Parent];
result.multMatrix(baseframe[Joints[i].Parent]);
result.multMatrix(m);
result.multMatrix(inversebaseframe[i]);
}
else
{
result = m;
result.multMatrix(inversebaseframe[i]);
}
}
actor->boneComponentData->trsmatrix = bones;
return bones;
}