FBX Export: allow export even when not in bind pose,
iff all bones have an offset matrix defined.pull/1812/head
parent
ce7673979b
commit
9d9acf6840
|
@ -1604,6 +1604,8 @@ void FBXExporter::WriteObjects ()
|
||||||
|
|
||||||
// TODO, FIXME: this won't work if anything is not in the bind pose.
|
// TODO, FIXME: this won't work if anything is not in the bind pose.
|
||||||
// for now if such a situation is detected, we throw an exception.
|
// for now if such a situation is detected, we throw an exception.
|
||||||
|
std::set<const aiBone*> not_in_bind_pose;
|
||||||
|
std::set<const aiNode*> no_offset_matrix;
|
||||||
|
|
||||||
// first get this mesh's position in world space,
|
// first get this mesh's position in world space,
|
||||||
// as we'll need it for each subdeformer.
|
// as we'll need it for each subdeformer.
|
||||||
|
@ -1612,10 +1614,6 @@ void FBXExporter::WriteObjects ()
|
||||||
// as it can be instanced to many nodes.
|
// as it can be instanced to many nodes.
|
||||||
// All we can do is assume no instancing,
|
// All we can do is assume no instancing,
|
||||||
// and take the first node we find that contains the mesh.
|
// and take the first node we find that contains the mesh.
|
||||||
//
|
|
||||||
// We could in stead take the transform from the bone's node,
|
|
||||||
// but there's no guarantee that the bone is in the bindpose,
|
|
||||||
// so this would be even less reliable.
|
|
||||||
aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode);
|
aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode);
|
||||||
aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
|
aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
|
||||||
|
|
||||||
|
@ -1632,6 +1630,9 @@ void FBXExporter::WriteObjects ()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!b) {
|
||||||
|
no_offset_matrix.insert(bone_node);
|
||||||
|
}
|
||||||
|
|
||||||
// start the subdeformer node
|
// start the subdeformer node
|
||||||
const int64_t subdeformer_uid = generate_uid();
|
const int64_t subdeformer_uid = generate_uid();
|
||||||
|
@ -1669,30 +1670,52 @@ void FBXExporter::WriteObjects ()
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform is the transform of the mesh, but in bone space.
|
// transform is the transform of the mesh, but in bone space.
|
||||||
// To get it we take the inverse of the world-space bone transform,
|
// if the skeleton is in the bind pose,
|
||||||
|
// we can take the inverse of the world-space bone transform
|
||||||
// and multiply by the world-space transform of the mesh.
|
// and multiply by the world-space transform of the mesh.
|
||||||
aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene);
|
aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene);
|
||||||
aiMatrix4x4 inverse_bone_xform = bone_xform;
|
aiMatrix4x4 inverse_bone_xform = bone_xform;
|
||||||
inverse_bone_xform.Inverse();
|
inverse_bone_xform.Inverse();
|
||||||
aiMatrix4x4 tr = inverse_bone_xform * mesh_xform;
|
aiMatrix4x4 tr = inverse_bone_xform * mesh_xform;
|
||||||
sdnode.AddChild("Transform", tr);
|
|
||||||
|
|
||||||
// this should match assimp's mOffsetMatrix.
|
// this should be the same as the bone's mOffsetMatrix.
|
||||||
// if it doesn't then we have a problem.
|
// if it's not the same, the skeleton isn't in the bind pose.
|
||||||
// as assimp doesn't store a mOffsetMatrix for bones with 0 weight
|
|
||||||
// we have no way of reconstructing that information.
|
|
||||||
const float epsilon = 1e-5; // some error is to be expected
|
const float epsilon = 1e-5; // some error is to be expected
|
||||||
|
bool bone_xform_okay = true;
|
||||||
if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) {
|
if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) {
|
||||||
std::stringstream err;
|
not_in_bind_pose.insert(b);
|
||||||
err << "transform matrix for bone \"" << b->mName.C_Str();
|
bone_xform_okay = false;
|
||||||
err << "\" does not match mOffsetMatrix!";
|
|
||||||
err << " Bones *must* be in the bind pose to export.";
|
|
||||||
throw DeadlyExportError(err.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// transformlink should be the position of the bone in world space,
|
// if we have a bone we should use the mOffsetMatrix,
|
||||||
// which we just calculated.
|
// otherwise try to just use the calculated transform.
|
||||||
|
if (b) {
|
||||||
|
sdnode.AddChild("Transform", b->mOffsetMatrix);
|
||||||
|
} else {
|
||||||
|
sdnode.AddChild("Transform", tr);
|
||||||
|
}
|
||||||
|
// note: it doesn't matter if we mix these,
|
||||||
|
// because if they disagree we'll throw an exception later.
|
||||||
|
// it could be that the skeleton is not in the bone pose
|
||||||
|
// but all bones are still defined,
|
||||||
|
// in which case this would use the mOffsetMatrix for everything
|
||||||
|
// and a correct skeleton would still be output.
|
||||||
|
|
||||||
|
// transformlink should be the position of the bone in world space.
|
||||||
|
// if the bone is in the bind pose (or nonexistant),
|
||||||
|
// we can just use the matrix we already calculated
|
||||||
|
if (bone_xform_okay) {
|
||||||
sdnode.AddChild("TransformLink", bone_xform);
|
sdnode.AddChild("TransformLink", bone_xform);
|
||||||
|
// otherwise we can only work it out using the mesh position.
|
||||||
|
} else {
|
||||||
|
aiMatrix4x4 trl = b->mOffsetMatrix;
|
||||||
|
trl.Inverse();
|
||||||
|
trl *= mesh_xform;
|
||||||
|
sdnode.AddChild("TransformLink", trl);
|
||||||
|
}
|
||||||
|
// note: this means we ALWAYS rely on the mesh node transform
|
||||||
|
// being unchanged from the time the skeleton was bound.
|
||||||
|
// there's not really any way around this at the moment.
|
||||||
|
|
||||||
// done
|
// done
|
||||||
sdnode.Dump(outstream);
|
sdnode.Dump(outstream);
|
||||||
|
@ -1708,6 +1731,29 @@ void FBXExporter::WriteObjects ()
|
||||||
connections.push_back(c); // TODO: emplace_back
|
connections.push_back(c); // TODO: emplace_back
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we cannot create a valid FBX file, simply die.
|
||||||
|
// this will both prevent unnecessary bug reports,
|
||||||
|
// and tell the user what they can do to fix the situation
|
||||||
|
// (i.e. export their model in the bind pose).
|
||||||
|
if (no_offset_matrix.size() && not_in_bind_pose.size()) {
|
||||||
|
std::stringstream err;
|
||||||
|
err << "Not enough information to construct bind pose";
|
||||||
|
err << " for mesh " << mi << "!";
|
||||||
|
err << " Transform matrix for bone \"";
|
||||||
|
err << (*not_in_bind_pose.begin())->mName.C_Str() << "\"";
|
||||||
|
if (not_in_bind_pose.size() > 1) {
|
||||||
|
err << " (and " << not_in_bind_pose.size() - 1 << " more)";
|
||||||
|
}
|
||||||
|
err << " does not match mOffsetMatrix,";
|
||||||
|
err << " and node \"";
|
||||||
|
err << (*no_offset_matrix.begin())->mName.C_Str() << "\"";
|
||||||
|
if (no_offset_matrix.size() > 1) {
|
||||||
|
err << " (and " << no_offset_matrix.size() - 1 << " more)";
|
||||||
|
}
|
||||||
|
err << " has no offset matrix to rely on.";
|
||||||
|
err << " Please ensure bones are in the bind pose to export.";
|
||||||
|
throw DeadlyExportError(err.str());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue