From 4b9d047197d154d460e7b214138b6ee73de06983 Mon Sep 17 00:00:00 2001 From: LHoG <1476261+lhog@users.noreply.github.com> Date: Thu, 19 Dec 2024 20:58:37 +0100 Subject: [PATCH 1/4] Enabled up to 65534 pieces per model Make it the least intrusive way, shaders should be backward compatible to the 254 pieces (on sane GPU drivers). It might even work, needs testing. --- .../shaders/GLSL/ModelVertProgGL4.glsl | 12 ++-- .../shaders/GLSL/ShadowGenVertProgGL4.glsl | 12 ++-- rts/Lua/LuaVBOImpl.cpp | 23 +++---- rts/Rendering/GL/myGL.h | 12 ++-- rts/Rendering/Models/3DModel.cpp | 10 ++- rts/Rendering/Models/3DModel.h | 49 ++++++++----- rts/Rendering/Models/3DModelVAO.cpp | 14 ++-- rts/Rendering/Models/AssParser.cpp | 68 +++++++++++++------ rts/Rendering/Models/IModelParser.cpp | 4 +- 9 files changed, 125 insertions(+), 79 deletions(-) diff --git a/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl b/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl index f3212610ef..898165ba29 100644 --- a/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl +++ b/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl @@ -5,12 +5,12 @@ layout (location = 1) in vec3 normal; layout (location = 2) in vec3 T; layout (location = 3) in vec3 B; layout (location = 4) in vec4 uv; -layout (location = 5) in uvec2 bonesInfo; //boneIDs, boneWeights +layout (location = 5) in uvec3 bonesInfo; //boneIDsLow, boneWeights, boneIDsHigh layout (location = 6) in uvec4 instData; // u32 matOffset // u32 uniOffset -// u32 {teamIdx, drawFlag, unused, numPieces} +// u32 {teamIdx, drawFlag, numPiecesH, numPiecesL}, note numPiecesH then numPiecesL // u32 bposeMatOffset layout(std140, binding = 0) uniform UniformMatrixBuffer { @@ -145,7 +145,7 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) float(GetUnpackedValue(bonesInfo.y, 3)) / 255.0 ); - uint b0 = GetUnpackedValue(bonesInfo.x, 0); //first boneID + uint b0 = GetUnpackedValue(bonesInfo.x, 0) + (GetUnpackedValue(bonesInfo.z, 0) << 8u); //first boneID mat4 b0BoneMat = mat[instData.x + b0 + uint(!staticModel)]; mat3 b0NormMat = mat3(b0BoneMat); @@ -163,14 +163,14 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) msNormal *= weights[0]; wSum += weights[0]; - uint numPieces = GetUnpackedValue(instData.z, 3); + uint numPieces = (GetUnpackedValue(instData.z, 2) << 8u) + GetUnpackedValue(instData.z, 3); mat4 bposeMat = mat[instData.w + b0]; // Vertex[ModelSpace,BoneX] = PieceMat[BoneX] * InverseBindPosMat[BoneX] * BindPosMat[Bone0] * Vertex[Bone0] for (uint bi = 1; bi < 3; ++bi) { - uint bID = GetUnpackedValue(bonesInfo.x, bi); + uint bID = GetUnpackedValue(bonesInfo.x, bi) + (GetUnpackedValue(bonesInfo.z, bi) << 8u); - if (bID == 0xFFu || weights[bi] == 0.0) + if (bID == 0xFFFFu || weights[bi] == 0.0) continue; mat4 bposeInvMat = mat[instData.w + numPieces + bID]; diff --git a/cont/base/springcontent/shaders/GLSL/ShadowGenVertProgGL4.glsl b/cont/base/springcontent/shaders/GLSL/ShadowGenVertProgGL4.glsl index bf99d74233..b2331dbe9a 100644 --- a/cont/base/springcontent/shaders/GLSL/ShadowGenVertProgGL4.glsl +++ b/cont/base/springcontent/shaders/GLSL/ShadowGenVertProgGL4.glsl @@ -5,12 +5,12 @@ layout (location = 1) in vec3 normal; layout (location = 2) in vec3 T; layout (location = 3) in vec3 B; layout (location = 4) in vec4 uv; -layout (location = 5) in uvec2 bonesInfo; //boneIDs, boneWeights +layout (location = 5) in uvec3 bonesInfo; //boneIDsLow, boneWeights, boneIDsHigh layout (location = 6) in uvec4 instData; // u32 matOffset // u32 uniOffset -// u32 {teamIdx, drawFlag, unused, numPieces} +// u32 {teamIdx, drawFlag, numPiecesH, numPiecesL}, note numPiecesH then numPiecesL // u32 bposeMatOffset layout(std140, binding = 0) uniform UniformMatrixBuffer { @@ -128,7 +128,7 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) float(GetUnpackedValue(bonesInfo.y, 3)) / 255.0 ); - uint b0 = GetUnpackedValue(bonesInfo.x, 0); //first boneID + uint b0 = GetUnpackedValue(bonesInfo.x, 0) + (GetUnpackedValue(bonesInfo.z, 0) << 8u); //first boneID mat4 b0BoneMat = mat[instData.x + b0 + 1u]; mat3 b0NormMat = mat3(b0BoneMat); @@ -146,14 +146,14 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) msNormal *= weights[0]; wSum += weights[0]; - uint numPieces = GetUnpackedValue(instData.z, 3); + uint numPieces = (GetUnpackedValue(instData.z, 2) << 8u) + GetUnpackedValue(instData.z, 3); mat4 bposeMat = mat[instData.w + b0]; // Vertex[ModelSpace,BoneX] = PieceMat[BoneX] * InverseBindPosMat[BoneX] * BindPosMat[Bone0] * Vertex[Bone0] for (uint bi = 1; bi < 3; ++bi) { - uint bID = GetUnpackedValue(bonesInfo.x, bi); + uint bID = GetUnpackedValue(bonesInfo.x, bi) + (GetUnpackedValue(bonesInfo.z, bi) << 8u); - if (bID == 0xFFu || weights[bi] == 0.0) + if (bID == 0xFFFFu || weights[bi] == 0.0) continue; mat4 bposeInvMat = mat[instData.w + numPieces + bID]; diff --git a/rts/Lua/LuaVBOImpl.cpp b/rts/Lua/LuaVBOImpl.cpp index 8f13e3148c..62a85bf99f 100644 --- a/rts/Lua/LuaVBOImpl.cpp +++ b/rts/Lua/LuaVBOImpl.cpp @@ -778,17 +778,16 @@ void LuaVBOImpl::UpdateModelsVBOElementCount() } /* - float3 pos; - float3 normal = UpVector; - float3 sTangent; - float3 tTangent; + vec3 pos; + vec3 normal = UpVector; + vec3 sTangent; + vec3 tTangent; // TODO: // with pieceIndex this struct is no longer 64 bytes in size which ATI's prefer // support an arbitrary number of channels, would be easy but overkill (for now) float2 texCoords[NUM_MODEL_UVCHANNS]; - - uint32_t pieceIndex = 0; + uvec3 in uvec3 bonesInfo; */ size_t LuaVBOImpl::ModelsVBOImpl() { @@ -851,12 +850,12 @@ size_t LuaVBOImpl::ModelsVBOImpl() // uint32_t pieceIndex this->bufferAttribDefs[5] = { GL_UNSIGNED_INT, //type - 2, //size + 3, //size GL_FALSE, //normalized "bonesInfo", //name - offsetof(SVertexData, boneIDs), //pointer + offsetof(SVertexData, boneIDsLow), //pointer sizeof(uint32_t), //typeSizeInBytes - 2 * sizeof(uint32_t) //strideSizeInBytes + 3 * sizeof(uint32_t) //strideSizeInBytes }; this->attributesCount = 6; @@ -999,14 +998,14 @@ SInstanceData LuaVBOImpl::InstanceDataFromGetData(int id, int attrID, uint8_t de drawFlags = obj->drawFlag; } - uint8_t numPieces = 0; + uint16_t numPieces = 0; size_t bposeIndex = 0; if constexpr (std::is_same::value) { - numPieces = static_cast(obj->numPieces); + numPieces = static_cast(obj->numPieces); bposeIndex = matrixUploader.GetElemOffset(obj); } else { - numPieces = static_cast(obj->model->numPieces); + numPieces = static_cast(obj->model->numPieces); bposeIndex = matrixUploader.GetElemOffset(obj->model); } diff --git a/rts/Rendering/GL/myGL.h b/rts/Rendering/GL/myGL.h index 96f95438ad..3acfdcd6a6 100644 --- a/rts/Rendering/GL/myGL.h +++ b/rts/Rendering/GL/myGL.h @@ -154,11 +154,13 @@ struct SDrawElementsIndirectCommand { struct SInstanceData { SInstanceData() = default; - SInstanceData(uint32_t matOffset_, uint8_t teamIndex, uint8_t drawFlags, uint8_t numPieces, uint32_t uniOffset_, uint32_t bposeMatOffset_) - : matOffset{ matOffset_ } // updated during the following draw frames - , uniOffset{ uniOffset_ } // updated during the following draw frames - , info{ teamIndex, drawFlags, 0, numPieces } // not updated during the following draw frames - , bposeMatOffset { bposeMatOffset_ } // updated during the following draw frames + SInstanceData(uint32_t matOffset_, uint8_t teamIndex, uint8_t drawFlags, uint16_t numPieces, uint32_t uniOffset_, uint32_t bposeMatOffset_) + : matOffset{ matOffset_ } // updated during the following draw frames + , uniOffset{ uniOffset_ } // updated during the following draw frames + , info{ teamIndex, drawFlags + , static_cast((numPieces >> 8) & 0xFF) + , static_cast((numPieces ) & 0xFF) } // not updated during the following draw frames + , bposeMatOffset { bposeMatOffset_ } // updated during the following draw frames {} uint32_t matOffset; diff --git a/rts/Rendering/Models/3DModel.cpp b/rts/Rendering/Models/3DModel.cpp index 7584c8bc83..8933c729c3 100644 --- a/rts/Rendering/Models/3DModel.cpp +++ b/rts/Rendering/Models/3DModel.cpp @@ -52,6 +52,7 @@ CR_REG_METADATA(LocalModel, ( CR_MEMBER(needsBoundariesRecalc) )) +static_assert(sizeof(SVertexData) == (3 + 3 + 3 + 3 + 4 + 2 + 1) * 4); void S3DModelHelpers::BindLegacyAttrVBOs() { @@ -250,9 +251,12 @@ void S3DModelPiece::PostProcessGeometry(uint32_t pieceIndex) return; - for (auto& v : vertices) - if (v.boneIDs == SVertexData::DEFAULT_BONEIDS) - v.boneIDs = { static_cast(pieceIndex), 255, 255, 255 }; + for (auto& v : vertices) { + if (v.boneIDsLow == SVertexData::DEFAULT_BONEIDS_LOW && v.boneIDsHigh == SVertexData::DEFAULT_BONEIDS_HIGH) { + v.boneIDsLow [0] = static_cast((pieceIndex ) & 0xFF); + v.boneIDsHigh[0] = static_cast((pieceIndex >> 8) & 0xFF); + } + } } void S3DModelPiece::DrawElements(GLuint prim) const diff --git a/rts/Rendering/Models/3DModel.h b/rts/Rendering/Models/3DModel.h index a72d82db12..c2e362e473 100644 --- a/rts/Rendering/Models/3DModel.h +++ b/rts/Rendering/Models/3DModel.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "ModelsMemStorage.h" #include "Lua/LuaObjectMaterial.h" @@ -19,10 +20,12 @@ #include "System/SpringMath.h" #include "System/creg/creg_cond.h" -constexpr int MAX_MODEL_OBJECTS = 3840; -constexpr int AVG_MODEL_PIECES = 16; // as it used to be -constexpr int NUM_MODEL_TEXTURES = 2; -constexpr int NUM_MODEL_UVCHANNS = 2; +static constexpr int MAX_MODEL_OBJECTS = 3840; +static constexpr int AVG_MODEL_PIECES = 16; // as it used to be +static constexpr int NUM_MODEL_TEXTURES = 2; +static constexpr int NUM_MODEL_UVCHANNS = 2; +static constexpr int MAX_PIECES_PER_MODEL = std::numeric_limits::max() - 1; +static constexpr int INV_PIECE_NUM = MAX_PIECES_PER_MODEL + 1; static constexpr float3 DEF_MIN_SIZE( 10000.0f, 10000.0f, 10000.0f); static constexpr float3 DEF_MAX_SIZE(-10000.0f, -10000.0f, -10000.0f); @@ -49,8 +52,10 @@ struct SVertexData { tTangent = float3{}; texCoords[0] = float2{}; texCoords[1] = float2{}; - boneIDs = DEFAULT_BONEIDS; + // boneIDs is initialized afterwards + boneIDsLow = DEFAULT_BONEIDS_LOW; boneWeights = DEFAULT_BONEWEIGHTS; + boneIDsHigh = DEFAULT_BONEIDS_HIGH; } SVertexData( const float3& p, @@ -67,8 +72,9 @@ struct SVertexData { texCoords[0] = uv0; texCoords[1] = uv1; // boneIDs is initialized afterwards - boneIDs = DEFAULT_BONEIDS; + boneIDsLow = DEFAULT_BONEIDS_LOW; boneWeights = DEFAULT_BONEWEIGHTS; + boneIDsHigh = DEFAULT_BONEIDS_HIGH; } float3 pos; @@ -76,19 +82,21 @@ struct SVertexData { float3 sTangent; float3 tTangent; float2 texCoords[NUM_MODEL_UVCHANNS]; - std::array boneIDs; - std::array boneWeights; + std::array boneIDsLow; + std::array boneWeights; + std::array boneIDsHigh; - static constexpr std::array DEFAULT_BONEIDS = { 255, 255, 255, 255 }; - static constexpr std::array DEFAULT_BONEWEIGHTS = { 255, 0, 0, 0 }; + static constexpr std::array DEFAULT_BONEIDS_HIGH = { 255, 255, 255, 255 }; + static constexpr std::array DEFAULT_BONEIDS_LOW = { 255, 255, 255, 255 }; + static constexpr std::array DEFAULT_BONEWEIGHTS = { 255, 0 , 0, 0 }; - void SetBones(const std::vector>& bi) { + void SetBones(const std::vector>& bi) { assert(bi.size() == 4); - boneIDs = { - bi[0].first, - bi[1].first, - bi[2].first, - bi[3].first + boneIDsLow = { + static_cast((bi[0].first ) & 0xFF), + static_cast((bi[1].first ) & 0xFF), + static_cast((bi[2].first ) & 0xFF), + static_cast((bi[3].first ) & 0xFF) }; boneWeights = { @@ -97,11 +105,16 @@ struct SVertexData { (static_cast(math::round(bi[2].second * 255.0f))), (static_cast(math::round(bi[3].second * 255.0f))) }; + + boneIDsHigh = { + static_cast((bi[0].first >> 8) & 0xFF), + static_cast((bi[1].first >> 8) & 0xFF), + static_cast((bi[2].first >> 8) & 0xFF), + static_cast((bi[3].first >> 8) & 0xFF) + }; } }; - - struct S3DModelPiecePart { public: struct RenderData { diff --git a/rts/Rendering/Models/3DModelVAO.cpp b/rts/Rendering/Models/3DModelVAO.cpp index 757379b6c3..86ec659fdf 100644 --- a/rts/Rendering/Models/3DModelVAO.cpp +++ b/rts/Rendering/Models/3DModelVAO.cpp @@ -29,7 +29,7 @@ void S3DModelVAO::EnableAttribs(bool inst) const glVertexAttribPointer (2, 3, GL_FLOAT , false, sizeof(SVertexData), (const void*)offsetof(SVertexData, sTangent )); glVertexAttribPointer (3, 3, GL_FLOAT , false, sizeof(SVertexData), (const void*)offsetof(SVertexData, tTangent )); glVertexAttribPointer (4, 4, GL_FLOAT , false, sizeof(SVertexData), (const void*)offsetof(SVertexData, texCoords[0])); - glVertexAttribIPointer(5, 2, GL_UNSIGNED_INT, sizeof(SVertexData), (const void*)offsetof(SVertexData, boneIDs )); + glVertexAttribIPointer(5, 3, GL_UNSIGNED_INT, sizeof(SVertexData), (const void*)offsetof(SVertexData, boneIDsLow )); } else { for (int i = 6; i <= 6; ++i) { @@ -293,14 +293,14 @@ bool S3DModelVAO::AddToSubmissionImpl(const TObj* obj, uint32_t indexStart, uint const auto uniIndex = modelsUniformsStorage.GetObjOffset(obj); //doesn't need to exist for defs and models. Don't check for validity - uint8_t numPieces = 0; + uint16_t numPieces = 0; size_t bposeIndex = 0; if constexpr (std::is_same::value) { - numPieces = static_cast(obj->numPieces); + numPieces = static_cast(obj->numPieces); bposeIndex = matrixUploader.GetElemOffset(obj); } else { - numPieces = static_cast(obj->model->numPieces); + numPieces = static_cast(obj->model->numPieces); bposeIndex = matrixUploader.GetElemOffset(obj->model); } @@ -420,14 +420,14 @@ bool S3DModelVAO::SubmitImmediatelyImpl(const TObj* obj, uint32_t indexStart, ui const auto uniIndex = modelsUniformsStorage.GetObjOffset(obj); //doesn't need to exist for defs. Don't check for validity - uint8_t numPieces = 0; + uint16_t numPieces = 0; size_t bposeIndex = 0; if constexpr (std::is_same::value) { - numPieces = static_cast(obj->numPieces); + numPieces = static_cast(obj->numPieces); bposeIndex = matrixUploader.GetElemOffset(obj); } else { - numPieces = static_cast(obj->model->numPieces); + numPieces = static_cast(obj->model->numPieces); bposeIndex = matrixUploader.GetElemOffset(obj->model); } diff --git a/rts/Rendering/Models/AssParser.cpp b/rts/Rendering/Models/AssParser.cpp index d56f56dee7..6de4520ba8 100644 --- a/rts/Rendering/Models/AssParser.cpp +++ b/rts/Rendering/Models/AssParser.cpp @@ -831,7 +831,7 @@ const std::vector CAssParser::GetModelSpaceMeshes(const ai meshVertexMapping.reserve(mesh->mNumVertices); //bones info - std::vector>> vertexWeights(mesh->mNumVertices); + std::vector>> vertexWeights(mesh->mNumVertices); for (uint32_t boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) { const aiBone* bone = mesh->mBones[boneIndex]; @@ -840,8 +840,8 @@ const std::vector CAssParser::GetModelSpaceMeshes(const ai const auto& vertWeight = bone->mWeights[weightIndex].mWeight; const std::string boneName = std::string(bone->mName.data); - uint8_t boneID = spring::SafeCast(model->FindPieceOffset(boneName)); - assert(boneID < 255); // 255 - invalid piece + auto boneID = spring::SafeCast(model->FindPieceOffset(boneName)); + assert(boneID < INV_PIECE_NUM); // == INV_PIECE_NUM - invalid piece vertexWeights[vertIndex].emplace_back(boneID, vertWeight); } @@ -966,6 +966,11 @@ const std::vector CAssParser::GetModelSpaceMeshes(const ai void CAssParser::ReparentMeshesTrianglesToBones(S3DModel* model, const std::vector& meshes) { RECOIL_DETAILED_TRACY_ZONE; + + auto GetBoneID = [](const SVertexData& vert, size_t wi) { + return vert.boneIDsLow[wi] | (vert.boneIDsHigh[wi] << 8); + }; + for (const auto& [verts, indcs, numUVs] : meshes) { for (size_t trID = 0; trID < indcs.size() / 3; ++trID) { std::array boneWeights = { 0 }; @@ -974,7 +979,7 @@ void CAssParser::ReparentMeshesTrianglesToBones(S3DModel* model, const std::vect const auto& vert = verts[indcs[trID * 3 + vi]]; for (size_t wi = 0; wi < 4; ++wi) { - boneWeights[vert.boneIDs[wi]] += vert.boneWeights[wi]; + boneWeights[GetBoneID(vert, wi)] += vert.boneWeights[wi]; } } @@ -982,7 +987,7 @@ void CAssParser::ReparentMeshesTrianglesToBones(S3DModel* model, const std::vect boneWeights.begin(), std::max_element(boneWeights.begin(), boneWeights.end()) ); - assert(maxWeightedBoneID < 255); // 255 - invalid bone + assert(maxWeightedBoneID < INV_PIECE_NUM); // INV_PIECE_NUM - invalid bone auto* maxWeightedPiece = static_cast(model->pieceObjects[maxWeightedBoneID]); maxWeightedPiece->SetNumTexCoorChannels(std::max(maxWeightedPiece->GetNumTexCoorChannels(), numUVs)); @@ -1001,21 +1006,30 @@ void CAssParser::ReparentMeshesTrianglesToBones(S3DModel* model, const std::vect // new vertex if (itTargVec == pieceVerts.end()) { // make sure maxWeightedBoneID comes first. It's a must, even if it doesn't exist in targVert.boneIDs! - if (targVert.boneIDs[0] != maxWeightedBoneID) { - auto it = std::find(targVert.boneIDs.begin() + 1, targVert.boneIDs.end(), maxWeightedBoneID); - if (it != targVert.boneIDs.end()) { + const auto boneID0 = GetBoneID(targVert, 0); + if (boneID0 != maxWeightedBoneID) { + size_t itPos = 0; + for (size_t jj = 1; jj < targVert.boneIDsLow.size(); ++jj) { + if (GetBoneID(targVert, jj) == maxWeightedBoneID) { + itPos = jj; + break; + } + } + if (itPos != 0) { // swap maxWeightedBoneID so it comes first in the boneIDs array - const size_t itPos = std::distance(targVert.boneIDs.begin(), it); - std::swap(targVert.boneIDs[0], targVert.boneIDs[itPos]); + std::swap(targVert.boneIDsLow[0], targVert.boneIDsLow[itPos]); std::swap(targVert.boneWeights[0], targVert.boneWeights[itPos]); + std::swap(targVert.boneIDsHigh[0], targVert.boneIDsHigh[itPos]); } else { // maxWeightedBoneID doesn't even exist in this targVert // replace the bone with the least weight with maxWeightedBoneID and swap it be first - targVert.boneIDs[3] = maxWeightedBoneID; + targVert.boneIDsLow[3] = static_cast((maxWeightedBoneID ) & 0xFF); targVert.boneWeights[3] = 0; - std::swap(targVert.boneIDs[0], targVert.boneIDs[3]); + targVert.boneIDsHigh[3] = static_cast((maxWeightedBoneID >> 8) & 0xFF); + std::swap(targVert.boneIDsLow[0], targVert.boneIDsLow[3]); std::swap(targVert.boneWeights[0], targVert.boneWeights[3]); + std::swap(targVert.boneIDsHigh[0], targVert.boneIDsHigh[3]); // renormalize weights (optional but nice for debugging) const float sumWeights = static_cast(std::reduce(targVert.boneWeights.begin(), targVert.boneWeights.end())) / 255.0; @@ -1056,11 +1070,16 @@ void CAssParser::ReparentMeshesTrianglesToBones(S3DModel* model, const std::vect void CAssParser::ReparentCompleteMeshesToBones(S3DModel* model, const std::vector& meshes) { RECOIL_DETAILED_TRACY_ZONE; + + auto GetBoneID = [](const SVertexData& vert, size_t wi) { + return vert.boneIDsLow[wi] | (vert.boneIDsHigh[wi] << 8); + }; + for (const auto& [verts, indcs, numUVs] : meshes) { std::array boneWeights = { 0 }; for (const auto& vert : verts) { for (size_t wi = 0; wi < 4; ++wi) { - boneWeights[vert.boneIDs[wi]] += vert.boneWeights[wi]; + boneWeights[GetBoneID(vert, wi)] += vert.boneWeights[wi]; } } const auto maxWeightedBoneID = std::distance( @@ -1081,21 +1100,30 @@ void CAssParser::ReparentCompleteMeshesToBones(S3DModel* model, const std::vecto // Just copy mesh as is. Modelers and assimp should have done necessary dedup for us. // make sure maxWeightedBoneID comes first. It's a must, even if it doesn't exist in targVert.boneIDs! - if (targVert.boneIDs[0] != maxWeightedBoneID) { - auto it = std::find(targVert.boneIDs.begin() + 1, targVert.boneIDs.end(), maxWeightedBoneID); - if (it != targVert.boneIDs.end()) { + const auto boneID0 = GetBoneID(targVert, 0); + if (boneID0 != maxWeightedBoneID) { + size_t itPos = 0; + for (size_t jj = 1; jj < targVert.boneIDsLow.size(); ++jj) { + if (GetBoneID(targVert, jj) == maxWeightedBoneID) { + itPos = jj; + break; + } + } + if (itPos != 0) { // swap maxWeightedBoneID so it comes first in the boneIDs array - const size_t itPos = std::distance(targVert.boneIDs.begin(), it); - std::swap(targVert.boneIDs[0], targVert.boneIDs[itPos]); + std::swap(targVert.boneIDsLow[0], targVert.boneIDsLow[itPos]); std::swap(targVert.boneWeights[0], targVert.boneWeights[itPos]); + std::swap(targVert.boneIDsHigh[0], targVert.boneIDsHigh[itPos]); } else { // maxWeightedBoneID doesn't even exist in this targVert // replace the bone with the least weight with maxWeightedBoneID and swap it be first - targVert.boneIDs[3] = maxWeightedBoneID; + targVert.boneIDsLow[3] = static_cast((maxWeightedBoneID) & 0xFF); targVert.boneWeights[3] = 0; - std::swap(targVert.boneIDs[0], targVert.boneIDs[3]); + targVert.boneIDsHigh[3] = static_cast((maxWeightedBoneID >> 8) & 0xFF); + std::swap(targVert.boneIDsLow[0], targVert.boneIDsLow[3]); std::swap(targVert.boneWeights[0], targVert.boneWeights[3]); + std::swap(targVert.boneIDsHigh[0], targVert.boneIDsHigh[3]); // renormalize weights (optional but nice for debugging) const float sumWeights = static_cast(std::reduce(targVert.boneWeights.begin(), targVert.boneWeights.end())) / 255.0; diff --git a/rts/Rendering/Models/IModelParser.cpp b/rts/Rendering/Models/IModelParser.cpp index 8d10f13ce0..0d5c4dd4ba 100644 --- a/rts/Rendering/Models/IModelParser.cpp +++ b/rts/Rendering/Models/IModelParser.cpp @@ -416,9 +416,9 @@ void CModelLoader::ParseModel(S3DModel& model, const std::string& name, const st } parser->Load(model, path); - if (model.numPieces > 254) { + if (model.numPieces > MAX_PIECES_PER_MODEL) { LoadDummyModel(model); - throw content_error("A model has too many pieces (>254)" + path); + throw content_error(fmt::sprintf("A model has too many pieces (>%u): %s", MAX_PIECES_PER_MODEL, path)); } } catch (const content_error& ex) { From a9d50a42423b8a9123e0c86cfacb757d89751758 Mon Sep 17 00:00:00 2001 From: lhog <1476261+lhog@users.noreply.github.com> Date: Sun, 29 Dec 2024 18:53:24 +0100 Subject: [PATCH 2/4] Update rts/Rendering/GL/myGL.h Co-authored-by: sprunk --- rts/Rendering/GL/myGL.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rts/Rendering/GL/myGL.h b/rts/Rendering/GL/myGL.h index 3acfdcd6a6..f3704577ee 100644 --- a/rts/Rendering/GL/myGL.h +++ b/rts/Rendering/GL/myGL.h @@ -157,9 +157,9 @@ struct SInstanceData { SInstanceData(uint32_t matOffset_, uint8_t teamIndex, uint8_t drawFlags, uint16_t numPieces, uint32_t uniOffset_, uint32_t bposeMatOffset_) : matOffset{ matOffset_ } // updated during the following draw frames , uniOffset{ uniOffset_ } // updated during the following draw frames - , info{ teamIndex, drawFlags - , static_cast((numPieces >> 8) & 0xFF) - , static_cast((numPieces ) & 0xFF) } // not updated during the following draw frames + , info{ teamIndex, drawFlags // not updated during the following draw frames + , static_cast((numPieces >> 8) & 0xFF) + , static_cast((numPieces ) & 0xFF) } , bposeMatOffset { bposeMatOffset_ } // updated during the following draw frames {} From 1afc44600ab8ec7980181fc82c7d5f2a77bf173c Mon Sep 17 00:00:00 2001 From: lhog <1476261+lhog@users.noreply.github.com> Date: Sun, 29 Dec 2024 18:54:51 +0100 Subject: [PATCH 3/4] Update myGL.h --- rts/Rendering/GL/myGL.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rts/Rendering/GL/myGL.h b/rts/Rendering/GL/myGL.h index f3704577ee..f233b2601b 100644 --- a/rts/Rendering/GL/myGL.h +++ b/rts/Rendering/GL/myGL.h @@ -157,7 +157,7 @@ struct SInstanceData { SInstanceData(uint32_t matOffset_, uint8_t teamIndex, uint8_t drawFlags, uint16_t numPieces, uint32_t uniOffset_, uint32_t bposeMatOffset_) : matOffset{ matOffset_ } // updated during the following draw frames , uniOffset{ uniOffset_ } // updated during the following draw frames - , info{ teamIndex, drawFlags // not updated during the following draw frames + , info{ teamIndex, drawFlags // not updated during the following draw frames , static_cast((numPieces >> 8) & 0xFF) , static_cast((numPieces ) & 0xFF) } , bposeMatOffset { bposeMatOffset_ } // updated during the following draw frames From 2a7bf4a970a97c175955a0489c5a752139efbe77 Mon Sep 17 00:00:00 2001 From: lhog <1476261+lhog@users.noreply.github.com> Date: Sun, 29 Dec 2024 18:56:21 +0100 Subject: [PATCH 4/4] Update 3DModel.h --- rts/Rendering/Models/3DModel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rts/Rendering/Models/3DModel.h b/rts/Rendering/Models/3DModel.h index c2e362e473..5514a92477 100644 --- a/rts/Rendering/Models/3DModel.h +++ b/rts/Rendering/Models/3DModel.h @@ -83,7 +83,7 @@ struct SVertexData { float3 tTangent; float2 texCoords[NUM_MODEL_UVCHANNS]; std::array boneIDsLow; - std::array boneWeights; + std::array boneWeights; std::array boneIDsHigh; static constexpr std::array DEFAULT_BONEIDS_HIGH = { 255, 255, 255, 255 };