#pragma once #include #include "model.h" #include "vectors.h" #include "matrix.h" #include "common/rendering/i_modelvertexbuffer.h" #include "m_swap.h" #include "name.h" class DBoneComponents; struct IQMMesh { FString Name; FString Material; uint32_t FirstVertex; uint32_t NumVertices; uint32_t FirstTriangle; uint32_t NumTriangles; FTextureID Skin; }; enum IQMVertexArrayType { IQM_POSITION = 0, // float, 3 IQM_TEXCOORD = 1, // float, 2 IQM_NORMAL = 2, // float, 3 IQM_TANGENT = 3, // float, 4 IQM_BLENDINDEXES = 4, // ubyte, 4 IQM_BLENDWEIGHTS = 5, // ubyte, 4 IQM_COLOR = 6, // ubyte, 4 IQM_CUSTOM = 0x10 }; enum IQMVertexArrayFormat { IQM_BYTE = 0, IQM_UBYTE = 1, IQM_SHORT = 2, IQM_USHORT = 3, IQM_INT = 4, IQM_UINT = 5, IQM_HALF = 6, IQM_FLOAT = 7, IQM_DOUBLE = 8, }; struct IQMVertexArray { IQMVertexArrayType Type; uint32_t Flags; IQMVertexArrayFormat Format; uint32_t Size; uint32_t Offset; }; struct IQMTriangle { uint32_t Vertex[3]; }; struct IQMAdjacency { uint32_t Triangle[3]; }; struct IQMJoint { FName Name; int32_t Parent; // parent < 0 means this is a root bone TArray Children; // indices of children FVector3 Translate; FQuaternion Quaternion; FVector3 Scale; FVector3 Position; FQuaternion Rotation; FVector3 Scaling; }; struct IQMPose { int32_t Parent; // parent < 0 means this is a root bone uint32_t ChannelMask; // mask of which 10 channels are present for this joint pose float ChannelOffset[10]; float ChannelScale[10]; // channels 0..2 are translation and channels 3..6 are quaternion rotation // rotation is in relative/parent local space // channels 7..9 are scale // output = (input*scale)*rotation + translation }; struct IQMAnim { FString Name; uint32_t FirstFrame; uint32_t NumFrames; float Framerate; bool Loop; }; struct IQMBounds { float BBMins[3]; float BBMaxs[3]; float XYRadius; float Radius; }; class IQMFileReader; class IQMModel : public FModel { public: IQMModel(); ~IQMModel(); bool Load(const char* fn, int lumpnum, const char* buffer, int length) override; int FindFrame(const char* name, bool nodefault) override; int FindFirstFrame(FName name) override; int FindLastFrame(FName name) override; double FindFramerate(FName name) override; void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, FTranslationID translation, const FTextureID* surfaceskinids, int boneStartPosition, void * act) override; void BuildVertexBuffer(FModelRenderer* renderer) override; void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override; const TArray* AttachAnimationData() override; ModelAnimFrame PrecalculateFrame(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData) override; const TArray* CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray* animationData, TArray *in, BoneInfo *out, double time) override; ModelAnimFramePrecalculatedIQM CalculateFrameIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray* animationData); const TArray* CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray* animationData, TArray *in, BoneInfo *out, double time); private: void LoadGeometry(); void UnloadGeometry(); void LoadPosition(IQMFileReader& reader, const IQMVertexArray& vertexArray); void LoadTexcoord(IQMFileReader& reader, const IQMVertexArray& vertexArray); void LoadNormal(IQMFileReader& reader, const IQMVertexArray& vertexArray); void LoadBlendIndexes(IQMFileReader& reader, const IQMVertexArray& vertexArray); void LoadBlendWeights(IQMFileReader& reader, const IQMVertexArray& vertexArray); int mLumpNum = -1; TMap NamedAnimations; TMap NamedJoints; TArray Meshes; TArray Triangles; TArray Adjacency; TArray Joints; TArray RootJoints; TArray Poses; TArray Anims; TArray Bounds; TArray VertexArrays; uint32_t NumVertices = 0; TArray boneData; // temporary array, used to hold animation data during rendering, set during CalculateBones, uploaded during RenderFrame TArray Vertices; TArray baseframe; TArray inversebaseframe; TArray TRSData; public: int NumJoints() override { return Joints.SSize(); } int FindJoint(FName name) override { int *j = NamedJoints.CheckKey(name); return j ? *j : -1; } int GetJointParent(int joint) override { return (joint >= 0 && joint < Joints.SSize()) ? Joints[joint].Parent : -1; } FName GetJointName(int joint) override { return (joint >= 0 && joint < Joints.SSize()) ? Joints[joint].Name : FName(NAME_None); } void GetRootJoints(TArray &out) override { out = RootJoints; } void GetJointChildren(int joint, TArray &out) override { if(joint >= 0 && joint < Joints.SSize()) { out = Joints[joint].Children; } } FQuaternion GetJointRotation(int joint) override { return (joint >= 0 && joint < Joints.SSize()) ? Joints[joint].Rotation : FQuaternion(0.0f,0.0f,0.0f,1.0f); } FVector3 GetJointPosition(int joint) override { 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; } }; struct IQMReadErrorException { }; class IQMFileReader { public: IQMFileReader(const void* buffer, int length) : buffer((const char*)buffer), length(length) { } uint8_t ReadUByte() { uint8_t value; Read(&value, sizeof(uint8_t)); return value; } int32_t ReadInt32() { int32_t value; Read(&value, sizeof(int32_t)); value = LittleLong(value); return value; } int16_t ReadInt16() { int16_t value; Read(&value, sizeof(int16_t)); value = LittleShort(value); return value; } uint32_t ReadUInt32() { return ReadInt32(); } uint16_t ReadUInt16() { return ReadInt16(); } float ReadFloat() { float value; Read(&value, sizeof(float)); return value; } FString ReadName(const TArray& textBuffer) { uint32_t nameOffset = ReadUInt32(); if (nameOffset >= textBuffer.Size()) throw IQMReadErrorException(); return textBuffer.Data() + nameOffset; } void Read(void* data, int size) { if (pos + size > length || size < 0 || size > 0x0fffffff) throw IQMReadErrorException(); memcpy(data, buffer + pos, size); pos += size; } void SeekTo(int newPos) { if (newPos < 0 || newPos > length) throw IQMReadErrorException(); pos = newPos; } private: const char* buffer = nullptr; int length = 0; int pos = 0; };