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:
parent
c36da35e37
commit
3f3cc5bbc3
7 changed files with 140 additions and 96 deletions
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue