mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-02-01 14:20:55 +00:00
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 142 additions and 98 deletions
|
@ -4,6 +4,8 @@
|
|||
#include "textureid.h"
|
||||
#include "i_modelvertexbuffer.h"
|
||||
#include "matrix.h"
|
||||
#include "TRS.h"
|
||||
#include "d_player.h"
|
||||
|
||||
class FModelRenderer;
|
||||
class FGameTexture;
|
||||
|
@ -74,8 +76,8 @@ public:
|
|||
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<VSMatrix>* AttachAnimationData() { return nullptr; };
|
||||
virtual const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData) { return {}; };
|
||||
virtual const TArray<TRS>* AttachAnimationData() { return nullptr; };
|
||||
virtual const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<TRS>& animationData, AActor* actor) { return {}; };
|
||||
|
||||
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
|
||||
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }
|
||||
|
|
|
@ -112,8 +112,8 @@ public:
|
|||
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int 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<VSMatrix>* AttachAnimationData() override;
|
||||
const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData) override;
|
||||
const TArray<TRS>* AttachAnimationData() override;
|
||||
const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<TRS>& animationData, AActor* actor) override;
|
||||
|
||||
private:
|
||||
void LoadGeometry();
|
||||
|
@ -132,7 +132,6 @@ private:
|
|||
TArray<IQMJoint> Joints;
|
||||
TArray<IQMPose> Poses;
|
||||
TArray<IQMAnim> Anims;
|
||||
TArray<VSMatrix> FrameTransforms;
|
||||
TArray<IQMBounds> Bounds;
|
||||
TArray<IQMVertexArray> VertexArrays;
|
||||
uint32_t NumVertices = 0;
|
||||
|
@ -141,6 +140,7 @@ private:
|
|||
|
||||
TArray<VSMatrix> baseframe;
|
||||
TArray<VSMatrix> inversebaseframe;
|
||||
TArray<TRS> TRSData;
|
||||
};
|
||||
|
||||
struct IQMReadErrorException { };
|
||||
|
|
|
@ -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;
|
||||
}
|
49
src/common/utility/TRS.h
Normal file
49
src/common/utility/TRS.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
** TRS
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2022 Andrew Clarke
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "vectors.h"
|
||||
|
||||
class TRS
|
||||
{
|
||||
public:
|
||||
FVector3 translation;
|
||||
FVector4 rotation;
|
||||
FVector3 scaling;
|
||||
|
||||
bool Equals(TRS& compare)
|
||||
{
|
||||
return compare.translation == this->translation && compare.rotation == this->rotation && compare.scaling == this->scaling;
|
||||
}
|
||||
};
|
||||
|
|
@ -47,6 +47,8 @@
|
|||
#include "g_level.h"
|
||||
#include "tflags.h"
|
||||
#include "portal.h"
|
||||
#include "matrix.h"
|
||||
#include "TRS.h"
|
||||
|
||||
struct subsector_t;
|
||||
struct FBlockNode;
|
||||
|
@ -689,6 +691,16 @@ public:
|
|||
virtual void Serialize(FSerializer& arc) override;
|
||||
};
|
||||
|
||||
class DBoneComponents : public DObject
|
||||
{
|
||||
DECLARE_CLASS(DBoneComponents, DObject);
|
||||
public:
|
||||
TArray<TRS> trscomponents;
|
||||
TArray<VSMatrix> trsmatrix;
|
||||
|
||||
DBoneComponents() = default;
|
||||
};
|
||||
|
||||
class DViewPosition : public DObject
|
||||
{
|
||||
DECLARE_CLASS(DViewPosition, DObject);
|
||||
|
@ -1085,6 +1097,7 @@ public:
|
|||
double Speed;
|
||||
double FloatSpeed;
|
||||
TObjPtr<DActorModelData*> modelData;
|
||||
TObjPtr<DBoneComponents*> boneComponentData;
|
||||
|
||||
// interaction info
|
||||
FBlockNode *BlockNode; // links in blocks (if needed)
|
||||
|
|
|
@ -159,6 +159,7 @@ CVAR (Int, cl_bloodtype, 0, CVAR_ARCHIVE);
|
|||
// CODE --------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(DActorModelData, false, false);
|
||||
IMPLEMENT_CLASS(DBoneComponents, false, false);
|
||||
IMPLEMENT_CLASS(AActor, false, true)
|
||||
|
||||
IMPLEMENT_POINTERS_START(AActor)
|
||||
|
@ -174,6 +175,7 @@ IMPLEMENT_POINTERS_START(AActor)
|
|||
IMPLEMENT_POINTER(alternative)
|
||||
IMPLEMENT_POINTER(ViewPos)
|
||||
IMPLEMENT_POINTER(modelData)
|
||||
IMPLEMENT_POINTER(boneComponentData)
|
||||
IMPLEMENT_POINTERS_END
|
||||
|
||||
AActor::~AActor ()
|
||||
|
|
|
@ -352,7 +352,7 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
|
|||
auto& ssids = surfaceskinids.Size() > 0 ? surfaceskinids : smf->surfaceskinIDs;
|
||||
auto ssidp = (unsigned)(i * MD3_MAX_SURFACES) < ssids.Size() ? &ssids[i * MD3_MAX_SURFACES] : nullptr;
|
||||
|
||||
const TArray<VSMatrix>* animationData = nullptr;
|
||||
const TArray<TRS>* animationData = nullptr;
|
||||
|
||||
bool nextFrame = smfNext && modelframe != modelframenext;
|
||||
|
||||
|
@ -363,7 +363,7 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
|
|||
|
||||
if (!(smf->flags & MDL_MODELSAREATTACHMENTS) || evaluatedSingle == false)
|
||||
{
|
||||
boneData = animation->CalculateBones(modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : 0.f, *animationData);
|
||||
boneData = animation->CalculateBones(modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : 0.f, *animationData, actor);
|
||||
boneStartingPosition = renderer->SetupFrame(animation, 0, 0, 0, boneData, -1);
|
||||
evaluatedSingle = true;
|
||||
}
|
||||
|
@ -372,7 +372,7 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
|
|||
{
|
||||
if (!(smf->flags & MDL_MODELSAREATTACHMENTS) || evaluatedSingle == false)
|
||||
{
|
||||
boneData = mdl->CalculateBones(modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : 0.f, *animationData);
|
||||
boneData = mdl->CalculateBones(modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : 0.f, *animationData, actor);
|
||||
boneStartingPosition = renderer->SetupFrame(mdl, 0, 0, 0, boneData, -1);
|
||||
evaluatedSingle = true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue