Add support for 8 bone influences per vertex. This generalizes FbxSkinningAccess to be agnostic to vertex types and support any number of bone influences. RawModel and Raw2Gltf still operates on (multiple) Vec4f Vec4i types for per vertex weights and bone ids. A good second step would be to generalize RawModel as well, though AddAttributeToPrimitive, GetAttributeArray and the attribute pointer in AttributeDefinition complicates this.

This commit is contained in:
hhalen 2019-04-10 13:11:24 -07:00
parent 395e376252
commit a7b9f80e32
7 changed files with 136 additions and 78 deletions

View File

@ -144,7 +144,7 @@ int main(int argc, char* argv[]) {
"-k,--keep-attribute",
[&](std::vector<std::string> attributes) -> bool {
gltfOptions.keepAttribs =
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1;
for (std::string attribute : attributes) {
if (attribute == "position") {
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION;

View File

@ -69,6 +69,27 @@ static RawMaterialType GetMaterialType(
return skinned ? RAW_MATERIAL_TYPE_SKINNED_OPAQUE : RAW_MATERIAL_TYPE_OPAQUE;
}
static void calcMinMax(RawSurface& rawSurface, const FbxSkinningAccess& skinning, const FbxVector4& globalPosition, const Vec4i& indices, const Vec4f& weights) {
for (int i = 0; i < 4; i++) {
if (weights[i] > 0.0f) {
const FbxVector4 localPosition =
skinning.GetJointInverseGlobalTransforms(indices[i]).MultNormalize(globalPosition);
Vec3f& mins = rawSurface.jointGeometryMins[indices[i]];
mins[0] = std::min(mins[0], (float)localPosition[0]);
mins[1] = std::min(mins[1], (float)localPosition[1]);
mins[2] = std::min(mins[2], (float)localPosition[2]);
Vec3f& maxs = rawSurface.jointGeometryMaxs[indices[i]];
maxs[0] = std::max(maxs[0], (float)localPosition[0]);
maxs[1] = std::max(maxs[1], (float)localPosition[1]);
maxs[2] = std::max(maxs[2], (float)localPosition[2]);
}
}
}
static void ReadMesh(
RawModel& raw,
FbxScene* pScene,
@ -159,8 +180,15 @@ static void ReadMesh(
raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_UV1);
}
if (skinning.IsSkinned()) {
raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS);
raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES);
FBX_ASSERT(skinning.MaxWeights() <= 8); // output only supports up to 8
raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0);
raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0);
if (skinning.MaxWeights() > 4) {
raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1);
raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1);
}
}
RawSurface& rawSurface = raw.GetSurface(rawSurfaceIndex);
@ -342,8 +370,10 @@ static void ReadMesh(
vertex.uv0[1] = (float)fbxUV0[1];
vertex.uv1[0] = (float)fbxUV1[0];
vertex.uv1[1] = (float)fbxUV1[1];
vertex.jointIndices = skinning.GetVertexIndices(controlPointIndex);
vertex.jointWeights = skinning.GetVertexWeights(controlPointIndex);
vertex.jointIndices0 = skinning.GetVertexIndices(controlPointIndex, 0);
vertex.jointWeights0 = skinning.GetVertexWeights(controlPointIndex, 0);
vertex.jointIndices1 = skinning.GetVertexIndices(controlPointIndex, 1);
vertex.jointWeights1 = skinning.GetVertexWeights(controlPointIndex, 1);
vertex.polarityUv0 = false;
// flag this triangle as transparent if any of its corner vertices substantially deviates from
@ -387,38 +417,15 @@ static void ReadMesh(
}
if (skinning.IsSkinned()) {
const int jointIndices[FbxSkinningAccess::MAX_WEIGHTS] = {vertex.jointIndices[0],
vertex.jointIndices[1],
vertex.jointIndices[2],
vertex.jointIndices[3]};
const float jointWeights[FbxSkinningAccess::MAX_WEIGHTS] = {vertex.jointWeights[0],
vertex.jointWeights[1],
vertex.jointWeights[2],
vertex.jointWeights[3]};
const FbxMatrix skinningMatrix =
skinning.GetJointSkinningTransform(jointIndices[0]) * jointWeights[0] +
skinning.GetJointSkinningTransform(jointIndices[1]) * jointWeights[1] +
skinning.GetJointSkinningTransform(jointIndices[2]) * jointWeights[2] +
skinning.GetJointSkinningTransform(jointIndices[3]) * jointWeights[3];
const FbxVector4 globalPosition = skinningMatrix.MultNormalize(fbxPosition);
for (int i = 0; i < FbxSkinningAccess::MAX_WEIGHTS; i++) {
if (jointWeights[i] > 0.0f) {
const FbxVector4 localPosition =
skinning.GetJointInverseGlobalTransforms(jointIndices[i])
.MultNormalize(globalPosition);
Vec3f& mins = rawSurface.jointGeometryMins[jointIndices[i]];
mins[0] = std::min(mins[0], (float)localPosition[0]);
mins[1] = std::min(mins[1], (float)localPosition[1]);
mins[2] = std::min(mins[2], (float)localPosition[2]);
Vec3f& maxs = rawSurface.jointGeometryMaxs[jointIndices[i]];
maxs[0] = std::max(maxs[0], (float)localPosition[0]);
maxs[1] = std::max(maxs[1], (float)localPosition[1]);
maxs[2] = std::max(maxs[2], (float)localPosition[2]);
}
}
FbxMatrix skinningMatrix = FbxMatrix() * 0.0;
for (int j = 0; j < 4; j++)
skinningMatrix += skinning.GetJointSkinningTransform(vertex.jointIndices0[j]) * vertex.jointWeights0[j];
for (int j = 0; j < 4; j++)
skinningMatrix += skinning.GetJointSkinningTransform(vertex.jointIndices1[j]) * vertex.jointWeights1[j];
const FbxVector4 globalPosition = skinningMatrix.MultNormalize(fbxPosition);
calcMinMax(rawSurface, skinning, globalPosition, vertex.jointIndices0, vertex.jointWeights0);
calcMinMax(rawSurface, skinning, globalPosition, vertex.jointIndices1, vertex.jointWeights1);
}
}

View File

@ -20,8 +20,8 @@ FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, Fbx
continue;
}
int controlPointCount = pMesh->GetControlPointsCount();
vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0));
vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f));
vertexJointIndices.resize(controlPointCount);
vertexJointWeights.resize(controlPointCount);
for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) {
FbxCluster* cluster = skin->GetCluster(clusterIndex);
@ -55,12 +55,12 @@ FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, Fbx
if (clusterIndices[i] < 0 || clusterIndices[i] >= controlPointCount) {
continue;
}
if (clusterWeights[i] <= vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1]) {
if (clusterWeights[i] <= 0.0 || vertexJointWeights[clusterIndices[i]].size() >= MAX_WEIGHTS) {
continue;
}
vertexJointIndices[clusterIndices[i]][MAX_WEIGHTS - 1] = clusterIndex;
vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1] = (float)clusterWeights[i];
for (int j = MAX_WEIGHTS - 1; j > 0; j--) {
vertexJointIndices[clusterIndices[i]].push_back(clusterIndex);
vertexJointWeights[clusterIndices[i]].push_back((float)clusterWeights[i]);
for (int j = vertexJointWeights[clusterIndices[i]].size() - 1; j > 0; j--) {
if (vertexJointWeights[clusterIndices[i]][j - 1] >=
vertexJointWeights[clusterIndices[i]][j]) {
break;
@ -75,8 +75,12 @@ FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, Fbx
}
}
for (int i = 0; i < controlPointCount; i++) {
const float weightSumRcp = 1.0 / (vertexJointWeights[i][0] + vertexJointWeights[i][1] + vertexJointWeights[i][2] + vertexJointWeights[i][3]);
vertexJointWeights[i] *= weightSumRcp;
float weightSum = 0.0;
for (int w = 0; w < vertexJointWeights[i].size(); ++w)
weightSum += vertexJointWeights[i][w];
float weightSumRcp = 1.0 / weightSum;
for (int w = 0; w < vertexJointWeights[i].size(); ++w)
vertexJointWeights[i][w] *= weightSumRcp;
}
}
}

View File

@ -21,7 +21,7 @@
class FbxSkinningAccess {
public:
static const int MAX_WEIGHTS = 4;
static const int MAX_WEIGHTS = 8;
FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, FbxNode* pNode);
@ -29,6 +29,10 @@ class FbxSkinningAccess {
return (vertexJointWeights.size() > 0);
}
int MaxWeights() const {
return MAX_WEIGHTS;
}
int GetNodeCount() const {
return (int)jointNodes.size();
}
@ -58,15 +62,23 @@ class FbxSkinningAccess {
return inverseBindMatrices[jointIndex];
}
const Vec4i GetVertexIndices(const int controlPointIndex) const {
return (!vertexJointIndices.empty()) ? vertexJointIndices[controlPointIndex]
: Vec4i(0, 0, 0, 0);
}
const Vec4f GetVertexWeights(const int controlPointIndex) const {
return (!vertexJointWeights.empty()) ? vertexJointWeights[controlPointIndex]
: Vec4f(0, 0, 0, 0);
}
const Vec4i GetVertexIndices(const int controlPointIndex, const int subset) const {
if (vertexJointIndices.empty())
return Vec4i(0, 0, 0, 0);
Vec4i indices(0, 0, 0, 0);
for (int k = subset * 4; k < subset * 4 + 4 && k < vertexJointIndices[controlPointIndex].size(); k++)
indices[k - subset * 4] = vertexJointIndices[controlPointIndex][k];
return indices;
}
const Vec4f GetVertexWeights(const int controlPointIndex, const int subset) const {
if (vertexJointWeights.empty())
return Vec4f(0.0f);
Vec4f weights(0.0f);
for (int k = subset * 4; k < subset * 4 + 4 && k < vertexJointWeights[controlPointIndex].size(); k++)
weights[k - subset * 4] = vertexJointWeights[controlPointIndex][k];
return weights;
}
private:
int rootIndex;
@ -75,6 +87,6 @@ class FbxSkinningAccess {
std::vector<FbxMatrix> jointSkinningTransforms;
std::vector<FbxMatrix> jointInverseGlobalTransforms;
std::vector<FbxAMatrix> inverseBindMatrices;
std::vector<Vec4i> vertexJointIndices;
std::vector<Vec4f> vertexJointWeights;
std::vector<std::vector<uint16_t>> vertexJointIndices;
std::vector<std::vector<float>> vertexJointWeights;
};

View File

@ -532,24 +532,42 @@ ModelData* Raw2Gltf(
draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) {
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0) != 0) {
const AttributeDefinition<Vec4i> ATTR_JOINTS(
"JOINTS_0",
&RawVertex::jointIndices,
&RawVertex::jointIndices0,
GLT_VEC4I,
draco::GeometryAttribute::GENERIC,
draco::DT_UINT16);
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) {
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0) != 0) {
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
"WEIGHTS_0",
&RawVertex::jointWeights,
&RawVertex::jointWeights0,
GLT_VEC4F,
draco::GeometryAttribute::GENERIC,
draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1) != 0) {
const AttributeDefinition<Vec4i> ATTR_JOINTS(
"JOINTS_1",
&RawVertex::jointIndices1,
GLT_VEC4I,
draco::GeometryAttribute::GENERIC,
draco::DT_UINT16);
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
}
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1) != 0) {
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
"WEIGHTS_1",
&RawVertex::jointWeights1,
GLT_VEC4F,
draco::GeometryAttribute::GENERIC,
draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
}
// each channel present in the mesh always ends up a target in the primitive
for (int channelIx = 0; channelIx < rawSurface.blendChannels.size(); channelIx++) {

View File

@ -26,8 +26,9 @@
bool RawVertex::operator==(const RawVertex& other) const {
return (position == other.position) && (normal == other.normal) && (tangent == other.tangent) &&
(binormal == other.binormal) && (color == other.color) && (uv0 == other.uv0) &&
(uv1 == other.uv1) && (jointIndices == other.jointIndices) &&
(jointWeights == other.jointWeights) && (polarityUv0 == other.polarityUv0) &&
(uv1 == other.uv1) && (jointIndices0 == other.jointIndices0) &&
(jointWeights0 == other.jointWeights0) && (jointIndices1 == other.jointIndices1) &&
(jointWeights1 == other.jointWeights1) && (polarityUv0 == other.polarityUv0) &&
(blendSurfaceIx == other.blendSurfaceIx) && (blends == other.blends);
}
@ -55,11 +56,17 @@ size_t RawVertex::Difference(const RawVertex& other) const {
attributes |= RAW_VERTEX_ATTRIBUTE_UV1;
}
// Always need both or neither.
if (jointIndices != other.jointIndices) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
}
if (jointWeights != other.jointWeights) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
if (jointIndices0 != other.jointIndices0) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0;
}
if (jointWeights0 != other.jointWeights0) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0;
}
if (jointIndices1 != other.jointIndices1) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1;
}
if (jointWeights1 != other.jointWeights1) {
attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1;
}
return attributes;
}
@ -573,7 +580,7 @@ void RawModel::CreateMaterialModels(
if (keepAttribs != -1) {
int keep = keepAttribs;
if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) {
keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 | RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1;
}
if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_AUTO) != 0) {
keep |= RAW_VERTEX_ATTRIBUTE_POSITION;
@ -614,11 +621,17 @@ void RawModel::CreateMaterialModels(
if ((keep & RAW_VERTEX_ATTRIBUTE_UV1) == 0) {
vertex.uv1 = defaultVertex.uv1;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) == 0) {
vertex.jointIndices = defaultVertex.jointIndices;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) == 0) {
vertex.jointWeights = defaultVertex.jointWeights;
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0) == 0) {
vertex.jointIndices0 = defaultVertex.jointIndices0;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0) == 0) {
vertex.jointWeights0 = defaultVertex.jointWeights0;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1) == 0) {
vertex.jointIndices1 = defaultVertex.jointIndices1;
}
if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1) == 0) {
vertex.jointWeights1 = defaultVertex.jointWeights1;
}
}

View File

@ -23,8 +23,10 @@ enum RawVertexAttribute {
RAW_VERTEX_ATTRIBUTE_COLOR = 1 << 4,
RAW_VERTEX_ATTRIBUTE_UV0 = 1 << 5,
RAW_VERTEX_ATTRIBUTE_UV1 = 1 << 6,
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES = 1 << 7,
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS = 1 << 8,
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES0 = 1 << 7,
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS0 = 1 << 8,
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES1 = 1 << 9,
RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS1 = 1 << 10,
RAW_VERTEX_ATTRIBUTE_AUTO = 1 << 31
};
@ -49,8 +51,10 @@ struct RawVertex {
Vec4f color{0.0f};
Vec2f uv0{0.0f};
Vec2f uv1{0.0f};
Vec4i jointIndices{0, 0, 0, 0};
Vec4f jointWeights{0.0f};
Vec4i jointIndices0{0,0,0,0};
Vec4i jointIndices1{0,0,0,0};
Vec4f jointWeights0{0.0f};
Vec4f jointWeights1{0.0f};
// end of members that directly correspond to vertex attributes
// if this vertex participates in a blend shape setup, the surfaceIx of its dedicated mesh;