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;