Skip to content

Commit 6ab56e2

Browse files
yutingyefacebook-github-bot
authored andcommitted
Improve convert_model to handle skeleton conversion (#829)
Summary: This is to handle the case where we want to apply anim curves in an fbx to a skinned model. Previously, we directly copy over joint parameters, assuming the anim curves are on an identical skeleton as the skinned model. However, even when two skeletons "look" the same, they could still have different joint orders or rest pose / bind pose. A direct copy will fail in these cases. This diff handles such cases more robustly, by mapping global joint transforms between them based on joint names, then convert the transforms back to joint parameters based on the target skeleton's bind pose. We could still be in trouble if InverseParameterTransform is lossy, but that's a different story. `convert_model` is due for refactoring... Differential Revision: D86828693
1 parent 2ef6f3a commit 6ab56e2

File tree

1 file changed

+44
-12
lines changed

1 file changed

+44
-12
lines changed

momentum/examples/convert_model/convert_model.cpp

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
#define DEFAULT_LOG_CHANNEL "convert_model"
99

1010
#include <momentum/character/character.h>
11+
#include <momentum/character/character_utility.h>
1112
#include <momentum/character/inverse_parameter_transform.h>
13+
#include <momentum/character/skeleton_state.h>
1214
#include <momentum/common/log.h>
1315
#include <momentum/io/character_io.h>
1416
#include <momentum/io/fbx/fbx_io.h>
@@ -180,25 +182,55 @@ int main(int argc, char** argv) {
180182
options->input_locator_file, character.skeleton, character.parameterTransform);
181183
}
182184
}
185+
183186
// Validate model compatibility
184187
if (c.skeleton.joints.size() != character.skeleton.joints.size()) {
185188
MT_LOGE("The motion is not on a compatible character");
186189
} else if (motionIndex >= 0) {
187-
if (character.parameterTransform.numAllModelParameters() == motions.at(0).rows()) {
188-
poses = std::move(motions.at(motionIndex));
189-
} else {
190-
// Use inverse parameter transform to convet from joint params to model params; may lose
191-
// info.
192-
const auto motion = motions.at(motionIndex);
193-
const size_t nFrames = motion.cols();
194-
poses.setZero(character.parameterTransform.numAllModelParameters(), nFrames);
195-
InverseParameterTransform inversePt(character.parameterTransform);
196-
for (size_t iFrame = 0; iFrame < nFrames; ++iFrame) {
197-
poses.col(iFrame) = inversePt.apply(motion.col(iFrame)).pose.v;
190+
// To account for differences in the rest pose, we will go from joint state to joint
191+
// parameters then to model parameters
192+
const auto motion = motions.at(motionIndex);
193+
const size_t nFrames = motion.cols();
194+
const size_t nJoints = c.skeleton.joints.size();
195+
poses.setZero(character.parameterTransform.numAllModelParameters(), nFrames);
196+
InverseParameterTransform inversePt(character.parameterTransform);
197+
SkeletonState sourceState;
198+
TransformList transforms(nJoints);
199+
200+
// name mapping: index_map[target_index] = source_index
201+
const auto jointNames = character.skeleton.getJointNames();
202+
std::vector<size_t> indexMap(nJoints, kInvalidIndex);
203+
for (size_t iJoint = 0; iJoint < nJoints; ++iJoint) {
204+
indexMap[iJoint] = c.skeleton.getJointIdByName(jointNames[iJoint]);
205+
}
206+
207+
for (size_t iFrame = 0; iFrame < nFrames; ++iFrame) {
208+
sourceState.set(motion.col(iFrame), c.skeleton);
209+
210+
for (size_t jJoint = 0; jJoint < nJoints; ++jJoint) {
211+
if (indexMap[jJoint] == kInvalidIndex) {
212+
// no correspondence in source
213+
const Joint& joint = character.skeleton.joints[jJoint];
214+
const Transform local(joint.translationOffset, joint.preRotation);
215+
const size_t parentIndex = character.skeleton.joints[jJoint].parent;
216+
if (parentIndex == kInvalidIndex) {
217+
transforms[jJoint] = local;
218+
} else {
219+
transforms[jJoint] = transforms[parentIndex] * local;
220+
}
221+
} else {
222+
transforms[jJoint] = sourceState.jointState[indexMap[jJoint]].transform;
223+
}
198224
}
225+
const auto jointParams = skeletonStateToJointParameters(transforms, character.skeleton);
226+
poses.col(iFrame) = inversePt.apply(jointParams).pose.v;
199227
}
200228
fps = framerate;
201-
offsets = character.parameterTransform.zero().v;
229+
if (offsets.size() > 0) {
230+
offsets = mapIdentityToCharacter({c.skeleton.getJointNames(), offsets}, character);
231+
} else {
232+
offsets = character.parameterTransform.zero().v;
233+
}
202234
}
203235

204236
if (saveMarkers) {

0 commit comments

Comments
 (0)