From 5eb9af1e009d46d29ec38b3f5cef3d777086dd63 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 20 Oct 2022 22:24:25 +0200 Subject: [PATCH] - Backend update from GZDoom IQM model support and a few bugfixes. --- source/CMakeLists.txt | 40 +- source/common/2d/v_draw.cpp | 44 +- source/common/fonts/v_font.cpp | 1 + source/common/models/model.cpp | 5 + source/common/models/model.h | 6 +- source/common/models/model_iqm.h | 220 +++++++ source/common/models/model_kvx.h | 10 +- source/common/models/model_md2.h | 4 +- source/common/models/model_md3.h | 4 +- source/common/models/model_obj.h | 2 +- source/common/models/model_ue1.h | 4 +- source/common/models/modelrenderer.h | 2 +- source/common/models/models_iqm.cpp | 610 ++++++++++++++++++ source/common/models/models_md2.cpp | 9 +- source/common/models/models_md3.cpp | 6 +- source/common/models/models_obj.cpp | 6 +- source/common/models/models_ue1.cpp | 6 +- source/common/models/models_voxel.cpp | 9 +- source/common/models/voxels.cpp | 1 + source/common/rendering/gl/gl_buffers.cpp | 13 +- source/common/rendering/gl/gl_buffers.h | 2 + source/common/rendering/gl/gl_framebuffer.cpp | 4 + source/common/rendering/gl/gl_renderstate.cpp | 17 + source/common/rendering/gl/gl_renderstate.h | 1 + source/common/rendering/gl/gl_shader.cpp | 28 +- source/common/rendering/gl/gl_shader.h | 1 + .../rendering/gles/gles_framebuffer.cpp | 4 + source/common/rendering/gles/gles_renderer.h | 1 + .../rendering/hwrenderer/data/buffers.h | 3 + .../hwrenderer/data/hw_bonebuffer.cpp | 112 ++++ .../rendering/hwrenderer/data/hw_bonebuffer.h | 43 ++ .../hwrenderer/data/hw_modelvertexbuffer.cpp | 4 +- .../hwrenderer/data/hw_renderstate.h | 7 + .../hwrenderer/data/shaderuniforms.h | 3 +- source/common/rendering/i_modelvertexbuffer.h | 18 + source/common/rendering/v_video.h | 2 + .../vulkan/renderer/vk_descriptorset.cpp | 4 +- .../vulkan/renderer/vk_renderpass.cpp | 9 +- .../vulkan/renderer/vk_renderstate.cpp | 1 + .../rendering/vulkan/shaders/vk_shader.cpp | 11 +- .../rendering/vulkan/shaders/vk_shader.h | 3 + .../rendering/vulkan/system/vk_buffer.cpp | 3 +- .../rendering/vulkan/system/vk_buffer.h | 1 + .../vulkan/system/vk_framebuffer.cpp | 3 + source/common/scripting/backend/codegen.cpp | 4 +- source/common/textures/gametexture.cpp | 7 +- source/common/utility/matrix.cpp | 38 ++ source/common/utility/matrix.h | 1 + source/core/rendering/hw_models.cpp | 3 +- source/core/rendering/hw_models.h | 2 +- source/core/rendering/scene/hw_sprites.cpp | 5 +- wadsrc/static/zscript/engine/base.zs | 8 +- 52 files changed, 1237 insertions(+), 118 deletions(-) create mode 100644 source/common/models/model_iqm.h create mode 100644 source/common/models/models_iqm.cpp create mode 100644 source/common/rendering/hwrenderer/data/hw_bonebuffer.cpp create mode 100644 source/common/rendering/hwrenderer/data/hw_bonebuffer.h diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 448147b65..b2ab0bd04 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -31,7 +31,6 @@ else() set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funsigned-char -Wno-missing-braces -Wno-char-subscripts -Wno-unused-variable" ) endif() -option( DYN_OPENAL "Dynamically load OpenAL" ON ) if( APPLE ) option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON ) @@ -46,7 +45,7 @@ endif() # Right now only 64 bit is supported. if( ${TARGET_ARCHITECTURE} MATCHES "x86_64" ) - set( X64 64 ) + set( X64 64 ) endif() if( X64 OR ${TARGET_ARCHITECTURE} MATCHES "i386" ) @@ -86,7 +85,6 @@ if( WIN32 ) dbghelp legacy_stdio_definitions ) - if( NOT DEM_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) set( PROJECT_LIBRARIES ${PROJECT_LIBRARIES} DelayImp ) endif() @@ -189,33 +187,9 @@ endif() # Decide on SSE setup -# SSE only matters on 32-bit targets. We check compiler flags to know if we can do it. -if( CMAKE_SIZEOF_VOID_P MATCHES "4" AND NOT CMAKE_OSX_ARCHITECTURES MATCHES ppc ) - CHECK_CXX_COMPILER_FLAG( "-msse2 -mfpmath=sse" CAN_DO_MFPMATH ) - CHECK_CXX_COMPILER_FLAG( -arch:SSE2 CAN_DO_ARCHSSE2 ) - if( CAN_DO_MFPMATH ) - set( SSE1_ENABLE "-msse -mfpmath=sse" ) - set( SSE2_ENABLE "-msse2 -mfpmath=sse" ) - elseif( CAN_DO_ARCHSSE2 ) - set( SSE1_ENABLE -arch:SSE ) - set( SSE2_ENABLE -arch:SSE2 ) - endif() -endif() - if( X64 ) set( HAVE_MMX 1 ) else( X64 ) - set( SAFE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ) - - if( DEM_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmmx") - endif( DEM_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) - - CHECK_CXX_SOURCE_COMPILES("#include - int main(void) { __m64 v = _m_from_int(0); }" - HAVE_MMX) - - set( CMAKE_CXX_FLAGS ${SAFE_CMAKE_CXX_FLAGS} ) endif( X64 ) CHECK_CXX_SOURCE_COMPILES("#include @@ -335,16 +309,6 @@ endif() # Check for functions that may or may not exist. -CHECK_FUNCTION_EXISTS( filelength FILELENGTH_EXISTS ) -if( FILELENGTH_EXISTS ) - add_definitions( -DHAVE_FILELENGTH=1 ) -endif() - -CHECK_FUNCTION_EXISTS( strupr STRUPR_EXISTS ) -if( NOT STRUPR_EXISTS ) - add_definitions( -DNEED_STRUPR=1 ) -endif() - require_stricmp() require_strnicmp() @@ -1140,6 +1104,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 @@ -1235,6 +1200,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 diff --git a/source/common/2d/v_draw.cpp b/source/common/2d/v_draw.cpp index 941306dd4..f78446fc8 100644 --- a/source/common/2d/v_draw.cpp +++ b/source/common/2d/v_draw.cpp @@ -393,10 +393,14 @@ DEFINE_ACTION_FUNCTION(FCanvas, DrawShapeFill) void F2DDrawer::SetClipRect(int x, int y, int w, int h) { - clipleft = clamp(x, 0, GetWidth()); - clipwidth = clamp(w, -1, GetWidth() - x); - cliptop = clamp(y, 0, GetHeight()); - clipheight = clamp(h, -1, GetHeight() - y); + if (x < 0) { w += x; x = 0; } + if (y < 0) { h += y; y = 0; } + if (x >= GetWidth()) { x = GetWidth(); w = 0; } + if (y >= GetHeight()) { x = GetHeight(); h = 0; } + clipleft = x; + clipwidth = w; + cliptop = y; + clipheight = h; } DEFINE_ACTION_FUNCTION(_Screen, SetClipRect) @@ -1564,10 +1568,10 @@ static void DrawLine(double x1, double y1, double x2, double y2, uint32_t realco DEFINE_ACTION_FUNCTION_NATIVE(_Screen, DrawLine, DrawLine) { PARAM_PROLOGUE; - PARAM_INT(x0); - PARAM_INT(y0); - PARAM_INT(x1); - PARAM_INT(y1); + PARAM_FLOAT(x0); + PARAM_FLOAT(y0); + PARAM_FLOAT(x1); + PARAM_FLOAT(y1); PARAM_INT(color); PARAM_INT(alpha); DrawLine(x0, y0, x1, y1, color, alpha); @@ -1577,10 +1581,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(_Screen, DrawLine, DrawLine) DEFINE_ACTION_FUNCTION(FCanvas, DrawLine) { PARAM_SELF_PROLOGUE(FCanvas); - PARAM_INT(x0); - PARAM_INT(y0); - PARAM_INT(x1); - PARAM_INT(y1); + PARAM_FLOAT(x0); + PARAM_FLOAT(y0); + PARAM_FLOAT(x1); + PARAM_FLOAT(y1); PARAM_INT(color); PARAM_INT(alpha); self->Drawer.AddLine(DVector2(x0, y0), DVector2(x1, y1), nullptr, color | MAKEARGB(255, 0, 0, 0), alpha); @@ -1597,10 +1601,10 @@ static void DrawThickLine(double x1, double y1, double x2, double y2, double thi DEFINE_ACTION_FUNCTION_NATIVE(_Screen, DrawThickLine, DrawThickLine) { PARAM_PROLOGUE; - PARAM_INT(x0); - PARAM_INT(y0); - PARAM_INT(x1); - PARAM_INT(y1); + PARAM_FLOAT(x0); + PARAM_FLOAT(y0); + PARAM_FLOAT(x1); + PARAM_FLOAT(y1); PARAM_FLOAT(thickness); PARAM_INT(color); PARAM_INT(alpha); @@ -1611,10 +1615,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(_Screen, DrawThickLine, DrawThickLine) DEFINE_ACTION_FUNCTION(FCanvas, DrawThickLine) { PARAM_SELF_PROLOGUE(FCanvas); - PARAM_INT(x0); - PARAM_INT(y0); - PARAM_INT(x1); - PARAM_INT(y1); + PARAM_FLOAT(x0); + PARAM_FLOAT(y0); + PARAM_FLOAT(x1); + PARAM_FLOAT(y1); PARAM_FLOAT(thickness); PARAM_INT(color); PARAM_INT(alpha); diff --git a/source/common/fonts/v_font.cpp b/source/common/fonts/v_font.cpp index 28af85202..dd3e47648 100644 --- a/source/common/fonts/v_font.cpp +++ b/source/common/fonts/v_font.cpp @@ -92,6 +92,7 @@ TArray TranslationColors; FFont *V_GetFont(const char *name, const char *fontlumpname) { + if (name == nullptr) return nullptr; if (!stricmp(name, "DBIGFONT")) name = "BigFont"; else if (!stricmp(name, "CONFONT")) name = "ConsoleFont"; // several mods have used the name CONFONT directly and effectively duplicated the font. else if (!stricmp(name, "INDEXFON")) name = "IndexFont"; // Same here - for whatever reason some people had to use its 8 character name... diff --git a/source/common/models/model.cpp b/source/common/models/model.cpp index 21f5c9f41..c4b184f0c 100644 --- a/source/common/models/model.cpp +++ b/source/common/models/model.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) { diff --git a/source/common/models/model.h b/source/common/models/model.h index edbcc4841..48e4ed34a 100644 --- a/source/common/models/model.h +++ b/source/common/models/model.h @@ -3,6 +3,7 @@ #include #include "textureid.h" #include "i_modelvertexbuffer.h" +#include "matrix.h" class FModelRenderer; class FGameTexture; @@ -26,6 +27,7 @@ struct FSpriteModelFrame TArray skinIDs; TArray surfaceskinIDs; TArray modelframes; + TArray animationIDs; float xscale, yscale, zscale; // [BB] Added zoffset, rotation parameters and flags. // Added xoffset, yoffset @@ -68,10 +70,12 @@ public: 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 void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData, int boneStartPosition) = 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* AttachAnimationData() { return nullptr; }; + virtual const TArray CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) { return {}; }; void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; } IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; } diff --git a/source/common/models/model_iqm.h b/source/common/models/model_iqm.h new file mode 100644 index 000000000..d91157c26 --- /dev/null +++ b/source/common/models/model_iqm.h @@ -0,0 +1,220 @@ +#pragma once + +#include +#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 and channels 3..6 are quaternion rotation + // rotation is in relative/parent local space + // channels 7..9 are scale + // output = (input*scale)*rotation + translation +}; + +struct IQMAnim +{ + FString Name; + uint32_t FirstFrame; + uint32_t NumFrames; + float Framerate; + bool Loop; +}; + +struct IQMBounds +{ + float BBMins[3]; + float BBMaxs[3]; + float XYRadius; + float Radius; +}; + +class IQMFileReader; + +class IQMModel : public FModel +{ +public: + IQMModel(); + ~IQMModel(); + + bool Load(const char* fn, int lumpnum, const char* buffer, int length) override; + int FindFrame(const char* name, bool nodefault) override; + void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData, int boneStartPosition) override; + void BuildVertexBuffer(FModelRenderer* renderer) override; + void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override; + const TArray* AttachAnimationData() override; + const TArray CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) override; + +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 Meshes; + TArray Triangles; + TArray Adjacency; + TArray Joints; + TArray Poses; + TArray Anims; + TArray FrameTransforms; + TArray Bounds; + TArray VertexArrays; + uint32_t NumVertices = 0; + + TArray Vertices; + + TArray baseframe; + TArray 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& 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; +}; diff --git a/source/common/models/model_kvx.h b/source/common/models/model_kvx.h index 1491b03b4..c801e5c01 100644 --- a/source/common/models/model_kvx.h +++ b/source/common/models/model_kvx.h @@ -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,8 +58,8 @@ 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& boneData, int boneStartPosition) override; virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override; FTextureID GetPaletteTexture() const { return mPalette; } void BuildVertexBuffer(FModelRenderer *renderer) override; diff --git a/source/common/models/model_md2.h b/source/common/models/model_md2.h index b1feef645..9ead5e60b 100644 --- a/source/common/models/model_md2.h +++ b/source/common/models/model_md2.h @@ -112,8 +112,8 @@ 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& boneData, int boneStartPosition) override; virtual void LoadGeometry(); virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override; diff --git a/source/common/models/model_md3.h b/source/common/models/model_md3.h index 3d81cb1c3..b53cd3ebb 100644 --- a/source/common/models/model_md3.h +++ b/source/common/models/model_md3.h @@ -66,8 +66,8 @@ 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& boneData, int boneStartPosition) override; void LoadGeometry(); void BuildVertexBuffer(FModelRenderer *renderer); virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override; diff --git a/source/common/models/model_obj.h b/source/common/models/model_obj.h index 55a47363c..e70679b0a 100644 --- a/source/common/models/model_obj.h +++ b/source/common/models/model_obj.h @@ -98,7 +98,7 @@ 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& boneData, int boneStartPosition) override; void BuildVertexBuffer(FModelRenderer* renderer) override; void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override; }; diff --git a/source/common/models/model_ue1.h b/source/common/models/model_ue1.h index f2ed1952c..5123a22e9 100644 --- a/source/common/models/model_ue1.h +++ b/source/common/models/model_ue1.h @@ -25,8 +25,8 @@ 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& boneData, int boneStartPosition) override; void BuildVertexBuffer(FModelRenderer *renderer) override; void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override; void LoadGeometry(); diff --git a/source/common/models/modelrenderer.h b/source/common/models/modelrenderer.h index 38786afe6..18c49f641 100644 --- a/source/common/models/modelrenderer.h +++ b/source/common/models/modelrenderer.h @@ -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 int SetupFrame(FModel* model, unsigned int frame1, unsigned int frame2, unsigned int size, const TArray& bones, int boneStartIndex) { return -1; }; }; diff --git a/source/common/models/models_iqm.cpp b/source/common/models/models_iqm.cpp new file mode 100644 index 000000000..314a6c600 --- /dev/null +++ b/source/common/models/models_iqm.cpp @@ -0,0 +1,610 @@ + +#include "filesystem.h" +#include "cmdlib.h" +#include "model_iqm.h" +#include "texturemanager.h" +#include "modelrenderer.h" +#include "engineerrors.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_meshes <= 0) + I_FatalError("Invalid model: \"%s%s\", no mesh data is unsupported", path, fileSystem.GetLongName(mLumpNum).GetChars()); + + if (num_text == 0) + return false; + + TArray 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) + { + int x = reader.ReadUByte(); + int y = reader.ReadUByte(); + int z = reader.ReadUByte(); + int w = reader.ReadUByte(); + v.SetBoneWeight(x, y, z, w); + } + } + 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& boneData, int boneStartPosition) +{ + renderer->SetupFrame(this, 0, 0, NumVertices, boneData, boneStartPosition); + + FGameTexture* lastSkin = nullptr; + for (unsigned 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 (unsigned i = 0; i < Meshes.Size(); i++) + { + if (surfaceskinids && surfaceskinids[i].isValid()) + hitlist[surfaceskinids[i].GetIndex()] |= FTextureManager::HIT_Flat; + } +} + +const TArray* IQMModel::AttachAnimationData() +{ + return &FrameTransforms; +} + +const TArray IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) +{ + const TArray& 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 bones(numbones, true); + for (int i = 0; i < numbones; i++) + { + const float* from = animationFrames[offset1 + i].get(); + const float* to = animationFrames[offset2 + i].get(); + + // Interpolate bone between the two frames + float bone[16]; + for (int j = 0; j < 16; j++) + { + bone[j] = from[j] * invt + to[j] * t; + } + + // Apply parent bone + if (Joints[i].Parent >= 0) + { + bones[i] = bones[Joints[i].Parent]; + bones[i].multMatrix(bone); + } + else + { + bones[i].loadMatrix(bone); + } + } + + return bones; +} \ No newline at end of file diff --git a/source/common/models/models_md2.cpp b/source/common/models/models_md2.cpp index 81d1c3666..4f2a12d4e 100644 --- a/source/common/models/models_md2.cpp +++ b/source/common/models/models_md2.cpp @@ -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& boneData, int boneStartPosition) { if (frameno >= info.numFrames || frameno2 >= info.numFrames) return; @@ -376,13 +376,11 @@ 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, {}, -1); renderer->DrawArrays(0, lodInfo[0].numTriangles * 3); renderer->SetInterpolation(0.f); } - - //=========================================================================== // // Internal data structures of MD2 files - only used during loading @@ -552,4 +550,3 @@ void FMD2Model::LoadGeometry() FMD2Model::~FMD2Model() { } - diff --git a/source/common/models/models_md3.cpp b/source/common/models/models_md3.cpp index 95ac49167..512794293 100644 --- a/source/common/models/models_md3.cpp +++ b/source/common/models/models_md3.cpp @@ -328,7 +328,7 @@ 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++) { @@ -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& boneData, int boneStartPosition) { if ((unsigned)frameno >= Frames.Size() || (unsigned)frameno2 >= Frames.Size()) return; @@ -373,7 +373,7 @@ 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, {}, -1); renderer->DrawElements(surf->numTriangles * 3, surf->iindex * sizeof(unsigned int)); } renderer->SetInterpolation(0.f); diff --git a/source/common/models/models_obj.cpp b/source/common/models/models_obj.cpp index d804ffff6..65ec72915 100644 --- a/source/common/models/models_obj.cpp +++ b/source/common/models/models_obj.cpp @@ -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& boneData, int boneStartPosition) { // 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, {}, -1); renderer->DrawArrays(0, surf->numTris * 3); } } diff --git a/source/common/models/models_ue1.cpp b/source/common/models/models_ue1.cpp index 3e47d87e0..416f9a391 100644 --- a/source/common/models/models_ue1.cpp +++ b/source/common/models/models_ue1.cpp @@ -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& boneData, int boneStartPosition) { // 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, {}, -1); renderer->DrawArrays(0,vsize); vofs += vsize; } diff --git a/source/common/models/models_voxel.cpp b/source/common/models/models_voxel.cpp index 41f5e854a..6af34cf23 100644 --- a/source/common/models/models_voxel.cpp +++ b/source/common/models/models_voxel.cpp @@ -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,9 @@ 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& boneData, int boneStartPosition) { renderer->SetMaterial(skin, true, translation); - renderer->SetupFrame(this, 0, 0, 0); + renderer->SetupFrame(this, 0, 0, 0, {}, -1); renderer->DrawElements(mNumIndices, 0); } - diff --git a/source/common/models/voxels.cpp b/source/common/models/voxels.cpp index b85f2f851..d8d2b2245 100644 --- a/source/common/models/voxels.cpp +++ b/source/common/models/voxels.cpp @@ -314,6 +314,7 @@ FVoxelDef *R_LoadVoxelDef(int lumpnum, int spin) else { FVoxelDef *voxdef = new FVoxelDef; + *voxdef = {}; voxdef->Voxel = vox; voxdef->Scale = 1.; voxdef->DroppedSpin = voxdef->PlacedSpin = spin; diff --git a/source/common/rendering/gl/gl_buffers.cpp b/source/common/rendering/gl/gl_buffers.cpp index 5439319a9..a576b6431 100644 --- a/source/common/rendering/gl/gl_buffers.cpp +++ b/source/common/rendering/gl/gl_buffers.cpp @@ -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++; } diff --git a/source/common/rendering/gl/gl_buffers.h b/source/common/rendering/gl/gl_buffers.h index d0c2b62c8..9bca819a1 100644 --- a/source/common/rendering/gl/gl_buffers.h +++ b/source/common/rendering/gl/gl_buffers.h @@ -46,6 +46,8 @@ class GLVertexBuffer : public IVertexBuffer, public GLBuffer { int bindingpoint; int format; + bool normalize; + bool integerType; int size; int offset; }; diff --git a/source/common/rendering/gl/gl_framebuffer.cpp b/source/common/rendering/gl/gl_framebuffer.cpp index 3913a21e1..6ea31e5bd 100644 --- a/source/common/rendering/gl/gl_framebuffer.cpp +++ b/source/common/rendering/gl/gl_framebuffer.cpp @@ -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(mLights->GetBuffer())->BindBase(); + static_cast(mBones->GetBuffer())->BindBase(); mDebug = std::make_unique(); mDebug->Update(); diff --git a/source/common/rendering/gl/gl_renderstate.cpp b/source/common/rendering/gl/gl_renderstate.cpp index 688e46904..5d7fd774b 100644 --- a/source/common/rendering/gl/gl_renderstate.cpp +++ b/source/common/rendering/gl/gl_renderstate.cpp @@ -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(screen->mBones->GetBuffer())->BindRange(nullptr, start, size); + } + } + activeShader->muBoneIndexBase.Set(index); + return true; } diff --git a/source/common/rendering/gl/gl_renderstate.h b/source/common/rendering/gl/gl_renderstate.h index 9cfe3d7a2..8b9bba25c 100644 --- a/source/common/rendering/gl/gl_renderstate.h +++ b/source/common/rendering/gl/gl_renderstate.h @@ -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 diff --git a/source/common/rendering/gl/gl_shader.cpp b/source/common/rendering/gl/gl_shader.cpp index 80e6a0e8a..c4e20f7ba 100644 --- a/source/common/rendering/gl/gl_shader.cpp +++ b/source/common/rendering/gl/gl_shader.cpp @@ -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); diff --git a/source/common/rendering/gl/gl_shader.h b/source/common/rendering/gl/gl_shader.h index 4f7debda3..6eb2b2226 100644 --- a/source/common/rendering/gl/gl_shader.h +++ b/source/common/rendering/gl/gl_shader.h @@ -241,6 +241,7 @@ class FShader FBufferedUniform4f muLightParms; FBufferedUniform2f muClipSplit; FBufferedUniform1i muLightIndex; + FBufferedUniform1i muBoneIndexBase; FBufferedUniformPE muFogColor; FBufferedUniform4f muDynLightColor; FBufferedUniformPE muObjectColor; diff --git a/source/common/rendering/gles/gles_framebuffer.cpp b/source/common/rendering/gles/gles_framebuffer.cpp index db2a69902..0d0a459c0 100644 --- a/source/common/rendering/gles/gles_framebuffer.cpp +++ b/source/common/rendering/gles/gles_framebuffer.cpp @@ -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(mLights->GetBuffer())->BindBase(); + static_cast(mBones->GetBuffer())->BindBase(); } //========================================================================== diff --git a/source/common/rendering/gles/gles_renderer.h b/source/common/rendering/gles/gles_renderer.h index 3322e06bf..b1e7d2346 100644 --- a/source/common/rendering/gles/gles_renderer.h +++ b/source/common/rendering/gles/gles_renderer.h @@ -17,6 +17,7 @@ class FFlatVertexBuffer; class FSkyVertexBuffer; class HWPortal; class FLightBuffer; +class BoneBuffer; class DPSprite; class FGLRenderBuffers; class FGL2DDrawer; diff --git a/source/common/rendering/hwrenderer/data/buffers.h b/source/common/rendering/hwrenderer/data/buffers.h index 0a914bb9c..5e2c02728 100644 --- a/source/common/rendering/hwrenderer/data/buffers.h +++ b/source/common/rendering/hwrenderer/data/buffers.h @@ -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 diff --git a/source/common/rendering/hwrenderer/data/hw_bonebuffer.cpp b/source/common/rendering/hwrenderer/data/hw_bonebuffer.cpp new file mode 100644 index 000000000..6335c3fc4 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_bonebuffer.cpp @@ -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 < 64 ? 1 : 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& 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); +} diff --git a/source/common/rendering/hwrenderer/data/hw_bonebuffer.h b/source/common/rendering/hwrenderer/data/hw_bonebuffer.h new file mode 100644 index 000000000..c4ad550c9 --- /dev/null +++ b/source/common/rendering/hwrenderer/data/hw_bonebuffer.h @@ -0,0 +1,43 @@ +#pragma once + +#include "tarray.h" +#include "hwrenderer/data/buffers.h" +#include "common/utility/matrix.h" +#include +#include + +class FRenderState; + +class BoneBuffer +{ + IDataBuffer *mBuffer; + IDataBuffer* mBufferPipeline[HW_MAX_PIPELINE_BUFFERS]; + int mPipelineNbr; + int mPipelinePos = 0; + + bool mBufferType; + std::atomic 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 &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; + } +}; diff --git a/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp b/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp index da5b3be3c..9dff499f9 100644 --- a/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp +++ b/source/common/rendering/hwrenderer/data/hw_modelvertexbuffer.cpp @@ -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); } //=========================================================================== diff --git a/source/common/rendering/hwrenderer/data/hw_renderstate.h b/source/common/rendering/hwrenderer/data/hw_renderstate.h index bd8fd4d9e..2ff77c53d 100644 --- a/source/common/rendering/hwrenderer/data/hw_renderstate.h +++ b/source/common/rendering/hwrenderer/data/hw_renderstate.h @@ -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; diff --git a/source/common/rendering/hwrenderer/data/shaderuniforms.h b/source/common/rendering/hwrenderer/data/shaderuniforms.h index 92995fcaa..4b11a3755 100644 --- a/source/common/rendering/hwrenderer/data/shaderuniforms.h +++ b/source/common/rendering/hwrenderer/data/shaderuniforms.h @@ -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 diff --git a/source/common/rendering/i_modelvertexbuffer.h b/source/common/rendering/i_modelvertexbuffer.h index bd3df3767..f68d4e117 100644 --- a/source/common/rendering/i_modelvertexbuffer.h +++ b/source/common/rendering/i_modelvertexbuffer.h @@ -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) diff --git a/source/common/rendering/v_video.h b/source/common/rendering/v_video.h index aff93435e..3be892efe 100644 --- a/source/common/rendering/v_video.h +++ b/source/common/rendering/v_video.h @@ -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; diff --git a/source/common/rendering/vulkan/renderer/vk_descriptorset.cpp b/source/common/rendering/vulkan/renderer/vk_descriptorset.cpp index 0f74124a0..ef6646580 100644 --- a/source/common/rendering/vulkan/renderer/vk_descriptorset.cpp +++ b/source/common/rendering/vulkan/renderer/vk_descriptorset.cpp @@ -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); diff --git a/source/common/rendering/vulkan/renderer/vk_renderpass.cpp b/source/common/rendering/vulkan/renderer/vk_renderpass.cpp index 083745a91..4a7531b74 100644 --- a/source/common/rendering/vulkan/renderer/vk_renderpass.cpp +++ b/source/common/rendering/vulkan/renderer/vk_renderpass.cpp @@ -230,10 +230,11 @@ std::unique_ptr 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 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); diff --git a/source/common/rendering/vulkan/renderer/vk_renderstate.cpp b/source/common/rendering/vulkan/renderer/vk_renderstate.cpp index d2a68d951..cb2667920 100644 --- a/source/common/rendering/vulkan/renderer/vk_renderstate.cpp +++ b/source/common/rendering/vulkan/renderer/vk_renderstate.cpp @@ -391,6 +391,7 @@ void VkRenderState::ApplyPushConstants() } mPushConstants.uLightIndex = mLightIndex; + mPushConstants.uBoneIndexBase = mBoneIndexBase; mPushConstants.uDataIndex = mStreamBufferWriter.DataIndex(); auto passManager = fb->GetRenderPassManager(); diff --git a/source/common/rendering/vulkan/shaders/vk_shader.cpp b/source/common/rendering/vulkan/shaders/vk_shader.cpp index c223cafa0..44c419dda 100644 --- a/source/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/source/common/rendering/vulkan/shaders/vk_shader.cpp @@ -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 diff --git a/source/common/rendering/vulkan/shaders/vk_shader.h b/source/common/rendering/vulkan/shaders/vk_shader.h index 11682dae9..ba13726a6 100644 --- a/source/common/rendering/vulkan/shaders/vk_shader.h +++ b/source/common/rendering/vulkan/shaders/vk_shader.h @@ -50,6 +50,9 @@ struct PushConstants // Blinn glossiness and specular level FVector2 uSpecularMaterial; + // bone animation + int uBoneIndexBase; + int uDataIndex; int padding1, padding2, padding3; }; diff --git a/source/common/rendering/vulkan/system/vk_buffer.cpp b/source/common/rendering/vulkan/system/vk_buffer.cpp index db7bc4ea8..2a45cc73c 100644 --- a/source/common/rendering/vulkan/system/vk_buffer.cpp +++ b/source/common/rendering/vulkan/system/vk_buffer.cpp @@ -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; } diff --git a/source/common/rendering/vulkan/system/vk_buffer.h b/source/common/rendering/vulkan/system/vk_buffer.h index 39cc1ce15..c54d60aac 100644 --- a/source/common/rendering/vulkan/system/vk_buffer.h +++ b/source/common/rendering/vulkan/system/vk_buffer.h @@ -33,6 +33,7 @@ public: VkHardwareDataBuffer* LightNodes = nullptr; VkHardwareDataBuffer* LightLines = nullptr; VkHardwareDataBuffer* LightList = nullptr; + VkHardwareDataBuffer* BoneBufferSSO = nullptr; std::unique_ptr MatrixBuffer; std::unique_ptr StreamBuffer; diff --git a/source/common/rendering/vulkan/system/vk_framebuffer.cpp b/source/common/rendering/vulkan/system/vk_framebuffer.cpp index b93e59784..b5925ad53 100644 --- a/source/common/rendering/vulkan/system/vk_framebuffer.cpp +++ b/source/common/rendering/vulkan/system/vk_framebuffer.cpp @@ -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(); diff --git a/source/common/scripting/backend/codegen.cpp b/source/common/scripting/backend/codegen.cpp index b274c88f9..de0408d05 100644 --- a/source/common/scripting/backend/codegen.cpp +++ b/source/common/scripting/backend/codegen.cpp @@ -1064,7 +1064,7 @@ FxExpression *FxFloatCast::Resolve(FCompileContext &ctx) ExpEmit FxFloatCast::Emit(VMFunctionBuilder *build) { ExpEmit from = basex->Emit(build); - assert(!from.Konst); + //assert(!from.Konst); assert(basex->ValueType->GetRegType() == REGT_INT); from.Free(build); ExpEmit to(build, REGT_FLOAT); @@ -7506,7 +7506,7 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) else { static int LKR_Ops[] = { OP_LK_R, OP_LKF_R, OP_LKS_R, OP_LKP_R }; - assert(start.RegType == ValueType->GetRegType()); + //assert(start.RegType == ValueType->GetRegType()); ExpEmit dest(build, start.RegType); if (start.RegNum <= 255) { diff --git a/source/common/textures/gametexture.cpp b/source/common/textures/gametexture.cpp index 8d0019081..23b98e8e3 100644 --- a/source/common/textures/gametexture.cpp +++ b/source/common/textures/gametexture.cpp @@ -96,8 +96,11 @@ void FGameTexture::Setup(FTexture *wrap) FGameTexture::~FGameTexture() { - FGameTexture* link = fileSystem.GetLinkedTexture(GetSourceLump()); - if (link == this) fileSystem.SetLinkedTexture(GetSourceLump(), nullptr); + if (Base != nullptr) + { + FGameTexture* link = fileSystem.GetLinkedTexture(GetSourceLump()); + if (link == this) fileSystem.SetLinkedTexture(GetSourceLump(), nullptr); + } if (SoftwareTexture != nullptr) { delete SoftwareTexture; diff --git a/source/common/utility/matrix.cpp b/source/common/utility/matrix.cpp index 476d09b60..3d84d48e7 100644 --- a/source/common/utility/matrix.cpp +++ b/source/common/utility/matrix.cpp @@ -98,6 +98,21 @@ VSMatrix::multMatrix(const float *aMatrix) } #endif +void VSMatrix::multQuaternion(const TVector4& 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 diff --git a/source/common/utility/matrix.h b/source/common/utility/matrix.h index c1c7c873c..265981e1b 100644 --- a/source/common/utility/matrix.h +++ b/source/common/utility/matrix.h @@ -53,6 +53,7 @@ class VSMatrix { { multMatrix(aMatrix.mMatrix); } + void multQuaternion(const TVector4& q); void loadMatrix(const FLOATTYPE *aMatrix); #ifdef USE_DOUBLE void loadMatrix(const float *aMatrix); diff --git a/source/core/rendering/hw_models.cpp b/source/core/rendering/hw_models.cpp index 3472d6215..81f42448a 100644 --- a/source/core/rendering/hw_models.cpp +++ b/source/core/rendering/hw_models.cpp @@ -109,10 +109,11 @@ void FHWModelRenderer::DrawElements(int numIndices, size_t offset) // //=========================================================================== -void FHWModelRenderer::SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size) +int FHWModelRenderer::SetupFrame(FModel* model, unsigned int frame1, unsigned int frame2, unsigned int size, const TArray& bones, int boneStartIndex) { auto mdbuff = static_cast(model->GetVertexBuffer(GetType())); state.SetVertexBuffer(mdbuff->vertexBuffer(), frame1, frame2); if (mdbuff->indexBuffer()) state.SetIndexBuffer(mdbuff->indexBuffer()); + return 0; } diff --git a/source/core/rendering/hw_models.h b/source/core/rendering/hw_models.h index b533aa428..7e41535e2 100644 --- a/source/core/rendering/hw_models.h +++ b/source/core/rendering/hw_models.h @@ -52,7 +52,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; + int SetupFrame(FModel* model, unsigned int frame1, unsigned int frame2, unsigned int size, const TArray& bones, int boneStartIndex) override; }; diff --git a/source/core/rendering/scene/hw_sprites.cpp b/source/core/rendering/scene/hw_sprites.cpp index 5f1a905dc..b2319f133 100644 --- a/source/core/rendering/scene/hw_sprites.cpp +++ b/source/core/rendering/scene/hw_sprites.cpp @@ -153,8 +153,9 @@ void HWSprite::DrawSprite(HWDrawInfo* di, FRenderState& state, bool translucent) model->BuildVertexBuffer(&mr); bool mirrored = ((Sprite->cstat & CSTAT_SPRITE_XFLIP) != 0) ^ ((Sprite->cstat & CSTAT_SPRITE_YFLIP) != 0) ^ portalState.isMirrored(); mr.BeginDrawModel(RenderStyle, nullptr, rotmat, mirrored); - mr.SetupFrame(model, 0, 0, 0); - model->RenderFrame(&mr, TexMan.GetGameTexture(model->GetPaletteTexture()), 0, 0, 0.f, TRANSLATION(Translation_Remap + curbasepal, palette), nullptr); + TArray a; + mr.SetupFrame(model, 0, 0, 0, a, 0); + model->RenderFrame(&mr, TexMan.GetGameTexture(model->GetPaletteTexture()), 0, 0, 0.f, TRANSLATION(Translation_Remap + curbasepal, palette), nullptr, a, 0); mr.EndDrawModel(RenderStyle, nullptr); state.SetDepthFunc(DF_Less); state.SetVertexBuffer(screen->mVertexData); diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index 650487c2a..9000d3895 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -514,9 +514,9 @@ class Canvas : Object native abstract native vararg void DrawShapeFill(Color col, double amount, Shape2D s, ...); native vararg void DrawChar(Font font, int normalcolor, double x, double y, int character, ...); native vararg void DrawText(Font font, int normalcolor, double x, double y, String text, ...); - native void DrawLine(int x0, int y0, int x1, int y1, Color color, int alpha = 255); + native void DrawLine(double x0, double y0, double x1, double y1, Color color, int alpha = 255); native void DrawLineFrame(Color color, int x0, int y0, int w, int h, int thickness = 1); - native void DrawThickLine(int x0, int y0, int x1, int y1, double thickness, Color color, int alpha = 255); + native void DrawThickLine(double x0, double y0, double x1, double y1, double thickness, Color color, int alpha = 255); native Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true); native void SetClipRect(int x, int y, int w, int h); native void ClearClipRect(); @@ -547,9 +547,9 @@ struct Screen native native static vararg void DrawShapeFill(Color col, double amount, Shape2D s, ...); native static vararg void DrawChar(Font font, int normalcolor, double x, double y, int character, ...); native static vararg void DrawText(Font font, int normalcolor, double x, double y, String text, ...); - native static void DrawLine(int x0, int y0, int x1, int y1, Color color, int alpha = 255); + native static void DrawLine(double x0, double y0, double x1, double y1, Color color, int alpha = 255); native static void DrawLineFrame(Color color, int x0, int y0, int w, int h, int thickness = 1); - native static void DrawThickLine(int x0, int y0, int x1, int y1, double thickness, Color color, int alpha = 255); + native static void DrawThickLine(double x0, double y0, double x1, double y1, double thickness, Color color, int alpha = 255); native static Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true); native static double GetAspectRatio(); native static void SetClipRect(int x, int y, int w, int h);