From 9a65d78cdfac853ac767c634ed7ed48e82db018f Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 28 Oct 2019 15:33:31 -0700 Subject: [PATCH] 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;