diff --git a/src/common/models/model.h b/src/common/models/model.h index 48e4ed34a..8a95ac783 100644 --- a/src/common/models/model.h +++ b/src/common/models/model.h @@ -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* AttachAnimationData() { return nullptr; }; - virtual const TArray CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) { return {}; }; + virtual const TArray* AttachAnimationData() { return nullptr; }; + virtual const TArray CalculateBones(int frame1, int frame2, double inter, const TArray& animationData, AActor* actor) { return {}; }; void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; } IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; } diff --git a/src/common/models/model_iqm.h b/src/common/models/model_iqm.h index d91157c26..29c778dd0 100644 --- a/src/common/models/model_iqm.h +++ b/src/common/models/model_iqm.h @@ -112,8 +112,8 @@ public: void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData, int boneStartPosition) override; void BuildVertexBuffer(FModelRenderer* renderer) override; void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override; - const TArray* AttachAnimationData() override; - const TArray CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) override; + const TArray* AttachAnimationData() override; + const TArray CalculateBones(int frame1, int frame2, double inter, const TArray& animationData, AActor* actor) override; private: void LoadGeometry(); @@ -132,7 +132,6 @@ private: TArray Joints; TArray Poses; TArray Anims; - TArray FrameTransforms; TArray Bounds; TArray VertexArrays; uint32_t NumVertices = 0; @@ -141,6 +140,7 @@ private: TArray baseframe; TArray inversebaseframe; + TArray TRSData; }; struct IQMReadErrorException { }; diff --git a/src/common/models/models_iqm.cpp b/src/common/models/models_iqm.cpp index 314a6c600..885b04a1b 100644 --- a/src/common/models/models_iqm.cpp +++ b/src/common/models/models_iqm.cpp @@ -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* IQMModel::AttachAnimationData() +const TArray* IQMModel::AttachAnimationData() { - return &FrameTransforms; + return &TRSData; } -const TArray IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) +const TArray IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray& animationData, AActor* actor) { - const TArray& animationFrames = &animationData ? animationData : FrameTransforms; + const TArray& animationFrames = &animationData ? animationData : TRSData; int numbones = Joints.Size(); + if (actor->boneComponentData == nullptr) + { + auto ptr = Create(); + 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 IQMModel::CalculateBones(int frame1, int frame2, double i TArray 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; } \ No newline at end of file diff --git a/src/common/utility/TRS.h b/src/common/utility/TRS.h new file mode 100644 index 000000000..d94e262fb --- /dev/null +++ b/src/common/utility/TRS.h @@ -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; + } +}; + diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 5ba4d5dbb..6bd8caf47 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -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 trscomponents; + TArray trsmatrix; + + DBoneComponents() = default; +}; + class DViewPosition : public DObject { DECLARE_CLASS(DViewPosition, DObject); @@ -1085,6 +1097,7 @@ public: double Speed; double FloatSpeed; TObjPtr modelData; + TObjPtr boneComponentData; // interaction info FBlockNode *BlockNode; // links in blocks (if needed) diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 019f94504..82287ddb4 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -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 () diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index 453d67180..4f0d487e1 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -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* animationData = nullptr; + const TArray* 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; }