Skip to content

Commit a39754d

Browse files
yutingyefacebook-github-bot
authored andcommitted
Add unified saveCharacter function for FBX/GLTF parity (#753)
Summary: After D85530152 added marker sequence support to fbx, we have functionality parity between fbx and gltf. This diff created a unified `saveCharacterToFile()` function in `character_io` that automatically selects between FBX and GLTF export based on file extension. Added .fbx support loadMarkers as the unified function for loading marker data from any supported format. Differential Revision: D85517572
1 parent 3960245 commit a39754d

File tree

5 files changed

+97
-0
lines changed

5 files changed

+97
-0
lines changed

momentum/io/character_io.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,43 @@ Character loadFullCharacterFromBuffer(
139139
return character.value();
140140
}
141141

142+
void saveCharacterToFile(
143+
const filesystem::path& filename,
144+
const Character& character,
145+
const MatrixXf& motion,
146+
const VectorXf& offsets,
147+
const std::vector<std::vector<Marker>>& markerSequence,
148+
float fps) {
149+
// Parse format from file extension
150+
const auto format = parseCharacterFormat(filename);
151+
MT_THROW_IF(
152+
format == CharacterFormat::Unknown,
153+
"Unknown character format for path: {}. Supported formats: .fbx, .glb, .gltf",
154+
filename.string());
155+
156+
if (format == CharacterFormat::Gltf) {
157+
saveCharacter(
158+
filename,
159+
character,
160+
fps,
161+
{character.parameterTransform.name, motion},
162+
{character.skeleton.getJointNames(), offsets},
163+
markerSequence);
164+
} else if (format == CharacterFormat::Fbx) {
165+
// Save as FBX
166+
saveFbx(
167+
filename,
168+
character,
169+
motion,
170+
offsets,
171+
static_cast<double>(fps),
172+
true, // saveMesh
173+
FBXCoordSystemInfo(),
174+
false, // permissive
175+
markerSequence);
176+
} else if (format == CharacterFormat::Usd) {
177+
MT_THROW("USD format is not yet supported for saving. Supported formats: .fbx, .glb, .gltf");
178+
}
179+
}
180+
142181
} // namespace momentum

momentum/io/character_io.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
#pragma once
99

1010
#include <momentum/character/fwd.h>
11+
#include <momentum/character/marker.h>
12+
#include <momentum/common/filesystem.h>
1113
#include <momentum/math/fwd.h>
1214
#include <momentum/math/types.h>
1315

1416
#include <string>
17+
#include <vector>
1518

1619
namespace momentum {
1720

@@ -53,4 +56,21 @@ enum class CharacterFormat : uint8_t {
5356
gsl::span<const std::byte> paramBuffer = gsl::span<const std::byte>(),
5457
gsl::span<const std::byte> locBuffer = gsl::span<const std::byte>());
5558

59+
/// High level function to save a character with motion and markers to any supported format.
60+
///
61+
/// The format is determined by the file extension (.fbx, .glb, .gltf).
62+
/// This is a unified interface that automatically selects between FBX and GLTF based on extension.
63+
/// @param[in] filename The path where the character file will be saved.
64+
/// @param[in] character The Character object to save.
65+
/// @param[in] motion The motion represented in model parameters (numModelParams, numFrames).
66+
/// @param[in] offsets Offset values per joint capturing skeleton bone lengths (7*numJoints, 1).
67+
/// @param[in] markerSequence Optional marker sequence data to save with the character.
68+
/// @param[in] fps Frame rate for the animation (default: 120.0).
69+
void saveCharacterToFile(
70+
const filesystem::path& filename,
71+
const Character& character,
72+
const MatrixXf& motion = MatrixXf(),
73+
const VectorXf& offsets = VectorXf(),
74+
const std::vector<std::vector<Marker>>& markerSequence = {},
75+
float fps = 120.0f);
5676
} // namespace momentum

momentum/io/marker/marker_io.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "momentum/character/marker.h"
1111
#include "momentum/common/filesystem.h"
1212
#include "momentum/common/log.h"
13+
#include "momentum/io/fbx/fbx_io.h"
1314
#include "momentum/io/gltf/gltf_io.h"
1415
#include "momentum/io/marker/c3d_io.h"
1516
#include "momentum/io/marker/trc_io.h"
@@ -40,6 +41,8 @@ std::vector<MarkerSequence> loadMarkers(
4041
return {loadTrc(filename, up)};
4142
} else if (ext == ".glb") {
4243
return {loadMarkerSequence(filename)};
44+
} else if (ext == ".fbx") {
45+
return {loadFbxMarkerSequence(filename)};
4346
} else {
4447
MT_LOGE("{} Unknown marker file type {}", __func__, filename);
4548
return {};

pymomentum/geometry/momentum_io.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@
1717
#include <momentum/io/gltf/gltf_io.h>
1818
#include <momentum/io/marker/marker_io.h>
1919

20+
// Forward declaration for unified save function
21+
namespace momentum {
22+
void saveCharacterToFile(
23+
const filesystem::path& filename,
24+
const Character& character,
25+
const MatrixXf& motion,
26+
const VectorXf& offsets,
27+
const std::vector<std::vector<Marker>>& markerSequence,
28+
float fps);
29+
} // namespace momentum
30+
2031
namespace pymomentum {
2132

2233
momentum::Character loadGLTFCharacterFromFile(const std::string& path) {
@@ -217,6 +228,22 @@ void saveFBXCharacterToFileWithJointParams(
217228
}
218229
}
219230

231+
void saveCharacterToFile(
232+
const std::string& path,
233+
const momentum::Character& character,
234+
const float fps,
235+
const std::optional<const Eigen::MatrixXf>& motion,
236+
const std::optional<const Eigen::VectorXf>& offsets,
237+
const std::optional<const std::vector<std::vector<momentum::Marker>>>& markers) {
238+
momentum::saveCharacterToFile(
239+
path,
240+
character,
241+
motion.has_value() ? motion.value().transpose() : Eigen::MatrixXf{},
242+
offsets.value_or(Eigen::VectorXf{}),
243+
markers.value_or(std::vector<std::vector<momentum::Marker>>{}),
244+
fps);
245+
}
246+
220247
std::tuple<momentum::Character, RowMatrixf, Eigen::VectorXf, float> loadGLTFCharacterWithMotion(
221248
const std::string& gltfFilename) {
222249
const auto [character, motion, identity, fps] = momentum::loadCharacterWithMotion(gltfFilename);

pymomentum/geometry/momentum_io.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ void saveFBXCharacterToFileWithJointParams(
7272
std::optional<const momentum::FBXCoordSystemInfo> coordSystemInfo,
7373
std::string_view fbxNamespace = "");
7474

75+
void saveCharacterToFile(
76+
const std::string& path,
77+
const momentum::Character& character,
78+
float fps,
79+
const std::optional<const Eigen::MatrixXf>& motion,
80+
const std::optional<const Eigen::VectorXf>& offsets,
81+
const std::optional<const std::vector<std::vector<momentum::Marker>>>& markers);
82+
7583
std::tuple<momentum::Character, RowMatrixf, Eigen::VectorXf, float> loadGLTFCharacterWithMotion(
7684
const std::string& gltfFilename);
7785

0 commit comments

Comments
 (0)