IQM Support Implemented

More info to come
This commit is contained in:
Shiny Metagross 2022-08-12 13:17:08 -07:00 committed by Christoph Oelckers
parent dbd6a9dea1
commit 08f520f1c6
49 changed files with 1356 additions and 62 deletions

View file

@ -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

View file

@ -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)
{

View file

@ -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]; }

View 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;
};

View file

@ -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;
};

View file

@ -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);

View file

@ -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;
};

View file

@ -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

View file

@ -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()

View file

@ -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;
};

View 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;
}

View file

@ -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()
{
}

View file

@ -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 {};
}

View file

@ -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 {};
}

View file

@ -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 {};
}

View file

@ -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 {};
}

View file

@ -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++;
}

View file

@ -46,6 +46,8 @@ class GLVertexBuffer : public IVertexBuffer, public GLBuffer
{
int bindingpoint;
int format;
bool normalize;
bool integerType;
int size;
int offset;
};

View file

@ -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();

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -241,6 +241,7 @@ class FShader
FBufferedUniform4f muLightParms;
FBufferedUniform2f muClipSplit;
FBufferedUniform1i muLightIndex;
FBufferedUniform1i muBoneIndexBase;
FBufferedUniformPE muFogColor;
FBufferedUniform4f muDynLightColor;
FBufferedUniformPE muObjectColor;

View file

@ -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();
}
//==========================================================================

View file

@ -17,6 +17,7 @@ class FFlatVertexBuffer;
class FSkyVertexBuffer;
class HWPortal;
class FLightBuffer;
class BoneBuffer;
class DPSprite;
class FGLRenderBuffers;
class FGL2DDrawer;

View file

@ -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

View 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);
}

View 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;
}
};

View file

@ -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);
}
//===========================================================================

View file

@ -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;

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -391,6 +391,7 @@ void VkRenderState::ApplyPushConstants()
}
mPushConstants.uLightIndex = mLightIndex;
mPushConstants.uBoneIndexBase = mBoneIndexBase;
mPushConstants.uDataIndex = mStreamBufferWriter.DataIndex();
auto passManager = fb->GetRenderPassManager();

View file

@ -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

View file

@ -50,6 +50,9 @@ struct PushConstants
// Blinn glossiness and specular level
FVector2 uSpecularMaterial;
// bone animation
int uBoneIndexBase;
int uDataIndex;
int padding1, padding2, padding3;
};

View file

@ -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;
}

View file

@ -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;

View file

@ -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();

View file

@ -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

View file

@ -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);

View file

@ -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();

View file

@ -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.

View file

@ -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());
}

View file

@ -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;
};

View file

@ -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

View file

@ -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