store cluster indices and weights in uint16_t

This commit is contained in:
Andrew Meadows 2017-03-24 10:35:10 -07:00
parent f6d6c62fab
commit 60bc76e1c4
3 changed files with 94 additions and 27 deletions

View file

@ -1634,8 +1634,12 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
glm::mat4 inverseModelTransform = glm::inverse(modelTransform);
if (clusterIDs.size() > 1) {
// this is a multi-mesh joint
extracted.mesh.clusterIndices.resize(extracted.mesh.vertices.size());
extracted.mesh.clusterWeights.resize(extracted.mesh.vertices.size());
const int WEIGHTS_PER_VERTEX = 4;
int numClusterIndices = extracted.mesh.vertices.size() * WEIGHTS_PER_VERTEX;
extracted.mesh.clusterIndices.fill(0, numClusterIndices);
QVector<float> weightAccumulators;
weightAccumulators.fill(0.0f, numClusterIndices);
for (int i = 0; i < clusterIDs.size(); i++) {
QString clusterID = clusterIDs.at(i);
const Cluster& cluster = clusters[clusterID];
@ -1665,45 +1669,59 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
float weight = cluster.weights.at(j);
for (QMultiHash<int, int>::const_iterator it = extracted.newIndices.constFind(oldIndex);
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
int newIndex = it.value();
// remember vertices with at least 1/4 weight
const float EXPANSION_WEIGHT_THRESHOLD = 0.99f;
if (weight > EXPANSION_WEIGHT_THRESHOLD) {
// transform to joint-frame and save for later
const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(it.value()));
const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(newIndex));
points.push_back(extractTranslation(vertexTransform) * clusterScale);
}
// look for an unused slot in the weights vector
glm::vec4& weights = extracted.mesh.clusterWeights[it.value()];
int weightIndex = newIndex * WEIGHTS_PER_VERTEX;
int lowestIndex = -1;
float lowestWeight = FLT_MAX;
int k = 0;
for (; k < 4; k++) {
if (weights[k] == 0.0f) {
extracted.mesh.clusterIndices[it.value()][k] = i;
weights[k] = weight;
for (; k < WEIGHTS_PER_VERTEX; k++) {
if (weightAccumulators[weightIndex + k] == 0.0f) {
extracted.mesh.clusterIndices[weightIndex + k] = i;
weightAccumulators[weightIndex + k] = weight;
break;
}
if (weights[k] < lowestWeight) {
if (weightAccumulators[weightIndex + k] < lowestWeight) {
lowestIndex = k;
lowestWeight = weights[k];
lowestWeight = weightAccumulators[weightIndex + k];
}
}
if (k == 4 && weight > lowestWeight) {
if (k == WEIGHTS_PER_VERTEX && weight > lowestWeight) {
// no space for an additional weight; we must replace the lowest
weights[lowestIndex] = weight;
extracted.mesh.clusterIndices[it.value()][lowestIndex] = i;
weightAccumulators[weightIndex + lowestIndex] = weight;
extracted.mesh.clusterIndices[weightIndex + lowestIndex] = i;
}
}
}
}
// normalize the weights if they don't add up to one
for (int i = 0; i < extracted.mesh.clusterWeights.size(); i++) {
glm::vec4& weights = extracted.mesh.clusterWeights[i];
float total = weights.x + weights.y + weights.z + weights.w;
if (total != 1.0f && total != 0.0f) {
weights /= total;
// now that we've accumulated the most relevant weights for each vertex
// normalize and compress to 8-bits
extracted.mesh.clusterWeights.fill(0, numClusterIndices);
int numVertices = extracted.mesh.vertices.size();
for (int i = 0; i < numVertices; ++i) {
int j = i * WEIGHTS_PER_VERTEX;
// normalize weights into uint8_t
float totalWeight = weightAccumulators[j];
for (int k = j + 1; k < j + WEIGHTS_PER_VERTEX; ++k) {
totalWeight += weightAccumulators[k];
}
if (totalWeight > 0.0f) {
const float ALMOST_HALF = 0.499f;
float weightScalingFactor = (float)(UINT8_MAX) / totalWeight;
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
extracted.mesh.clusterWeights[k] = (uint8_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF);
}
}
}
} else {

View file

@ -202,7 +202,7 @@ public:
/// A single mesh (with optional blendshapes) extracted from an FBX document.
class FBXMesh {
public:
QVector<FBXMeshPart> parts;
QVector<glm::vec3> vertices;
@ -211,9 +211,9 @@ public:
QVector<glm::vec3> colors;
QVector<glm::vec2> texCoords;
QVector<glm::vec2> texCoords1;
QVector<glm::vec4> clusterIndices;
QVector<glm::vec4> clusterWeights;
QVector<uint16_t> clusterIndices;
QVector<uint8_t> clusterWeights;
QVector<FBXCluster> clusters;
Extents meshExtents;

View file

@ -422,8 +422,18 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
int colorsSize = fbxMesh.colors.size() * sizeof(glm::vec3);
int texCoordsSize = fbxMesh.texCoords.size() * sizeof(glm::vec2);
int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(glm::vec2);
int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(glm::vec4);
int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(glm::vec4);
#define UNFORTUNATE_HACK
#ifdef UNFORTUNATE_HACK
// HACK: store clusterIndices in floats rather than short integers
int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(float);
#else // UNFORTUNATE_HACK
int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(uint8_t);
if (fbxMesh.clusters.size() > UINT8_MAX) {
// we need 16 bits instead of just 8 for clusterIndices
clusterIndicesSize *= 2;
}
#endif // UNFORTUNATE_HACK
int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t);
int normalsOffset = 0;
int tangentsOffset = normalsOffset + normalsSize;
@ -442,7 +452,31 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
attribBuffer->setSubData(colorsOffset, colorsSize, (gpu::Byte*) fbxMesh.colors.constData());
attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (gpu::Byte*) fbxMesh.texCoords.constData());
attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (gpu::Byte*) fbxMesh.texCoords1.constData());
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) fbxMesh.clusterIndices.constData());
#ifdef UNFORTUNATE_HACK
// HACK: copy clusterIndices back into floats to appease render pipeline
QVector<float> clusterIndices;
int32_t numIndices = fbxMesh.clusterIndices.size();
clusterIndices.resize(numIndices);
for (int32_t i = 0; i < numIndices; ++i) {
assert(fbxMesh.clusterIndices[i] <= UINT8_MAX);
clusterIndices[i] = (float)(fbxMesh.clusterIndices[i]);
}
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) clusterIndices.constData());
#else // UNFORTUNATE_HACK
if (fbxMesh.clusters.size() < UINT8_MAX) {
// yay! we can fit the clusterIndices within 8-bits
int32_t numIndices = fbxMesh.clusterIndices.size();
QVector<uint8_t> clusterIndices;
clusterIndices.resize(numIndices);
for (int32_t i = 0; i < numIndices; ++i) {
assert(fbxMesh.clusterIndices[i] <= UINT8_MAX);
clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]);
}
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) clusterIndices.constData());
} else {
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) fbxMesh.clusterIndices.constData());
}
#endif // UNFORTUNATE_HACK
attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (gpu::Byte*) fbxMesh.clusterWeights.constData());
if (normalsSize) {
@ -475,15 +509,30 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)));
}
#ifdef UNFORTUNATE_HACK
// HACK: cluster indices are submitted to render pipeline in FLOAT format
if (clusterIndicesSize) {
mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_INDEX,
model::BufferView(attribBuffer, clusterIndicesOffset, clusterIndicesSize,
gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW)));
}
#else // UNFORTUNATE_HACK
if (clusterIndicesSize) {
if (fbxMesh.clusters.size() < UINT8_MAX) {
mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_INDEX,
model::BufferView(attribBuffer, clusterIndicesOffset, clusterIndicesSize,
gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW)));
} else {
mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_INDEX,
model::BufferView(attribBuffer, clusterIndicesOffset, clusterIndicesSize,
gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW)));
}
}
#endif // UNFORTUNATE_HACK
if (clusterWeightsSize) {
mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT,
model::BufferView(attribBuffer, clusterWeightsOffset, clusterWeightsSize,
gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW)));
gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::XYZW)));
}