Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ mt_library(
skeleton
PRIVATE_LINK_LIBRARIES
io_marker
io
)

if(MOMENTUM_BUILD_RENDERER)
Expand Down
61 changes: 47 additions & 14 deletions momentum/examples/convert_model/convert_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#define DEFAULT_LOG_CHANNEL "convert_model"

#include <momentum/character/character.h>
#include <momentum/character/character_utility.h>
#include <momentum/character/inverse_parameter_transform.h>
#include <momentum/character/skeleton_state.h>
#include <momentum/common/log.h>
#include <momentum/io/character_io.h>
#include <momentum/io/fbx/fbx_io.h>
Expand Down Expand Up @@ -180,25 +182,55 @@ int main(int argc, char** argv) {
options->input_locator_file, character.skeleton, character.parameterTransform);
}
}

// Validate model compatibility
if (c.skeleton.joints.size() != character.skeleton.joints.size()) {
MT_LOGE("The motion is not on a compatible character");
} else if (motionIndex >= 0) {
if (character.parameterTransform.numAllModelParameters() == motions.at(0).rows()) {
poses = std::move(motions.at(motionIndex));
} else {
// Use inverse parameter transform to convet from joint params to model params; may lose
// info.
const auto motion = motions.at(motionIndex);
const size_t nFrames = motion.cols();
poses.setZero(character.parameterTransform.numAllModelParameters(), nFrames);
InverseParameterTransform inversePt(character.parameterTransform);
for (size_t iFrame = 0; iFrame < nFrames; ++iFrame) {
poses.col(iFrame) = inversePt.apply(motion.col(iFrame)).pose.v;
// To account for differences in the rest pose, we will go from joint state to joint
// parameters then to model parameters
const auto motion = motions.at(motionIndex);
const size_t nFrames = motion.cols();
const size_t nJoints = c.skeleton.joints.size();
poses.setZero(character.parameterTransform.numAllModelParameters(), nFrames);
InverseParameterTransform inversePt(character.parameterTransform);
SkeletonState sourceState;
TransformList transforms(nJoints);

// name mapping: index_map[target_index] = source_index
const auto jointNames = character.skeleton.getJointNames();
std::vector<size_t> indexMap(nJoints, kInvalidIndex);
for (size_t iJoint = 0; iJoint < nJoints; ++iJoint) {
indexMap[iJoint] = c.skeleton.getJointIdByName(jointNames[iJoint]);
}

for (size_t iFrame = 0; iFrame < nFrames; ++iFrame) {
sourceState.set(motion.col(iFrame), c.skeleton);

for (size_t jJoint = 0; jJoint < nJoints; ++jJoint) {
if (indexMap[jJoint] == kInvalidIndex) {
// no correspondence in source
const Joint& joint = character.skeleton.joints[jJoint];
const Transform local(joint.translationOffset, joint.preRotation);
const size_t parentIndex = character.skeleton.joints[jJoint].parent;
if (parentIndex == kInvalidIndex) {
transforms[jJoint] = local;
} else {
transforms[jJoint] = transforms[parentIndex] * local;
}
} else {
transforms[jJoint] = sourceState.jointState[indexMap[jJoint]].transform;
}
}
const auto jointParams = skeletonStateToJointParameters(transforms, character.skeleton);
poses.col(iFrame) = inversePt.apply(jointParams).pose.v;
}
fps = framerate;
offsets = character.parameterTransform.zero().v;
if (offsets.size() > 0) {
offsets = mapIdentityToCharacter({c.skeleton.getJointNames(), offsets}, character);
} else {
offsets = character.parameterTransform.zero().v;
}
}

if (saveMarkers) {
Expand All @@ -216,8 +248,9 @@ int main(int argc, char** argv) {
// save output
if (oextension == ".fbx") {
MT_LOGI("Saving fbx file...");
saveFbx(
options->output_model_file, character, poses, offsets, fps, options->character_mesh_save);
FileSaveOptions fbxOptions;
fbxOptions.mesh = options->character_mesh_save;
saveFbx(options->output_model_file, character, poses, offsets, fps, {}, fbxOptions);
} else if (oextension == ".glb" || oextension == ".gltf") {
MT_LOGI("Saving gltf/glb file...");
if (hasMotion) {
Expand Down
9 changes: 2 additions & 7 deletions momentum/examples/refine_motion/refine_motion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <momentum/character/character_utility.h>
#include <momentum/character/inverse_parameter_transform.h>
#include <momentum/io/character_io.h>
#include <momentum/io/gltf/gltf_io.h>
#include <momentum/io/marker/marker_io.h>
#include <momentum/io/skeleton/parameter_transform_io.h>
Expand Down Expand Up @@ -81,13 +82,7 @@ int main(int argc, char* argv[]) {

MatrixXf finalMotion = refineMotion(markerData, motion, *config, character);
// save results
saveMotion(
ioOpt->outputFile,
character,
finalMotion.col(0) /*contains newly solved id*/,
finalMotion,
markerData,
fps);
saveCharacter(ioOpt->outputFile, character, fps, finalMotion, markerData);
MT_LOGI("{} saved", ioOpt->outputFile);
} catch (std::exception& e) {
MT_LOGE("{}", e.what());
Expand Down
49 changes: 4 additions & 45 deletions momentum/io/character_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,36 +155,18 @@ void saveCharacter(
filename.string());

if (format == CharacterFormat::Gltf) {
// Convert FileSaveOptions to GltfOptions
GltfOptions gltfOptions;
gltfOptions.extensions = options.extensions;
gltfOptions.collisions = options.collisions;
gltfOptions.locators = options.locators;
gltfOptions.mesh = options.mesh;
gltfOptions.blendShapes = options.blendShapes;

saveGltfCharacter(
filename,
character,
fps,
{character.parameterTransform.name, motion},
{},
markerSequence,
options.gltfFileFormat,
gltfOptions);
options);
} else if (format == CharacterFormat::Fbx) {
// Save as FBX
saveFbx(
filename,
character,
motion,
VectorXf(),
static_cast<double>(fps),
options.mesh,
options.coordSystemInfo,
options.permissive,
markerSequence,
options.fbxNamespace);
filename, character, motion, VectorXf(), static_cast<double>(fps), markerSequence, options);
} else {
MT_THROW(
"{} is not a supported format. Supported formats: .fbx, .glb, .gltf", filename.string());
Expand All @@ -206,34 +188,11 @@ void saveCharacter(
filename.string());

if (format == CharacterFormat::Gltf) {
// Convert FileSaveOptions to GltfOptions
GltfOptions gltfOptions;
gltfOptions.extensions = options.extensions;
gltfOptions.collisions = options.collisions;
gltfOptions.locators = options.locators;
gltfOptions.mesh = options.mesh;
gltfOptions.blendShapes = options.blendShapes;

saveGltfCharacter(
filename,
character,
fps,
skeletonStates,
markerSequence,
options.gltfFileFormat,
gltfOptions);
saveGltfCharacter(filename, character, fps, skeletonStates, markerSequence, options);
} else if (format == CharacterFormat::Fbx) {
// Save as FBX
saveFbxWithSkeletonStates(
filename,
character,
skeletonStates,
static_cast<double>(fps),
options.mesh,
options.coordSystemInfo,
options.permissive,
markerSequence,
options.fbxNamespace);
filename, character, skeletonStates, static_cast<double>(fps), markerSequence, options);
} else {
MT_THROW(
"{} is not a supported format. Supported formats: .fbx, .glb, .gltf", filename.string());
Expand Down
89 changes: 22 additions & 67 deletions momentum/io/fbx/fbx_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -795,134 +795,100 @@ MarkerSequence loadFbxMarkerSequence(const filesystem::path& filename, bool stri
void saveFbx(
const filesystem::path& filename,
const Character& character,
const MatrixXf& poses, // model parameters
const MatrixXf& poses,
const VectorXf& identity,
const double framerate,
const bool saveMesh,
const FbxCoordSystemInfo& coordSystemInfo,
const bool permissive,
std::span<const std::vector<Marker>> markerSequence,
std::string_view fbxNamespace) {
const FileSaveOptions& options) {
CharacterParameters params;
if (identity.size() == character.parameterTransform.numJointParameters()) {
params.offsets = identity;
} else {
params.offsets = character.parameterTransform.bindPose();
}

// first convert model parameters to joint values
CharacterState state;
MatrixXf jointValues;
if (poses.cols() > 0) {
// Set the initial pose to initialize the state
params.pose = poses.col(0);
state.set(params, character, false, false, false);

// Resize the jointValues matrix based on the size of joint parameters and number of poses
jointValues.resize(state.skeletonState.jointParameters.v.size(), poses.cols());

// Store the joint parameters for the initial pose
jointValues.col(0) = state.skeletonState.jointParameters.v;

// Iterate through each subsequent pose
for (Eigen::Index f = 1; f < poses.cols(); f++) {
// set the current pose
params.pose = poses.col(f);
state.set(params, character, false, false, false);
jointValues.col(f) = state.skeletonState.jointParameters.v;
}
}

// Call the helper function to save FBX file with joint values
saveFbxCommon(
filename,
character,
jointValues,
framerate,
saveMesh,
options.mesh,
false,
coordSystemInfo,
permissive,
options.coordSystemInfo,
options.permissive,
markerSequence,
fbxNamespace);
options.fbxNamespace);
}

void saveFbxWithJointParams(
const filesystem::path& filename,
const Character& character,
const MatrixXf& jointParams,
const double framerate,
const bool saveMesh,
const FbxCoordSystemInfo& coordSystemInfo,
const bool permissive,
std::span<const std::vector<Marker>> markerSequence,
std::string_view fbxNamespace) {
// Call the helper function to save FBX file with joint values.
// Set skipActiveJointParamCheck=true to skip the active joint param check as the joint params are
// passed in directly from user.
const FileSaveOptions& options) {
saveFbxCommon(
filename,
character,
jointParams,
framerate,
saveMesh,
options.mesh,
true,
coordSystemInfo,
permissive,
options.coordSystemInfo,
options.permissive,
markerSequence,
fbxNamespace);
options.fbxNamespace);
}

void saveFbxWithSkeletonStates(
const filesystem::path& filename,
const Character& character,
std::span<const SkeletonState> skeletonStates,
const double framerate,
const bool saveMesh,
const FbxCoordSystemInfo& coordSystemInfo,
const bool permissive,
std::span<const std::vector<Marker>> markerSequence,
std::string_view fbxNamespace) {
const FileSaveOptions& options) {
const size_t nFrames = skeletonStates.size();
MatrixXf jointParams(character.parameterTransform.zero().v.size(), nFrames);
for (size_t iFrame = 0; iFrame < nFrames; ++iFrame) {
jointParams.col(iFrame) =
skeletonStateToJointParameters(skeletonStates[iFrame], character.skeleton).v;
}

// Call the helper function to save FBX file with joint values.
// Set skipActiveJointParamCheck=true to skip the active joint param check as the joint params are
// passed in directly from user.
saveFbxCommon(
filename,
character,
jointParams,
framerate,
saveMesh,
options.mesh,
true,
coordSystemInfo,
permissive,
options.coordSystemInfo,
options.permissive,
markerSequence,
fbxNamespace);
options.fbxNamespace);
}

void saveFbxModel(
const filesystem::path& filename,
const Character& character,
const FbxCoordSystemInfo& coordSystemInfo,
bool permissive,
std::string_view fbxNamespace) {
saveFbx(
filename,
character,
MatrixXf(),
VectorXf(),
120.0,
true,
coordSystemInfo,
permissive,
{},
fbxNamespace);
const FileSaveOptions& options) {
saveFbx(filename, character, MatrixXf(), VectorXf(), 120.0, {}, options);
}

#else // !MOMENTUM_WITH_FBX_SDK
Expand All @@ -933,11 +899,8 @@ void saveFbx(
const MatrixXf& /* poses */,
const VectorXf& /* identity */,
const double /* framerate */,
const bool /* saveMesh */,
const FbxCoordSystemInfo& /* coordSystemInfo */,
const bool /* permissive */,
std::span<const std::vector<Marker>> /* markerSequence */,
std::string_view /* fbxNamespace */) {
const FileSaveOptions& /* options */) {
MT_THROW(
"FBX saving is not supported in OpenFBX-only mode. FBX loading is available via OpenFBX, but saving requires the full Autodesk FBX SDK.");
}
Expand All @@ -947,11 +910,8 @@ void saveFbxWithJointParams(
const Character& /* character */,
const MatrixXf& /* jointParams */,
const double /* framerate */,
const bool /* saveMesh */,
const FbxCoordSystemInfo& /* coordSystemInfo */,
const bool /* permissive */,
std::span<const std::vector<Marker>> /* markerSequence */,
std::string_view /* fbxNamespace */) {
const FileSaveOptions& /* options */) {
MT_THROW(
"FBX saving is not supported in OpenFBX-only mode. FBX loading is available via OpenFBX, but saving requires the full Autodesk FBX SDK.");
}
Expand All @@ -961,21 +921,16 @@ void saveFbxWithSkeletonStates(
const Character& /* character */,
std::span<const SkeletonState> /* skeletonStates */,
const double /* framerate */,
const bool /* saveMesh */,
const FbxCoordSystemInfo& /* coordSystemInfo */,
const bool /* permissive */,
std::span<const std::vector<Marker>> /* markerSequence */,
std::string_view /* fbxNamespace */) {
const FileSaveOptions& /* options */) {
MT_THROW(
"FBX saving is not supported in OpenFBX-only mode. FBX loading is available via OpenFBX, but saving requires the full Autodesk FBX SDK.");
}

void saveFbxModel(
const filesystem::path& /* filename */,
const Character& /* character */,
const FbxCoordSystemInfo& /* coordSystemInfo */,
bool /* permissive */,
std::string_view /* fbxNamespace */) {
const FileSaveOptions& /* options */) {
MT_THROW(
"FBX saving is not supported in OpenFBX-only mode. FBX loading is available via OpenFBX, but saving requires the full Autodesk FBX SDK.");
}
Expand Down
Loading
Loading