From a7b9f80e328e49973f60cbf69a4846c327b74e42 Mon Sep 17 00:00:00 2001 From: hhalen Date: Wed, 10 Apr 2019 13:11:24 -0700 Subject: [PATCH] Add support for 8 bone influences per vertex. This generalizes FbxSkinningAccess to be agnostic to vertex types and support any number of bone influences. RawModel and Raw2Gltf still operates on (multiple) Vec4f Vec4i types for per vertex weights and bone ids. A good second step would be to generalize RawModel as well, though AddAttributeToPrimitive, GetAttributeArray and the attribute pointer in AttributeDefinition complicates this. --- src/FBX2glTF.cpp | 2 +- src/fbx/Fbx2Raw.cpp | 79 +++++++++++++++++++---------------- src/fbx/FbxSkinningAccess.cpp | 20 +++++---- src/fbx/FbxSkinningAccess.hpp | 36 ++++++++++------ src/gltf/Raw2Gltf.cpp | 26 ++++++++++-- src/raw/RawModel.cpp | 39 +++++++++++------ src/raw/RawModel.hpp | 12 ++++-- 7 files changed, 136 insertions(+), 78 deletions(-) diff --git a/src/FBX2glTF.cpp b/src/FBX2glTF.cpp index 270fcd7..430cabc 100644 --- a/src/FBX2glTF.cpp +++ b/src/FBX2glTF.cpp @@ -144,7 +144,7 @@ int main(int argc, char* argv[]) { "-k,--keep-attribute", [&](std::vector attributes) -> bool { gltfOptions.keepAttribs = - RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1; for (std::string attribute : attributes) { if (attribute == "position") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION; diff --git a/src/fbx/Fbx2Raw.cpp b/src/fbx/Fbx2Raw.cpp index 9a11bdd..31aa086 100644 --- a/src/fbx/Fbx2Raw.cpp +++ b/src/fbx/Fbx2Raw.cpp @@ -69,6 +69,27 @@ 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) { + const FbxVector4 localPosition = + skinning.GetJointInverseGlobalTransforms(indices[i]).MultNormalize(globalPosition); + + Vec3f& mins = rawSurface.jointGeometryMins[indices[i]]; + 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]]; + 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]); + } + } +} + + + static void ReadMesh( RawModel& raw, FbxScene* pScene, @@ -159,8 +180,15 @@ static void ReadMesh( raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_UV1); } if (skinning.IsSkinned()) { - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS); - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES); + 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); @@ -342,8 +370,10 @@ static void ReadMesh( vertex.uv0[1] = (float)fbxUV0[1]; vertex.uv1[0] = (float)fbxUV1[0]; vertex.uv1[1] = (float)fbxUV1[1]; - vertex.jointIndices = skinning.GetVertexIndices(controlPointIndex); - vertex.jointWeights = skinning.GetVertexWeights(controlPointIndex); + vertex.jointIndices0 = skinning.GetVertexIndices(controlPointIndex, 0); + vertex.jointWeights0 = skinning.GetVertexWeights(controlPointIndex, 0); + vertex.jointIndices1 = skinning.GetVertexIndices(controlPointIndex, 1); + vertex.jointWeights1 = skinning.GetVertexWeights(controlPointIndex, 1); vertex.polarityUv0 = false; // flag this triangle as transparent if any of its corner vertices substantially deviates from @@ -387,38 +417,15 @@ static void ReadMesh( } if (skinning.IsSkinned()) { - const int jointIndices[FbxSkinningAccess::MAX_WEIGHTS] = {vertex.jointIndices[0], - vertex.jointIndices[1], - vertex.jointIndices[2], - vertex.jointIndices[3]}; - const float jointWeights[FbxSkinningAccess::MAX_WEIGHTS] = {vertex.jointWeights[0], - vertex.jointWeights[1], - vertex.jointWeights[2], - vertex.jointWeights[3]}; - const FbxMatrix skinningMatrix = - skinning.GetJointSkinningTransform(jointIndices[0]) * jointWeights[0] + - skinning.GetJointSkinningTransform(jointIndices[1]) * jointWeights[1] + - skinning.GetJointSkinningTransform(jointIndices[2]) * jointWeights[2] + - skinning.GetJointSkinningTransform(jointIndices[3]) * jointWeights[3]; - - const FbxVector4 globalPosition = skinningMatrix.MultNormalize(fbxPosition); - for (int i = 0; i < FbxSkinningAccess::MAX_WEIGHTS; i++) { - if (jointWeights[i] > 0.0f) { - const FbxVector4 localPosition = - skinning.GetJointInverseGlobalTransforms(jointIndices[i]) - .MultNormalize(globalPosition); - - Vec3f& mins = rawSurface.jointGeometryMins[jointIndices[i]]; - 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[jointIndices[i]]; - 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]); - } - } + 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]; + + const FbxVector4 globalPosition = skinningMatrix.MultNormalize(fbxPosition); + calcMinMax(rawSurface, skinning, globalPosition, vertex.jointIndices0, vertex.jointWeights0); + calcMinMax(rawSurface, skinning, globalPosition, vertex.jointIndices1, vertex.jointWeights1); } } diff --git a/src/fbx/FbxSkinningAccess.cpp b/src/fbx/FbxSkinningAccess.cpp index 795cece..e57bc90 100644 --- a/src/fbx/FbxSkinningAccess.cpp +++ b/src/fbx/FbxSkinningAccess.cpp @@ -20,8 +20,8 @@ FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, Fbx continue; } int controlPointCount = pMesh->GetControlPointsCount(); - vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0)); - vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f)); + vertexJointIndices.resize(controlPointCount); + vertexJointWeights.resize(controlPointCount); for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) { FbxCluster* cluster = skin->GetCluster(clusterIndex); @@ -55,12 +55,12 @@ FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, Fbx if (clusterIndices[i] < 0 || clusterIndices[i] >= controlPointCount) { continue; } - if (clusterWeights[i] <= vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1]) { + if (clusterWeights[i] <= 0.0 || vertexJointWeights[clusterIndices[i]].size() >= MAX_WEIGHTS) { continue; } - vertexJointIndices[clusterIndices[i]][MAX_WEIGHTS - 1] = clusterIndex; - vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1] = (float)clusterWeights[i]; - for (int j = MAX_WEIGHTS - 1; j > 0; j--) { + 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; @@ -75,8 +75,12 @@ FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, Fbx } } for (int i = 0; i < controlPointCount; i++) { - const float weightSumRcp = 1.0 / (vertexJointWeights[i][0] + vertexJointWeights[i][1] + vertexJointWeights[i][2] + vertexJointWeights[i][3]); - vertexJointWeights[i] *= weightSumRcp; + 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; } } } diff --git a/src/fbx/FbxSkinningAccess.hpp b/src/fbx/FbxSkinningAccess.hpp index d2b42d8..aa4e6c7 100644 --- a/src/fbx/FbxSkinningAccess.hpp +++ b/src/fbx/FbxSkinningAccess.hpp @@ -21,7 +21,7 @@ class FbxSkinningAccess { public: - static const int MAX_WEIGHTS = 4; + static const int MAX_WEIGHTS = 8; FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, FbxNode* pNode); @@ -29,6 +29,10 @@ class FbxSkinningAccess { return (vertexJointWeights.size() > 0); } + int MaxWeights() const { + return MAX_WEIGHTS; + } + int GetNodeCount() const { return (int)jointNodes.size(); } @@ -58,15 +62,23 @@ class FbxSkinningAccess { return inverseBindMatrices[jointIndex]; } - const Vec4i GetVertexIndices(const int controlPointIndex) const { - return (!vertexJointIndices.empty()) ? vertexJointIndices[controlPointIndex] - : Vec4i(0, 0, 0, 0); - } - - const Vec4f GetVertexWeights(const int controlPointIndex) const { - return (!vertexJointWeights.empty()) ? vertexJointWeights[controlPointIndex] - : Vec4f(0, 0, 0, 0); - } + 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; + } private: int rootIndex; @@ -75,6 +87,6 @@ class FbxSkinningAccess { std::vector jointSkinningTransforms; std::vector jointInverseGlobalTransforms; std::vector inverseBindMatrices; - std::vector vertexJointIndices; - std::vector vertexJointWeights; + std::vector> vertexJointIndices; + std::vector> vertexJointWeights; }; diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index 84ec82b..3ed539e 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -532,24 +532,42 @@ ModelData* Raw2Gltf( draco::DT_FLOAT32); gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1); } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) { + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0) != 0) { const AttributeDefinition ATTR_JOINTS( "JOINTS_0", - &RawVertex::jointIndices, + &RawVertex::jointIndices0, GLT_VEC4I, draco::GeometryAttribute::GENERIC, draco::DT_UINT16); gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) { + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0) != 0) { const AttributeDefinition ATTR_WEIGHTS( "WEIGHTS_0", - &RawVertex::jointWeights, + &RawVertex::jointWeights0, 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); + } // each channel present in the mesh always ends up a target in the primitive for (int channelIx = 0; channelIx < rawSurface.blendChannels.size(); channelIx++) { diff --git a/src/raw/RawModel.cpp b/src/raw/RawModel.cpp index cafc076..19c4fd5 100644 --- a/src/raw/RawModel.cpp +++ b/src/raw/RawModel.cpp @@ -26,8 +26,9 @@ bool RawVertex::operator==(const RawVertex& other) const { return (position == other.position) && (normal == other.normal) && (tangent == other.tangent) && (binormal == other.binormal) && (color == other.color) && (uv0 == other.uv0) && - (uv1 == other.uv1) && (jointIndices == other.jointIndices) && - (jointWeights == other.jointWeights) && (polarityUv0 == other.polarityUv0) && + (uv1 == other.uv1) && (jointIndices0 == other.jointIndices0) && + (jointWeights0 == other.jointWeights0) && (jointIndices1 == other.jointIndices1) && + (jointWeights1 == other.jointWeights1) && (polarityUv0 == other.polarityUv0) && (blendSurfaceIx == other.blendSurfaceIx) && (blends == other.blends); } @@ -55,11 +56,17 @@ size_t RawVertex::Difference(const RawVertex& other) const { attributes |= RAW_VERTEX_ATTRIBUTE_UV1; } // Always need both or neither. - if (jointIndices != other.jointIndices) { - attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; - } - if (jointWeights != other.jointWeights) { - attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; + if (jointIndices0 != other.jointIndices0) { + 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) { + 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; } return attributes; } @@ -573,7 +580,7 @@ void RawModel::CreateMaterialModels( if (keepAttribs != -1) { int keep = keepAttribs; if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { - keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; + keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1; } if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_AUTO) != 0) { keep |= RAW_VERTEX_ATTRIBUTE_POSITION; @@ -614,11 +621,17 @@ void RawModel::CreateMaterialModels( if ((keep & RAW_VERTEX_ATTRIBUTE_UV1) == 0) { vertex.uv1 = defaultVertex.uv1; } - if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) == 0) { - vertex.jointIndices = defaultVertex.jointIndices; - } - if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) == 0) { - vertex.jointWeights = defaultVertex.jointWeights; + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0) == 0) { + vertex.jointIndices0 = defaultVertex.jointIndices0; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0) == 0) { + vertex.jointWeights0 = defaultVertex.jointWeights0; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1) == 0) { + vertex.jointIndices1 = defaultVertex.jointIndices1; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1) == 0) { + vertex.jointWeights1 = defaultVertex.jointWeights1; } } diff --git a/src/raw/RawModel.hpp b/src/raw/RawModel.hpp index c21c4bb..d09f0c3 100644 --- a/src/raw/RawModel.hpp +++ b/src/raw/RawModel.hpp @@ -23,8 +23,10 @@ enum RawVertexAttribute { RAW_VERTEX_ATTRIBUTE_COLOR = 1 << 4, RAW_VERTEX_ATTRIBUTE_UV0 = 1 << 5, RAW_VERTEX_ATTRIBUTE_UV1 = 1 << 6, - RAW_VERTEX_ATTRIBUTE_JOINT_INDICES = 1 << 7, - RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS = 1 << 8, + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 = 1 << 7, + RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 = 1 << 8, + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 = 1 << 9, + RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1 = 1 << 10, RAW_VERTEX_ATTRIBUTE_AUTO = 1 << 31 }; @@ -49,8 +51,10 @@ struct RawVertex { Vec4f color{0.0f}; Vec2f uv0{0.0f}; Vec2f uv1{0.0f}; - Vec4i jointIndices{0, 0, 0, 0}; - Vec4f jointWeights{0.0f}; + Vec4i jointIndices0{0,0,0,0}; + Vec4i jointIndices1{0,0,0,0}; + Vec4f jointWeights0{0.0f}; + Vec4f jointWeights1{0.0f}; // end of members that directly correspond to vertex attributes // if this vertex participates in a blend shape setup, the surfaceIx of its dedicated mesh;