Skip to content

Commit 63aa703

Browse files
committed
Add tests to python binding (#752)
Summary: Pull Request resolved: #752 Add python tests for marker sequence io in fbx and the unified save function. Reviewed By: jeongseok-meta Differential Revision: D85739418
1 parent cf536d9 commit 63aa703

File tree

5 files changed

+288
-61
lines changed

5 files changed

+288
-61
lines changed

momentum/io/fbx/fbx_io.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ std::vector<::fbxsdk::FbxNode*> createMarkerNodes(
318318
return markerNodes;
319319
}
320320

321+
// Set the framerate for the scene
322+
setFrameRate(scene, framerate);
323+
321324
// Create a root node for all markers
322325
::fbxsdk::FbxNode* markersRootNode = ::fbxsdk::FbxNode::Create(scene, "Markers");
323326
::fbxsdk::FbxNull* markersRootAttr = ::fbxsdk::FbxNull::Create(scene, "MarkersRootNull");

momentum/io/fbx/openfbx_loader.cpp

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,7 @@ parseMarkerSequence(const ofbx::IScene* scene, const ofbx::Object* root, const f
10581058

10591059
markersRoot = findMarkersRoot(root);
10601060
if (!markersRoot) {
1061+
// Return empty sequence with no frames
10611062
return result;
10621063
}
10631064

@@ -1076,12 +1077,14 @@ parseMarkerSequence(const ofbx::IScene* scene, const ofbx::Object* root, const f
10761077
}
10771078

10781079
if (markerNodes.empty()) {
1080+
// Return empty sequence with no frames
10791081
return result;
10801082
}
10811083

10821084
// Get animation data for all marker nodes
10831085
const ofbx::AnimationStack* animStack = scene->getAnimationStack(0);
10841086
if (!animStack) {
1087+
// Return empty sequence with no frames
10851088
return result;
10861089
}
10871090

@@ -1105,6 +1108,7 @@ parseMarkerSequence(const ofbx::IScene* scene, const ofbx::Object* root, const f
11051108
}
11061109

11071110
if (markerAnimCurves.empty()) {
1111+
// Return empty sequence with no frames
11081112
return result;
11091113
}
11101114

@@ -1131,35 +1135,46 @@ parseMarkerSequence(const ofbx::IScene* scene, const ofbx::Object* root, const f
11311135
}
11321136

11331137
if (translationCurve) {
1134-
// Collect all unique keyframe times across X, Y, Z channels
1138+
// Get the individual animation curves for X, Y, Z components
1139+
const ofbx::AnimationCurve* curveX = translationCurve->getCurve(0);
1140+
const ofbx::AnimationCurve* curveY = translationCurve->getCurve(1);
1141+
const ofbx::AnimationCurve* curveZ = translationCurve->getCurve(2);
1142+
1143+
// Collect all unique keyframe times from all three curves
11351144
std::set<ofbx::i64> keyframeTimes;
1136-
for (int iChannel = 0; iChannel < 3; ++iChannel) {
1137-
const ofbx::AnimationCurve* channel = translationCurve->getCurve(iChannel);
1138-
if (channel == nullptr) {
1139-
continue;
1145+
if (curveX) {
1146+
const int keyCount = curveX->getKeyCount();
1147+
const ofbx::i64* times = curveX->getKeyTime();
1148+
for (int i = 0; i < keyCount; ++i) {
1149+
keyframeTimes.insert(times[i]);
11401150
}
1141-
const int keyCount = channel->getKeyCount();
1142-
if (keyCount <= 0) {
1143-
continue;
1151+
}
1152+
if (curveY) {
1153+
const int keyCount = curveY->getKeyCount();
1154+
const ofbx::i64* times = curveY->getKeyTime();
1155+
for (int i = 0; i < keyCount; ++i) {
1156+
keyframeTimes.insert(times[i]);
11441157
}
1145-
const ofbx::i64* times = channel->getKeyTime();
1158+
}
1159+
if (curveZ) {
1160+
const int keyCount = curveZ->getKeyCount();
1161+
const ofbx::i64* times = curveZ->getKeyTime();
11461162
for (int i = 0; i < keyCount; ++i) {
11471163
keyframeTimes.insert(times[i]);
11481164
}
11491165
}
11501166

1151-
// For each unique keyframe time, add a marker at that frame
1152-
// This matches GLTF behavior: only add markers at explicitly keyed frames
1153-
for (ofbx::i64 fbxTime : keyframeTimes) {
1167+
// Add markers only at frames where keyframes exist
1168+
for (const ofbx::i64 fbxTime : keyframeTimes) {
11541169
const double timeInSeconds = ofbx::fbxTimeToSeconds(fbxTime);
1155-
const auto frameIndex = static_cast<size_t>(std::lround(timeInSeconds * fps));
1170+
const size_t frameIndex = std::round(timeInSeconds * fps);
11561171

11571172
// Skip if frame index is out of bounds
11581173
if (frameIndex >= numFrames) {
11591174
continue;
11601175
}
11611176

1162-
// Evaluate position at this specific keyframe time
1177+
// Evaluate position at this keyframe time
11631178
const ofbx::DVec3 position = translationCurve->getNodeLocalTransform(timeInSeconds);
11641179

11651180
Marker marker;

momentum/io/marker/marker_io.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,9 @@ int findMainSubjectIndex(std::span<const MarkerSequence> markerSequences) {
8787
}
8888

8989
// Special case when there's only one actor.
90+
// Return the actor even if it has no markers (empty sequence is valid).
9091
if (numActors == 1) {
91-
if (maxMarkers.at(0) > 0) {
92-
return 0;
93-
} else {
94-
return -1;
95-
}
92+
return 0;
9693
}
9794

9895
// find the max marker count with a name

pymomentum/geometry/momentum_io.cpp

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -167,26 +167,18 @@ void saveFBXCharacterToFile(
167167
const std::optional<const std::vector<std::vector<momentum::Marker>>>& markers,
168168
const std::optional<const momentum::FBXCoordSystemInfo>& coordSystemInfo,
169169
std::string_view fbxNamespace) {
170-
if (motion.has_value() && offsets.has_value()) {
171-
momentum::saveFbx(
172-
path,
173-
character,
174-
motion.value().transpose(),
175-
offsets.value(),
176-
fps,
177-
true, /*saveMesh*/
178-
coordSystemInfo.value_or(momentum::FBXCoordSystemInfo()),
179-
false, /*permissive*/
180-
markers.value_or(std::vector<std::vector<momentum::Marker>>{}),
181-
fbxNamespace);
182-
} else {
183-
momentum::saveFbxModel(
184-
path,
185-
character,
186-
coordSystemInfo.value_or(momentum::FBXCoordSystemInfo()),
187-
false, /*permissive*/
188-
fbxNamespace);
189-
}
170+
// Always use saveFbx to support markers even without motion
171+
momentum::saveFbx(
172+
path,
173+
character,
174+
motion.has_value() ? motion.value().transpose() : Eigen::MatrixXf(),
175+
offsets.has_value() ? offsets.value() : Eigen::VectorXf(),
176+
fps,
177+
true, /*saveMesh*/
178+
coordSystemInfo.value_or(momentum::FBXCoordSystemInfo()),
179+
false, /*permissive*/
180+
markers.value_or(std::vector<std::vector<momentum::Marker>>{}),
181+
fbxNamespace);
190182
}
191183

192184
void saveFBXCharacterToFileWithJointParams(
@@ -197,25 +189,17 @@ void saveFBXCharacterToFileWithJointParams(
197189
const std::optional<const std::vector<std::vector<momentum::Marker>>>& markers,
198190
const std::optional<const momentum::FBXCoordSystemInfo>& coordSystemInfo,
199191
std::string_view fbxNamespace) {
200-
if (jointParams.has_value()) {
201-
momentum::saveFbxWithJointParams(
202-
path,
203-
character,
204-
jointParams.value().transpose(),
205-
fps,
206-
true, /*saveMesh*/
207-
coordSystemInfo.value_or(momentum::FBXCoordSystemInfo()),
208-
false, /*permissive*/
209-
markers.value_or(std::vector<std::vector<momentum::Marker>>{}),
210-
fbxNamespace);
211-
} else {
212-
momentum::saveFbxModel(
213-
path,
214-
character,
215-
coordSystemInfo.value_or(momentum::FBXCoordSystemInfo()),
216-
false, /*permissive*/
217-
fbxNamespace);
218-
}
192+
// Always use saveFbxWithJointParams to support markers even without motion
193+
momentum::saveFbxWithJointParams(
194+
path,
195+
character,
196+
jointParams.has_value() ? jointParams.value().transpose() : Eigen::MatrixXf(),
197+
fps,
198+
true, /*saveMesh*/
199+
coordSystemInfo.value_or(momentum::FBXCoordSystemInfo()),
200+
false, /*permissive*/
201+
markers.value_or(std::vector<std::vector<momentum::Marker>>{}),
202+
fbxNamespace);
219203
}
220204

221205
void saveCharacterToFile(

0 commit comments

Comments
 (0)