mirror of
https://github.com/overte-org/overte.git
synced 2025-04-06 18:53:16 +02:00
Merge branch 'instancing' of github.com:highfidelity/hifi into instancing
This commit is contained in:
commit
0321e2c2c3
13 changed files with 131 additions and 208 deletions
|
@ -20,6 +20,7 @@
|
|||
#include <FaceshiftConstants.h>
|
||||
|
||||
#include <hfm/ModelFormatLogging.h>
|
||||
#include <hfm/HFMModelMath.h>
|
||||
|
||||
// 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<hfm::SkinCluster> 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<hfm::SkinCluster> 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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1037,8 +1037,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();
|
||||
|
|
|
@ -248,8 +248,8 @@ public:
|
|||
glm::mat4 modelTransform; // DEPRECATED (see hfm::Joint::globalTransform, hfm::Shape::transform, hfm::Model::joints)
|
||||
|
||||
// Skinning cluster attributes
|
||||
QVector<uint16_t> clusterIndices;
|
||||
QVector<uint16_t> clusterWeights;
|
||||
std::vector<uint16_t> clusterIndices;
|
||||
std::vector<uint16_t> clusterWeights;
|
||||
|
||||
// Blendshape attributes
|
||||
QVector<Blendshape> 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<uint32_t> indices;
|
||||
|
@ -305,7 +305,7 @@ public:
|
|||
|
||||
class SkinDeformer {
|
||||
public:
|
||||
std::vector<uint16_t> skinClusterIndices;
|
||||
std::vector<uint16_t> skinClusterIndices; // DEPRECATED (see hfm::Mesh.clusterIndices, hfm::Mesh.clusterWeights)
|
||||
std::vector<Cluster> clusters;
|
||||
};
|
||||
|
||||
|
@ -337,7 +337,6 @@ public:
|
|||
std::vector<Material> materials;
|
||||
|
||||
std::vector<SkinDeformer> skinDeformers;
|
||||
std::vector<SkinCluster> skinClusters;
|
||||
|
||||
std::vector<Joint> joints;
|
||||
QHash<QString, int> jointIndices; ///< 1-based, so as to more easily detect missing indices
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
#include "HFMModelMath.h"
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
namespace hfm {
|
||||
|
||||
void forEachIndex(const hfm::MeshPart& meshPart, std::function<void(uint32_t)> func) {
|
||||
|
@ -63,4 +66,76 @@ void calculateExtentsForModel(Extents& modelExtents, const std::vector<hfm::Shap
|
|||
}
|
||||
}
|
||||
|
||||
ReweightedDeformers getReweightedDeformers(const size_t numMeshVertices, const std::vector<hfm::SkinCluster> 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<float> 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -25,6 +25,16 @@ void calculateExtentsForShape(hfm::Shape& shape, const std::vector<hfm::Mesh>& m
|
|||
|
||||
void calculateExtentsForModel(Extents& modelExtents, const std::vector<hfm::Shape>& shapes);
|
||||
|
||||
const uint16_t NUM_SKINNING_WEIGHTS_PER_VERTEX = 4;
|
||||
|
||||
class ReweightedDeformers {
|
||||
public:
|
||||
std::vector<uint16_t> indices;
|
||||
std::vector<uint16_t> weights;
|
||||
bool trimmedToMatch { false };
|
||||
};
|
||||
|
||||
ReweightedDeformers getReweightedDeformers(const size_t numMeshVertices, const std::vector<hfm::SkinCluster> skinClusters, const uint16_t weightsPerVertex = NUM_SKINNING_WEIGHTS_PER_VERTEX);
|
||||
};
|
||||
|
||||
#endif // #define hifi_hfm_ModelMath_h
|
||||
|
|
|
@ -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<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, std::vector<hfm::Joint>, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>, std::vector<hfm::SkinCluster>, Extents>;
|
||||
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 JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
|
||||
|
||||
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<GetModelPartsTask::Output>(4);
|
||||
const auto shapesIn = modelPartsIn.getN<GetModelPartsTask::Output>(5);
|
||||
const auto skinDeformersIn = modelPartsIn.getN<GetModelPartsTask::Output>(6);
|
||||
const auto skinClustersIn = modelPartsIn.getN<GetModelPartsTask::Output>(7);
|
||||
const auto modelExtentsIn = modelPartsIn.getN<GetModelPartsTask::Output>(8);
|
||||
const auto modelExtentsIn = modelPartsIn.getN<GetModelPartsTask::Output>(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<CalculateBlendshapeTangentsTask>("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<ReweightDeformersTask>("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<CollectShapeVerticesTask>("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<BuildGraphicsMeshTask>("BuildGraphicsMesh", buildGraphicsMeshInputs);
|
||||
|
||||
// Prepare joint information
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <LogHandler.h>
|
||||
#include "ModelBakerLogging.h"
|
||||
#include <hfm/HFMModelMath.h>
|
||||
#include "ModelMath.h"
|
||||
|
||||
using vec2h = glm::tvec2<glm::detail::hdata>;
|
||||
|
@ -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<graphics::Mesh>();
|
||||
|
||||
// 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<uint8_t> 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) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
class BuildGraphicsMeshTask {
|
||||
public:
|
||||
using Input = baker::VaryingSet8<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>, std::vector<baker::ReweightedDeformers>>;
|
||||
using Input = baker::VaryingSet7<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::NormalsPerMesh, baker::TangentsPerMesh, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>>;
|
||||
using Output = std::vector<graphics::MeshPointer>;
|
||||
using JobModel = baker::Job::ModelIO<BuildGraphicsMeshTask, Input, Output>;
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <hfm/HFMModelMath.h>
|
||||
|
||||
// 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<uint16_t>::max() / 4;
|
||||
if (clusterIndex != j || clusterWeight < EXPANSION_WEIGHT_THRESHOLD) {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
class CollectShapeVerticesTask {
|
||||
public:
|
||||
using Input = baker::VaryingSet5<std::vector<hfm::Mesh>, std::vector<hfm::Shape>, std::vector<hfm::Joint>, std::vector<hfm::SkinDeformer>, std::vector<baker::ReweightedDeformers>>;
|
||||
using Input = baker::VaryingSet4<std::vector<hfm::Mesh>, std::vector<hfm::Shape>, std::vector<hfm::Joint>, std::vector<hfm::SkinDeformer>>;
|
||||
using Output = std::vector<ShapeVertices>;
|
||||
using JobModel = baker::Job::ModelIO<CollectShapeVerticesTask, Input, Output>;
|
||||
|
||||
|
|
|
@ -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<const hfm::SkinCluster*> 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<float> 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<uint32_t> 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<const hfm::SkinCluster*> 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));
|
||||
}
|
||||
}
|
|
@ -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 <hfm/HFM.h>
|
||||
|
||||
#include "Engine.h"
|
||||
#include "BakerTypes.h"
|
||||
|
||||
class ReweightDeformersTask {
|
||||
public:
|
||||
using Input = baker::VaryingSet4<std::vector<hfm::Mesh>, std::vector<hfm::Shape>, std::vector<hfm::SkinDeformer>, std::vector<hfm::SkinCluster>>;
|
||||
using Output = std::vector<baker::ReweightedDeformers>;
|
||||
using JobModel = baker::Job::ModelIO<ReweightDeformersTask, Input, Output>;
|
||||
|
||||
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||
};
|
||||
|
||||
#endif // hifi_ReweightDeformersTask_h
|
Loading…
Reference in a new issue