mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-26 05:51:20 +00:00
IQM Support Implemented
More info to come
This commit is contained in:
parent
dbd6a9dea1
commit
08f520f1c6
49 changed files with 1356 additions and 62 deletions
|
@ -1084,6 +1084,7 @@ set (PCH_SOURCES
|
|||
common/models/models_voxel.cpp
|
||||
common/models/models_ue1.cpp
|
||||
common/models/models_obj.cpp
|
||||
common/models/models_iqm.cpp
|
||||
common/models/model.cpp
|
||||
common/models/voxels.cpp
|
||||
common/console/c_commandline.cpp
|
||||
|
@ -1179,6 +1180,7 @@ set (PCH_SOURCES
|
|||
common/rendering/hwrenderer/data/hw_cvars.cpp
|
||||
common/rendering/hwrenderer/data/hw_vrmodes.cpp
|
||||
common/rendering/hwrenderer/data/hw_lightbuffer.cpp
|
||||
common/rendering/hwrenderer/data/hw_bonebuffer.cpp
|
||||
common/rendering/hwrenderer/data/hw_aabbtree.cpp
|
||||
common/rendering/hwrenderer/data/hw_shadowmap.cpp
|
||||
common/rendering/hwrenderer/data/hw_shaderpatcher.cpp
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "model_md2.h"
|
||||
#include "model_md3.h"
|
||||
#include "model_kvx.h"
|
||||
#include "model_iqm.h"
|
||||
#include "i_time.h"
|
||||
#include "voxels.h"
|
||||
#include "texturemanager.h"
|
||||
|
@ -208,6 +209,10 @@ unsigned FindModel(const char * path, const char * modelfile)
|
|||
{
|
||||
model = new FMD3Model;
|
||||
}
|
||||
else if (!memcmp(buffer, "INTERQUAKEMODEL\0", 16))
|
||||
{
|
||||
model = new IQMModel;
|
||||
}
|
||||
|
||||
if (model != nullptr)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdint.h>
|
||||
#include "textureid.h"
|
||||
#include "i_modelvertexbuffer.h"
|
||||
#include "matrix.h"
|
||||
|
||||
class FModelRenderer;
|
||||
class FGameTexture;
|
||||
|
@ -26,6 +27,7 @@ struct FSpriteModelFrame
|
|||
TArray<FTextureID> skinIDs;
|
||||
TArray<FTextureID> surfaceskinIDs;
|
||||
TArray<int> modelframes;
|
||||
TArray<int> animationIDs;
|
||||
float xscale, yscale, zscale;
|
||||
// [BB] Added zoffset, rotation parameters and flags.
|
||||
// Added xoffset, yoffset
|
||||
|
@ -67,11 +69,12 @@ public:
|
|||
virtual ~FModel();
|
||||
|
||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0;
|
||||
virtual int FindFrame(const char * name, bool nodefault = false) = 0;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) = 0;
|
||||
virtual int FindFrame(const char* name, bool nodefault = false) = 0;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) = 0;
|
||||
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() = 0;
|
||||
|
||||
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
|
||||
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }
|
||||
|
|
219
src/common/models/model_iqm.h
Normal file
219
src/common/models/model_iqm.h
Normal file
|
@ -0,0 +1,219 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "model.h"
|
||||
#include "vectors.h"
|
||||
#include "matrix.h"
|
||||
#include "common/rendering/i_modelvertexbuffer.h"
|
||||
|
||||
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
|
||||
{
|
||||
FString Name;
|
||||
int32_t Parent; // parent < 0 means this is a root bone
|
||||
FVector3 Translate;
|
||||
FVector4 Quaternion;
|
||||
FVector3 Scale;
|
||||
};
|
||||
|
||||
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 <Tx, Ty, Tz> and channels 3..6 are quaternion rotation <Qx, Qy, Qz, Qw>
|
||||
// rotation is in relative/parent local space
|
||||
// channels 7..9 are scale <Sx, Sy, Sz>
|
||||
// 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;
|
||||
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
|
||||
void BuildVertexBuffer(FModelRenderer* renderer) override;
|
||||
void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override;
|
||||
const TArray<VSMatrix>* AttachAnimationData() override;
|
||||
|
||||
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;
|
||||
TArray<IQMMesh> Meshes;
|
||||
TArray<IQMTriangle> Triangles;
|
||||
TArray<IQMAdjacency> Adjacency;
|
||||
TArray<IQMJoint> Joints;
|
||||
TArray<IQMPose> Poses;
|
||||
TArray<IQMAnim> Anims;
|
||||
TArray<VSMatrix> FrameTransforms;
|
||||
TArray<IQMBounds> Bounds;
|
||||
TArray<IQMVertexArray> VertexArrays;
|
||||
uint32_t NumVertices = 0;
|
||||
|
||||
TArray<FModelVertex> Vertices;
|
||||
|
||||
TArray<VSMatrix> baseframe;
|
||||
TArray<VSMatrix> inversebaseframe;
|
||||
};
|
||||
|
||||
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<char>& 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;
|
||||
};
|
|
@ -15,9 +15,9 @@ struct FVoxelVertexHash
|
|||
// Returns the hash value for a key.
|
||||
hash_t Hash(const FModelVertex &key)
|
||||
{
|
||||
int ix = int(key.x);
|
||||
int iy = int(key.y);
|
||||
int iz = int(key.z);
|
||||
int ix = int(key.x);
|
||||
int iy = int(key.y);
|
||||
int iz = int(key.z);
|
||||
return (hash_t)(ix + (iy<<9) + (iz<<18));
|
||||
}
|
||||
|
||||
|
@ -58,12 +58,13 @@ public:
|
|||
~FVoxelModel();
|
||||
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||
void Initialize();
|
||||
virtual int FindFrame(const char * name, bool nodefault) override;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||
virtual int FindFrame(const char* name, bool nodefault) override;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
|
||||
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
|
||||
FTextureID GetPaletteTexture() const { return mPalette; }
|
||||
void BuildVertexBuffer(FModelRenderer *renderer) override;
|
||||
float getAspectFactor(float vscale) override;
|
||||
const TArray<VSMatrix>* AttachAnimationData() override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -112,10 +112,11 @@ public:
|
|||
virtual ~FDMDModel();
|
||||
|
||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||
virtual int FindFrame(const char * name, bool nodefault) override;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||
virtual int FindFrame(const char* name, bool nodefault) override;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
|
||||
virtual void LoadGeometry();
|
||||
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
|
||||
const TArray<VSMatrix>* AttachAnimationData() override;
|
||||
|
||||
void UnloadGeometry();
|
||||
void BuildVertexBuffer(FModelRenderer *renderer);
|
||||
|
|
|
@ -66,10 +66,11 @@ public:
|
|||
FMD3Model() = default;
|
||||
|
||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||
virtual int FindFrame(const char * name, bool nodefault) override;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||
virtual int FindFrame(const char* name, bool nodefault) override;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
|
||||
void LoadGeometry();
|
||||
void BuildVertexBuffer(FModelRenderer *renderer);
|
||||
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
|
||||
const TArray<VSMatrix>* AttachAnimationData() override;
|
||||
};
|
||||
|
||||
|
|
|
@ -98,9 +98,10 @@ public:
|
|||
~FOBJModel();
|
||||
bool Load(const char* fn, int lumpnum, const char* buffer, int length) override;
|
||||
int FindFrame(const char* name, bool nodefault) override;
|
||||
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
|
||||
void BuildVertexBuffer(FModelRenderer* renderer) override;
|
||||
void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override;
|
||||
const TArray<VSMatrix>* AttachAnimationData() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,10 +25,11 @@ public:
|
|||
};
|
||||
|
||||
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||
int FindFrame(const char * name, bool nodefault) override;
|
||||
void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||
int FindFrame(const char* name, bool nodefault) override;
|
||||
void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
|
||||
void BuildVertexBuffer(FModelRenderer *renderer) override;
|
||||
void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
|
||||
const TArray<VSMatrix>* AttachAnimationData() override;
|
||||
void LoadGeometry();
|
||||
void UnloadGeometry();
|
||||
FUE1Model()
|
||||
|
|
|
@ -24,6 +24,6 @@ public:
|
|||
virtual void SetMaterial(FGameTexture *skin, bool clampNoFilter, int translation) = 0;
|
||||
virtual void DrawArrays(int start, int count) = 0;
|
||||
virtual void DrawElements(int numIndices, size_t offset) = 0;
|
||||
virtual void SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size) = 0;
|
||||
virtual void SetupFrame(FModel* model, unsigned int frame1, unsigned int frame2, unsigned int size, const TArray<VSMatrix>& bones) = 0;
|
||||
};
|
||||
|
||||
|
|
599
src/common/models/models_iqm.cpp
Normal file
599
src/common/models/models_iqm.cpp
Normal file
|
@ -0,0 +1,599 @@
|
|||
|
||||
#include "filesystem.h"
|
||||
#include "cmdlib.h"
|
||||
#include "model_iqm.h"
|
||||
#include "texturemanager.h"
|
||||
#include "modelrenderer.h"
|
||||
#include "engineerrors.h"
|
||||
#include "r_utility.h"
|
||||
|
||||
IQMModel::IQMModel()
|
||||
{
|
||||
}
|
||||
|
||||
IQMModel::~IQMModel()
|
||||
{
|
||||
}
|
||||
|
||||
bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int length)
|
||||
{
|
||||
mLumpNum = lumpnum;
|
||||
|
||||
try
|
||||
{
|
||||
IQMFileReader reader(buffer, length);
|
||||
|
||||
char magic[16];
|
||||
reader.Read(magic, 16);
|
||||
if (memcmp(magic, "INTERQUAKEMODEL\0", 16) != 0)
|
||||
return false;
|
||||
|
||||
uint32_t version = reader.ReadUInt32();
|
||||
if (version != 2)
|
||||
return false;
|
||||
|
||||
uint32_t filesize = reader.ReadUInt32();
|
||||
uint32_t flags = reader.ReadUInt32();
|
||||
uint32_t num_text = reader.ReadUInt32();
|
||||
uint32_t ofs_text = reader.ReadUInt32();
|
||||
uint32_t num_meshes = reader.ReadUInt32();
|
||||
uint32_t ofs_meshes = reader.ReadUInt32();
|
||||
uint32_t num_vertexarrays = reader.ReadUInt32();
|
||||
uint32_t num_vertices = reader.ReadUInt32();
|
||||
uint32_t ofs_vertexarrays = reader.ReadUInt32();
|
||||
uint32_t num_triangles = reader.ReadUInt32();
|
||||
uint32_t ofs_triangles = reader.ReadUInt32();
|
||||
uint32_t ofs_adjacency = reader.ReadUInt32();
|
||||
uint32_t num_joints = reader.ReadUInt32();
|
||||
uint32_t ofs_joints = reader.ReadUInt32();
|
||||
uint32_t num_poses = reader.ReadUInt32();
|
||||
uint32_t ofs_poses = reader.ReadUInt32();
|
||||
uint32_t num_anims = reader.ReadUInt32();
|
||||
uint32_t ofs_anims = reader.ReadUInt32();
|
||||
uint32_t num_frames = reader.ReadUInt32();
|
||||
uint32_t num_framechannels = reader.ReadUInt32();
|
||||
uint32_t ofs_frames = reader.ReadUInt32();
|
||||
uint32_t ofs_bounds = reader.ReadUInt32();
|
||||
uint32_t num_comment = reader.ReadUInt32();
|
||||
uint32_t ofs_comment = reader.ReadUInt32();
|
||||
uint32_t num_extensions = reader.ReadUInt32();
|
||||
uint32_t ofs_extensions = reader.ReadUInt32();
|
||||
|
||||
if (num_text == 0)
|
||||
return false;
|
||||
|
||||
TArray<char> text(num_text, true);
|
||||
reader.SeekTo(ofs_text);
|
||||
reader.Read(text.Data(), text.Size());
|
||||
text[text.Size() - 1] = 0;
|
||||
|
||||
Meshes.Resize(num_meshes);
|
||||
Triangles.Resize(num_triangles);
|
||||
Adjacency.Resize(num_triangles);
|
||||
Joints.Resize(num_joints);
|
||||
Poses.Resize(num_poses);
|
||||
Anims.Resize(num_anims);
|
||||
Bounds.Resize(num_frames);
|
||||
VertexArrays.Resize(num_vertexarrays);
|
||||
NumVertices = num_vertices;
|
||||
|
||||
reader.SeekTo(ofs_meshes);
|
||||
for (IQMMesh& mesh : Meshes)
|
||||
{
|
||||
mesh.Name = reader.ReadName(text);
|
||||
mesh.Material = reader.ReadName(text);
|
||||
mesh.FirstVertex = reader.ReadUInt32();
|
||||
mesh.NumVertices = reader.ReadUInt32();
|
||||
mesh.FirstTriangle = reader.ReadUInt32();
|
||||
mesh.NumTriangles = reader.ReadUInt32();
|
||||
mesh.Skin = LoadSkin(path, mesh.Material.GetChars());
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_triangles);
|
||||
for (IQMTriangle& triangle : Triangles)
|
||||
{
|
||||
triangle.Vertex[0] = reader.ReadUInt32();
|
||||
triangle.Vertex[1] = reader.ReadUInt32();
|
||||
triangle.Vertex[2] = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_adjacency);
|
||||
for (IQMAdjacency& adj : Adjacency)
|
||||
{
|
||||
adj.Triangle[0] = reader.ReadUInt32();
|
||||
adj.Triangle[1] = reader.ReadUInt32();
|
||||
adj.Triangle[2] = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_joints);
|
||||
for (IQMJoint& joint : Joints)
|
||||
{
|
||||
joint.Name = reader.ReadName(text);
|
||||
joint.Parent = reader.ReadInt32();
|
||||
joint.Translate.X = reader.ReadFloat();
|
||||
joint.Translate.Y = reader.ReadFloat();
|
||||
joint.Translate.Z = reader.ReadFloat();
|
||||
joint.Quaternion.X = reader.ReadFloat();
|
||||
joint.Quaternion.Y = reader.ReadFloat();
|
||||
joint.Quaternion.Z = reader.ReadFloat();
|
||||
joint.Quaternion.W = reader.ReadFloat();
|
||||
joint.Quaternion.MakeUnit();
|
||||
joint.Scale.X = reader.ReadFloat();
|
||||
joint.Scale.Y = reader.ReadFloat();
|
||||
joint.Scale.Z = reader.ReadFloat();
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_poses);
|
||||
for (IQMPose& pose : Poses)
|
||||
{
|
||||
pose.Parent = reader.ReadInt32();
|
||||
pose.ChannelMask = reader.ReadUInt32();
|
||||
for (int i = 0; i < 10; i++) pose.ChannelOffset[i] = reader.ReadFloat();
|
||||
for (int i = 0; i < 10; i++) pose.ChannelScale[i] = reader.ReadFloat();
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_anims);
|
||||
for (IQMAnim& anim : Anims)
|
||||
{
|
||||
anim.Name = reader.ReadName(text);
|
||||
anim.FirstFrame = reader.ReadUInt32();
|
||||
anim.NumFrames = reader.ReadUInt32();
|
||||
anim.Framerate = reader.ReadFloat();
|
||||
anim.Loop = !!(reader.ReadUInt32() & 1);
|
||||
}
|
||||
|
||||
baseframe.Resize(num_joints);
|
||||
inversebaseframe.Resize(num_joints);
|
||||
|
||||
for (uint32_t i = 0; i < num_joints; i++)
|
||||
{
|
||||
const IQMJoint& j = Joints[i];
|
||||
|
||||
VSMatrix m, invm;
|
||||
m.loadIdentity();
|
||||
m.translate(j.Translate.X, j.Translate.Y, j.Translate.Z);
|
||||
m.multQuaternion(j.Quaternion);
|
||||
m.scale(j.Scale.X, j.Scale.Y, j.Scale.Z);
|
||||
m.inverseMatrix(invm);
|
||||
if (j.Parent >= 0)
|
||||
{
|
||||
baseframe[i] = baseframe[j.Parent];
|
||||
baseframe[i].multMatrix(m);
|
||||
inversebaseframe[i] = invm;
|
||||
inversebaseframe[i].multMatrix(inversebaseframe[j.Parent]);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
reader.SeekTo(ofs_frames);
|
||||
for (uint32_t i = 0; i < num_frames; i++)
|
||||
{
|
||||
for (uint32_t j = 0; j < num_poses; j++)
|
||||
{
|
||||
const IQMPose& p = Poses[j];
|
||||
|
||||
FVector3 translate;
|
||||
translate.X = p.ChannelOffset[0]; if (p.ChannelMask & 0x01) translate.X += reader.ReadUInt16() * p.ChannelScale[0];
|
||||
translate.Y = p.ChannelOffset[1]; if (p.ChannelMask & 0x02) translate.Y += reader.ReadUInt16() * p.ChannelScale[1];
|
||||
translate.Z = p.ChannelOffset[2]; if (p.ChannelMask & 0x04) translate.Z += reader.ReadUInt16() * p.ChannelScale[2];
|
||||
|
||||
FVector4 quaternion;
|
||||
quaternion.X = p.ChannelOffset[3]; if (p.ChannelMask & 0x08) quaternion.X += reader.ReadUInt16() * p.ChannelScale[3];
|
||||
quaternion.Y = p.ChannelOffset[4]; if (p.ChannelMask & 0x10) quaternion.Y += reader.ReadUInt16() * p.ChannelScale[4];
|
||||
quaternion.Z = p.ChannelOffset[5]; if (p.ChannelMask & 0x20) quaternion.Z += reader.ReadUInt16() * p.ChannelScale[5];
|
||||
quaternion.W = p.ChannelOffset[6]; if (p.ChannelMask & 0x40) quaternion.W += reader.ReadUInt16() * p.ChannelScale[6];
|
||||
quaternion.MakeUnit();
|
||||
|
||||
FVector3 scale;
|
||||
scale.X = p.ChannelOffset[7]; if (p.ChannelMask & 0x80) scale.X += reader.ReadUInt16() * p.ChannelScale[7];
|
||||
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];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//If a model doesn't have an animation loaded, it will crash. We don't want that!
|
||||
if (num_frames <= 0)
|
||||
{
|
||||
num_frames = 1;
|
||||
FrameTransforms.Resize(num_joints);
|
||||
|
||||
for (uint32_t j = 0; j < num_joints; j++)
|
||||
{
|
||||
FVector3 translate;
|
||||
translate.X = Joints[j].Translate.X;
|
||||
translate.Y = Joints[j].Translate.Y;
|
||||
translate.Z = Joints[j].Translate.Z;
|
||||
|
||||
FVector4 quaternion;
|
||||
quaternion.X = Joints[j].Quaternion.X;
|
||||
quaternion.Y = Joints[j].Quaternion.Y;
|
||||
quaternion.Z = Joints[j].Quaternion.Z;
|
||||
quaternion.W = Joints[j].Quaternion.W;
|
||||
quaternion.MakeUnit();
|
||||
|
||||
FVector3 scale;
|
||||
scale.X = Joints[j].Scale.X;
|
||||
scale.Y = Joints[j].Scale.Y;
|
||||
scale.Z = Joints[j].Scale.Z;
|
||||
|
||||
VSMatrix m;
|
||||
m.loadIdentity();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_bounds);
|
||||
for (IQMBounds& bound : Bounds)
|
||||
{
|
||||
bound.BBMins[0] = reader.ReadFloat();
|
||||
bound.BBMins[1] = reader.ReadFloat();
|
||||
bound.BBMins[2] = reader.ReadFloat();
|
||||
bound.BBMaxs[0] = reader.ReadFloat();
|
||||
bound.BBMaxs[1] = reader.ReadFloat();
|
||||
bound.BBMaxs[2] = reader.ReadFloat();
|
||||
bound.XYRadius = reader.ReadFloat();
|
||||
bound.Radius = reader.ReadFloat();
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_vertexarrays);
|
||||
for (IQMVertexArray& vertexArray : VertexArrays)
|
||||
{
|
||||
vertexArray.Type = (IQMVertexArrayType)reader.ReadUInt32();
|
||||
vertexArray.Flags = reader.ReadUInt32();
|
||||
vertexArray.Format = (IQMVertexArrayFormat)reader.ReadUInt32();
|
||||
vertexArray.Size = reader.ReadUInt32();
|
||||
vertexArray.Offset = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (IQMReadErrorException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::LoadGeometry()
|
||||
{
|
||||
try
|
||||
{
|
||||
FileData lumpdata = fileSystem.ReadFile(mLumpNum);
|
||||
IQMFileReader reader(lumpdata.GetMem(), (int)lumpdata.GetSize());
|
||||
|
||||
Vertices.Resize(NumVertices);
|
||||
for (IQMVertexArray& vertexArray : VertexArrays)
|
||||
{
|
||||
reader.SeekTo(vertexArray.Offset);
|
||||
if (vertexArray.Type == IQM_POSITION)
|
||||
{
|
||||
LoadPosition(reader, vertexArray);
|
||||
}
|
||||
else if (vertexArray.Type == IQM_TEXCOORD)
|
||||
{
|
||||
LoadTexcoord(reader, vertexArray);
|
||||
}
|
||||
else if (vertexArray.Type == IQM_NORMAL)
|
||||
{
|
||||
LoadNormal(reader, vertexArray);
|
||||
}
|
||||
else if (vertexArray.Type == IQM_BLENDINDEXES)
|
||||
{
|
||||
LoadBlendIndexes(reader, vertexArray);
|
||||
}
|
||||
else if (vertexArray.Type == IQM_BLENDWEIGHTS)
|
||||
{
|
||||
LoadBlendWeights(reader, vertexArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IQMReadErrorException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::LoadPosition(IQMFileReader& reader, const IQMVertexArray& vertexArray)
|
||||
{
|
||||
float lu = 0.0f, lv = 0.0f, lindex = -1.0f;
|
||||
if (vertexArray.Format == IQM_FLOAT && vertexArray.Size == 3)
|
||||
{
|
||||
for (FModelVertex& v : Vertices)
|
||||
{
|
||||
v.x = reader.ReadFloat();
|
||||
v.z = reader.ReadFloat();
|
||||
v.y = reader.ReadFloat();
|
||||
|
||||
v.lu = lu;
|
||||
v.lv = lv;
|
||||
v.lindex = lindex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
I_FatalError("Unsupported IQM_POSITION vertex format");
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::LoadTexcoord(IQMFileReader& reader, const IQMVertexArray& vertexArray)
|
||||
{
|
||||
if (vertexArray.Format == IQM_FLOAT && vertexArray.Size == 2)
|
||||
{
|
||||
for (FModelVertex& v : Vertices)
|
||||
{
|
||||
v.u = reader.ReadFloat();
|
||||
v.v = reader.ReadFloat();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
I_FatalError("Unsupported IQM_TEXCOORD vertex format");
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::LoadNormal(IQMFileReader& reader, const IQMVertexArray& vertexArray)
|
||||
{
|
||||
if (vertexArray.Format == IQM_FLOAT && vertexArray.Size == 3)
|
||||
{
|
||||
for (FModelVertex& v : Vertices)
|
||||
{
|
||||
float x = reader.ReadFloat();
|
||||
float y = reader.ReadFloat();
|
||||
float z = reader.ReadFloat();
|
||||
|
||||
v.SetNormal(x, z, y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
I_FatalError("Unsupported IQM_NORMAL vertex format");
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::LoadBlendIndexes(IQMFileReader& reader, const IQMVertexArray& vertexArray)
|
||||
{
|
||||
if (vertexArray.Format == IQM_UBYTE && vertexArray.Size == 4)
|
||||
{
|
||||
for (FModelVertex& v : Vertices)
|
||||
{
|
||||
int x = reader.ReadUByte();
|
||||
int y = reader.ReadUByte();
|
||||
int z = reader.ReadUByte();
|
||||
int w = reader.ReadUByte();
|
||||
v.SetBoneSelector(x, y, z, w);
|
||||
}
|
||||
}
|
||||
else if (vertexArray.Format == IQM_INT && vertexArray.Size == 4)
|
||||
{
|
||||
for (FModelVertex& v : Vertices)
|
||||
{
|
||||
int x = reader.ReadInt32();
|
||||
int y = reader.ReadInt32();
|
||||
int z = reader.ReadInt32();
|
||||
int w = reader.ReadInt32();
|
||||
v.SetBoneSelector(x, y, z, w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
I_FatalError("Unsupported IQM_BLENDINDEXES vertex format");
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::LoadBlendWeights(IQMFileReader& reader, const IQMVertexArray& vertexArray)
|
||||
{
|
||||
if (vertexArray.Format == IQM_UBYTE && vertexArray.Size == 4)
|
||||
{
|
||||
for (FModelVertex& v : Vertices)
|
||||
{
|
||||
v.SetBoneWeight(reader.ReadUByte(), reader.ReadUByte(), reader.ReadUByte(), reader.ReadUByte());
|
||||
}
|
||||
}
|
||||
else if (vertexArray.Format == IQM_FLOAT && vertexArray.Size == 4)
|
||||
{
|
||||
for (FModelVertex& v : Vertices)
|
||||
{
|
||||
uint8_t x = (int)clamp(reader.ReadFloat() * 255.0f, 0.0f, 255.0f);
|
||||
uint8_t y = (int)clamp(reader.ReadFloat() * 255.0f, 0.0f, 255.0f);
|
||||
uint8_t z = (int)clamp(reader.ReadFloat() * 255.0f, 0.0f, 255.0f);
|
||||
uint8_t w = (int)clamp(reader.ReadFloat() * 255.0f, 0.0f, 255.0f);
|
||||
v.SetBoneWeight(x, y, z, w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
I_FatalError("Unsupported IQM_BLENDWEIGHTS vertex format");
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::UnloadGeometry()
|
||||
{
|
||||
Vertices.Reset();
|
||||
}
|
||||
|
||||
int IQMModel::FindFrame(const char* name, bool nodefault)
|
||||
{
|
||||
// This doesn't really mean all that much for IQM
|
||||
for (unsigned i = 0; i < Anims.Size(); i++)
|
||||
{
|
||||
if (!stricmp(name, Anims[i].Name.GetChars())) return i;
|
||||
}
|
||||
return FErr_NotFound;
|
||||
}
|
||||
|
||||
void IQMModel::RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame1, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData)
|
||||
{
|
||||
const TArray<VSMatrix>& animationFrames = &animationData ? animationData : FrameTransforms;
|
||||
|
||||
int numbones = Joints.Size();
|
||||
|
||||
frame1 = clamp(frame1, 0, ((int)animationFrames.Size() - 1)/numbones);
|
||||
frame2 = clamp(frame2, 0, ((int)animationFrames.Size() - 1)/numbones);
|
||||
|
||||
int offset1 = frame1 * numbones;
|
||||
int offset2 = frame2 * numbones;
|
||||
float t = (float)inter;
|
||||
float invt = 1.0f - t;
|
||||
|
||||
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 i = 0; i < 16; i++)
|
||||
{
|
||||
bone[i] = from[i] * invt + to[i] * t;
|
||||
}
|
||||
|
||||
// Apply parent bone
|
||||
if (Joints[i].Parent >= 0)
|
||||
{
|
||||
bones[i] = bones[Joints[i].Parent];
|
||||
bones[i].multMatrix(bone);
|
||||
}
|
||||
else
|
||||
{
|
||||
bones[i].loadMatrix(bone);
|
||||
}
|
||||
}
|
||||
|
||||
renderer->SetupFrame(this, 0, 0, NumVertices, bones);
|
||||
|
||||
FGameTexture* lastSkin = nullptr;
|
||||
for (int i = 0; i < Meshes.Size(); i++)
|
||||
{
|
||||
FGameTexture* meshSkin = skin;
|
||||
|
||||
if (!meshSkin)
|
||||
{
|
||||
if (surfaceskinids && surfaceskinids[i].isValid())
|
||||
{
|
||||
meshSkin = TexMan.GetGameTexture(surfaceskinids[i], true);
|
||||
}
|
||||
else if (!Meshes[i].Skin.isValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshSkin = TexMan.GetGameTexture(Meshes[i].Skin, true);
|
||||
}
|
||||
if (!meshSkin) continue;
|
||||
}
|
||||
|
||||
if (meshSkin != lastSkin)
|
||||
{
|
||||
renderer->SetMaterial(meshSkin, false, translation);
|
||||
lastSkin = meshSkin;
|
||||
}
|
||||
|
||||
renderer->DrawElements(Meshes[i].NumTriangles * 3, Meshes[i].FirstTriangle * 3 * sizeof(unsigned int));
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::BuildVertexBuffer(FModelRenderer* renderer)
|
||||
{
|
||||
if (!GetVertexBuffer(renderer->GetType()))
|
||||
{
|
||||
LoadGeometry();
|
||||
|
||||
auto vbuf = renderer->CreateVertexBuffer(true, true);
|
||||
SetVertexBuffer(renderer->GetType(), vbuf);
|
||||
|
||||
FModelVertex* vertptr = vbuf->LockVertexBuffer(Vertices.Size());
|
||||
memcpy(vertptr, Vertices.Data(), Vertices.Size() * sizeof(FModelVertex));
|
||||
vbuf->UnlockVertexBuffer();
|
||||
|
||||
unsigned int* indxptr = vbuf->LockIndexBuffer(Triangles.Size() * 3);
|
||||
memcpy(indxptr, Triangles.Data(), Triangles.Size() * sizeof(unsigned int) * 3);
|
||||
vbuf->UnlockIndexBuffer();
|
||||
|
||||
UnloadGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void IQMModel::AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids)
|
||||
{
|
||||
for (int i = 0; i < Meshes.Size(); i++)
|
||||
{
|
||||
if (surfaceskinids && surfaceskinids[i].isValid())
|
||||
hitlist[surfaceskinids[i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
}
|
||||
|
||||
const TArray<VSMatrix>* IQMModel::AttachAnimationData()
|
||||
{
|
||||
return &FrameTransforms;
|
||||
}
|
|
@ -348,7 +348,7 @@ void FDMDModel::AddSkins(uint8_t *hitlist, const FTextureID*)
|
|||
// FDMDModel::FindFrame
|
||||
//
|
||||
//===========================================================================
|
||||
int FDMDModel::FindFrame(const char * name, bool nodefault)
|
||||
int FDMDModel::FindFrame(const char* name, bool nodefault)
|
||||
{
|
||||
for (int i=0;i<info.numFrames;i++)
|
||||
{
|
||||
|
@ -363,7 +363,7 @@ int FDMDModel::FindFrame(const char * name, bool nodefault)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID*)
|
||||
void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID*, const TArray<VSMatrix>& animationData)
|
||||
{
|
||||
if (frameno >= info.numFrames || frameno2 >= info.numFrames) return;
|
||||
|
||||
|
@ -376,11 +376,22 @@ void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int f
|
|||
|
||||
renderer->SetInterpolation(inter);
|
||||
renderer->SetMaterial(skin, false, translation);
|
||||
renderer->SetupFrame(this, frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3);
|
||||
renderer->SetupFrame(this, frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3, {});
|
||||
renderer->DrawArrays(0, lodInfo[0].numTriangles * 3);
|
||||
renderer->SetInterpolation(0.f);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Pointless for this format
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
const TArray<VSMatrix>* FDMDModel::AttachAnimationData()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========================================================================
|
||||
|
@ -552,4 +563,3 @@ void FMD2Model::LoadGeometry()
|
|||
FMD2Model::~FMD2Model()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -328,13 +328,13 @@ void FMD3Model::AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
int FMD3Model::FindFrame(const char * name, bool nodefault)
|
||||
int FMD3Model::FindFrame(const char* name, bool nodefault)
|
||||
{
|
||||
for (unsigned i = 0; i < Frames.Size(); i++)
|
||||
{
|
||||
if (!stricmp(name, Frames[i].Name)) return i;
|
||||
}
|
||||
return FErr_NotFound;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
@ -343,7 +343,7 @@ int FMD3Model::FindFrame(const char * name, bool nodefault)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids)
|
||||
void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData)
|
||||
{
|
||||
if ((unsigned)frameno >= Frames.Size() || (unsigned)frameno2 >= Frames.Size()) return;
|
||||
|
||||
|
@ -373,9 +373,19 @@ void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int f
|
|||
}
|
||||
|
||||
renderer->SetMaterial(surfaceSkin, false, translation);
|
||||
renderer->SetupFrame(this, surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices);
|
||||
renderer->SetupFrame(this, surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices, {});
|
||||
renderer->DrawElements(surf->numTriangles * 3, surf->iindex * sizeof(unsigned int));
|
||||
}
|
||||
renderer->SetInterpolation(0.f);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
const TArray<VSMatrix>* FMD3Model::AttachAnimationData()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -615,7 +615,7 @@ FVector3 FOBJModel::CalculateNormalSmooth(unsigned int vidx, unsigned int smooth
|
|||
*/
|
||||
int FOBJModel::FindFrame(const char* name, bool nodefault)
|
||||
{
|
||||
return nodefault? FErr_Singleframe : 0; // OBJs are not animated.
|
||||
return nodefault ? FErr_Singleframe : 0; // OBJs are not animated.
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -628,7 +628,7 @@ int FOBJModel::FindFrame(const char* name, bool nodefault)
|
|||
* @param inter The amount to interpolate the two frames.
|
||||
* @param translation The translation for the skin
|
||||
*/
|
||||
void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids)
|
||||
void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData)
|
||||
{
|
||||
// Prevent the model from rendering if the frame number is < 0
|
||||
if (frameno < 0 || frameno2 < 0) return;
|
||||
|
@ -657,7 +657,7 @@ void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int f
|
|||
}
|
||||
|
||||
renderer->SetMaterial(userSkin, false, translation);
|
||||
renderer->SetupFrame(this, surf->vbStart, surf->vbStart, surf->numTris * 3);
|
||||
renderer->SetupFrame(this, surf->vbStart, surf->vbStart, surf->numTris * 3, {});
|
||||
renderer->DrawArrays(0, surf->numTris * 3);
|
||||
}
|
||||
}
|
||||
|
@ -700,3 +700,14 @@ FOBJModel::~FOBJModel()
|
|||
faces.Clear();
|
||||
surfaces.Clear();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
const TArray<VSMatrix>* FOBJModel::AttachAnimationData()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -221,7 +221,7 @@ void FUE1Model::UnloadGeometry()
|
|||
groups.Reset();
|
||||
}
|
||||
|
||||
int FUE1Model::FindFrame( const char *name, bool nodefault )
|
||||
int FUE1Model::FindFrame(const char* name, bool nodefault)
|
||||
{
|
||||
// there are no named frames, but we need something here to properly interface with it. So just treat the string as an index number.
|
||||
auto index = strtol(name, nullptr, 0);
|
||||
|
@ -229,7 +229,7 @@ int FUE1Model::FindFrame( const char *name, bool nodefault )
|
|||
return index;
|
||||
}
|
||||
|
||||
void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids)
|
||||
void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData)
|
||||
{
|
||||
// the moment of magic
|
||||
if ( (frame < 0) || (frame2 < 0) || (frame >= numFrames) || (frame2 >= numFrames) ) return;
|
||||
|
@ -260,7 +260,7 @@ void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int f
|
|||
// TODO: Handle per-group render styles and other flags once functions for it are implemented
|
||||
// Future note: poly renderstyles should always be enforced unless the actor itself has a style other than Normal
|
||||
renderer->SetMaterial(sskin,false,translation);
|
||||
renderer->SetupFrame(this, vofs+frame*fsize,vofs+frame2*fsize,vsize);
|
||||
renderer->SetupFrame(this, vofs + frame * fsize, vofs + frame2 * fsize, vsize, {});
|
||||
renderer->DrawArrays(0,vsize);
|
||||
vofs += vsize;
|
||||
}
|
||||
|
@ -321,3 +321,8 @@ FUE1Model::~FUE1Model()
|
|||
{
|
||||
UnloadGeometry();
|
||||
}
|
||||
|
||||
const TArray<VSMatrix>* FUE1Model::AttachAnimationData()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -378,9 +378,9 @@ bool FVoxelModel::Load(const char * fn, int lumpnum, const char * buffer, int le
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
int FVoxelModel::FindFrame(const char * name, bool nodefault)
|
||||
int FVoxelModel::FindFrame(const char* name, bool nodefault)
|
||||
{
|
||||
return nodefault? FErr_Voxel : 0; // -2, not -1 because voxels are special.
|
||||
return nodefault ? FErr_Voxel : 0; // -2, not -1 because voxels are special.
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
@ -400,10 +400,21 @@ float FVoxelModel::getAspectFactor(float stretch)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
void FVoxelModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID*)
|
||||
void FVoxelModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID*, const TArray<VSMatrix>& animationData)
|
||||
{
|
||||
renderer->SetMaterial(skin, true, translation);
|
||||
renderer->SetupFrame(this, 0, 0, 0);
|
||||
renderer->SetupFrame(this, 0, 0, 0, {});
|
||||
renderer->DrawElements(mNumIndices, 0);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Voxels don't use bones
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
const TArray<VSMatrix>* FVoxelModel::AttachAnimationData()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -211,8 +211,10 @@ void GLBuffer::GPUWaitSync()
|
|||
|
||||
void GLVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
|
||||
{
|
||||
static int VFmtToGLFmt[] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_UNSIGNED_BYTE, GL_INT_2_10_10_10_REV };
|
||||
static uint8_t VFmtToSize[] = {4, 3, 2, 1, 4, 4};
|
||||
static int VFmtToGLFmt[] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_UNSIGNED_BYTE, GL_INT_2_10_10_10_REV, GL_UNSIGNED_BYTE };
|
||||
static uint8_t VFmtToSize[] = { 4, 3, 2, 1, 4, 4, 4 };
|
||||
static bool VFmtToNormalize[] = { false, false, false, false, true, true, false };
|
||||
static bool VFmtToIntegerType[] = { false, false, false, false, false, false, true };
|
||||
|
||||
mStride = stride;
|
||||
mNumBindingPoints = numBindingPoints;
|
||||
|
@ -226,6 +228,8 @@ void GLVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t s
|
|||
attrinf.size = VFmtToSize[attrs[i].format];
|
||||
attrinf.offset = attrs[i].offset;
|
||||
attrinf.bindingpoint = attrs[i].binding;
|
||||
attrinf.normalize = VFmtToNormalize[attrs[i].format];
|
||||
attrinf.integerType = VFmtToIntegerType[attrs[i].format];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +250,10 @@ void GLVertexBuffer::Bind(int *offsets)
|
|||
{
|
||||
glEnableVertexAttribArray(i);
|
||||
size_t ofs = offsets == nullptr ? attrinf.offset : attrinf.offset + mStride * offsets[attrinf.bindingpoint];
|
||||
glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.format != GL_FLOAT, (GLsizei)mStride, (void*)(intptr_t)ofs);
|
||||
if (!attrinf.integerType)
|
||||
glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.normalize, (GLsizei)mStride, (void*)(intptr_t)ofs);
|
||||
else
|
||||
glVertexAttribIPointer(i, attrinf.size, attrinf.format, (GLsizei)mStride, (void*)(intptr_t)ofs);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ class GLVertexBuffer : public IVertexBuffer, public GLBuffer
|
|||
{
|
||||
int bindingpoint;
|
||||
int format;
|
||||
bool normalize;
|
||||
bool integerType;
|
||||
int size;
|
||||
int offset;
|
||||
};
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "hw_skydome.h"
|
||||
#include "hw_viewpointbuffer.h"
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "hw_bonebuffer.h"
|
||||
#include "gl_shaderprogram.h"
|
||||
#include "gl_debug.h"
|
||||
#include "r_videoscale.h"
|
||||
|
@ -104,6 +105,7 @@ OpenGLFrameBuffer::~OpenGLFrameBuffer()
|
|||
if (mSkyData != nullptr) delete mSkyData;
|
||||
if (mViewpoints != nullptr) delete mViewpoints;
|
||||
if (mLights != nullptr) delete mLights;
|
||||
if (mBones != nullptr) delete mBones;
|
||||
mShadowMap.Reset();
|
||||
|
||||
if (GLRenderer)
|
||||
|
@ -171,9 +173,11 @@ void OpenGLFrameBuffer::InitializeState()
|
|||
mSkyData = new FSkyVertexBuffer;
|
||||
mViewpoints = new HWViewpointBuffer(screen->mPipelineNbr);
|
||||
mLights = new FLightBuffer(screen->mPipelineNbr);
|
||||
mBones = new BoneBuffer(screen->mPipelineNbr);
|
||||
GLRenderer = new FGLRenderer(this);
|
||||
GLRenderer->Initialize(GetWidth(), GetHeight());
|
||||
static_cast<GLDataBuffer*>(mLights->GetBuffer())->BindBase();
|
||||
static_cast<GLDataBuffer*>(mBones->GetBuffer())->BindBase();
|
||||
|
||||
mDebug = std::make_unique<FGLDebug>();
|
||||
mDebug->Update();
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "gl_shader.h"
|
||||
#include "gl_renderer.h"
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "hw_bonebuffer.h"
|
||||
#include "gl_renderbuffers.h"
|
||||
#include "gl_hwtexture.h"
|
||||
#include "gl_buffers.h"
|
||||
|
@ -133,6 +134,7 @@ bool FGLRenderState::ApplyShader()
|
|||
activeShader->muTimer.Set((double)(screen->FrameTime - firstFrame) * (double)mShaderTimer / 1000.);
|
||||
activeShader->muAlphaThreshold.Set(mAlphaThreshold);
|
||||
activeShader->muLightIndex.Set(-1);
|
||||
activeShader->muBoneIndexBase.Set(-1);
|
||||
activeShader->muClipSplit.Set(mClipSplit);
|
||||
activeShader->muSpecularMaterial.Set(mGlossiness, mSpecularLevel);
|
||||
activeShader->muAddColor.Set(mStreamData.uAddColor);
|
||||
|
@ -210,6 +212,21 @@ bool FGLRenderState::ApplyShader()
|
|||
}
|
||||
|
||||
activeShader->muLightIndex.Set(index);
|
||||
|
||||
index = mBoneIndexBase;
|
||||
if (!screen->mBones->GetBufferType() && index >= 0) // Uniform buffer fallback support
|
||||
{
|
||||
size_t start, size;
|
||||
index = screen->mBones->GetBinding(index, &start, &size);
|
||||
|
||||
if (start != mLastMappedBoneIndexBase || screen->mPipelineNbr > 1) // If multiple buffers always bind
|
||||
{
|
||||
mLastMappedBoneIndexBase = start;
|
||||
static_cast<GLDataBuffer*>(screen->mBones->GetBuffer())->BindRange(nullptr, start, size);
|
||||
}
|
||||
}
|
||||
activeShader->muBoneIndexBase.Set(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ class FGLRenderState final : public FRenderState
|
|||
int lastTranslation = 0;
|
||||
int maxBoundMaterial = -1;
|
||||
size_t mLastMappedLightIndex = SIZE_MAX;
|
||||
size_t mLastMappedBoneIndexBase = SIZE_MAX;
|
||||
|
||||
IVertexBuffer *mCurrentVertexBuffer;
|
||||
int mCurrentVertexOffsets[2]; // one per binding point
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "shaderuniforms.h"
|
||||
#include "hw_viewpointuniforms.h"
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "hw_bonebuffer.h"
|
||||
#include "i_specialpaths.h"
|
||||
#include "printf.h"
|
||||
#include "version.h"
|
||||
|
@ -276,6 +277,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
|
|||
// dynamic lights
|
||||
uniform int uLightIndex;
|
||||
|
||||
// bone animation
|
||||
uniform int uBoneIndexBase;
|
||||
|
||||
// Blinn glossiness and specular level
|
||||
uniform vec2 uSpecularMaterial;
|
||||
|
||||
|
@ -297,6 +301,19 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
|
|||
};
|
||||
#endif
|
||||
|
||||
// bone matrix buffers
|
||||
#ifdef SHADER_STORAGE_BONES
|
||||
layout(std430, binding = 7) buffer BoneBufferSSO
|
||||
{
|
||||
mat4 bones[];
|
||||
};
|
||||
#elif defined NUM_UBO_BONES
|
||||
uniform BoneBufferUBO
|
||||
{
|
||||
mat4 bones[NUM_UBO_BONES];
|
||||
};
|
||||
#endif
|
||||
|
||||
// textures
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D ShadowMap;
|
||||
|
@ -369,20 +386,21 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
|
|||
FString vp_comb;
|
||||
|
||||
assert(screen->mLights != NULL);
|
||||
assert(screen->mBones != NULL);
|
||||
|
||||
bool lightbuffertype = screen->mLights->GetBufferType();
|
||||
unsigned int lightbuffersize = screen->mLights->GetBlockSize();
|
||||
if (!lightbuffertype)
|
||||
{
|
||||
vp_comb.Format("#version 330 core\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize);
|
||||
vp_comb.Format("#version 330 core\n#define NUM_UBO_LIGHTS %d\n#define NUM_UBO_BONES %d\n", lightbuffersize, screen->mBones->GetBlockSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
// This differentiation is for Intel which do not seem to expose the full extension, even if marked as required.
|
||||
if (gl.glslversion < 4.3f)
|
||||
vp_comb = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n";
|
||||
vp_comb = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n#define SHADER_STORAGE_BONES\n";
|
||||
else
|
||||
vp_comb = "#version 430 core\n#define SHADER_STORAGE_LIGHTS\n";
|
||||
vp_comb = "#version 430 core\n#define SHADER_STORAGE_LIGHTS\n#define SHADER_STORAGE_BONES\n";
|
||||
}
|
||||
|
||||
if ((gl.flags & RFL_SHADER_STORAGE_BUFFER) && screen->allowSSBO())
|
||||
|
@ -576,6 +594,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
|
|||
muLightParms.Init(hShader, "uLightAttr");
|
||||
muClipSplit.Init(hShader, "uClipSplit");
|
||||
muLightIndex.Init(hShader, "uLightIndex");
|
||||
muBoneIndexBase.Init(hShader, "uBoneIndexBase");
|
||||
muFogColor.Init(hShader, "uFogColor");
|
||||
muDynLightColor.Init(hShader, "uDynLightColor");
|
||||
muObjectColor.Init(hShader, "uObjectColor");
|
||||
|
@ -610,6 +629,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char *
|
|||
{
|
||||
int tempindex = glGetUniformBlockIndex(hShader, "LightBufferUBO");
|
||||
if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, LIGHTBUF_BINDINGPOINT);
|
||||
|
||||
tempindex = glGetUniformBlockIndex(hShader, "BoneBufferUBO");
|
||||
if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, BONEBUF_BINDINGPOINT);
|
||||
}
|
||||
int tempindex = glGetUniformBlockIndex(hShader, "ViewpointUBO");
|
||||
if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, VIEWPOINT_BINDINGPOINT);
|
||||
|
|
|
@ -241,6 +241,7 @@ class FShader
|
|||
FBufferedUniform4f muLightParms;
|
||||
FBufferedUniform2f muClipSplit;
|
||||
FBufferedUniform1i muLightIndex;
|
||||
FBufferedUniform1i muBoneIndexBase;
|
||||
FBufferedUniformPE muFogColor;
|
||||
FBufferedUniform4f muDynLightColor;
|
||||
FBufferedUniformPE muObjectColor;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
#include "hw_skydome.h"
|
||||
#include "hw_viewpointbuffer.h"
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "hw_bonebuffer.h"
|
||||
#include "gles_shaderprogram.h"
|
||||
#include "r_videoscale.h"
|
||||
#include "gles_buffers.h"
|
||||
|
@ -101,6 +102,7 @@ OpenGLFrameBuffer::~OpenGLFrameBuffer()
|
|||
if (mSkyData != nullptr) delete mSkyData;
|
||||
if (mViewpoints != nullptr) delete mViewpoints;
|
||||
if (mLights != nullptr) delete mLights;
|
||||
if (mBones != nullptr) delete mBones;
|
||||
mShadowMap.Reset();
|
||||
|
||||
if (GLRenderer)
|
||||
|
@ -154,9 +156,11 @@ void OpenGLFrameBuffer::InitializeState()
|
|||
mSkyData = new FSkyVertexBuffer;
|
||||
mViewpoints = new HWViewpointBuffer(mPipelineNbr);
|
||||
mLights = new FLightBuffer(mPipelineNbr);
|
||||
mBones = new BoneBuffer(mPipelineNbr);
|
||||
GLRenderer = new FGLRenderer(this);
|
||||
GLRenderer->Initialize(GetWidth(), GetHeight());
|
||||
static_cast<GLDataBuffer*>(mLights->GetBuffer())->BindBase();
|
||||
static_cast<GLDataBuffer*>(mBones->GetBuffer())->BindBase();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -17,6 +17,7 @@ class FFlatVertexBuffer;
|
|||
class FSkyVertexBuffer;
|
||||
class HWPortal;
|
||||
class FLightBuffer;
|
||||
class BoneBuffer;
|
||||
class DPSprite;
|
||||
class FGLRenderBuffers;
|
||||
class FGL2DDrawer;
|
||||
|
|
|
@ -25,6 +25,8 @@ enum
|
|||
VATTR_NORMAL,
|
||||
VATTR_NORMAL2,
|
||||
VATTR_LIGHTMAP,
|
||||
VATTR_BONEWEIGHT,
|
||||
VATTR_BONESELECTOR,
|
||||
VATTR_MAX
|
||||
};
|
||||
|
||||
|
@ -36,6 +38,7 @@ enum EVertexAttributeFormat
|
|||
VFmt_Float,
|
||||
VFmt_Byte4,
|
||||
VFmt_Packed_A2R10G10B10,
|
||||
VFmt_Byte4_UInt
|
||||
};
|
||||
|
||||
struct FVertexBufferAttribute
|
||||
|
|
112
src/common/rendering/hwrenderer/data/hw_bonebuffer.cpp
Normal file
112
src/common/rendering/hwrenderer/data/hw_bonebuffer.cpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2014-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "hw_bonebuffer.h"
|
||||
#include "hw_dynlightdata.h"
|
||||
#include "shaderuniforms.h"
|
||||
|
||||
static const int BONE_SIZE = (16*sizeof(float));
|
||||
|
||||
BoneBuffer::BoneBuffer(int pipelineNbr) : mPipelineNbr(pipelineNbr)
|
||||
{
|
||||
int maxNumberOfBones = 80000;
|
||||
|
||||
mBufferSize = maxNumberOfBones;
|
||||
mByteSize = mBufferSize * BONE_SIZE;
|
||||
|
||||
// Hack alert: On Intel's GL driver SSBO's perform quite worse than UBOs.
|
||||
// We only want to disable using SSBOs for bones but not disable the feature entirely.
|
||||
// Note that using an uniform buffer here will limit the number of bones per model so it isn't done for NVidia and AMD.
|
||||
if (screen->IsVulkan() || screen->IsPoly() || ((screen->hwcaps & RFL_SHADER_STORAGE_BUFFER) && screen->allowSSBO() && !strstr(screen->vendorstring, "Intel")))
|
||||
{
|
||||
mBufferType = true;
|
||||
mBlockAlign = 0;
|
||||
mBlockSize = mBufferSize;
|
||||
mMaxUploadSize = mBlockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
mBufferType = false;
|
||||
mBlockSize = screen->maxuniformblock / BONE_SIZE;
|
||||
mBlockAlign = screen->uniformblockalignment / BONE_SIZE;
|
||||
mMaxUploadSize = (mBlockSize - mBlockAlign);
|
||||
}
|
||||
|
||||
for (int n = 0; n < mPipelineNbr; n++)
|
||||
{
|
||||
mBufferPipeline[n] = screen->CreateDataBuffer(BONEBUF_BINDINGPOINT, mBufferType, false);
|
||||
mBufferPipeline[n]->SetData(mByteSize, nullptr, BufferUsageType::Persistent);
|
||||
}
|
||||
|
||||
Clear();
|
||||
}
|
||||
|
||||
BoneBuffer::~BoneBuffer()
|
||||
{
|
||||
delete mBuffer;
|
||||
}
|
||||
|
||||
void BoneBuffer::Clear()
|
||||
{
|
||||
mIndex = 0;
|
||||
|
||||
mPipelinePos++;
|
||||
mPipelinePos %= mPipelineNbr;
|
||||
|
||||
mBuffer = mBufferPipeline[mPipelinePos];
|
||||
}
|
||||
|
||||
int BoneBuffer::UploadBones(const TArray<VSMatrix>& bones)
|
||||
{
|
||||
int totalsize = bones.Size();
|
||||
if (totalsize > (int)mMaxUploadSize)
|
||||
{
|
||||
totalsize = mMaxUploadSize;
|
||||
}
|
||||
|
||||
uint8_t *mBufferPointer = (uint8_t*)mBuffer->Memory();
|
||||
assert(mBufferPointer != nullptr);
|
||||
if (mBufferPointer == nullptr) return -1;
|
||||
if (totalsize <= 0) return -1; // there are no bones
|
||||
|
||||
unsigned int thisindex = mIndex.fetch_add(totalsize);
|
||||
|
||||
if (thisindex + totalsize <= mBufferSize)
|
||||
{
|
||||
memcpy(mBufferPointer + thisindex * BONE_SIZE, bones.Data(), totalsize * BONE_SIZE);
|
||||
return thisindex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1; // Buffer is full. Since it is being used live at the point of the upload we cannot do much here but to abort.
|
||||
}
|
||||
}
|
||||
|
||||
int BoneBuffer::GetBinding(unsigned int index, size_t* pOffset, size_t* pSize)
|
||||
{
|
||||
// this function will only get called if a uniform buffer is used. For a shader storage buffer we only need to bind the buffer once at the start.
|
||||
unsigned int offset = (index / mBlockAlign) * mBlockAlign;
|
||||
|
||||
*pOffset = offset * BONE_SIZE;
|
||||
*pSize = mBlockSize * BONE_SIZE;
|
||||
return (index - offset);
|
||||
}
|
43
src/common/rendering/hwrenderer/data/hw_bonebuffer.h
Normal file
43
src/common/rendering/hwrenderer/data/hw_bonebuffer.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "tarray.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include "common/utility/matrix.h"
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
class FRenderState;
|
||||
|
||||
class BoneBuffer
|
||||
{
|
||||
IDataBuffer *mBuffer;
|
||||
IDataBuffer* mBufferPipeline[HW_MAX_PIPELINE_BUFFERS];
|
||||
int mPipelineNbr;
|
||||
int mPipelinePos = 0;
|
||||
|
||||
bool mBufferType;
|
||||
std::atomic<unsigned int> mIndex;
|
||||
unsigned int mBlockAlign;
|
||||
unsigned int mBlockSize;
|
||||
unsigned int mBufferSize;
|
||||
unsigned int mByteSize;
|
||||
unsigned int mMaxUploadSize;
|
||||
|
||||
public:
|
||||
BoneBuffer(int pipelineNbr = 1);
|
||||
~BoneBuffer();
|
||||
|
||||
void Clear();
|
||||
int UploadBones(const TArray<VSMatrix> &bones);
|
||||
void Map() { mBuffer->Map(); }
|
||||
void Unmap() { mBuffer->Unmap(); }
|
||||
unsigned int GetBlockSize() const { return mBlockSize; }
|
||||
bool GetBufferType() const { return mBufferType; }
|
||||
int GetBinding(unsigned int index, size_t* pOffset, size_t* pSize);
|
||||
|
||||
// OpenGL needs the buffer to mess around with the binding.
|
||||
IDataBuffer* GetBuffer() const
|
||||
{
|
||||
return mBuffer;
|
||||
}
|
||||
};
|
|
@ -47,10 +47,12 @@ FModelVertexBuffer::FModelVertexBuffer(bool needindex, bool singleframe)
|
|||
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FModelVertex, u) },
|
||||
{ 0, VATTR_NORMAL, VFmt_Packed_A2R10G10B10, (int)myoffsetof(FModelVertex, packedNormal) },
|
||||
{ 0, VATTR_LIGHTMAP, VFmt_Float3, (int)myoffsetof(FModelVertex, lu) },
|
||||
{ 0, VATTR_BONESELECTOR, VFmt_Byte4_UInt, (int)myoffsetof(FModelVertex, boneselector[0])},
|
||||
{ 0, VATTR_BONEWEIGHT, VFmt_Byte4, (int)myoffsetof(FModelVertex, boneweight[0]) },
|
||||
{ 1, VATTR_VERTEX2, VFmt_Float3, (int)myoffsetof(FModelVertex, x) },
|
||||
{ 1, VATTR_NORMAL2, VFmt_Packed_A2R10G10B10, (int)myoffsetof(FModelVertex, packedNormal) }
|
||||
};
|
||||
mVertexBuffer->SetFormat(2, 6, sizeof(FModelVertex), format);
|
||||
mVertexBuffer->SetFormat(2, 8, sizeof(FModelVertex), format);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
|
@ -217,6 +217,7 @@ protected:
|
|||
uint8_t mBrightmapEnabled : 1;
|
||||
|
||||
int mLightIndex;
|
||||
int mBoneIndexBase;
|
||||
int mSpecialEffect;
|
||||
int mTextureMode;
|
||||
int mTextureClamp;
|
||||
|
@ -278,6 +279,7 @@ public:
|
|||
mLightParms[3] = -1.f;
|
||||
mSpecialEffect = EFF_NONE;
|
||||
mLightIndex = -1;
|
||||
mBoneIndexBase = -1;
|
||||
mStreamData.uInterpolationFactor = 0;
|
||||
mRenderStyle = DefaultRenderStyle();
|
||||
mMaterial.Reset();
|
||||
|
@ -568,6 +570,11 @@ public:
|
|||
mLightIndex = index;
|
||||
}
|
||||
|
||||
void SetBoneIndexBase(int index)
|
||||
{
|
||||
mBoneIndexBase = index;
|
||||
}
|
||||
|
||||
void SetRenderStyle(FRenderStyle rs)
|
||||
{
|
||||
mRenderStyle = rs;
|
||||
|
|
|
@ -11,7 +11,8 @@ enum
|
|||
VIEWPOINT_BINDINGPOINT = 3,
|
||||
LIGHTNODES_BINDINGPOINT = 4,
|
||||
LIGHTLINES_BINDINGPOINT = 5,
|
||||
LIGHTLIST_BINDINGPOINT = 6
|
||||
LIGHTLIST_BINDINGPOINT = 6,
|
||||
BONEBUF_BINDINGPOINT = 7
|
||||
};
|
||||
|
||||
enum class UniformType
|
||||
|
|
|
@ -9,6 +9,8 @@ struct FModelVertex
|
|||
unsigned packedNormal; // normal vector as GL_INT_2_10_10_10_REV.
|
||||
float lu, lv; // lightmap texture coordinates
|
||||
float lindex; // lightmap texture index
|
||||
uint8_t boneselector[4];
|
||||
uint8_t boneweight[4];
|
||||
|
||||
void Set(float xx, float yy, float zz, float uu, float vv)
|
||||
{
|
||||
|
@ -30,6 +32,22 @@ struct FModelVertex
|
|||
int inw = 0;
|
||||
packedNormal = (inw << 30) | ((inz & 1023) << 20) | ((iny & 1023) << 10) | (inx & 1023);
|
||||
}
|
||||
|
||||
void SetBoneSelector(int x, int y, int z, int w)
|
||||
{
|
||||
boneselector[0] = x;
|
||||
boneselector[1] = y;
|
||||
boneselector[2] = z;
|
||||
boneselector[3] = w;
|
||||
}
|
||||
|
||||
void SetBoneWeight(int x, int y, int z, int w)
|
||||
{
|
||||
boneweight[0] = x;
|
||||
boneweight[1] = y;
|
||||
boneweight[2] = z;
|
||||
boneweight[3] = w;
|
||||
}
|
||||
};
|
||||
|
||||
#define VMO ((FModelVertex*)nullptr)
|
||||
|
|
|
@ -59,6 +59,7 @@ struct HWDrawInfo;
|
|||
class FMaterial;
|
||||
class FGameTexture;
|
||||
class FRenderState;
|
||||
class BoneBuffer;
|
||||
|
||||
enum EHWCaps
|
||||
{
|
||||
|
@ -143,6 +144,7 @@ public:
|
|||
FFlatVertexBuffer *mVertexData = nullptr; // Global vertex data
|
||||
HWViewpointBuffer *mViewpoints = nullptr; // Viewpoint render data.
|
||||
FLightBuffer *mLights = nullptr; // Dynamic lights
|
||||
BoneBuffer* mBones = nullptr; // Model bones
|
||||
IShadowMap mShadowMap;
|
||||
|
||||
IntRect mScreenViewport;
|
||||
|
|
|
@ -85,6 +85,7 @@ void VkDescriptorSetManager::UpdateHWBufferSet()
|
|||
.AddBuffer(HWBufferSet.get(), 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, fb->GetBufferManager()->MatrixBuffer->UniformBuffer->mBuffer.get(), 0, sizeof(MatricesUBO))
|
||||
.AddBuffer(HWBufferSet.get(), 2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, fb->GetBufferManager()->StreamBuffer->UniformBuffer->mBuffer.get(), 0, sizeof(StreamUBO))
|
||||
.AddBuffer(HWBufferSet.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetBufferManager()->LightBufferSSO->mBuffer.get())
|
||||
.AddBuffer(HWBufferSet.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetBufferManager()->BoneBufferSSO->mBuffer.get())
|
||||
.Execute(fb->device);
|
||||
}
|
||||
|
||||
|
@ -252,6 +253,7 @@ void VkDescriptorSetManager::CreateHWBufferSetLayout()
|
|||
.AddBinding(1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
|
||||
.AddBinding(2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
|
||||
.AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
|
||||
.AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT)
|
||||
.DebugName("VkDescriptorSetManager.HWBufferSetLayout")
|
||||
.Create(fb->device);
|
||||
}
|
||||
|
@ -271,7 +273,7 @@ void VkDescriptorSetManager::CreateHWBufferPool()
|
|||
{
|
||||
HWBufferDescriptorPool = DescriptorPoolBuilder()
|
||||
.AddPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 3 * maxSets)
|
||||
.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1 * maxSets)
|
||||
.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2 * maxSets)
|
||||
.MaxSets(maxSets)
|
||||
.DebugName("VkDescriptorSetManager.HWBufferDescriptorPool")
|
||||
.Create(fb->device);
|
||||
|
|
|
@ -230,10 +230,11 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipeli
|
|||
VK_FORMAT_R32G32_SFLOAT,
|
||||
VK_FORMAT_R32_SFLOAT,
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VK_FORMAT_A2B10G10R10_SNORM_PACK32
|
||||
VK_FORMAT_A2B10G10R10_SNORM_PACK32,
|
||||
VK_FORMAT_R8G8B8A8_UINT
|
||||
};
|
||||
|
||||
bool inputLocations[7] = { false, false, false, false, false, false, false };
|
||||
bool inputLocations[VATTR_MAX] = {};
|
||||
|
||||
for (size_t i = 0; i < vfmt.Attrs.size(); i++)
|
||||
{
|
||||
|
@ -243,10 +244,10 @@ std::unique_ptr<VulkanPipeline> VkRenderPassSetup::CreatePipeline(const VkPipeli
|
|||
}
|
||||
|
||||
// Vulkan requires an attribute binding for each location specified in the shader
|
||||
for (int i = 0; i < 7; i++)
|
||||
for (int i = 0; i < VATTR_MAX; i++)
|
||||
{
|
||||
if (!inputLocations[i])
|
||||
builder.AddVertexAttribute(i, 0, VK_FORMAT_R32G32B32_SFLOAT, 0);
|
||||
builder.AddVertexAttribute(i, 0, i != 8 ? VK_FORMAT_R32G32B32_SFLOAT : VK_FORMAT_R8G8B8A8_UINT, 0);
|
||||
}
|
||||
|
||||
builder.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT);
|
||||
|
|
|
@ -391,6 +391,7 @@ void VkRenderState::ApplyPushConstants()
|
|||
}
|
||||
|
||||
mPushConstants.uLightIndex = mLightIndex;
|
||||
mPushConstants.uBoneIndexBase = mBoneIndexBase;
|
||||
mPushConstants.uDataIndex = mStreamBufferWriter.DataIndex();
|
||||
|
||||
auto passManager = fb->GetRenderPassManager();
|
||||
|
|
|
@ -229,6 +229,12 @@ static const char *shaderBindings = R"(
|
|||
vec4 lights[];
|
||||
};
|
||||
|
||||
// bone matrix buffers
|
||||
layout(set = 1, binding = 4, std430) buffer BoneBufferSSO
|
||||
{
|
||||
mat4 bones[];
|
||||
};
|
||||
|
||||
// textures
|
||||
layout(set = 2, binding = 0) uniform sampler2D tex;
|
||||
layout(set = 2, binding = 1) uniform sampler2D texture2;
|
||||
|
@ -262,8 +268,11 @@ static const char *shaderBindings = R"(
|
|||
// Blinn glossiness and specular level
|
||||
vec2 uSpecularMaterial;
|
||||
|
||||
// bone animation
|
||||
int uBoneIndexBase;
|
||||
|
||||
int uDataIndex;
|
||||
int padding1, padding2, padding3;
|
||||
int padding2, padding3;
|
||||
};
|
||||
|
||||
// material types
|
||||
|
|
|
@ -50,6 +50,9 @@ struct PushConstants
|
|||
// Blinn glossiness and specular level
|
||||
FVector2 uSpecularMaterial;
|
||||
|
||||
// bone animation
|
||||
int uBoneIndexBase;
|
||||
|
||||
int uDataIndex;
|
||||
int padding1, padding2, padding3;
|
||||
};
|
||||
|
|
|
@ -58,7 +58,7 @@ void VkBufferManager::RemoveBuffer(VkHardwareBuffer* buffer)
|
|||
buffer->fb = nullptr;
|
||||
Buffers.erase(buffer->it);
|
||||
|
||||
for (VkHardwareDataBuffer** knownbuf : { &ViewpointUBO, &LightBufferSSO, &LightNodes, &LightLines, &LightList })
|
||||
for (VkHardwareDataBuffer** knownbuf : { &ViewpointUBO, &LightBufferSSO, &LightNodes, &LightLines, &LightList, &BoneBufferSSO })
|
||||
{
|
||||
if (buffer == *knownbuf) *knownbuf = nullptr;
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ IDataBuffer* VkBufferManager::CreateDataBuffer(int bindingpoint, bool ssbo, bool
|
|||
case LIGHTNODES_BINDINGPOINT: LightNodes = buffer; break;
|
||||
case LIGHTLINES_BINDINGPOINT: LightLines = buffer; break;
|
||||
case LIGHTLIST_BINDINGPOINT: LightList = buffer; break;
|
||||
case BONEBUF_BINDINGPOINT: BoneBufferSSO = buffer; break;
|
||||
case POSTPROCESS_BINDINGPOINT: break;
|
||||
default: break;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ public:
|
|||
VkHardwareDataBuffer* LightNodes = nullptr;
|
||||
VkHardwareDataBuffer* LightLines = nullptr;
|
||||
VkHardwareDataBuffer* LightList = nullptr;
|
||||
VkHardwareDataBuffer* BoneBufferSSO = nullptr;
|
||||
|
||||
std::unique_ptr<VkStreamBuffer> MatrixBuffer;
|
||||
std::unique_ptr<VkStreamBuffer> StreamBuffer;
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "flatvertices.h"
|
||||
#include "hwrenderer/data/shaderuniforms.h"
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "hw_bonebuffer.h"
|
||||
|
||||
#include "vk_framebuffer.h"
|
||||
#include "vk_hwbuffer.h"
|
||||
|
@ -98,6 +99,7 @@ VulkanFrameBuffer::~VulkanFrameBuffer()
|
|||
delete mSkyData;
|
||||
delete mViewpoints;
|
||||
delete mLights;
|
||||
delete mBones;
|
||||
mShadowMap.Reset();
|
||||
|
||||
if (mDescriptorSetManager)
|
||||
|
@ -155,6 +157,7 @@ void VulkanFrameBuffer::InitializeState()
|
|||
mSkyData = new FSkyVertexBuffer;
|
||||
mViewpoints = new HWViewpointBuffer;
|
||||
mLights = new FLightBuffer();
|
||||
mBones = new BoneBuffer();
|
||||
|
||||
mShaderManager.reset(new VkShaderManager(this));
|
||||
mDescriptorSetManager->Init();
|
||||
|
|
|
@ -98,6 +98,21 @@ VSMatrix::multMatrix(const float *aMatrix)
|
|||
}
|
||||
#endif
|
||||
|
||||
void VSMatrix::multQuaternion(const TVector4<FLOATTYPE>& q)
|
||||
{
|
||||
FLOATTYPE m[16] = { FLOATTYPE(0.0) };
|
||||
m[0 * 4 + 0] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.Y * q.Y - FLOATTYPE(2.0) * q.Z * q.Z;
|
||||
m[1 * 4 + 0] = FLOATTYPE(2.0) * q.X * q.Y - FLOATTYPE(2.0) * q.W * q.Z;
|
||||
m[2 * 4 + 0] = FLOATTYPE(2.0) * q.X * q.Z + FLOATTYPE(2.0) * q.W * q.Y;
|
||||
m[0 * 4 + 1] = FLOATTYPE(2.0) * q.X * q.Y + FLOATTYPE(2.0) * q.W * q.Z;
|
||||
m[1 * 4 + 1] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.X * q.X - FLOATTYPE(2.0) * q.Z * q.Z;
|
||||
m[2 * 4 + 1] = FLOATTYPE(2.0) * q.Y * q.Z - FLOATTYPE(2.0) * q.W * q.X;
|
||||
m[0 * 4 + 2] = FLOATTYPE(2.0) * q.X * q.Z - FLOATTYPE(2.0) * q.W * q.Y;
|
||||
m[1 * 4 + 2] = FLOATTYPE(2.0) * q.Y * q.Z + FLOATTYPE(2.0) * q.W * q.X;
|
||||
m[2 * 4 + 2] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.X * q.X - FLOATTYPE(2.0) * q.Y * q.Y;
|
||||
m[3 * 4 + 3] = FLOATTYPE(1.0);
|
||||
multMatrix(m);
|
||||
}
|
||||
|
||||
|
||||
// gl LoadMatrix implementation
|
||||
|
@ -129,6 +144,29 @@ VSMatrix::translate(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
|
|||
mMatrix[14] = mMatrix[2] * x + mMatrix[6] * y + mMatrix[10] * z + mMatrix[14];
|
||||
}
|
||||
|
||||
void VSMatrix::transpose()
|
||||
{
|
||||
FLOATTYPE original[16];
|
||||
for (int cnt = 0; cnt < 16; cnt++)
|
||||
original[cnt] = mMatrix[cnt];
|
||||
|
||||
mMatrix[0] = original[0];
|
||||
mMatrix[1] = original[4];
|
||||
mMatrix[2] = original[8];
|
||||
mMatrix[3] = original[12];
|
||||
mMatrix[4] = original[1];
|
||||
mMatrix[5] = original[5];
|
||||
mMatrix[6] = original[9];
|
||||
mMatrix[7] = original[13];
|
||||
mMatrix[8] = original[2];
|
||||
mMatrix[9] = original[6];
|
||||
mMatrix[10] = original[10];
|
||||
mMatrix[11] = original[14];
|
||||
mMatrix[12] = original[3];
|
||||
mMatrix[13] = original[7];
|
||||
mMatrix[14] = original[11];
|
||||
mMatrix[15] = original[15];
|
||||
}
|
||||
|
||||
// gl Scale implementation
|
||||
void
|
||||
|
|
|
@ -53,6 +53,7 @@ class VSMatrix {
|
|||
{
|
||||
multMatrix(aMatrix.mMatrix);
|
||||
}
|
||||
void multQuaternion(const TVector4<FLOATTYPE>& q);
|
||||
void loadMatrix(const FLOATTYPE *aMatrix);
|
||||
#ifdef USE_DOUBLE
|
||||
void loadMatrix(const float *aMatrix);
|
||||
|
|
|
@ -343,10 +343,18 @@ 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 = {};
|
||||
|
||||
if (smf->animationIDs[i] >= 0)
|
||||
{
|
||||
FModel* animation = Models[smf->animationIDs[i]];
|
||||
animationData = animation->AttachAnimationData();
|
||||
}
|
||||
|
||||
if (smfNext && modelframe != modelframenext)
|
||||
mdl->RenderFrame(renderer, tex, modelframe, modelframenext, inter, translation, ssidp);
|
||||
mdl->RenderFrame(renderer, tex, modelframe, modelframenext, inter, translation, ssidp, *animationData);
|
||||
else
|
||||
mdl->RenderFrame(renderer, tex, modelframe, modelframe, 0.f, translation, ssidp);
|
||||
mdl->RenderFrame(renderer, tex, modelframe, modelframe, 0.f, translation, ssidp, *animationData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -491,6 +499,7 @@ static void ParseModelDefLump(int Lump)
|
|||
initArray(smf.modelIDs, smf.modelsAmount, -1);
|
||||
initArray(smf.skinIDs, smf.modelsAmount, FNullTextureID());
|
||||
initArray(smf.surfaceskinIDs, smf.modelsAmount * MD3_MAX_SURFACES, FNullTextureID());
|
||||
initArray(smf.animationIDs, smf.modelsAmount, -1);
|
||||
initArray(smf.modelframes, smf.modelsAmount, 0);
|
||||
|
||||
sc.RestorePos(scPos);
|
||||
|
@ -525,6 +534,26 @@ static void ParseModelDefLump(int Lump)
|
|||
Printf("%s: model not found in %s\n", sc.String, path.GetChars());
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("animation"))
|
||||
{
|
||||
sc.MustGetNumber();
|
||||
index = sc.Number;
|
||||
if (index < 0)
|
||||
{
|
||||
sc.ScriptError("Animation index must be 0 or greater in %s", type->TypeName.GetChars());
|
||||
}
|
||||
else if (index >= smf.modelsAmount)
|
||||
{
|
||||
sc.ScriptError("Too many models in %s", type->TypeName.GetChars());
|
||||
}
|
||||
sc.MustGetString();
|
||||
FixPathSeperator(sc.String);
|
||||
smf.animationIDs[index] = FindModel(path.GetChars(), sc.String);
|
||||
if (smf.animationIDs[index] == -1)
|
||||
{
|
||||
Printf("%s: animation model not found in %s\n", sc.String, path.GetChars());
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("scale"))
|
||||
{
|
||||
sc.MustGetFloat();
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "v_draw.h"
|
||||
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "hw_bonebuffer.h"
|
||||
#include "hw_cvars.h"
|
||||
#include "hwrenderer/data/hw_viewpointbuffer.h"
|
||||
#include "hwrenderer/scene/hw_fakeflat.h"
|
||||
|
@ -276,6 +277,7 @@ void WriteSavePic(player_t* player, FileWriter* file, int width, int height)
|
|||
screen->mVertexData->Reset();
|
||||
RenderState.SetVertexBuffer(screen->mVertexData);
|
||||
screen->mLights->Clear();
|
||||
screen->mBones->Clear();
|
||||
screen->mViewpoints->Clear();
|
||||
|
||||
// This shouldn't overwrite the global viewpoint even for a short time.
|
||||
|
@ -340,6 +342,7 @@ sector_t* RenderView(player_t* player)
|
|||
else r_viewpoint.TicFrac = I_GetTimeFrac();
|
||||
|
||||
screen->mLights->Clear();
|
||||
screen->mBones->Clear();
|
||||
screen->mViewpoints->Clear();
|
||||
|
||||
// NoInterpolateView should have no bearing on camera textures, but needs to be preserved for the main view below.
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hw_renderstate.h"
|
||||
#include "hwrenderer/scene/hw_portal.h"
|
||||
#include "hw_bonebuffer.h"
|
||||
#include "hw_models.h"
|
||||
|
||||
CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE)
|
||||
|
@ -71,6 +72,7 @@ void FHWModelRenderer::BeginDrawModel(FRenderStyle style, FSpriteModelFrame *smf
|
|||
|
||||
void FHWModelRenderer::EndDrawModel(FRenderStyle style, FSpriteModelFrame *smf)
|
||||
{
|
||||
state.SetBoneIndexBase(-1);
|
||||
state.EnableModelMatrix(false);
|
||||
state.SetDepthFunc(DF_Less);
|
||||
if (!(style == DefaultRenderStyle()) && !(smf->flags & MDL_DONTCULLBACKFACES))
|
||||
|
@ -134,9 +136,10 @@ void FHWModelRenderer::DrawElements(int numIndices, size_t offset)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
void FHWModelRenderer::SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size)
|
||||
void FHWModelRenderer::SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size, const TArray<VSMatrix>& bones)
|
||||
{
|
||||
auto mdbuff = static_cast<FModelVertexBuffer*>(model->GetVertexBuffer(GetType()));
|
||||
state.SetBoneIndexBase(screen->mBones->UploadBones(bones));
|
||||
state.SetVertexBuffer(mdbuff->vertexBuffer(), frame1, frame2);
|
||||
if (mdbuff->indexBuffer()) state.SetIndexBuffer(mdbuff->indexBuffer());
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ public:
|
|||
void SetMaterial(FGameTexture *skin, bool clampNoFilter, int translation) override;
|
||||
void DrawArrays(int start, int count) override;
|
||||
void DrawElements(int numIndices, size_t offset) override;
|
||||
void SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size) override;
|
||||
void SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size, const TArray<VSMatrix>& bones) override;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "hw_viewpointbuffer.h"
|
||||
#include "flatvertices.h"
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "hw_bonebuffer.h"
|
||||
#include "hw_vrmodes.h"
|
||||
#include "hw_clipper.h"
|
||||
#include "v_draw.h"
|
||||
|
@ -444,6 +445,7 @@ void HWDrawInfo::CreateScene(bool drawpsprites)
|
|||
// clip the scene and fill the drawlists
|
||||
screen->mVertexData->Map();
|
||||
screen->mLights->Map();
|
||||
screen->mBones->Map();
|
||||
|
||||
RenderBSP(Level->HeadNode(), drawpsprites);
|
||||
|
||||
|
@ -456,6 +458,7 @@ void HWDrawInfo::CreateScene(bool drawpsprites)
|
|||
PrepareUnhandledMissingTextures();
|
||||
DispatchRenderHacks();
|
||||
screen->mLights->Unmap();
|
||||
screen->mBones->Unmap();
|
||||
screen->mVertexData->Unmap();
|
||||
|
||||
ProcessAll.Unclock();
|
||||
|
@ -696,6 +699,7 @@ void HWDrawInfo::DrawCoronas(FRenderState& state)
|
|||
state.SetDepthMask(true);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Draws player sprites and color blend
|
||||
|
|
|
@ -12,6 +12,8 @@ layout(location = 3) in vec4 aVertex2;
|
|||
layout(location = 4) in vec4 aNormal;
|
||||
layout(location = 5) in vec4 aNormal2;
|
||||
layout(location = 6) in vec3 aLightmap;
|
||||
layout(location = 7) in vec4 aBoneWeight;
|
||||
layout(location = 8) in uvec4 aBoneSelector;
|
||||
|
||||
layout(location = 2) out vec4 pixelpos;
|
||||
layout(location = 3) out vec3 glowdist;
|
||||
|
@ -25,15 +27,25 @@ layout(location = 7) out vec4 ClipDistanceA;
|
|||
layout(location = 8) out vec4 ClipDistanceB;
|
||||
#endif
|
||||
|
||||
struct BonesResult
|
||||
{
|
||||
vec3 Normal;
|
||||
vec4 Position;
|
||||
};
|
||||
|
||||
BonesResult ApplyBones();
|
||||
|
||||
void main()
|
||||
{
|
||||
float ClipDistance0, ClipDistance1, ClipDistance2, ClipDistance3, ClipDistance4;
|
||||
|
||||
vec2 parmTexCoord;
|
||||
vec4 parmPosition;
|
||||
|
||||
|
||||
BonesResult bones = ApplyBones();
|
||||
|
||||
parmTexCoord = aTexCoord;
|
||||
parmPosition = aPosition;
|
||||
parmPosition = bones.Position;
|
||||
|
||||
#ifndef SIMPLE
|
||||
vec4 worldcoord = ModelMatrix * mix(parmPosition, aVertex2, uInterpolationFactor);
|
||||
|
@ -82,14 +94,7 @@ void main()
|
|||
ClipDistance4 = worldcoord.y - ((uSplitBottomPlane.w + uSplitBottomPlane.x * worldcoord.x + uSplitBottomPlane.y * worldcoord.z) * uSplitBottomPlane.z);
|
||||
}
|
||||
|
||||
#ifdef HAS_UNIFORM_VERTEX_DATA
|
||||
if ((useVertexData & 2) == 0)
|
||||
vWorldNormal = NormalModelMatrix * vec4(normalize(uVertexNormal.xyz), 1.0);
|
||||
else
|
||||
vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0);
|
||||
#else
|
||||
vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0);
|
||||
#endif
|
||||
vWorldNormal = NormalModelMatrix * vec4(normalize(bones.Normal), 1.0);
|
||||
vEyeNormal = NormalViewMatrix * vec4(normalize(vWorldNormal.xyz), 1.0);
|
||||
#endif
|
||||
|
||||
|
@ -146,3 +151,66 @@ void main()
|
|||
|
||||
gl_PointSize = 1.0;
|
||||
}
|
||||
|
||||
#if !defined(SIMPLE)
|
||||
vec3 GetAttrNormal()
|
||||
{
|
||||
#ifdef HAS_UNIFORM_VERTEX_DATA
|
||||
if ((useVertexData & 2) == 0)
|
||||
return uVertexNormal.xyz;
|
||||
else
|
||||
return mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor);
|
||||
#else
|
||||
return mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddWeightedBone(uint boneIndex, float weight, inout vec4 position, inout vec3 normal)
|
||||
{
|
||||
if (weight != 0.0)
|
||||
{
|
||||
mat4 transform = bones[uBoneIndexBase + boneIndex];
|
||||
mat3 rotation = mat3(transform);
|
||||
position += (transform * aPosition) * weight;
|
||||
normal += (rotation * aNormal.xyz) * weight;
|
||||
}
|
||||
}
|
||||
|
||||
BonesResult ApplyBones()
|
||||
{
|
||||
BonesResult result;
|
||||
if (uBoneIndexBase >= 0 && aBoneWeight != vec4(0.0))
|
||||
{
|
||||
result.Position = vec4(0.0);
|
||||
result.Normal = vec3(0.0);
|
||||
|
||||
// We use low precision input for our bone weights. Rescale so the sum still is 1.0
|
||||
float totalWeight = aBoneWeight.x + aBoneWeight.y + aBoneWeight.z + aBoneWeight.w;
|
||||
float weightMultiplier = 1.0 / totalWeight;
|
||||
vec4 boneWeight = aBoneWeight * weightMultiplier;
|
||||
|
||||
AddWeightedBone(aBoneSelector.x, boneWeight.x, result.Position, result.Normal);
|
||||
AddWeightedBone(aBoneSelector.y, boneWeight.y, result.Position, result.Normal);
|
||||
AddWeightedBone(aBoneSelector.z, boneWeight.z, result.Position, result.Normal);
|
||||
AddWeightedBone(aBoneSelector.w, boneWeight.w, result.Position, result.Normal);
|
||||
|
||||
result.Position.w = 1.0; // For numerical stability
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Position = aPosition;
|
||||
result.Normal = GetAttrNormal();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
BonesResult ApplyBones()
|
||||
{
|
||||
BonesResult result;
|
||||
result.Position = aPosition;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue