From b6d7fd04f258f36c83ea438259fc440828a140e8 Mon Sep 17 00:00:00 2001 From: hhalen Date: Wed, 17 Apr 2019 19:00:08 -0700 Subject: [PATCH] Missing files for commit a7b9f8, adding up to 16 bone influences. --- src/FBX2glTF.cpp | 19 +++++- src/FBX2glTF.h | 4 ++ src/fbx/Fbx2Raw.cpp | 41 +++++-------- src/fbx/FbxSkinningAccess.cpp | 34 +++-------- src/fbx/FbxSkinningAccess.hpp | 34 ++++------- src/gltf/GltfModel.hpp | 32 ++++++++++ src/gltf/Raw2Gltf.cpp | 70 ++++++++++++++++------ src/raw/RawModel.cpp | 108 ++++++++++++++++++++++++++++++---- src/raw/RawModel.hpp | 30 ++++++++-- 9 files changed, 263 insertions(+), 109 deletions(-) diff --git a/src/FBX2glTF.cpp b/src/FBX2glTF.cpp index 430cabc..b5ae8b0 100644 --- a/src/FBX2glTF.cpp +++ b/src/FBX2glTF.cpp @@ -140,11 +140,24 @@ int main(int argc, char* argv[]) { gltfOptions.useBlendShapeTangents, "Include blend shape tangents, if reported present by the FBX SDK."); + app.add_flag( + "--normalize-weights", + gltfOptions.normalizeSkinningWeights, + "Normalize skinning weights."); + + app.add_option( + "--skinning-weights", + gltfOptions.maxSkinningWeights, + "How many joint influences per vertex are allowed.", + true) + ->check(CLI::Range(1, RawModel::MAX_SUPPORTED_WEIGHTS)); + app.add_option( "-k,--keep-attribute", [&](std::vector attributes) -> bool { gltfOptions.keepAttribs = - RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1; + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1 | + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3; for (std::string attribute : attributes) { if (attribute == "position") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION; @@ -204,7 +217,7 @@ int main(int argc, char* argv[]) { app.add_option( "--draco-bits-for-normals", gltfOptions.draco.quantBitsNormal, - "How many bits to quantize nornals to.", + "How many bits to quantize normals to.", true) ->check(CLI::Range(1, 32)) ->group("Draco"); @@ -310,7 +323,7 @@ int main(int argc, char* argv[]) { if (!texturesTransforms.empty()) { raw.TransformTextures(texturesTransforms); } - raw.Condense(); + raw.Condense(gltfOptions.maxSkinningWeights, gltfOptions.normalizeSkinningWeights); raw.TransformGeometry(gltfOptions.computeNormals); std::ofstream outStream; // note: auto-flushes in destructor diff --git a/src/FBX2glTF.h b/src/FBX2glTF.h index 5d3f07a..67b73cb 100644 --- a/src/FBX2glTF.h +++ b/src/FBX2glTF.h @@ -99,6 +99,10 @@ struct GltfOptions bool useBlendShapeNormals { false }; /** Whether to include blend shape tangents, if present according to the SDK. */ bool useBlendShapeTangents { false }; + /** Whether to normalized skinning weights. */ + bool normalizeSkinningWeights { true }; + /** Maximum number of bone influences per vertex. */ + int maxSkinningWeights { 4 }; /** When to compute vertex normals from geometry. */ ComputeNormalsOption computeNormals = ComputeNormalsOption::BROKEN; /** When to use 32-bit indices. */ diff --git a/src/fbx/Fbx2Raw.cpp b/src/fbx/Fbx2Raw.cpp index 31aa086..d4d2f29 100644 --- a/src/fbx/Fbx2Raw.cpp +++ b/src/fbx/Fbx2Raw.cpp @@ -69,18 +69,18 @@ static RawMaterialType GetMaterialType( return skinned ? RAW_MATERIAL_TYPE_SKINNED_OPAQUE : RAW_MATERIAL_TYPE_OPAQUE; } -static void calcMinMax(RawSurface& rawSurface, const FbxSkinningAccess& skinning, const FbxVector4& globalPosition, const Vec4i& indices, const Vec4f& weights) { - for (int i = 0; i < 4; i++) { - if (weights[i] > 0.0f) { +static void calcMinMax(RawSurface& rawSurface, const FbxSkinningAccess& skinning, const FbxVector4& globalPosition, const std::vector& indicesAndWeights) { + for (int i = 0; i < indicesAndWeights.size(); i++) { + if (indicesAndWeights[i].jointWeight > 0.0f) { const FbxVector4 localPosition = - skinning.GetJointInverseGlobalTransforms(indices[i]).MultNormalize(globalPosition); + skinning.GetJointInverseGlobalTransforms(indicesAndWeights[i].jointIndex).MultNormalize(globalPosition); - Vec3f& mins = rawSurface.jointGeometryMins[indices[i]]; + Vec3f& mins = rawSurface.jointGeometryMins[indicesAndWeights[i].jointIndex]; mins[0] = std::min(mins[0], (float)localPosition[0]); mins[1] = std::min(mins[1], (float)localPosition[1]); mins[2] = std::min(mins[2], (float)localPosition[2]); - Vec3f& maxs = rawSurface.jointGeometryMaxs[indices[i]]; + Vec3f& maxs = rawSurface.jointGeometryMaxs[indicesAndWeights[i].jointIndex]; maxs[0] = std::max(maxs[0], (float)localPosition[0]); maxs[1] = std::max(maxs[1], (float)localPosition[1]); maxs[2] = std::max(maxs[2], (float)localPosition[2]); @@ -179,17 +179,6 @@ static void ReadMesh( if (uvLayer1.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_UV1); } - if (skinning.IsSkinned()) { - FBX_ASSERT(skinning.MaxWeights() <= 8); // output only supports up to 8 - - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0); - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0); - if (skinning.MaxWeights() > 4) { - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1); - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1); - } - - } RawSurface& rawSurface = raw.GetSurface(rawSurfaceIndex); @@ -370,10 +359,11 @@ static void ReadMesh( vertex.uv0[1] = (float)fbxUV0[1]; vertex.uv1[0] = (float)fbxUV1[0]; vertex.uv1[1] = (float)fbxUV1[1]; - vertex.jointIndices0 = skinning.GetVertexIndices(controlPointIndex, 0); - vertex.jointWeights0 = skinning.GetVertexWeights(controlPointIndex, 0); - vertex.jointIndices1 = skinning.GetVertexIndices(controlPointIndex, 1); - vertex.jointWeights1 = skinning.GetVertexWeights(controlPointIndex, 1); + const std::vector skinningInfo = skinning.GetVertexSkinningInfo(controlPointIndex); + for (int skinningIndex = 0; skinningIndex < skinningInfo.size(); skinningIndex++) { + const FbxVertexSkinningInfo& sourceSkinningInfo = skinningInfo[skinningIndex]; + vertex.skinningInfo.push_back(RawVertexSkinningInfo{ sourceSkinningInfo.jointId, sourceSkinningInfo.weight }); + } vertex.polarityUv0 = false; // flag this triangle as transparent if any of its corner vertices substantially deviates from @@ -418,14 +408,11 @@ static void ReadMesh( if (skinning.IsSkinned()) { FbxMatrix skinningMatrix = FbxMatrix() * 0.0; - for (int j = 0; j < 4; j++) - skinningMatrix += skinning.GetJointSkinningTransform(vertex.jointIndices0[j]) * vertex.jointWeights0[j]; - for (int j = 0; j < 4; j++) - skinningMatrix += skinning.GetJointSkinningTransform(vertex.jointIndices1[j]) * vertex.jointWeights1[j]; + for (int j = 0; j < vertex.skinningInfo.size(); j++) + skinningMatrix += skinning.GetJointSkinningTransform(vertex.skinningInfo[j].jointIndex) * vertex.skinningInfo[j].jointWeight; const FbxVector4 globalPosition = skinningMatrix.MultNormalize(fbxPosition); - calcMinMax(rawSurface, skinning, globalPosition, vertex.jointIndices0, vertex.jointWeights0); - calcMinMax(rawSurface, skinning, globalPosition, vertex.jointIndices1, vertex.jointWeights1); + calcMinMax(rawSurface, skinning, globalPosition, vertex.skinningInfo); } } diff --git a/src/fbx/FbxSkinningAccess.cpp b/src/fbx/FbxSkinningAccess.cpp index e57bc90..b6a8541 100644 --- a/src/fbx/FbxSkinningAccess.cpp +++ b/src/fbx/FbxSkinningAccess.cpp @@ -20,8 +20,7 @@ FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, Fbx continue; } int controlPointCount = pMesh->GetControlPointsCount(); - vertexJointIndices.resize(controlPointCount); - vertexJointWeights.resize(controlPointCount); + vertexSkinning.resize(controlPointCount); for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) { FbxCluster* cluster = skin->GetCluster(clusterIndex); @@ -55,33 +54,18 @@ FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, Fbx if (clusterIndices[i] < 0 || clusterIndices[i] >= controlPointCount) { continue; } - if (clusterWeights[i] <= 0.0 || vertexJointWeights[clusterIndices[i]].size() >= MAX_WEIGHTS) { + if (clusterWeights[i] <= 0.0) { continue; } - vertexJointIndices[clusterIndices[i]].push_back(clusterIndex); - vertexJointWeights[clusterIndices[i]].push_back((float)clusterWeights[i]); - for (int j = vertexJointWeights[clusterIndices[i]].size() - 1; j > 0; j--) { - if (vertexJointWeights[clusterIndices[i]][j - 1] >= - vertexJointWeights[clusterIndices[i]][j]) { - break; - } - std::swap( - vertexJointIndices[clusterIndices[i]][j - 1], - vertexJointIndices[clusterIndices[i]][j]); - std::swap( - vertexJointWeights[clusterIndices[i]][j - 1], - vertexJointWeights[clusterIndices[i]][j]); - } + + vertexSkinning[clusterIndices[i]].push_back(FbxVertexSkinningInfo{(int) clusterIndex, (float)clusterWeights[i]}); + } } - for (int i = 0; i < controlPointCount; i++) { - float weightSum = 0.0; - for (int w = 0; w < vertexJointWeights[i].size(); ++w) - weightSum += vertexJointWeights[i][w]; - float weightSumRcp = 1.0 / weightSum; - for (int w = 0; w < vertexJointWeights[i].size(); ++w) - vertexJointWeights[i][w] *= weightSumRcp; - } + + for (int i = 0; i < vertexSkinning.size(); i++) + maxBoneInfluences = std::max((int) vertexSkinning[i].size(), maxBoneInfluences); + } } diff --git a/src/fbx/FbxSkinningAccess.hpp b/src/fbx/FbxSkinningAccess.hpp index aa4e6c7..07b0fe9 100644 --- a/src/fbx/FbxSkinningAccess.hpp +++ b/src/fbx/FbxSkinningAccess.hpp @@ -19,19 +19,21 @@ #include "FBX2glTF.h" +struct FbxVertexSkinningInfo { + int jointId; + float weight; +}; + + class FbxSkinningAccess { public: - static const int MAX_WEIGHTS = 8; FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, FbxNode* pNode); bool IsSkinned() const { - return (vertexJointWeights.size() > 0); + return (vertexSkinning.size() > 0); } - int MaxWeights() const { - return MAX_WEIGHTS; - } int GetNodeCount() const { return (int)jointNodes.size(); @@ -61,32 +63,18 @@ class FbxSkinningAccess { const FbxAMatrix& GetInverseBindMatrix(const int jointIndex) const { return inverseBindMatrices[jointIndex]; } - - const Vec4i GetVertexIndices(const int controlPointIndex, const int subset) const { - if (vertexJointIndices.empty()) - return Vec4i(0, 0, 0, 0); - Vec4i indices(0, 0, 0, 0); - for (int k = subset * 4; k < subset * 4 + 4 && k < vertexJointIndices[controlPointIndex].size(); k++) - indices[k - subset * 4] = vertexJointIndices[controlPointIndex][k]; - return indices; - } - const Vec4f GetVertexWeights(const int controlPointIndex, const int subset) const { - if (vertexJointWeights.empty()) - return Vec4f(0.0f); - Vec4f weights(0.0f); - for (int k = subset * 4; k < subset * 4 + 4 && k < vertexJointWeights[controlPointIndex].size(); k++) - weights[k - subset * 4] = vertexJointWeights[controlPointIndex][k]; - return weights; + const std::vector GetVertexSkinningInfo(const int controlPointIndex) const { + return vertexSkinning[controlPointIndex]; } private: int rootIndex; + int maxBoneInfluences; std::vector jointIds; std::vector jointNodes; std::vector jointSkinningTransforms; std::vector jointInverseGlobalTransforms; std::vector inverseBindMatrices; - std::vector> vertexJointIndices; - std::vector> vertexJointWeights; + std::vector> vertexSkinning; }; diff --git a/src/gltf/GltfModel.hpp b/src/gltf/GltfModel.hpp index f82b09f..4702c08 100644 --- a/src/gltf/GltfModel.hpp +++ b/src/gltf/GltfModel.hpp @@ -123,6 +123,38 @@ class GltfModel { return accessor; }; + template + std::shared_ptr AddArrayAttributeToPrimitive( + BufferData& buffer, + const RawModel& surfaceModel, + PrimitiveData& primitive, + const AttributeDefinition& attrDef) { + // copy attribute data into vector + std::vector> attribArr; + surfaceModel.GetAttributeArray(attribArr, attrDef.rawAttributeIx); + + const AttributeDefinition ATTR_WEIGHTS( + "WEIGHTS_0", + &RawVertex::jointWeights, + attrDef.glType, + attrDef.dracoAttribute, + draco::DT_FLOAT32); + + std::shared_ptr accessor; + if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr) { + primitive.AddDracoAttrib(attrDef, attribArr); + + accessor = accessors.hold(new AccessorData(attrDef.glType)); + accessor->count = attribArr.size(); + } + else { + auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER); + accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string("")); + } + primitive.AddAttrib(attrDef.gltfName, *accessor); + return accessor; + }; + template void serializeHolder(json& glTFJson, std::string key, const Holder holder) { if (!holder.ptrs.empty()) { diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index 3ed539e..15834a9 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -550,23 +550,59 @@ ModelData* Raw2Gltf( draco::DT_FLOAT32); gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1) != 0) { - const AttributeDefinition ATTR_JOINTS( - "JOINTS_1", - &RawVertex::jointIndices1, - GLT_VEC4I, - draco::GeometryAttribute::GENERIC, - draco::DT_UINT16); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); - } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1) != 0) { - const AttributeDefinition ATTR_WEIGHTS( - "WEIGHTS_1", - &RawVertex::jointWeights1, - GLT_VEC4F, - draco::GeometryAttribute::GENERIC, - draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1) != 0) { + const AttributeDefinition ATTR_JOINTS( + "JOINTS_1", + &RawVertex::jointIndices1, + GLT_VEC4I, + draco::GeometryAttribute::GENERIC, + draco::DT_UINT16); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1) != 0) { + const AttributeDefinition ATTR_WEIGHTS( + "WEIGHTS_1", + &RawVertex::jointWeights1, + GLT_VEC4F, + draco::GeometryAttribute::GENERIC, + draco::DT_FLOAT32); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2) != 0) { + const AttributeDefinition ATTR_JOINTS( + "JOINTS_0", + &RawVertex::jointIndices2, + GLT_VEC4I, + draco::GeometryAttribute::GENERIC, + draco::DT_UINT16); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2) != 0) { + const AttributeDefinition ATTR_WEIGHTS( + "WEIGHTS_0", + &RawVertex::jointWeights2, + GLT_VEC4F, + draco::GeometryAttribute::GENERIC, + draco::DT_FLOAT32); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3) != 0) { + const AttributeDefinition ATTR_JOINTS( + "JOINTS_0", + &RawVertex::jointIndices3, + GLT_VEC4I, + draco::GeometryAttribute::GENERIC, + draco::DT_UINT16); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3) != 0) { + const AttributeDefinition ATTR_WEIGHTS( + "WEIGHTS_0", + &RawVertex::jointWeights3, + GLT_VEC4F, + draco::GeometryAttribute::GENERIC, + draco::DT_FLOAT32); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); } // each channel present in the mesh always ends up a target in the primitive diff --git a/src/raw/RawModel.cpp b/src/raw/RawModel.cpp index 19c4fd5..c63dba6 100644 --- a/src/raw/RawModel.cpp +++ b/src/raw/RawModel.cpp @@ -56,22 +56,22 @@ size_t RawVertex::Difference(const RawVertex& other) const { attributes |= RAW_VERTEX_ATTRIBUTE_UV1; } // Always need both or neither. - if (jointIndices0 != other.jointIndices0) { + if (jointIndices0 != other.jointIndices0 || jointWeights0 != other.jointWeights0) { attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0; } - if (jointWeights0 != other.jointWeights0) { - attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0; - } - if (jointIndices1 != other.jointIndices1) { + if (jointIndices1 != other.jointIndices1 || jointWeights1 != other.jointWeights1) { attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1; } - if (jointWeights1 != other.jointWeights1) { - attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1; + if (jointIndices2 != other.jointIndices2 || jointWeights2 != other.jointWeights2) { + attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2; + } + if (jointIndices3 != other.jointIndices3 || jointWeights3 != other.jointWeights3) { + attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3; } return attributes; } -RawModel::RawModel() : vertexAttributes(0) {} +RawModel::RawModel() : vertexAttributes(0){} void RawModel::AddVertexAttribute(const RawVertexAttribute attrib) { vertexAttributes |= attrib; @@ -334,7 +334,7 @@ int RawModel::AddNode(const long id, const char* name, const long parentId) { return (int)nodes.size() - 1; } -void RawModel::Condense() { +void RawModel::Condense(const int maxSkinningWeights, const bool normalizeWeights) { // Only keep surfaces that are referenced by one or more triangles. { std::vector oldSurfaces = surfaces; @@ -404,6 +404,81 @@ void RawModel::Condense() { } } } + + { + int globalMaxWeights = 0; + for (auto& vertex: vertices) { + + // Sort from largest to smallest weight. + std::sort(vertex.skinningInfo.begin(), vertex.skinningInfo.end(), std::greater()); + + // Reduce to fit the requirements. + if (maxSkinningWeights < vertex.skinningInfo.size()) + vertex.skinningInfo.resize(maxSkinningWeights); + globalMaxWeights = std::max(globalMaxWeights, (int) vertex.skinningInfo.size()); + + // Normalize weights if requested. + if (normalizeWeights) { + float weightSum = 0; + for (auto& jointWeight : vertex.skinningInfo) + weightSum += jointWeight.jointWeight; + const float weightSumRcp = 1.0 / weightSum; + for (auto& jointWeight : vertex.skinningInfo) + jointWeight.jointWeight *= weightSumRcp; + } + } + + if (globalMaxWeights > 0) { + AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0); + AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0); + } + if (globalMaxWeights > 1) { + AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1); + AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1); + } + if (globalMaxWeights > 2) { + AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2); + AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2); + } + if (globalMaxWeights > 3) { + AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3); + AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3); + } + + assert(globalMaxWeights <= RawModel::MAX_SUPPORTED_WEIGHTS); + // Copy to gltf friendly structure + for (auto& vertex : vertices) { + for (int i = 0; i < globalMaxWeights; i += 4) { // Ensure all vertices have the same number of streams + Vec4f weights{0.0}; + Vec4i jointIds{0,0,0,0}; + for (int j = i; j < i + 4 && j < vertex.skinningInfo.size(); j++) { + weights[j - i] = vertex.skinningInfo[j].jointWeight; + jointIds[j - i] = vertex.skinningInfo[j].jointIndex; + } + const int vertexStream = i / 4; + switch (vertexStream) { + case 0: + vertex.jointIndices0 = jointIds; + vertex.jointWeights0 = weights; + break; + case 1: + vertex.jointIndices1 = jointIds; + vertex.jointWeights1 = weights; + break; + case 2: + vertex.jointIndices2 = jointIds; + vertex.jointWeights2 = weights; + break; + case 3: + vertex.jointIndices3 = jointIds; + vertex.jointWeights3 = weights; + break; + default: + assert(0); + } + } + } + } } void RawModel::TransformGeometry(ComputeNormalsOption normals) { @@ -580,7 +655,8 @@ void RawModel::CreateMaterialModels( if (keepAttribs != -1) { int keep = keepAttribs; if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { - keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1; + keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1 | + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3; } if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_AUTO) != 0) { keep |= RAW_VERTEX_ATTRIBUTE_POSITION; @@ -633,6 +709,18 @@ void RawModel::CreateMaterialModels( if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1) == 0) { vertex.jointWeights1 = defaultVertex.jointWeights1; } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2) == 0) { + vertex.jointIndices2 = defaultVertex.jointIndices2; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2) == 0) { + vertex.jointWeights2 = defaultVertex.jointWeights2; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3) == 0) { + vertex.jointIndices3 = defaultVertex.jointIndices3; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3) == 0) { + vertex.jointWeights3 = defaultVertex.jointWeights3; + } } verts[j] = model->AddVertex(vertex); diff --git a/src/raw/RawModel.hpp b/src/raw/RawModel.hpp index d09f0c3..2f42f31 100644 --- a/src/raw/RawModel.hpp +++ b/src/raw/RawModel.hpp @@ -27,6 +27,10 @@ enum RawVertexAttribute { RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 = 1 << 8, RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 = 1 << 9, RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1 = 1 << 10, + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES2 = 1 << 11, + RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS2 = 1 << 12, + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES3 = 1 << 13, + RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS3 = 1 << 14, RAW_VERTEX_ATTRIBUTE_AUTO = 1 << 31 }; @@ -41,6 +45,16 @@ struct RawBlendVertex { } }; +struct RawVertexSkinningInfo +{ + int jointIndex; + float jointWeight; + + bool operator>(const RawVertexSkinningInfo& rjw) const { + return jointWeight > rjw.jointWeight; + } +}; + struct RawVertex { RawVertex() : polarityUv0(false), pad1(false), pad2(false), pad3(false) {} @@ -51,10 +65,16 @@ struct RawVertex { Vec4f color{0.0f}; Vec2f uv0{0.0f}; Vec2f uv1{0.0f}; - Vec4i jointIndices0{0,0,0,0}; - Vec4i jointIndices1{0,0,0,0}; - Vec4f jointWeights0{0.0f}; + Vec4i jointIndices0{0,0,0,0}; + Vec4i jointIndices1{0,0,0,0}; + Vec4i jointIndices2{0,0,0,0}; + Vec4i jointIndices3{0,0,0,0}; + Vec4f jointWeights0{0.0f}; Vec4f jointWeights1{0.0f}; + Vec4f jointWeights2{0.0f}; + Vec4f jointWeights3{0.0f}; + + std::vector skinningInfo; // end of members that directly correspond to vertex attributes // if this vertex participates in a blend shape setup, the surfaceIx of its dedicated mesh; @@ -361,6 +381,8 @@ struct RawNode { class RawModel { public: + static const int MAX_SUPPORTED_WEIGHTS = 16; + RawModel(); // Add geometry. @@ -421,7 +443,7 @@ class RawModel { // Remove unused vertices, textures or materials after removing vertex attributes, textures, // materials or surfaces. - void Condense(); + void Condense(const int maxSkinningWeights, const bool normalizeWeights); void TransformGeometry(ComputeNormalsOption);