From 9a65d78cdfac853ac767c634ed7ed48e82db018f Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 28 Oct 2019 15:33:31 -0700 Subject: [PATCH 1/3] Fix model baking to work with new hfm::Shape material definition (1 material per mesh part) --- libraries/baking/src/OBJBaker.cpp | 34 +++++----- libraries/baking/src/OBJBaker.h | 2 +- .../model-baker/src/model-baker/Baker.cpp | 6 +- .../src/model-baker/BuildDracoMeshTask.cpp | 67 +++++++++++++------ .../src/model-baker/BuildDracoMeshTask.h | 2 +- 5 files changed, 70 insertions(+), 41 deletions(-) diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp index 4adaa01845..d726dee897 100644 --- a/libraries/baking/src/OBJBaker.cpp +++ b/libraries/baking/src/OBJBaker.cpp @@ -37,10 +37,10 @@ const QByteArray MESH = "Mesh"; void OBJBaker::bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) { // Write OBJ Data as FBX tree nodes - createFBXNodeTree(_rootNode, hfmModel, dracoMeshes[0]); + createFBXNodeTree(_rootNode, hfmModel, dracoMeshes[0], dracoMaterialLists[0]); } -void OBJBaker::createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh) { +void OBJBaker::createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh, const std::vector& dracoMaterialList) { // Make all generated nodes children of rootNode rootNode.children = { FBXNode(), FBXNode(), FBXNode() }; FBXNode& globalSettingsNode = rootNode.children[0]; @@ -100,24 +100,22 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& h } // Generating Objects node's child - Material node - auto& meshParts = hfmModel->meshes[0].parts; - for (auto& meshPart : meshParts) { + + // Each material ID should only appear once thanks to deduplication in BuildDracoMeshTask, but we want to make sure they are created in the right order + std::unordered_map materialIDToIndex; + for (uint32_t materialIndex = 0; materialIndex < hfmModel->materials.size(); ++materialIndex) { + const auto& material = hfmModel->materials[materialIndex]; + materialIDToIndex[material.materialID] = materialIndex; + } + + // Create nodes for each material in the material list + for (const auto& dracoMaterial : dracoMaterialList) { + const QString materialID = QString(dracoMaterial); + const uint32_t materialIndex = materialIDToIndex[materialID]; + const auto& material = hfmModel->materials[materialIndex]; FBXNode materialNode; materialNode.name = MATERIAL_NODE_NAME; - if (hfmModel->materials.size() == 1) { - // case when no material information is provided, OBJSerializer considers it as a single default material - for (auto& material : hfmModel->materials) { - setMaterialNodeProperties(materialNode, material.name, material, hfmModel); - } - } else { - for (auto& material : hfmModel->materials) { - if (material.name == meshPart.materialID) { - setMaterialNodeProperties(materialNode, meshPart.materialID, material, hfmModel); - break; - } - } - } - + setMaterialNodeProperties(materialNode, material.materialID, material, hfmModel); objectNode.children.append(materialNode); } diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h index 044c51d0cc..778b4da341 100644 --- a/libraries/baking/src/OBJBaker.h +++ b/libraries/baking/src/OBJBaker.h @@ -27,7 +27,7 @@ protected: virtual void bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector& dracoMeshes, const std::vector>& dracoMaterialLists) override; private: - void createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh); + void createFBXNodeTree(FBXNode& rootNode, const hfm::Model::Pointer& hfmModel, const hifi::ByteArray& dracoMesh, const std::vector& dracoMaterialList); void setMaterialNodeProperties(FBXNode& materialNode, const QString& materialName, const hfm::Material& material, const hfm::Model::Pointer& hfmModel); NodeID nextNodeID() { return _nodeID++; } diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index 662b4670ee..d200df211d 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -30,7 +30,7 @@ namespace baker { class GetModelPartsTask { public: using Input = hfm::Model::Pointer; - using Output = VaryingSet8, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector, std::vector, std::vector, Extents>; + using Output = VaryingSet9, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector, std::vector, std::vector, Extents, std::vector>; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { @@ -47,6 +47,7 @@ namespace baker { output.edit5() = hfmModelIn->shapes; output.edit6() = hfmModelIn->skinDeformers; output.edit7() = hfmModelIn->meshExtents; + output.edit8() = hfmModelIn->materials; } }; @@ -170,6 +171,7 @@ namespace baker { const auto shapesIn = modelPartsIn.getN(5); const auto skinDeformersIn = modelPartsIn.getN(6); const auto modelExtentsIn = modelPartsIn.getN(7); + const auto materialsIn = modelPartsIn.getN(8); // Calculate normals and tangents for meshes and blendshapes if they do not exist // Note: Normals are never calculated here for OBJ models. OBJ files optionally define normals on a per-face basis, so for consistency normals are calculated beforehand in OBJSerializer. @@ -214,7 +216,7 @@ namespace baker { // TODO: Tangent support (Needs changes to FBXSerializer_Mesh as well) // NOTE: Due to an unresolved linker error, BuildDracoMeshTask is not functional on Android // TODO: Figure out why BuildDracoMeshTask.cpp won't link with draco on Android - const auto buildDracoMeshInputs = BuildDracoMeshTask::Input(meshesIn, normalsPerMesh, tangentsPerMesh).asVarying(); + const auto buildDracoMeshInputs = BuildDracoMeshTask::Input(shapesOut, meshesIn, materialsIn, normalsPerMesh, tangentsPerMesh).asVarying(); const auto buildDracoMeshOutputs = model.addJob("BuildDracoMesh", buildDracoMeshInputs); const auto dracoMeshes = buildDracoMeshOutputs.getN(0); const auto dracoErrors = buildDracoMeshOutputs.getN(1); diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp index 12347c30b1..5c9d1dac25 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp @@ -39,19 +39,47 @@ #include "ModelMath.h" #ifndef Q_OS_ANDROID -std::vector createMaterialList(const hfm::Mesh& mesh) { - std::vector materialList; - for (const auto& meshPart : mesh.parts) { - auto materialID = QVariant(meshPart.materialID).toByteArray(); - const auto materialIt = std::find(materialList.cbegin(), materialList.cend(), materialID); - if (materialIt == materialList.cend()) { - materialList.push_back(materialID); + +void reindexMaterials(const std::vector& originalMaterialIndices, std::vector& materials, std::vector& materialIndices) { + materialIndices.resize(originalMaterialIndices.size()); + for (size_t i = 0; i < originalMaterialIndices.size(); ++i) { + uint32_t material = originalMaterialIndices[i]; + auto foundMaterial = std::find(materials.cbegin(), materials.cend(), material); + if (foundMaterial == materials.cend()) { + materials.push_back(material); + materialIndices[i] = (uint16_t)(materials.size() - 1); + } else { + materialIndices[i] = (uint16_t)(foundMaterial - materials.cbegin()); } } - return materialList; } -std::tuple, bool> createDracoMesh(const hfm::Mesh& mesh, const std::vector& normals, const std::vector& tangents, const std::vector& materialList) { +void createMaterialLists(const std::vector& shapes, const std::vector& meshes, const std::vector& hfmMaterials, std::vector>& materialIndexLists, std::vector>& partMaterialIndicesPerMesh) { + std::vector> materialsPerMesh; + for (const auto& mesh : meshes) { + materialsPerMesh.emplace_back(mesh.parts.size(), hfm::UNDEFINED_KEY); + } + for (const auto& shape : shapes) { + materialsPerMesh[shape.mesh][shape.meshPart] = shape.material; + } + + materialIndexLists.resize(materialsPerMesh.size()); + partMaterialIndicesPerMesh.resize(materialsPerMesh.size()); + for (size_t i = 0; i < materialsPerMesh.size(); ++i) { + const std::vector& materials = materialsPerMesh[i]; + std::vector uniqueMaterials; + + reindexMaterials(materials, uniqueMaterials, partMaterialIndicesPerMesh[i]); + + materialIndexLists[i].reserve(uniqueMaterials.size()); + for (const uint32_t material : uniqueMaterials) { + const auto& hfmMaterial = hfmMaterials[material]; + materialIndexLists[i].push_back(QVariant(hfmMaterial.materialID).toByteArray()); + } + } +} + +std::tuple, bool> createDracoMesh(const hfm::Mesh& mesh, const std::vector& normals, const std::vector& tangents, const std::vector& partMaterialIndices) { Q_ASSERT(normals.size() == 0 || (int)normals.size() == mesh.vertices.size()); Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); @@ -122,11 +150,9 @@ std::tuple, bool> createDracoMesh(const hfm::Mesh& auto partIndex = 0; draco::FaceIndex face; - uint16_t materialID; for (auto& part : mesh.parts) { - auto materialIt = std::find(materialList.cbegin(), materialList.cend(), QVariant(part.materialID).toByteArray()); - materialID = (uint16_t)(materialIt - materialList.cbegin()); + uint16_t materialID = partMaterialIndices[partIndex]; auto addFace = [&](const QVector& indices, int index, draco::FaceIndex face) { int32_t idx0 = indices[index]; @@ -214,30 +240,33 @@ void BuildDracoMeshTask::run(const baker::BakeContextPointer& context, const Inp #ifdef Q_OS_ANDROID qCWarning(model_baker) << "BuildDracoMesh is disabled on Android. Output meshes will be empty."; #else - const auto& meshes = input.get0(); - const auto& normalsPerMesh = input.get1(); - const auto& tangentsPerMesh = input.get2(); + const auto& shapes = input.get0(); + const auto& meshes = input.get1(); + const auto& materials = input.get2(); + const auto& normalsPerMesh = input.get3(); + const auto& tangentsPerMesh = input.get4(); auto& dracoBytesPerMesh = output.edit0(); auto& dracoErrorsPerMesh = output.edit1(); + auto& materialLists = output.edit2(); + std::vector> partMaterialIndicesPerMesh; + createMaterialLists(shapes, meshes, materials, materialLists, partMaterialIndicesPerMesh); dracoBytesPerMesh.reserve(meshes.size()); // vector is an exception to the std::vector conventions as it is a bit field // So a bool reference to an element doesn't work dracoErrorsPerMesh.resize(meshes.size()); - materialLists.reserve(meshes.size()); for (size_t i = 0; i < meshes.size(); i++) { const auto& mesh = meshes[i]; const auto& normals = baker::safeGet(normalsPerMesh, i); const auto& tangents = baker::safeGet(tangentsPerMesh, i); dracoBytesPerMesh.emplace_back(); auto& dracoBytes = dracoBytesPerMesh.back(); - materialLists.push_back(createMaterialList(mesh)); - const auto& materialList = materialLists.back(); + const auto& partMaterialIndices = partMaterialIndicesPerMesh[i]; bool dracoError; std::unique_ptr dracoMesh; - std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, materialList); + std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, partMaterialIndices); dracoErrorsPerMesh[i] = dracoError; if (dracoMesh) { diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h index ac9ad648ab..a83f2ae163 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.h @@ -33,7 +33,7 @@ public: class BuildDracoMeshTask { public: using Config = BuildDracoMeshConfig; - using Input = baker::VaryingSet3, baker::NormalsPerMesh, baker::TangentsPerMesh>; + using Input = baker::VaryingSet5, std::vector, std::vector, baker::NormalsPerMesh, baker::TangentsPerMesh>; using Output = baker::VaryingSet3, std::vector, std::vector>>; using JobModel = baker::Job::ModelIO; From 21699526137993589f36fca5527fc0e5f8f42733 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 11 Nov 2019 14:20:13 -0800 Subject: [PATCH 2/3] Remove unused mesh.part.materialID --- libraries/fbx/src/FBXSerializer.cpp | 6 ------ libraries/fbx/src/OBJSerializer.cpp | 7 ------- libraries/hfm/src/hfm/HFM.h | 2 -- 3 files changed, 15 deletions(-) diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index f09182c0e6..de6117eff3 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -1485,12 +1485,6 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const shape.mesh = meshIndex; shape.meshPart = i; shape.joint = transformIndex; - - auto matName = mesh.parts[i].materialID; - auto materialIt = materialNameToID.find(matName.toStdString()); - if (materialIt != materialNameToID.end()) { - shape.material = materialIt->second; - } } // For FBX_DRACO_MESH_VERSION < 2, or unbaked models, get materials from the partMaterialTextures diff --git a/libraries/fbx/src/OBJSerializer.cpp b/libraries/fbx/src/OBJSerializer.cpp index 31f92555f1..99299dcdec 100644 --- a/libraries/fbx/src/OBJSerializer.cpp +++ b/libraries/fbx/src/OBJSerializer.cpp @@ -174,11 +174,6 @@ glm::vec2 OBJTokenizer::getVec2() { return v; } - -void setMeshPartDefaults(HFMMeshPart& meshPart, QString materialID) { - meshPart.materialID = materialID; -} - // OBJFace // NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just // pairing it with the vertices vector for consistency. @@ -501,8 +496,6 @@ bool OBJSerializer::parseOBJGroup(OBJTokenizer& tokenizer, const hifi::VariantHa bool anyVertexColor { false }; int vertexCount { 0 }; - setMeshPartDefaults(meshPart, QString("dontknow") + QString::number(mesh.parts.size())); - while (true) { int tokenType = tokenizer.nextToken(); if (tokenType == OBJTokenizer::COMMENT_TOKEN) { diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index f092c91e99..c7b6789094 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -160,8 +160,6 @@ public: QVector quadIndices; // original indices from the FBX mesh QVector quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles QVector triangleIndices; // original indices from the FBX mesh - - QString materialID; // DEPRECATED }; class Material { From f39121c53be633f6c14baccce23a92527def380e Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 12 Nov 2019 09:52:58 -0800 Subject: [PATCH 3/3] Fix build error/warning --- libraries/fbx/src/OBJSerializer.cpp | 1 - tools/vhacd-util/src/VHACDUtil.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/libraries/fbx/src/OBJSerializer.cpp b/libraries/fbx/src/OBJSerializer.cpp index 99299dcdec..f9ba8e8c84 100644 --- a/libraries/fbx/src/OBJSerializer.cpp +++ b/libraries/fbx/src/OBJSerializer.cpp @@ -488,7 +488,6 @@ bool OBJSerializer::parseOBJGroup(OBJTokenizer& tokenizer, const hifi::VariantHa FaceGroup faces; HFMMesh& mesh = hfmModel.meshes[0]; mesh.parts.push_back(HFMMeshPart()); - HFMMeshPart& meshPart = mesh.parts.back(); bool sawG = false; bool result = true; int originalFaceCountForDebugging = 0; diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index f0eb94a1cf..da20339123 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -149,7 +149,6 @@ void vhacd::VHACDUtil::fattenMesh(const HFMMesh& mesh, const glm::mat4& modelOff result.vertices << p3; // add the new point to the result mesh HFMMeshPart newMeshPart; - setMeshPartDefaults(newMeshPart, "unknown"); newMeshPart.triangleIndices << index0 << index1 << index2; newMeshPart.triangleIndices << index0 << index3 << index1; newMeshPart.triangleIndices << index1 << index3 << index2;