Skip to content

Commit 6a6021f

Browse files
author
Fabien Servant
committed
Create USD Exporter
1 parent 9689331 commit 6a6021f

File tree

8 files changed

+762
-314
lines changed

8 files changed

+762
-314
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
__version__ = "1.0"
2+
3+
from meshroom.core import desc
4+
from meshroom.core.utils import VERBOSE_LEVEL
5+
6+
7+
class ExportMeshUSD(desc.AVCommandLineNode):
8+
commandLine = "aliceVision_exportMeshUSD {allParams}"
9+
size = desc.DynamicNodeSize("input")
10+
11+
category = "Export"
12+
documentation = """ Export a mesh (OBJ file) to USD format. """
13+
14+
inputs = [
15+
desc.File(
16+
name="input",
17+
label="Input",
18+
description="Input mesh file.",
19+
value="",
20+
),
21+
desc.ChoiceParam(
22+
name="fileType",
23+
label="USD File Format",
24+
description="Output USD file format.",
25+
value="usda",
26+
values=["usda", "usdc", "usdz"]
27+
),
28+
desc.ChoiceParam(
29+
name="verboseLevel",
30+
label="Verbose Level",
31+
description="Verbosity level (fatal, error, warning, info, debug, trace).",
32+
values=VERBOSE_LEVEL,
33+
value="info",
34+
),
35+
]
36+
37+
outputs = [
38+
desc.File(
39+
name="output",
40+
label="Output",
41+
description="Path to the output file.",
42+
value="{nodeCacheFolder}/output.{fileTypeValue}",
43+
),
44+
]

meshroom/aliceVision/ExportUSD.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,25 @@ class ExportUSD(desc.AVCommandLineNode):
88
commandLine = "aliceVision_exportUSD {allParams}"
99
size = desc.DynamicNodeSize("input")
1010

11-
category = "Utils"
12-
documentation = """ Export a mesh (OBJ file) to USD format. """
11+
category = "Export"
12+
documentation = """
13+
Convert cameras from an SfM scene into an animated camera in USD format.
14+
Based on the input image filenames, this node detects video sequences and creates the corresponding animated camera.
15+
"""
1316

1417
inputs = [
1518
desc.File(
1619
name="input",
17-
label="Input",
18-
description="Input mesh file.",
20+
label="Input SfMData",
21+
description="SfMData file containing a complete SfM.",
1922
value="",
2023
),
21-
desc.ChoiceParam(
22-
name="fileType",
23-
label="USD File Format",
24-
description="Output USD file format.",
25-
value="usda",
26-
values=["usda", "usdc", "usdz"]
24+
desc.FloatParam(
25+
name="frameRate",
26+
label="Camera Frame Rate",
27+
description="Define the camera's frames per second.",
28+
value=24.0,
29+
range=(1.0, 60.0, 1.0),
2730
),
2831
desc.ChoiceParam(
2932
name="verboseLevel",
@@ -37,8 +40,8 @@ class ExportUSD(desc.AVCommandLineNode):
3740
outputs = [
3841
desc.File(
3942
name="output",
40-
label="Output",
41-
description="Path to the output file.",
42-
value="{nodeCacheFolder}/output.{fileTypeValue}",
43-
),
43+
label="USD filename",
44+
description="Output usd filename",
45+
value="{nodeCacheFolder}/animated.usda",
46+
)
4447
]

src/aliceVision/sfmDataIO/CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ if (ALICEVISION_HAVE_ALEMBIC)
3737
)
3838
endif()
3939

40+
if (ALICEVISION_HAVE_USD)
41+
42+
list(APPEND sfmDataIO_files_headers
43+
UsdExporter.hpp
44+
)
45+
list(APPEND sfmDataIO_files_sources
46+
UsdExporter.cpp
47+
)
48+
endif()
49+
4050
alicevision_add_library(aliceVision_sfmDataIO
4151
SOURCES ${sfmDataIO_files_headers} ${sfmDataIO_files_sources}
4252
PUBLIC_LINKS
@@ -60,6 +70,17 @@ if (ALICEVISION_HAVE_ALEMBIC)
6070
)
6171
endif()
6272

73+
if (ALICEVISION_HAVE_USD)
74+
target_link_libraries(aliceVision_sfmDataIO
75+
PRIVATE usd
76+
usdGeom
77+
gf
78+
tf
79+
vt
80+
sdf
81+
)
82+
endif()
83+
6384
# Unit tests
6485

6586
alicevision_add_test(sfmDataIO_test.cpp
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 AliceVision contributors.
3+
// This Source Code Form is subject to the terms of the Mozilla Public License,
4+
// v. 2.0. If a copy of the MPL was not distributed with this file,
5+
// You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
#include <aliceVision/sfmDataIO/UsdExporter.hpp>
8+
#include <aliceVision/camera/IntrinsicScaleOffset.hpp>
9+
#include <aliceVision/system/Logger.hpp>
10+
11+
#include <pxr/usd/usd/stage.h>
12+
13+
#include <pxr/usd/usdGeom/xform.h>
14+
#include <pxr/usd/usdGeom/points.h>
15+
#include <pxr/usd/usdGeom/camera.h>
16+
17+
#include <pxr/base/gf/vec3f.h>
18+
#include <pxr/base/vt/array.h>
19+
20+
21+
PXR_NAMESPACE_USING_DIRECTIVE
22+
23+
namespace aliceVision {
24+
namespace sfmDataIO {
25+
26+
27+
UsdExporter::UsdExporter(const std::string & filename, double frameRate)
28+
{
29+
_stage = UsdStage::CreateNew(filename);
30+
if (_stage)
31+
{
32+
ALICEVISION_THROW_ERROR("UsdStage::CreateNew failed");
33+
}
34+
35+
UsdGeomXform worldPrim = UsdGeomXform::Define(_stage, SdfPath("/World"));
36+
37+
_stage->SetTimeCodesPerSecond(frameRate);
38+
_stage->SetFramesPerSecond(frameRate);
39+
_startTimeCode = std::numeric_limits<IndexT>::max();
40+
_endTimeCode = 0;
41+
}
42+
43+
void UsdExporter::terminate()
44+
{
45+
// If no frames have been exported, _startTimeCode will still be at its sentinel value.
46+
// In that case, avoid writing invalid start/end time codes to the USD stage.
47+
if (_startTimeCode == std::numeric_limits<IndexT>::max())
48+
{
49+
ALICEVISION_LOG_WARNING("UsdExporter::terminate called but no frames have been exported; "
50+
"skipping start/end time code settings.");
51+
}
52+
else
53+
{
54+
_stage->SetStartTimeCode(_startTimeCode);
55+
_stage->SetEndTimeCode(_endTimeCode);
56+
}
57+
58+
_stage->Save();
59+
}
60+
61+
void UsdExporter::createNewCamera(const std::string & cameraName)
62+
{
63+
SdfPath cameraPath("/World/" + cameraName);
64+
UsdGeomCamera camera = UsdGeomCamera::Define(_stage, cameraPath);
65+
66+
UsdAttribute projectionAttr = camera.GetProjectionAttr();
67+
projectionAttr.Set(UsdGeomTokens->perspective);
68+
69+
UsdGeomXformable xformable(camera);
70+
UsdGeomXformOp motion = xformable.MakeMatrixXform();
71+
GfMatrix4d identity(1.0);
72+
motion.Set(identity);
73+
}
74+
75+
void UsdExporter::addFrame(const std::string & cameraName, const sfmData::CameraPose & pose, const camera::Pinhole & intrinsic, IndexT frameId)
76+
{
77+
SdfPath cameraPath("/World/" + cameraName);
78+
UsdGeomCamera camera = UsdGeomCamera::Get(_stage, cameraPath);
79+
80+
_startTimeCode = std::min(_startTimeCode, frameId);
81+
_endTimeCode = std::max(_endTimeCode, frameId);
82+
83+
UsdAttribute focalLengthAttr = camera.GetFocalLengthAttr();
84+
UsdAttribute horizontalApertureAttr = camera.GetHorizontalApertureAttr();
85+
UsdAttribute verticalApertureAttr = camera.GetVerticalApertureAttr();
86+
UsdAttribute horizontalApertureOffsetAttr = camera.GetHorizontalApertureOffsetAttr();
87+
UsdAttribute verticalApertureOffsetAttr = camera.GetVerticalApertureOffsetAttr();
88+
89+
horizontalApertureAttr.Set(static_cast<float>(intrinsic.sensorWidth()));
90+
verticalApertureAttr.Set(static_cast<float>(intrinsic.sensorHeight()));
91+
92+
UsdTimeCode t(frameId);
93+
double pixToMillimeters = intrinsic.sensorWidth() / intrinsic.w();
94+
95+
horizontalApertureOffsetAttr.Set(static_cast<float>(intrinsic.getOffset().x() * pixToMillimeters), t);
96+
verticalApertureOffsetAttr.Set(static_cast<float>(intrinsic.getOffset().y() * pixToMillimeters), t);
97+
focalLengthAttr.Set(static_cast<float>(intrinsic.getFocalLength()), t);
98+
99+
100+
101+
//Transform sfmData pose to usd pose
102+
Eigen::Matrix4d glTransform = Eigen::Matrix4d::Identity();
103+
glTransform(1, 1) = -1.0;
104+
glTransform(2, 2) = -1.0;
105+
106+
// Inverse the pose and change the geometric frame
107+
Eigen::Matrix4d camera_T_world = pose.getTransform().getHomogeneous();
108+
Eigen::Matrix4d world_T_camera = camera_T_world.inverse();
109+
Eigen::Matrix4d world_gl_T_camera_gl = glTransform * world_T_camera * glTransform;
110+
111+
//Copy element by element while transposing
112+
GfMatrix4d usdT;
113+
for (int i = 0; i < 4; i++)
114+
{
115+
for (int j = 0; j < 4; j++)
116+
{
117+
usdT[j][i] = world_gl_T_camera_gl(i, j);
118+
}
119+
}
120+
121+
//Assign pose to motion
122+
UsdGeomXformable xformable(camera);
123+
bool dummy = false;
124+
std::vector<UsdGeomXformOp> xformOps = xformable.GetOrderedXformOps(&dummy);
125+
126+
127+
if (!xformOps.empty()) {
128+
UsdGeomXformOp motion = xformOps[0];
129+
motion.Set(usdT, t);
130+
}
131+
}
132+
133+
} // namespace sfmDataIO
134+
} // namespace aliceVision
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2025 AliceVision contributors.
3+
// This Source Code Form is subject to the terms of the Mozilla Public License,
4+
// v. 2.0. If a copy of the MPL was not distributed with this file,
5+
// You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
#pragma once
8+
9+
#include <aliceVision/sfmData/SfMData.hpp>
10+
#include <aliceVision/camera/Pinhole.hpp>
11+
#include <pxr/usd/usd/stage.h>
12+
13+
namespace aliceVision {
14+
namespace sfmDataIO {
15+
16+
/**
17+
* @brief Export SfM data to USD (Universal Scene Description) format.
18+
*
19+
* The UsdExporter class provides functionality to export camera poses and intrinsics
20+
* from AliceVision's SfM data to Pixar's USD format, enabling integration with
21+
* USD-based pipelines and DCC applications.
22+
*/
23+
class UsdExporter
24+
{
25+
public:
26+
/**
27+
* @brief Construct a new USD Exporter.
28+
*
29+
* @param filename The output USD file path
30+
* @param frameRate The frame rate for the animation timeline (e.g., 24.0 for 24 fps)
31+
*/
32+
UsdExporter(const std::string & filename, double frameRate);
33+
34+
/**
35+
* @brief Create a new camera in the USD stage.
36+
*
37+
* Creates a USD camera primitive with the specified name. This camera
38+
* can then be animated using addFrame().
39+
*
40+
* @param cameraName The unique name for the camera in the USD stage
41+
*/
42+
void createNewCamera(const std::string & cameraName);
43+
44+
/**
45+
* @brief Add a frame of camera data (pose and intrinsics) to an existing camera.
46+
*
47+
* Records the camera's pose and intrinsic parameters at a specific frame,
48+
* creating keyframe animation data in the USD file.
49+
*
50+
* @param cameraName The name of the camera to add the frame to
51+
* @param pose The camera pose (position and orientation) for this frame
52+
* @param camera The pinhole camera model containing intrinsic parameters
53+
* @param frameId The frame index/time code for this keyframe
54+
*/
55+
void addFrame(const std::string & cameraName, const sfmData::CameraPose & pose, const camera::Pinhole & camera, IndexT frameId);
56+
57+
/**
58+
* @brief Finalize and save the USD file.
59+
*
60+
* Completes the USD export process and writes the data to disk.
61+
* Should be called after all cameras and frames have been added.
62+
*/
63+
void terminate();
64+
65+
private:
66+
pxr::UsdStageRefPtr _stage; ///< USD stage containing the scene hierarchy
67+
IndexT _startTimeCode; ///< First frame index in the animation timeline
68+
IndexT _endTimeCode; ///< Last frame index in the animation timeline
69+
};
70+
71+
} // namespace sfmDataIO
72+
} // namespace aliceVision

src/software/export/CMakeLists.txt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ if (ALICEVISION_BUILD_SFM)
202202

203203
# Export geometry and textures as USD
204204
if (ALICEVISION_HAVE_USD)
205-
alicevision_add_software(aliceVision_exportUSD
206-
SOURCE main_exportUSD.cpp
205+
alicevision_add_software(aliceVision_exportMeshUSD
206+
SOURCE main_exportMeshUSD.cpp
207207
FOLDER ${FOLDER_SOFTWARE_EXPORT}
208208
LINKS aliceVision_system
209209
aliceVision_cmdline
@@ -214,6 +214,18 @@ if (ALICEVISION_BUILD_SFM)
214214
usdImaging
215215
usdShade
216216
)
217+
218+
alicevision_add_software(aliceVision_exportUSD
219+
SOURCE main_exportUSD.cpp
220+
FOLDER ${FOLDER_SOFTWARE_EXPORT}
221+
LINKS aliceVision_system
222+
aliceVision_cmdline
223+
aliceVision_sfmData
224+
aliceVision_sfmDataIO
225+
Boost::program_options
226+
Boost::boost
227+
usd
228+
)
217229
endif()
218230

219231
# Export distortion to be used in external tools

0 commit comments

Comments
 (0)