mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Fix model baking to work with new hfm::Shape material definition (1
material per mesh part)
This commit is contained in:
parent
eebb9ad51f
commit
9a65d78cdf
5 changed files with 70 additions and 41 deletions
|
@ -37,10 +37,10 @@ const QByteArray MESH = "Mesh";
|
|||
|
||||
void OBJBaker::bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector<hifi::ByteArray>& dracoMeshes, const std::vector<std::vector<hifi::ByteArray>>& 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<hifi::ByteArray>& 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<QString, uint32_t> 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ protected:
|
|||
virtual void bakeProcessedSource(const hfm::Model::Pointer& hfmModel, const std::vector<hifi::ByteArray>& dracoMeshes, const std::vector<std::vector<hifi::ByteArray>>& 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<hifi::ByteArray>& dracoMaterialList);
|
||||
void setMaterialNodeProperties(FBXNode& materialNode, const QString& materialName, const hfm::Material& material, const hfm::Model::Pointer& hfmModel);
|
||||
NodeID nextNodeID() { return _nodeID++; }
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace baker {
|
|||
class GetModelPartsTask {
|
||||
public:
|
||||
using Input = hfm::Model::Pointer;
|
||||
using Output = VaryingSet8<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector<hfm::Joint>, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>, Extents>;
|
||||
using Output = VaryingSet9<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector<hfm::Joint>, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>, Extents, std::vector<hfm::Material>>;
|
||||
using JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
|
||||
|
||||
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<GetModelPartsTask::Output>(5);
|
||||
const auto skinDeformersIn = modelPartsIn.getN<GetModelPartsTask::Output>(6);
|
||||
const auto modelExtentsIn = modelPartsIn.getN<GetModelPartsTask::Output>(7);
|
||||
const auto materialsIn = modelPartsIn.getN<GetModelPartsTask::Output>(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<BuildDracoMeshTask>("BuildDracoMesh", buildDracoMeshInputs);
|
||||
const auto dracoMeshes = buildDracoMeshOutputs.getN<BuildDracoMeshTask::Output>(0);
|
||||
const auto dracoErrors = buildDracoMeshOutputs.getN<BuildDracoMeshTask::Output>(1);
|
||||
|
|
|
@ -39,19 +39,47 @@
|
|||
#include "ModelMath.h"
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
std::vector<hifi::ByteArray> createMaterialList(const hfm::Mesh& mesh) {
|
||||
std::vector<hifi::ByteArray> 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<uint32_t>& originalMaterialIndices, std::vector<uint32_t>& materials, std::vector<uint16_t>& 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<std::unique_ptr<draco::Mesh>, bool> createDracoMesh(const hfm::Mesh& mesh, const std::vector<glm::vec3>& normals, const std::vector<glm::vec3>& tangents, const std::vector<hifi::ByteArray>& materialList) {
|
||||
void createMaterialLists(const std::vector<hfm::Shape>& shapes, const std::vector<hfm::Mesh>& meshes, const std::vector<hfm::Material>& hfmMaterials, std::vector<std::vector<hifi::ByteArray>>& materialIndexLists, std::vector<std::vector<uint16_t>>& partMaterialIndicesPerMesh) {
|
||||
std::vector<std::vector<uint32_t>> 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<uint32_t>& materials = materialsPerMesh[i];
|
||||
std::vector<uint32_t> 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<std::unique_ptr<draco::Mesh>, bool> createDracoMesh(const hfm::Mesh& mesh, const std::vector<glm::vec3>& normals, const std::vector<glm::vec3>& tangents, const std::vector<uint16_t>& 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<std::unique_ptr<draco::Mesh>, 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<int>& 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<std::vector<uint16_t>> partMaterialIndicesPerMesh;
|
||||
createMaterialLists(shapes, meshes, materials, materialLists, partMaterialIndicesPerMesh);
|
||||
|
||||
dracoBytesPerMesh.reserve(meshes.size());
|
||||
// vector<bool> 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<draco::Mesh> dracoMesh;
|
||||
std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, materialList);
|
||||
std::tie(dracoMesh, dracoError) = createDracoMesh(mesh, normals, tangents, partMaterialIndices);
|
||||
dracoErrorsPerMesh[i] = dracoError;
|
||||
|
||||
if (dracoMesh) {
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
class BuildDracoMeshTask {
|
||||
public:
|
||||
using Config = BuildDracoMeshConfig;
|
||||
using Input = baker::VaryingSet3<std::vector<hfm::Mesh>, baker::NormalsPerMesh, baker::TangentsPerMesh>;
|
||||
using Input = baker::VaryingSet5<std::vector<hfm::Shape>, std::vector<hfm::Mesh>, std::vector<hfm::Material>, baker::NormalsPerMesh, baker::TangentsPerMesh>;
|
||||
using Output = baker::VaryingSet3<std::vector<hifi::ByteArray>, std::vector<bool>, std::vector<std::vector<hifi::ByteArray>>>;
|
||||
using JobModel = baker::Job::ModelIO<BuildDracoMeshTask, Input, Output, Config>;
|
||||
|
||||
|
|
Loading…
Reference in a new issue