diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index d507a3ae06..4b3311c95a 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -20,6 +20,7 @@ #include #include +#include // TOOL: Uncomment the following line to enable the filtering of all the unkwnon fields of a node so we can break point easily while loading a model with problems... //#define DEBUG_FBXSERIALIZER @@ -1593,7 +1594,6 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const if (clusterIDs.size() > 0) { hfm::SkinDeformer skinDeformer; auto& clusters = skinDeformer.clusters; - std::vector skinClusters; for (const auto& clusterID : clusterIDs) { HFMCluster hfmCluster; const Cluster& fbxCluster = fbxClusters[clusterID]; @@ -1638,7 +1638,8 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const cluster.jointIndex = transformIndex; clusters.push_back(cluster); - // Skinned mesh instances have a dynamic transform + std::vector skinClusters; + // Skinned mesh instances have an hfm::SkinDeformer skinDeformer.skinClusterIndices.reserve(clusterIDs.size()); for (const auto& clusterID : clusterIDs) { const Cluster& fbxCluster = fbxClusters[clusterID]; @@ -1661,12 +1662,16 @@ HFMModel* FBXSerializer::extractHFMModel(const hifi::VariantHash& mapping, const } } } - - // Store this model's deformers, this dynamic transform's deformer IDs - uint32_t deformerMinID = (uint32_t)hfmModel.skinClusters.size(); - hfmModel.skinClusters.insert(hfmModel.skinClusters.end(), skinClusters.cbegin(), skinClusters.cend()); - skinDeformer.skinClusterIndices.resize(skinClusters.size()); - std::iota(skinDeformer.skinClusterIndices.begin(), skinDeformer.skinClusterIndices.end(), deformerMinID); + // It seems odd that this mesh-related code should be inside of the for loop for instanced model IDs. + // However, in practice, skinned FBX models appear to not be instanced, as the skinning includes both the weights and joints. + { + hfm::ReweightedDeformers reweightedDeformers = hfm::getReweightedDeformers(mesh.vertices.size(), skinClusters); + if (reweightedDeformers.trimmedToMatch) { + qDebug(modelformat) << "FBXSerializer -- The number of indices and weights for a skinning deformer had different sizes and have been trimmed to match"; + } + mesh.clusterIndices = std::move(reweightedDeformers.indices); + mesh.clusterWeights = std::move(reweightedDeformers.weights); + } // Store the model's dynamic transform, and put its ID in the shapes hfmModel.skinDeformers.push_back(skinDeformer); diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index db272a534c..da48c3d2e3 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -1401,7 +1401,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& int numVertices = mesh.vertices.size() - prevMeshVerticesCount; // Append new cluster indices and weights for this mesh part - int prevMeshClusterWeightCount = mesh.clusterWeights.count(); + size_t prevMeshClusterWeightCount = mesh.clusterWeights.size(); for (int i = 0; i < numVertices * WEIGHTS_PER_VERTEX; ++i) { mesh.clusterIndices.push_back(mesh.clusters.size() - 1); mesh.clusterWeights.push_back(0); diff --git a/libraries/fbx/src/OBJSerializer.cpp b/libraries/fbx/src/OBJSerializer.cpp index e1fc85ca2a..d3bde02e70 100644 --- a/libraries/fbx/src/OBJSerializer.cpp +++ b/libraries/fbx/src/OBJSerializer.cpp @@ -1018,8 +1018,8 @@ void hfmDebugDump(const HFMModel& hfmModel) { qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); qCDebug(modelformat) << " texCoords.count() =" << mesh.texCoords.count(); qCDebug(modelformat) << " texCoords1.count() =" << mesh.texCoords1.count(); - qCDebug(modelformat) << " clusterIndices.count() =" << mesh.clusterIndices.count(); - qCDebug(modelformat) << " clusterWeights.count() =" << mesh.clusterWeights.count(); + qCDebug(modelformat) << " clusterIndices.size() =" << mesh.clusterIndices.size(); + qCDebug(modelformat) << " clusterWeights.size() =" << mesh.clusterWeights.size(); qCDebug(modelformat) << " meshExtents =" << mesh.meshExtents; qCDebug(modelformat) << " modelTransform =" << mesh.modelTransform; qCDebug(modelformat) << " parts.count() =" << mesh.parts.size(); diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index 15ed876d94..b2d8147ac6 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -248,8 +248,8 @@ public: glm::mat4 modelTransform; // DEPRECATED (see hfm::Joint::globalTransform, hfm::Shape::transform, hfm::Model::joints) // Skinning cluster attributes - QVector clusterIndices; - QVector clusterWeights; + std::vector clusterIndices; + std::vector clusterWeights; // Blendshape attributes QVector blendshapes; @@ -296,7 +296,7 @@ public: bool shouldInitCollisions() const { return _collisionsConfig.size() > 0; } }; -// Formerly contained in hfm::Mesh +// A different skinning representation, used by FBXSerializer. We convert this to our graphics-optimized runtime representation contained within the mesh. class SkinCluster { public: std::vector indices; @@ -305,7 +305,7 @@ public: class SkinDeformer { public: - std::vector skinClusterIndices; + std::vector skinClusterIndices; // DEPRECATED (see hfm::Mesh.clusterIndices, hfm::Mesh.clusterWeights) std::vector clusters; }; @@ -337,7 +337,6 @@ public: std::vector materials; std::vector skinDeformers; - std::vector skinClusters; std::vector joints; QHash jointIndices; ///< 1-based, so as to more easily detect missing indices diff --git a/libraries/hfm/src/hfm/HFMModelMath.cpp b/libraries/hfm/src/hfm/HFMModelMath.cpp index de308297c4..09083ab4cc 100644 --- a/libraries/hfm/src/hfm/HFMModelMath.cpp +++ b/libraries/hfm/src/hfm/HFMModelMath.cpp @@ -11,6 +11,9 @@ #include "HFMModelMath.h" +#include +#include "ModelFormatLogging.h" + namespace hfm { void forEachIndex(const hfm::MeshPart& meshPart, std::function func) { @@ -63,4 +66,76 @@ void calculateExtentsForModel(Extents& modelExtents, const std::vector skinClusters, const uint16_t weightsPerVertex) { + ReweightedDeformers reweightedDeformers; + if (skinClusters.size() == 0) { + return reweightedDeformers; + } + + size_t numClusterIndices = numMeshVertices * weightsPerVertex; + reweightedDeformers.indices.resize(numClusterIndices, (uint16_t)(skinClusters.size() - 1)); + reweightedDeformers.weights.resize(numClusterIndices, 0); + + std::vector weightAccumulators; + weightAccumulators.resize(numClusterIndices, 0.0f); + for (uint16_t i = 0; i < (uint16_t)skinClusters.size(); ++i) { + const hfm::SkinCluster& skinCluster = skinClusters[i]; + + if (skinCluster.indices.size() != skinCluster.weights.size()) { + reweightedDeformers.trimmedToMatch = true; + } + size_t numIndicesOrWeights = std::min(skinCluster.indices.size(), skinCluster.weights.size()); + for (size_t j = 0; j < numIndicesOrWeights; ++j) { + uint32_t index = skinCluster.indices[j]; + float weight = skinCluster.weights[j]; + + // look for an unused slot in the weights vector + uint32_t weightIndex = index * weightsPerVertex; + uint32_t lowestIndex = -1; + float lowestWeight = FLT_MAX; + uint16_t k = 0; + for (; k < weightsPerVertex; k++) { + if (weightAccumulators[weightIndex + k] == 0.0f) { + reweightedDeformers.indices[weightIndex + k] = i; + weightAccumulators[weightIndex + k] = weight; + break; + } + if (weightAccumulators[weightIndex + k] < lowestWeight) { + lowestIndex = k; + lowestWeight = weightAccumulators[weightIndex + k]; + } + } + if (k == weightsPerVertex && weight > lowestWeight) { + // no space for an additional weight; we must replace the lowest + weightAccumulators[weightIndex + lowestIndex] = weight; + reweightedDeformers.indices[weightIndex + lowestIndex] = i; + } + } + } + + // now that we've accumulated the most relevant weights for each vertex + // normalize and compress to 16-bits + for (size_t i = 0; i < numMeshVertices; ++i) { + size_t j = i * weightsPerVertex; + + // normalize weights into uint16_t + float totalWeight = 0.0f; + for (size_t k = j; k < j + weightsPerVertex; ++k) { + totalWeight += weightAccumulators[k]; + } + + const float ALMOST_HALF = 0.499f; + if (totalWeight > 0.0f) { + float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; + for (size_t k = j; k < j + weightsPerVertex; ++k) { + reweightedDeformers.weights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); + } + } else { + reweightedDeformers.weights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); + } + } + + return reweightedDeformers; +} + }; diff --git a/libraries/hfm/src/hfm/HFMModelMath.h b/libraries/hfm/src/hfm/HFMModelMath.h index d1e3c09763..9420c96f08 100644 --- a/libraries/hfm/src/hfm/HFMModelMath.h +++ b/libraries/hfm/src/hfm/HFMModelMath.h @@ -25,6 +25,16 @@ void calculateExtentsForShape(hfm::Shape& shape, const std::vector& m void calculateExtentsForModel(Extents& modelExtents, const std::vector& shapes); +const uint16_t NUM_SKINNING_WEIGHTS_PER_VERTEX = 4; + +class ReweightedDeformers { +public: + std::vector indices; + std::vector weights; + bool trimmedToMatch { false }; +}; + +ReweightedDeformers getReweightedDeformers(const size_t numMeshVertices, const std::vector skinClusters, const uint16_t weightsPerVertex = NUM_SKINNING_WEIGHTS_PER_VERTEX); }; #endif // #define hifi_hfm_ModelMath_h diff --git a/libraries/model-baker/src/model-baker/Baker.cpp b/libraries/model-baker/src/model-baker/Baker.cpp index 5522ebc9f5..c63495c169 100644 --- a/libraries/model-baker/src/model-baker/Baker.cpp +++ b/libraries/model-baker/src/model-baker/Baker.cpp @@ -13,7 +13,6 @@ #include "BakerTypes.h" #include "ModelMath.h" -#include "ReweightDeformersTask.h" #include "CollectShapeVerticesTask.h" #include "BuildGraphicsMeshTask.h" #include "CalculateMeshNormalsTask.h" @@ -30,7 +29,7 @@ namespace baker { class GetModelPartsTask { public: using Input = hfm::Model::Pointer; - using Output = VaryingSet9, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector, std::vector, std::vector, std::vector, Extents>; + using Output = VaryingSet8, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector, std::vector, std::vector, Extents>; using JobModel = Job::ModelIO; void run(const BakeContextPointer& context, const Input& input, Output& output) { @@ -46,8 +45,7 @@ namespace baker { output.edit4() = hfmModelIn->joints; output.edit5() = hfmModelIn->shapes; output.edit6() = hfmModelIn->skinDeformers; - output.edit7() = hfmModelIn->skinClusters; - output.edit8() = hfmModelIn->meshExtents; + output.edit7() = hfmModelIn->meshExtents; } }; @@ -148,8 +146,7 @@ namespace baker { const auto jointsIn = modelPartsIn.getN(4); const auto shapesIn = modelPartsIn.getN(5); const auto skinDeformersIn = modelPartsIn.getN(6); - const auto skinClustersIn = modelPartsIn.getN(7); - const auto modelExtentsIn = modelPartsIn.getN(8); + const auto modelExtentsIn = modelPartsIn.getN(7); // 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. @@ -161,16 +158,12 @@ namespace baker { const auto calculateBlendshapeTangentsInputs = CalculateBlendshapeTangentsTask::Input(normalsPerBlendshapePerMesh, blendshapesPerMeshIn, meshesIn).asVarying(); const auto tangentsPerBlendshapePerMesh = model.addJob("CalculateBlendshapeTangents", calculateBlendshapeTangentsInputs); - // Skinning weight calculations - // NOTE: Due to limitations in the current graphics::MeshPointer representation, the output list of ReweightedDeformers is per-mesh. An element is empty if there are no deformers for the mesh of the same index. - const auto reweightDeformersInputs = ReweightDeformersTask::Input(meshesIn, shapesIn, skinDeformersIn, skinClustersIn).asVarying(); - const auto reweightedDeformers = model.addJob("ReweightDeformers", reweightDeformersInputs); - // Shape vertices are included/rejected based on skinning weight, and thus must use the reweighted deformers. - const auto collectShapeVerticesInputs = CollectShapeVerticesTask::Input(meshesIn, shapesIn, jointsIn, skinDeformersIn, reweightedDeformers).asVarying(); + // Calculate shape vertices. These rely on the weight-normalized clusterIndices/clusterWeights in the mesh, and are used later for computing the joint kdops + const auto collectShapeVerticesInputs = CollectShapeVerticesTask::Input(meshesIn, shapesIn, jointsIn, skinDeformersIn).asVarying(); const auto shapeVerticesPerJoint = model.addJob("CollectShapeVertices", collectShapeVerticesInputs); // Build the graphics::MeshPointer for each hfm::Mesh - const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh, shapesIn, skinDeformersIn, reweightedDeformers).asVarying(); + const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh, shapesIn, skinDeformersIn).asVarying(); const auto graphicsMeshes = model.addJob("BuildGraphicsMesh", buildGraphicsMeshInputs); // Prepare joint information diff --git a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp index 88546e0975..6af0f9edf7 100644 --- a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.cpp @@ -15,6 +15,7 @@ #include #include "ModelBakerLogging.h" +#include #include "ModelMath.h" using vec2h = glm::tvec2; @@ -27,7 +28,7 @@ glm::vec3 normalizeDirForPacking(const glm::vec3& dir) { return dir; } -void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphicsMeshPointer, const baker::MeshNormals& meshNormals, const baker::MeshTangents& meshTangentsIn, uint16_t numDeformerControllers, const baker::ReweightedDeformers reweightedDeformers) { +void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphicsMeshPointer, const baker::MeshNormals& meshNormals, const baker::MeshTangents& meshTangentsIn, uint16_t numDeformerControllers) { auto graphicsMesh = std::make_shared(); // Fill tangents with a dummy value to force tangents to be present if there are normals @@ -90,12 +91,8 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics // 4 Weights are normalized 16bits const auto clusterWeightElement = gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW); - // Cluster indices and weights must be the same sizes - if (reweightedDeformers.trimmedToMatch) { - HIFI_FCDEBUG_ID(model_baker(), repeatMessageID, "BuildGraphicsMeshTask -- The number of indices and weights for a deformer had different sizes and have been trimmed to match"); - } // Record cluster sizes - const size_t numVertClusters = (reweightedDeformers.weightsPerVertex ? reweightedDeformers.indices.size() / reweightedDeformers.weightsPerVertex : 0); + const size_t numVertClusters = hfmMesh.clusterIndices.size() / hfm::NUM_SKINNING_WEIGHTS_PER_VERTEX; const size_t clusterIndicesSize = numVertClusters * clusterIndiceElement.getSize(); const size_t clusterWeightsSize = numVertClusters * clusterWeightElement.getSize(); @@ -186,20 +183,20 @@ void buildGraphicsMesh(const hfm::Mesh& hfmMesh, graphics::MeshPointer& graphics if (clusterIndicesSize > 0) { if (numDeformerControllers < (uint16_t)UINT8_MAX) { // yay! we can fit the clusterIndices within 8-bits - int32_t numIndices = (int32_t)reweightedDeformers.indices.size(); + int32_t numIndices = (int32_t)hfmMesh.clusterIndices.size(); std::vector packedDeformerIndices; packedDeformerIndices.resize(numIndices); for (int32_t i = 0; i < numIndices; ++i) { assert(hfmMesh.clusterIndices[i] <= UINT8_MAX); - packedDeformerIndices[i] = (uint8_t)(reweightedDeformers.indices[i]); + packedDeformerIndices[i] = (uint8_t)(hfmMesh.clusterIndices[i]); } vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) packedDeformerIndices.data()); } else { - vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) reweightedDeformers.indices.data()); + vertBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) hfmMesh.clusterIndices.data()); } } if (clusterWeightsSize > 0) { - vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) reweightedDeformers.weights.data()); + vertBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) hfmMesh.clusterWeights.data()); } @@ -382,7 +379,6 @@ void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const const auto& tangentsPerMesh = input.get4(); const auto& shapes = input.get5(); const auto& skinDeformers = input.get6(); - const auto& reweightedDeformersPerMesh = input.get7(); // Currently, there is only (at most) one skinDeformer per mesh // An undefined shape.skinDeformer has the value hfm::UNDEFINED_KEY @@ -399,19 +395,16 @@ void BuildGraphicsMeshTask::run(const baker::BakeContextPointer& context, const for (int i = 0; i < n; i++) { graphicsMeshes.emplace_back(); auto& graphicsMesh = graphicsMeshes[i]; - const auto& reweightedDeformers = reweightedDeformersPerMesh[i]; uint16_t numDeformerControllers = 0; - if (reweightedDeformers.weightsPerVertex != 0) { - uint32_t skinDeformerIndex = skinDeformerPerMesh[i]; - if (skinDeformerIndex != hfm::UNDEFINED_KEY) { - const hfm::SkinDeformer& skinDeformer = skinDeformers[skinDeformerIndex]; - numDeformerControllers = (uint16_t)skinDeformer.skinClusterIndices.size(); - } + uint32_t skinDeformerIndex = skinDeformerPerMesh[i]; + if (skinDeformerIndex != hfm::UNDEFINED_KEY) { + const hfm::SkinDeformer& skinDeformer = skinDeformers[skinDeformerIndex]; + numDeformerControllers = (uint16_t)skinDeformer.clusters.size(); } // Try to create the graphics::Mesh - buildGraphicsMesh(meshes[i], graphicsMesh, baker::safeGet(normalsPerMesh, i), baker::safeGet(tangentsPerMesh, i), numDeformerControllers, reweightedDeformers); + buildGraphicsMesh(meshes[i], graphicsMesh, baker::safeGet(normalsPerMesh, i), baker::safeGet(tangentsPerMesh, i), numDeformerControllers); // Choose a name for the mesh if (graphicsMesh) { diff --git a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h index b60f6f7a43..34128eabe8 100644 --- a/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h +++ b/libraries/model-baker/src/model-baker/BuildGraphicsMeshTask.h @@ -20,7 +20,7 @@ class BuildGraphicsMeshTask { public: - using Input = baker::VaryingSet8, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh, std::vector, std::vector, std::vector>; + using Input = baker::VaryingSet7, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh, std::vector, std::vector>; using Output = std::vector; using JobModel = baker::Job::ModelIO; diff --git a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp index e597bbf507..5ede25a42c 100644 --- a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp +++ b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.cpp @@ -13,6 +13,8 @@ #include +#include + // Used to track and avoid duplicate shape vertices, as multiple shapes can have the same mesh and skinDeformer class VertexSource { public: @@ -30,7 +32,6 @@ void CollectShapeVerticesTask::run(const baker::BakeContextPointer& context, con const auto& shapes = input.get1(); const auto& joints = input.get2(); const auto& skinDeformers = input.get3(); - const auto& reweightedDeformers = input.get4(); auto& shapeVerticesPerJoint = output; shapeVerticesPerJoint.resize(joints.size()); @@ -59,10 +60,9 @@ void CollectShapeVerticesTask::run(const baker::BakeContextPointer& context, con const auto& mesh = meshes[shape.mesh]; const auto& vertices = mesh.vertices; - const auto& reweightedDeformer = reweightedDeformers[shape.mesh]; const glm::mat4 meshToJoint = cluster.inverseBindMatrix; - const uint16_t weightsPerVertex = reweightedDeformer.weightsPerVertex; + const uint16_t weightsPerVertex = hfm::NUM_SKINNING_WEIGHTS_PER_VERTEX; if (weightsPerVertex == 0) { for (int vertexIndex = 0; vertexIndex < (int)vertices.size(); ++vertexIndex) { const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertices[vertexIndex]); @@ -71,9 +71,9 @@ void CollectShapeVerticesTask::run(const baker::BakeContextPointer& context, con } else { for (int vertexIndex = 0; vertexIndex < (int)vertices.size(); ++vertexIndex) { for (uint16_t weightIndex = 0; weightIndex < weightsPerVertex; ++weightIndex) { - const size_t index = vertexIndex*4 + weightIndex; - const uint16_t clusterIndex = reweightedDeformer.indices[index]; - const uint16_t clusterWeight = reweightedDeformer.weights[index]; + const size_t index = vertexIndex*weightsPerVertex + weightIndex; + const uint16_t clusterIndex = mesh.clusterIndices[index]; + const uint16_t clusterWeight = mesh.clusterWeights[index]; // Remember vertices associated with this joint with at least 1/4 weight const uint16_t EXPANSION_WEIGHT_THRESHOLD = std::numeric_limits::max() / 4; if (clusterIndex != j || clusterWeight < EXPANSION_WEIGHT_THRESHOLD) { diff --git a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h index f14c440f2f..a665004d6b 100644 --- a/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h +++ b/libraries/model-baker/src/model-baker/CollectShapeVerticesTask.h @@ -19,7 +19,7 @@ class CollectShapeVerticesTask { public: - using Input = baker::VaryingSet5, std::vector, std::vector, std::vector, std::vector>; + using Input = baker::VaryingSet4, std::vector, std::vector, std::vector>; using Output = std::vector; using JobModel = baker::Job::ModelIO; diff --git a/libraries/model-baker/src/model-baker/ReweightDeformersTask.cpp b/libraries/model-baker/src/model-baker/ReweightDeformersTask.cpp deleted file mode 100644 index f210a5dd6f..0000000000 --- a/libraries/model-baker/src/model-baker/ReweightDeformersTask.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// -// ReweightDeformersTask.h -// model-baker/src/model-baker -// -// Created by Sabrina Shanman on 2019/09/26. -// 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 "ReweightDeformersTask.h" - -baker::ReweightedDeformers getReweightedDeformers(size_t numMeshVertices, const std::vector skinClusters, const uint16_t weightsPerVertex) { - baker::ReweightedDeformers reweightedDeformers; - if (skinClusters.size() == 0) { - return reweightedDeformers; - } - - size_t numClusterIndices = numMeshVertices * weightsPerVertex; - reweightedDeformers.weightsPerVertex = weightsPerVertex; - // TODO: Consider having a rootCluster property in the DynamicTransform rather than appending the root to the end of the cluster list. - reweightedDeformers.indices.resize(numClusterIndices, (uint16_t)(skinClusters.size() - 1)); - reweightedDeformers.weights.resize(numClusterIndices, 0); - - std::vector weightAccumulators; - weightAccumulators.resize(numClusterIndices, 0.0f); - for (uint16_t i = 0; i < (uint16_t)skinClusters.size(); ++i) { - const hfm::SkinCluster& skinCluster = *skinClusters[i]; - - if (skinCluster.indices.size() != skinCluster.weights.size()) { - reweightedDeformers.trimmedToMatch = true; - } - size_t numIndicesOrWeights = std::min(skinCluster.indices.size(), skinCluster.weights.size()); - for (size_t j = 0; j < numIndicesOrWeights; ++j) { - uint32_t index = skinCluster.indices[j]; - float weight = skinCluster.weights[j]; - - // look for an unused slot in the weights vector - uint32_t weightIndex = index * weightsPerVertex; - uint32_t lowestIndex = -1; - float lowestWeight = FLT_MAX; - uint16_t k = 0; - for (; k < weightsPerVertex; k++) { - if (weightAccumulators[weightIndex + k] == 0.0f) { - reweightedDeformers.indices[weightIndex + k] = i; - weightAccumulators[weightIndex + k] = weight; - break; - } - if (weightAccumulators[weightIndex + k] < lowestWeight) { - lowestIndex = k; - lowestWeight = weightAccumulators[weightIndex + k]; - } - } - if (k == weightsPerVertex && weight > lowestWeight) { - // no space for an additional weight; we must replace the lowest - weightAccumulators[weightIndex + lowestIndex] = weight; - reweightedDeformers.indices[weightIndex + lowestIndex] = i; - } - } - } - - // now that we've accumulated the most relevant weights for each vertex - // normalize and compress to 16-bits - for (size_t i = 0; i < numMeshVertices; ++i) { - size_t j = i * weightsPerVertex; - - // normalize weights into uint16_t - float totalWeight = 0.0f; - for (size_t k = j; k < j + weightsPerVertex; ++k) { - totalWeight += weightAccumulators[k]; - } - - const float ALMOST_HALF = 0.499f; - if (totalWeight > 0.0f) { - float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; - for (size_t k = j; k < j + weightsPerVertex; ++k) { - reweightedDeformers.weights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); - } - } else { - reweightedDeformers.weights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); - } - } - - return reweightedDeformers; -} - -void ReweightDeformersTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) { - const uint16_t NUM_WEIGHTS_PER_VERTEX { 4 }; - - const auto& meshes = input.get0(); - const auto& shapes = input.get1(); - const auto& skinDeformers = input.get2(); - const auto& skinClusters = input.get3(); - auto& reweightedDeformers = output; - - // Currently, there is only (at most) one skinDeformer per mesh - // An undefined shape.skinDeformer has the value hfm::UNDEFINED_KEY - std::vector skinDeformerPerMesh; - skinDeformerPerMesh.resize(meshes.size(), hfm::UNDEFINED_KEY); - for (const auto& shape : shapes) { - uint32_t skinDeformerIndex = shape.skinDeformer; - skinDeformerPerMesh[shape.mesh] = skinDeformerIndex; - } - - reweightedDeformers.reserve(meshes.size()); - for (size_t i = 0; i < meshes.size(); ++i) { - const auto& mesh = meshes[i]; - uint32_t skinDeformerIndex = skinDeformerPerMesh[i]; - - const hfm::SkinDeformer* skinDeformer = nullptr; - std::vector meshSkinClusters; - if (skinDeformerIndex != hfm::UNDEFINED_KEY) { - skinDeformer = &skinDeformers[skinDeformerIndex]; - for (const auto& skinClusterIndex : skinDeformer->skinClusterIndices) { - const auto& skinCluster = skinClusters[skinClusterIndex]; - meshSkinClusters.push_back(&skinCluster); - } - } - - reweightedDeformers.push_back(getReweightedDeformers((size_t)mesh.vertices.size(), meshSkinClusters, NUM_WEIGHTS_PER_VERTEX)); - } -} diff --git a/libraries/model-baker/src/model-baker/ReweightDeformersTask.h b/libraries/model-baker/src/model-baker/ReweightDeformersTask.h deleted file mode 100644 index c40ad4c1b4..0000000000 --- a/libraries/model-baker/src/model-baker/ReweightDeformersTask.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// ReweightDeformersTask.h -// model-baker/src/model-baker -// -// Created by Sabrina Shanman on 2019/09/26. -// 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_ReweightDeformersTask_h -#define hifi_ReweightDeformersTask_h - -#include - -#include "Engine.h" -#include "BakerTypes.h" - -class ReweightDeformersTask { -public: - using Input = baker::VaryingSet4, std::vector, std::vector, std::vector>; - using Output = std::vector; - using JobModel = baker::Job::ModelIO; - - void run(const baker::BakeContextPointer& context, const Input& input, Output& output); -}; - -#endif // hifi_ReweightDeformersTask_h