From 3e7a80ac4c779d367d75f56b78fdbbbcf61191cd Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 25 Jan 2019 18:07:17 -0800 Subject: [PATCH] Move FST joint name and rig processing to the model preparation step --- libraries/fbx/src/FBXSerializer.cpp | 59 ------------ .../model-baker/src/model-baker/Baker.cpp | 31 +++++-- libraries/model-baker/src/model-baker/Baker.h | 4 +- .../src/model-baker/PrepareJointsTask.cpp | 89 +++++++++++++++++++ .../src/model-baker/PrepareJointsTask.h | 30 +++++++ .../src/model-networking/ModelCache.cpp | 8 +- 6 files changed, 150 insertions(+), 71 deletions(-) create mode 100644 libraries/model-baker/src/model-baker/PrepareJointsTask.cpp create mode 100644 libraries/model-baker/src/model-baker/PrepareJointsTask.h diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 25e0c0fd3a..92d5e7e774 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -384,43 +384,6 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { return filepath.mid(filepath.lastIndexOf('/') + 1); } -QMap getJointNameMapping(const QVariantHash& mapping) { - static const QString JOINT_NAME_MAPPING_FIELD = "jointMap"; - QMap hfmToHifiJointNameMap; - if (!mapping.isEmpty() && mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].type() == QVariant::Hash) { - auto jointNames = mapping[JOINT_NAME_MAPPING_FIELD].toHash(); - for (auto itr = jointNames.begin(); itr != jointNames.end(); itr++) { - hfmToHifiJointNameMap.insert(itr.key(), itr.value().toString()); - qCDebug(modelformat) << "the mapped key " << itr.key() << " has a value of " << hfmToHifiJointNameMap[itr.key()]; - } - } - return hfmToHifiJointNameMap; -} - -QMap getJointRotationOffsets(const QVariantHash& mapping) { - QMap jointRotationOffsets; - static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; - if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { - auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - QString line = itr.value().toString(); - auto quatCoords = line.split(','); - if (quatCoords.size() == 4) { - float quatX = quatCoords[0].mid(1).toFloat(); - float quatY = quatCoords[1].toFloat(); - float quatZ = quatCoords[2].toFloat(); - float quatW = quatCoords[3].mid(0, quatCoords[3].size() - 1).toFloat(); - if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) { - glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ); - jointRotationOffsets.insert(jointName, rotationOffset); - } - } - } - } - return jointRotationOffsets; -} - HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QString& url) { const FBXNode& node = _rootNode; QMap meshes; @@ -471,7 +434,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr HFMModel& hfmModel = *hfmModelPtr; hfmModel.originalURL = url; - auto hfmToHifiJointNameMapping = getJointNameMapping(mapping); float unitScaleFactor = 1.0f; glm::vec3 ambientColor; @@ -1284,13 +1246,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } // convert the models to joints - QVariantList freeJoints = mapping.values("freeJoint"); hfmModel.hasSkeletonJoints = false; foreach (const QString& modelID, modelIDs) { const FBXModel& fbxModel = fbxModels[modelID]; HFMJoint joint; - joint.isFree = freeJoints.contains(fbxModel.name); joint.parentIndex = fbxModel.parentIndex; // get the indices of all ancestors starting with the first free one (if any) @@ -1338,14 +1298,10 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } joint.inverseBindRotation = joint.inverseDefaultRotation; joint.name = fbxModel.name; - if (hfmToHifiJointNameMapping.contains(hfmToHifiJointNameMapping.key(joint.name))) { - joint.name = hfmToHifiJointNameMapping.key(fbxModel.name); - } joint.bindTransformFoundInCluster = false; hfmModel.joints.append(joint); - hfmModel.jointIndices.insert(joint.name, hfmModel.joints.size()); QString rotationID = localRotations.value(modelID); AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID)); @@ -1718,21 +1674,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } } - auto offsets = getJointRotationOffsets(mapping); - hfmModel.jointRotationOffsets.clear(); - for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { - QString jointName = itr.key(); - glm::quat rotationOffset = itr.value(); - int jointIndex = hfmModel.getJointIndex(jointName); - if (hfmToHifiJointNameMapping.contains(jointName)) { - jointIndex = hfmModel.getJointIndex(jointName); - } - if (jointIndex != -1) { - hfmModel.jointRotationOffsets.insert(jointIndex, rotationOffset); - } - qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; - } - return hfmModelPtr; } diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index 8d1a82518d..1c2a2f5c63 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -19,13 +19,14 @@ #include "CalculateMeshTangentsTask.h" #include "CalculateBlendshapeNormalsTask.h" #include "CalculateBlendshapeTangentsTask.h" +#include "PrepareJointsTask.h" namespace baker { class GetModelPartsTask { public: using Input = hfm::Model::Pointer; - using Output = VaryingSet5, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, QHash>; + using Output = VaryingSet6, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, QHash, std::vector>; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { @@ -39,6 +40,7 @@ namespace baker { blendshapesPerMesh.push_back(hfmModelIn->meshes[i].blendshapes.toStdVector()); } output.edit4() = hfmModelIn->materials; + output.edit5() = hfmModelIn->joints.toStdVector(); } }; @@ -99,23 +101,29 @@ namespace baker { class BuildModelTask { public: - using Input = VaryingSet2>; + using Input = VaryingSet5, std::vector, QMap /*jointRotationOffsets*/, QHash /*jointIndices*/>; using Output = hfm::Model::Pointer; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { auto hfmModelOut = input.get0(); hfmModelOut->meshes = QVector::fromStdVector(input.get1()); + hfmModelOut->joints = QVector::fromStdVector(input.get2()); + hfmModelOut->jointRotationOffsets = input.get3(); + hfmModelOut->jointIndices = input.get4(); output = hfmModelOut; } }; class BakerEngineBuilder { public: - using Input = hfm::Model::Pointer; + using Input = VaryingSet2; using Output = hfm::Model::Pointer; using JobModel = Task::ModelIO; - void build(JobModel& model, const Varying& hfmModelIn, Varying& hfmModelOut) { + void build(JobModel& model, const Varying& input, Varying& hfmModelOut) { + const auto& hfmModelIn = input.getN(0); + const auto& mapping = input.getN(1); + // Split up the inputs from hfm::Model const auto modelPartsIn = model.addJob("GetModelParts", hfmModelIn); const auto meshesIn = modelPartsIn.getN(0); @@ -123,6 +131,7 @@ namespace baker { const auto meshIndicesToModelNames = modelPartsIn.getN(2); const auto blendshapesPerMeshIn = modelPartsIn.getN(3); const auto materials = modelPartsIn.getN(4); + const auto jointsIn = modelPartsIn.getN(5); // 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. @@ -138,19 +147,27 @@ namespace baker { const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh).asVarying(); const auto graphicsMeshes = model.addJob("BuildGraphicsMesh", buildGraphicsMeshInputs); + // Prepare joint information + const auto prepareJointsInputs = PrepareJointsTask::Input(jointsIn, mapping).asVarying(); + const auto jointInfoOut = model.addJob("PrepareJoints", prepareJointsInputs); + const auto jointsOut = jointInfoOut.getN(0); + const auto jointRotationOffsets = jointInfoOut.getN(1); + const auto jointIndices = jointInfoOut.getN(2); + // Combine the outputs into a new hfm::Model const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying(); const auto blendshapesPerMeshOut = model.addJob("BuildBlendshapes", buildBlendshapesInputs); const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying(); const auto meshesOut = model.addJob("BuildMeshes", buildMeshesInputs); - const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut).asVarying(); + const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices).asVarying(); hfmModelOut = model.addJob("BuildModel", buildModelInputs); } }; - Baker::Baker(const hfm::Model::Pointer& hfmModel) : + Baker::Baker(const hfm::Model::Pointer& hfmModel, const QVariantHash& mapping) : _engine(std::make_shared(BakerEngineBuilder::JobModel::create("Baker"), std::make_shared())) { - _engine->feedInput(hfmModel); + _engine->feedInput(0, hfmModel); + _engine->feedInput(1, mapping); } void Baker::run() { diff --git a/libraries/model-baker/src/model-baker/Baker.h b/libraries/model-baker/src/model-baker/Baker.h index 7fb3f420e0..41989d73df 100644 --- a/libraries/model-baker/src/model-baker/Baker.h +++ b/libraries/model-baker/src/model-baker/Baker.h @@ -12,6 +12,8 @@ #ifndef hifi_baker_Baker_h #define hifi_baker_Baker_h +#include + #include #include "Engine.h" @@ -19,7 +21,7 @@ namespace baker { class Baker { public: - Baker(const hfm::Model::Pointer& hfmModel); + Baker(const hfm::Model::Pointer& hfmModel, const QVariantHash& mapping); void run(); diff --git a/libraries/model-baker/src/model-baker/PrepareJointsTask.cpp b/libraries/model-baker/src/model-baker/PrepareJointsTask.cpp new file mode 100644 index 0000000000..82ad77d651 --- /dev/null +++ b/libraries/model-baker/src/model-baker/PrepareJointsTask.cpp @@ -0,0 +1,89 @@ +// +// PrepareJointsTask.cpp +// model-baker/src/model-baker +// +// Created by Sabrina Shanman on 2019/01/25. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "PrepareJointsTask.h" + +#include "ModelBakerLogging.h" + +QMap getJointNameMapping(const QVariantHash& mapping) { + static const QString JOINT_NAME_MAPPING_FIELD = "jointMap"; + QMap hfmToHifiJointNameMap; + if (!mapping.isEmpty() && mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].type() == QVariant::Hash) { + auto jointNames = mapping[JOINT_NAME_MAPPING_FIELD].toHash(); + for (auto itr = jointNames.begin(); itr != jointNames.end(); itr++) { + hfmToHifiJointNameMap.insert(itr.key(), itr.value().toString()); + qCDebug(model_baker) << "the mapped key " << itr.key() << " has a value of " << hfmToHifiJointNameMap[itr.key()]; + } + } + return hfmToHifiJointNameMap; +} + +QMap getJointRotationOffsets(const QVariantHash& mapping) { + QMap jointRotationOffsets; + static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset"; + if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) { + auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash(); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + QString line = itr.value().toString(); + auto quatCoords = line.split(','); + if (quatCoords.size() == 4) { + float quatX = quatCoords[0].mid(1).toFloat(); + float quatY = quatCoords[1].toFloat(); + float quatZ = quatCoords[2].toFloat(); + float quatW = quatCoords[3].mid(0, quatCoords[3].size() - 1).toFloat(); + if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) { + glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ); + jointRotationOffsets.insert(jointName, rotationOffset); + } + } + } + } + return jointRotationOffsets; +} + +void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) { + const auto& jointsIn = input.get0(); + const auto& mapping = input.get1(); + auto& jointsOut = output.edit0(); + auto& jointRotationOffsets = output.edit1(); + auto& jointIndices = output.edit2(); + + // Get which joints are free from FST file mappings + QVariantList freeJoints = mapping.values("freeJoint"); + // Get joint renames + auto jointNameMapping = getJointNameMapping(mapping); + // Apply joint metadata from FST file mappings + for (const auto& jointIn : jointsIn) { + jointsOut.push_back(jointIn); + auto& jointOut = jointsOut[jointsOut.size()-1]; + + jointOut.isFree = freeJoints.contains(jointIn.name); + + if (jointNameMapping.contains(jointNameMapping.key(jointIn.name))) { + jointOut.name = jointNameMapping.key(jointIn.name); + } + + jointIndices.insert(jointOut.name, (int)jointsOut.size()); + } + + // Get joint rotation offsets from FST file mappings + auto offsets = getJointRotationOffsets(mapping); + for (auto itr = offsets.begin(); itr != offsets.end(); itr++) { + QString jointName = itr.key(); + glm::quat rotationOffset = itr.value(); + int jointIndex = jointIndices.value(jointName) - 1; + if (jointIndex != -1) { + jointRotationOffsets.insert(jointIndex, rotationOffset); + } + qCDebug(model_baker) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; + } +} diff --git a/libraries/model-baker/src/model-baker/PrepareJointsTask.h b/libraries/model-baker/src/model-baker/PrepareJointsTask.h new file mode 100644 index 0000000000..e12d8ffd2c --- /dev/null +++ b/libraries/model-baker/src/model-baker/PrepareJointsTask.h @@ -0,0 +1,30 @@ +// +// PrepareJointsTask.h +// model-baker/src/model-baker +// +// Created by Sabrina Shanman on 2019/01/25. +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PrepareJointsTask_h +#define hifi_PrepareJointsTask_h + +#include + +#include + +#include "Engine.h" + +class PrepareJointsTask { +public: + using Input = baker::VaryingSet2, QVariantHash /*mapping*/>; + using Output = baker::VaryingSet3, QMap /*jointRotationOffsets*/, QHash /*jointIndices*/>; + using JobModel = baker::Job::ModelIO; + + void run(const baker::BakeContextPointer& context, const Input& input, Output& output); +}; + +#endif // hifi_PrepareJointsTask_h \ No newline at end of file diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 05c4aa0e03..f18d4ab28c 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -233,7 +233,7 @@ void GeometryReader::run() { } QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", - Q_ARG(HFMModel::Pointer, hfmModel)); + Q_ARG(HFMModel::Pointer, hfmModel), Q_ARG(QVariantHash, _mapping)); } catch (const std::exception&) { auto resource = _resource.toStrongRef(); if (resource) { @@ -261,7 +261,7 @@ public: virtual void downloadFinished(const QByteArray& data) override; protected: - Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel); + Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping); private: ModelLoader _modelLoader; @@ -277,9 +277,9 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mapping, data, _combineParts, _request->getWebMediaType())); } -void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel) { +void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping) { // Do processing on the model - baker::Baker modelBaker(hfmModel); + baker::Baker modelBaker(hfmModel, mapping); modelBaker.run(); // Assume ownership of the processed HFMModel