diff options
| author | Anton Novoselov <[email protected]> | 2017-08-14 16:41:28 +0300 |
|---|---|---|
| committer | Anton Novoselov <[email protected]> | 2017-08-14 16:41:28 +0300 |
| commit | 9a1c1d814f3fa0b54b49d90b43130c02bc280f44 (patch) | |
| tree | eae6ead883173c66619c30b6a1ed085f3cb70f4d /sdk/extensions/exporter/source | |
| parent | Updated to CL 22627414: (diff) | |
| download | blast-9a1c1d814f3fa0b54b49d90b43130c02bc280f44.tar.xz blast-9a1c1d814f3fa0b54b49d90b43130c02bc280f44.zip | |
Updated to CL 22661993:
* docs updates
* authoring fixes
* asset view in sample fix
* latest blast_tools_and_samples-windows.zip
Diffstat (limited to 'sdk/extensions/exporter/source')
| -rw-r--r-- | sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp | 371 | ||||
| -rw-r--r-- | sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h | 3 |
2 files changed, 354 insertions, 20 deletions
diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp index 161ec5f..eda8f7c 100644 --- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp @@ -30,6 +30,9 @@ #include <iostream> #include <sstream> #include <iomanip> +#include <map> +#include <algorithm> +#include <set> #include "NvBlastTypes.h" #include "NvBlastGlobals.h" #include "NvBlastTkFramework.h" @@ -212,13 +215,11 @@ bool FbxFileWriter::appendMesh(const AuthoringResult& aResult, const char* asset if (!smElement) { //If no smoothing groups, generate them - FbxGeometryConverter fbxConv(mesh->GetFbxManager()); - if (fbxConv.ComputeEdgeSmoothingFromNormals(mesh)) - { - fbxConv.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0); - } + generateSmoothingGroups(mesh, skin); } + removeDuplicateControlPoints(mesh, skin); + if (aResult.collisionHull != nullptr) { return appendCollisionMesh(chunkCount, aResult.collisionHullOffset, aResult.collisionHull, assetName); @@ -544,13 +545,11 @@ void FbxFileWriter::createChunkRecursiveNonSkinned(const std::string& meshName, if (!mesh->GetElementSmoothing()) { //If no smoothing groups, generate them - FbxGeometryConverter fbxConv(mesh->GetFbxManager()); - if (fbxConv.ComputeEdgeSmoothingFromNormals(mesh)) - { - fbxConv.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0); - } + generateSmoothingGroups(mesh, nullptr); } + removeDuplicateControlPoints(mesh, nullptr); + for (uint32_t i = chunk->firstChildIndex; i < chunk->childIndexStop; i++) { createChunkRecursiveNonSkinned(meshName, i, meshNode, materials, meshData); @@ -673,13 +672,12 @@ void FbxFileWriter::createChunkRecursiveNonSkinned(const std::string& meshName, if (!smElement) { //If no smoothing groups, generate them - FbxGeometryConverter fbxConv(mesh->GetFbxManager()); - if (fbxConv.ComputeEdgeSmoothingFromNormals(mesh)) - { - fbxConv.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0); - } + generateSmoothingGroups(mesh, nullptr); + } + removeDuplicateControlPoints(mesh, nullptr); + for (uint32_t i = chunk->firstChildIndex; i < chunk->childIndexStop; i++) { createChunkRecursiveNonSkinned(meshName, i, meshNode, materials, aResult); @@ -1079,13 +1077,11 @@ bool FbxFileWriter::appendMesh(const ExporterMeshData& meshData, const char* ass if (!mesh->GetElementSmoothing()) { //If no smoothing groups, generate them - FbxGeometryConverter fbxConv(mesh->GetFbxManager()); - if (fbxConv.ComputeEdgeSmoothingFromNormals(mesh)) - { - fbxConv.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0); - } + generateSmoothingGroups(mesh, skin); } + removeDuplicateControlPoints(mesh, skin); + if (meshData.hulls != nullptr) { return appendCollisionMesh(chunkCount, meshData.hullsOffsets, meshData.hulls, assetName); @@ -1093,3 +1089,338 @@ bool FbxFileWriter::appendMesh(const ExporterMeshData& meshData, const char* ass return true; } +void FbxFileWriter::generateSmoothingGroups(fbxsdk::FbxMesh* mesh, FbxSkin* skin) +{ + if (mesh->GetElementSmoothing(0) || !mesh->IsTriangleMesh()) + { + //they already exist or we can't make it + return; + } + + const FbxGeometryElementNormal* geNormal = mesh->GetElementNormal(); + if (!geNormal || geNormal->GetMappingMode() != FbxGeometryElement::eByPolygonVertex || geNormal->GetReferenceMode() != FbxGeometryElement::eDirect) + { + //We just set this up, but just incase + return; + } + + int clusterCount = 0; + std::vector<std::vector<int>> cpsPerCluster; + if (skin) + { + clusterCount = skin->GetClusterCount(); + cpsPerCluster.resize(clusterCount); + for (int c = 0; c < clusterCount; c++) + { + FbxCluster* cluster = skin->GetCluster(c); + int* clusterCPList = cluster->GetControlPointIndices(); + const int clusterCPListLength = cluster->GetControlPointIndicesCount(); + + cpsPerCluster[c].resize(clusterCPListLength); + memcpy(cpsPerCluster[c].data(), clusterCPList, sizeof(int) * clusterCPListLength); + std::sort(cpsPerCluster[c].begin(), cpsPerCluster[c].end()); + } + } + + auto smElement = mesh->CreateElementSmoothing(); + smElement->SetMappingMode(FbxGeometryElement::eByPolygon); + smElement->SetReferenceMode(FbxGeometryElement::eDirect); + + FbxVector4* cpList = mesh->GetControlPoints(); + const int cpCount = mesh->GetControlPointsCount(); + + const int triangleCount = mesh->GetPolygonCount(); + const int cornerCount = triangleCount * 3; + + int* polygonCPList = mesh->GetPolygonVertices(); + const auto& normalByCornerList = geNormal->GetDirectArray(); + + std::multimap<int, int> overlappingCorners; + //sort them by z for faster overlap checking + std::vector<std::pair<double, int>> cornerIndexesByZ(cornerCount); + for (int c = 0; c < cornerCount; c++) + { + cornerIndexesByZ[c] = std::pair<double, int>(cpList[polygonCPList[c]][2], c); + } + std::sort(cornerIndexesByZ.begin(), cornerIndexesByZ.end()); + + for (int i = 0; i < cornerCount; i++) + { + const int cornerA = cornerIndexesByZ[i].second; + const int cpiA = polygonCPList[cornerA]; + FbxVector4 cpA = cpList[cpiA]; + cpA[3] = 0; + + int clusterIndexA = -1; + for (int c = 0; c < clusterCount; c++) + { + if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiA)) + { + clusterIndexA = c; + break; + } + } + + for (int j = i + 1; j < cornerCount; j++) + { + if (std::abs(cornerIndexesByZ[j].first - cornerIndexesByZ[i].first) > FBXSDK_TOLERANCE) + { + break; // if the z's don't match other values don't matter + } + const int cornerB = cornerIndexesByZ[j].second; + const int cpiB = polygonCPList[cornerB]; + FbxVector4 cpB = cpList[cpiB]; + + cpB[3] = 0; + + //uses FBXSDK_TOLERANCE + if (cpA == cpB) + { + int clusterIndexB = -1; + for (int c = 0; c < clusterCount; c++) + { + if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiB)) + { + clusterIndexB = c; + break; + } + } + + if (clusterIndexA == clusterIndexB) + { + overlappingCorners.emplace(cornerA, cornerB); + overlappingCorners.emplace(cornerB, cornerA); + } + } + } + } + + auto& smoothingGroupByTri = smElement->GetDirectArray(); + for (int i = 0; i < triangleCount; i++) + { + smoothingGroupByTri.Add(0); + } + //first one + smoothingGroupByTri.SetAt(0, 1); + + for (int i = 1; i < triangleCount; i++) + { + int sharedMask = 0, unsharedMask = 0; + for (int c = 0; c < 3; c++) + { + int myCorner = i * 3 + c; + FbxVector4 myNormal = normalByCornerList.GetAt(myCorner); + myNormal.Normalize(); + myNormal[3] = 0; + + auto otherCornersRangeBegin = overlappingCorners.lower_bound(myCorner); + auto otherCornersRangeEnd = overlappingCorners.upper_bound(myCorner); + for (auto it = otherCornersRangeBegin; it != otherCornersRangeEnd; it++) + { + int otherCorner = it->second; + FbxVector4 otherNormal = normalByCornerList.GetAt(otherCorner); + otherNormal.Normalize(); + otherNormal[3] = 0; + if (otherNormal == myNormal) + { + sharedMask |= smoothingGroupByTri[otherCorner / 3]; + } + else + { + unsharedMask |= smoothingGroupByTri[otherCorner / 3]; + } + } + } + + //Easy case, no overlap + if ((sharedMask & unsharedMask) == 0 && sharedMask != 0) + { + smoothingGroupByTri.SetAt(i, sharedMask); + } + else + { + for (int sm = 0; sm < 32; sm++) + { + int val = 1 << sm; + if (((val & sharedMask) == sharedMask) && !(val & unsharedMask)) + { + smoothingGroupByTri.SetAt(i, val); + break; + } + } + } + } + +} + +namespace +{ + //These methods have different names for some reason + inline double* getControlPointBlendWeights(FbxSkin* skin) + { + return skin->GetControlPointBlendWeights(); + } + + inline double* getControlPointBlendWeights(FbxCluster* cluster) + { + return cluster->GetControlPointWeights(); + } + + template <typename T> + void remapCPsAndRemoveDuplicates(const int newCPCount, const std::vector<int>& oldToNewCPMapping, T* skinOrCluster) + { + //Need to avoid duplicate entires since UE doesn't seem to normalize this correctly + std::vector<bool> addedCP(newCPCount, false); + std::vector<std::pair<int, double>> newCPsAndWeights; + newCPsAndWeights.reserve(newCPCount); + + int* skinCPList = skinOrCluster->GetControlPointIndices(); + double* skinCPWeights = getControlPointBlendWeights(skinOrCluster); + const int skinCPListLength = skinOrCluster->GetControlPointIndicesCount(); + + for (int bw = 0; bw < skinCPListLength; bw++) + { + int newCPIdx = oldToNewCPMapping[skinCPList[bw]]; + if (!addedCP[newCPIdx]) + { + addedCP[newCPIdx] = true; + newCPsAndWeights.emplace_back(newCPIdx, skinCPWeights[bw]); + } + } + skinOrCluster->SetControlPointIWCount(newCPsAndWeights.size()); + skinCPList = skinOrCluster->GetControlPointIndices(); + skinCPWeights = getControlPointBlendWeights(skinOrCluster); + for (size_t bw = 0; bw < newCPsAndWeights.size(); bw++) + { + skinCPList[bw] = newCPsAndWeights[bw].first; + skinCPWeights[bw] = newCPsAndWeights[bw].second; + } + } +} + +//Do this otherwise Maya shows the mesh as faceted due to not being welded +void FbxFileWriter::removeDuplicateControlPoints(fbxsdk::FbxMesh* mesh, FbxSkin* skin) +{ + FbxVector4* cpList = mesh->GetControlPoints(); + const int cpCount = mesh->GetControlPointsCount(); + + std::vector<int> oldToNewCPMapping(cpCount, -1); + //sort them by z for faster overlap checking + std::vector<std::pair<double, int>> cpIndexesByZ(cpCount); + for (int cp = 0; cp < cpCount; cp++) + { + cpIndexesByZ[cp] = std::pair<double, int>(cpList[cp][2], cp); + } + std::sort(cpIndexesByZ.begin(), cpIndexesByZ.end()); + + int clusterCount = 0; + std::vector<std::vector<int>> cpsPerCluster; + if (skin) + { + clusterCount = skin->GetClusterCount(); + cpsPerCluster.resize(clusterCount); + for (int c = 0; c < clusterCount; c++) + { + FbxCluster* cluster = skin->GetCluster(c); + int* clusterCPList = cluster->GetControlPointIndices(); + const int clusterCPListLength = cluster->GetControlPointIndicesCount(); + + cpsPerCluster[c].resize(clusterCPListLength); + memcpy(cpsPerCluster[c].data(), clusterCPList, sizeof(int) * clusterCPListLength); + std::sort(cpsPerCluster[c].begin(), cpsPerCluster[c].end()); + } + } + + std::vector<FbxVector4> uniqueCPs; + uniqueCPs.reserve(cpCount); + + for (int i = 0; i < cpCount; i++) + { + const int cpiA = cpIndexesByZ[i].second; + FbxVector4 cpA = cpList[cpiA]; + if (!(oldToNewCPMapping[cpiA] < 0)) + { + //already culled this one + continue; + } + const int newIdx = int(uniqueCPs.size()); + oldToNewCPMapping[cpiA] = newIdx; + uniqueCPs.push_back(cpA); + + int clusterIndexA = -1; + for (int c = 0; c < clusterCount; c++) + { + if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiA)) + { + clusterIndexA = c; + break; + } + } + + for (int j = i + 1; j < cpCount; j++) + { + if (std::abs(cpIndexesByZ[j].first - cpIndexesByZ[i].first) > FBXSDK_TOLERANCE) + { + break; // if the z's don't match other values don't matter + } + + const int cpiB = cpIndexesByZ[j].second; + FbxVector4 cpB = cpList[cpiB]; + + //uses FBXSDK_TOLERANCE + if (cpA == cpB) + { + int clusterIndexB = -1; + for (int c = 0; c < clusterCount; c++) + { + if (std::binary_search(cpsPerCluster[c].begin(), cpsPerCluster[c].end(), cpiB)) + { + clusterIndexB = c; + break; + } + } + + //don't merge unless they share the same clusters + if (clusterIndexA == clusterIndexB) + { + oldToNewCPMapping[cpiB] = newIdx; + } + } + } + } + + const int originalCPCount = cpCount; + const int newCPCount = int(uniqueCPs.size()); + + if (newCPCount == cpCount) + { + //don't bother, it will just scramble it for no reason + return; + } + + mesh->InitControlPoints(newCPCount); + cpList = mesh->GetControlPoints(); + + for (int cp = 0; cp < newCPCount; cp++) + { + cpList[cp] = uniqueCPs[cp]; + } + + int* polygonCPList = mesh->GetPolygonVertices(); + const int polygonCPListLength = mesh->GetPolygonVertexCount(); + for (int pv = 0; pv < polygonCPListLength; pv++) + { + polygonCPList[pv] = oldToNewCPMapping[polygonCPList[pv]]; + } + + if (skin) + { + remapCPsAndRemoveDuplicates(newCPCount, oldToNewCPMapping, skin); + for (int c = 0; c < skin->GetClusterCount(); c++) + { + FbxCluster* cluster = skin->GetCluster(c); + remapCPsAndRemoveDuplicates(newCPCount, oldToNewCPMapping, cluster); + } + + } +}
\ No newline at end of file diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h index 985b32c..f8490cb 100644 --- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h +++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h @@ -126,6 +126,9 @@ private: void addControlPoints(fbxsdk::FbxMesh* mesh, const ExporterMeshData& meshData); void addBindPose(); + + void generateSmoothingGroups(fbxsdk::FbxMesh* mesh, FbxSkin* skin); + void removeDuplicateControlPoints(fbxsdk::FbxMesh* mesh, FbxSkin* skin); }; } |