Blender V4.3
usd_curves_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "testing/testing.h"
7
8#include <pxr/base/plug/registry.h>
9#include <pxr/base/tf/stringUtils.h>
10#include <pxr/base/vt/types.h>
11#include <pxr/base/vt/value.h>
12#include <pxr/usd/sdf/types.h>
13#include <pxr/usd/usd/object.h>
14#include <pxr/usd/usd/prim.h>
15#include <pxr/usd/usd/stage.h>
16#include <pxr/usd/usdGeom/basisCurves.h>
17#include <pxr/usd/usdGeom/curves.h>
18#include <pxr/usd/usdGeom/mesh.h>
19#include <pxr/usd/usdGeom/nurbsCurves.h>
20#include <pxr/usd/usdGeom/subset.h>
21#include <pxr/usd/usdGeom/tokens.h>
22
23#include "DNA_material_types.h"
24#include "DNA_node_types.h"
25
26#include "BKE_context.hh"
27#include "BKE_lib_id.hh"
28#include "BKE_main.hh"
29#include "BKE_mesh.hh"
30#include "BKE_node.hh"
31#include "BLI_fileops.h"
33#include "BLI_path_utils.hh"
34#include "BLO_readfile.hh"
35
36#include "BKE_node_runtime.hh"
37
38#include "DEG_depsgraph.hh"
39
40#include "WM_api.hh"
41
42#include "usd.hh"
43
44namespace blender::io::usd {
45
46const StringRefNull usd_curves_test_filename = "usd/usd_curves_test.blend";
47const StringRefNull output_filename = "usd/output.usda";
48
49static void check_catmullRom_curve(const pxr::UsdPrim prim,
50 const bool is_periodic,
51 const int vertex_count);
52static void check_bezier_curve(const pxr::UsdPrim bezier_prim,
53 const bool is_periodic,
54 const int vertex_count);
55static void check_nurbs_curve(const pxr::UsdPrim nurbs_prim,
56 const int vertex_count,
57 const int knots_count,
58 const int order);
59static void check_nurbs_circle(const pxr::UsdPrim nurbs_prim,
60 const int vertex_count,
61 const int knots_count,
62 const int order);
63
65 protected:
66 bContext *context = nullptr;
67
68 public:
70 const eEvaluationMode eval_mode = DAG_EVAL_VIEWPORT)
71 {
72 if (!blendfile_load(filepath.c_str())) {
73 return false;
74 }
75 depsgraph_create(eval_mode);
76
77 context = CTX_create();
78 CTX_data_main_set(context, bfile->main);
80
81 return true;
82 }
83
84 virtual void SetUp() override
85 {
86 BlendfileLoadingBaseTest::SetUp();
87 }
88
89 virtual void TearDown() override
90 {
92 CTX_free(context);
93 context = nullptr;
94
96 BLI_delete(output_filename.c_str(), false, false);
97 }
98 }
99};
100
101TEST_F(UsdCurvesTest, usd_export_curves)
102{
103 if (!load_file_and_depsgraph(usd_curves_test_filename)) {
104 ADD_FAILURE();
105 return;
106 }
107
108 /* File sanity check. */
109 EXPECT_EQ(BLI_listbase_count(&bfile->main->objects), 6);
110
112
113 const bool result = USD_export(context, output_filename.c_str(), &params, false, nullptr);
114 EXPECT_TRUE(result) << "USD export should succed.";
115
116 pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(output_filename);
117 ASSERT_NE(stage, nullptr) << "Stage should not be null after opening usd file.";
118
119 {
120 std::string prim_name = pxr::TfMakeValidIdentifier("BezierCurve");
121 pxr::UsdPrim test_prim = stage->GetPrimAtPath(pxr::SdfPath("/BezierCurve/" + prim_name));
122 EXPECT_TRUE(test_prim.IsValid());
123 check_bezier_curve(test_prim, false, 7);
124 }
125
126 {
127 std::string prim_name = pxr::TfMakeValidIdentifier("BezierCircle");
128 pxr::UsdPrim test_prim = stage->GetPrimAtPath(pxr::SdfPath("/BezierCircle/" + prim_name));
129 EXPECT_TRUE(test_prim.IsValid());
130 check_bezier_curve(test_prim, true, 12);
131 }
132
133 {
134 std::string prim_name = pxr::TfMakeValidIdentifier("NurbsCurve");
135 pxr::UsdPrim test_prim = stage->GetPrimAtPath(pxr::SdfPath("/NurbsCurve/" + prim_name));
136 EXPECT_TRUE(test_prim.IsValid());
137 check_nurbs_curve(test_prim, 6, 20, 4);
138 }
139
140 {
141 std::string prim_name = pxr::TfMakeValidIdentifier("NurbsCircle");
142 pxr::UsdPrim test_prim = stage->GetPrimAtPath(pxr::SdfPath("/NurbsCircle/" + prim_name));
143 EXPECT_TRUE(test_prim.IsValid());
144 check_nurbs_circle(test_prim, 8, 13, 3);
145 }
146
147 {
148 std::string prim_name = pxr::TfMakeValidIdentifier("Curves");
149 pxr::UsdPrim test_prim = stage->GetPrimAtPath(pxr::SdfPath("/Cube/Curves/" + prim_name));
150 EXPECT_TRUE(test_prim.IsValid());
151 check_catmullRom_curve(test_prim, false, 8);
152 }
153}
154
159static void check_catmullRom_curve(const pxr::UsdPrim prim,
160 const bool is_periodic,
161 const int vertex_count)
162{
163 auto curve = pxr::UsdGeomBasisCurves(prim);
164
165 pxr::VtValue basis;
166 pxr::UsdAttribute basis_attr = curve.GetBasisAttr();
167 basis_attr.Get(&basis);
168 auto basis_token = basis.Get<pxr::TfToken>();
169
170 EXPECT_EQ(basis_token, pxr::UsdGeomTokens->catmullRom)
171 << "Basis token should be catmullRom for catmullRom curve";
172
173 pxr::VtValue type;
174 pxr::UsdAttribute type_attr = curve.GetTypeAttr();
175 type_attr.Get(&type);
176 auto type_token = type.Get<pxr::TfToken>();
177
178 EXPECT_EQ(type_token, pxr::UsdGeomTokens->cubic)
179 << "Type token should be cubic for catmullRom curve";
180
181 pxr::VtValue wrap;
182 pxr::UsdAttribute wrap_attr = curve.GetWrapAttr();
183 wrap_attr.Get(&wrap);
184 auto wrap_token = wrap.Get<pxr::TfToken>();
185
186 if (is_periodic) {
187 EXPECT_EQ(wrap_token, pxr::UsdGeomTokens->periodic)
188 << "Wrap token should be periodic for periodic curve";
189 }
190 else {
191 EXPECT_EQ(wrap_token, pxr::UsdGeomTokens->pinned)
192 << "Wrap token should be pinned for nonperiodic catmullRom curve";
193 }
194
195 pxr::UsdAttribute vert_count_attr = curve.GetCurveVertexCountsAttr();
196 pxr::VtArray<int> vert_counts;
197 vert_count_attr.Get(&vert_counts);
198
199 EXPECT_EQ(vert_counts.size(), 3) << "Prim should contain verts for three curves";
200 EXPECT_EQ(vert_counts[0], vertex_count) << "Curve 0 should have " << vertex_count << " verts.";
201 EXPECT_EQ(vert_counts[1], vertex_count) << "Curve 1 should have " << vertex_count << " verts.";
202 EXPECT_EQ(vert_counts[2], vertex_count) << "Curve 2 should have " << vertex_count << " verts.";
203}
204
209static void check_bezier_curve(const pxr::UsdPrim bezier_prim,
210 const bool is_periodic,
211 const int vertex_count)
212{
213 auto curve = pxr::UsdGeomBasisCurves(bezier_prim);
214
215 pxr::VtValue basis;
216 pxr::UsdAttribute basis_attr = curve.GetBasisAttr();
217 basis_attr.Get(&basis);
218 auto basis_token = basis.Get<pxr::TfToken>();
219
220 EXPECT_EQ(basis_token, pxr::UsdGeomTokens->bezier)
221 << "Basis token should be bezier for bezier curve";
222
223 pxr::VtValue type;
224 pxr::UsdAttribute type_attr = curve.GetTypeAttr();
225 type_attr.Get(&type);
226 auto type_token = type.Get<pxr::TfToken>();
227
228 EXPECT_EQ(type_token, pxr::UsdGeomTokens->cubic)
229 << "Type token should be cubic for bezier curve";
230
231 pxr::VtValue wrap;
232 pxr::UsdAttribute wrap_attr = curve.GetWrapAttr();
233 wrap_attr.Get(&wrap);
234 auto wrap_token = wrap.Get<pxr::TfToken>();
235
236 if (is_periodic) {
237 EXPECT_EQ(wrap_token, pxr::UsdGeomTokens->periodic)
238 << "Wrap token should be periodic for periodic curve";
239 }
240 else {
241 EXPECT_EQ(wrap_token, pxr::UsdGeomTokens->nonperiodic)
242 << "Wrap token should be nonperiodic for nonperiodic curve";
243 }
244
245 auto widths_interp_token = curve.GetWidthsInterpolation();
246 EXPECT_EQ(widths_interp_token, pxr::UsdGeomTokens->varying)
247 << "Widths interpolation token should be varying for bezier curve";
248
249 pxr::UsdAttribute vert_count_attr = curve.GetCurveVertexCountsAttr();
250 pxr::VtArray<int> vert_counts;
251 vert_count_attr.Get(&vert_counts);
252
253 EXPECT_EQ(vert_counts.size(), 1) << "Prim should only contains verts for a single curve";
254 EXPECT_EQ(vert_counts[0], vertex_count) << "Curve should have " << vertex_count << " verts.";
255}
256
262static void check_nurbs_curve(const pxr::UsdPrim nurbs_prim,
263 const int vertex_count,
264 const int knots_count,
265 const int order)
266{
267 auto curve = pxr::UsdGeomNurbsCurves(nurbs_prim);
268
269 pxr::UsdAttribute order_attr = curve.GetOrderAttr();
270 pxr::VtArray<int> orders;
271 order_attr.Get(&orders);
272
273 EXPECT_EQ(orders.size(), 2) << "Prim should contain orders for two curves";
274 EXPECT_EQ(orders[0], order) << "Curves should have order " << order;
275 EXPECT_EQ(orders[1], order) << "Curves should have order " << order;
276
277 pxr::UsdAttribute knots_attr = curve.GetKnotsAttr();
278 pxr::VtArray<double> knots;
279 knots_attr.Get(&knots);
280
281 EXPECT_EQ(knots.size(), knots_count) << "Curve should have " << knots_count << " knots.";
282 for (int i = 0; i < 2; i++) {
283 int zeroth_knot_index = i * (knots_count / 2);
284
285 EXPECT_EQ(knots[zeroth_knot_index], knots[zeroth_knot_index + 1])
286 << "NURBS curve should satisfy this knots rule for a nonperiodic curve";
287 EXPECT_EQ(knots[knots.size() - 1], knots[knots.size() - 2])
288 << "NURBS curve should satisfy this knots rule for a nonperiodic curve";
289 }
290
291 auto widths_interp_token = curve.GetWidthsInterpolation();
292 EXPECT_EQ(widths_interp_token, pxr::UsdGeomTokens->vertex)
293 << "Widths interpolation token should be vertex for NURBS curve";
294
295 pxr::UsdAttribute vert_count_attr = curve.GetCurveVertexCountsAttr();
296 pxr::VtArray<int> vert_counts;
297 vert_count_attr.Get(&vert_counts);
298
299 EXPECT_EQ(vert_counts.size(), 2) << "Prim should contain verts for two curves";
300 EXPECT_EQ(vert_counts[0], vertex_count) << "Curve should have " << vertex_count << " verts.";
301 EXPECT_EQ(vert_counts[1], vertex_count) << "Curve should have " << vertex_count << " verts.";
302}
303
309static void check_nurbs_circle(const pxr::UsdPrim nurbs_prim,
310 const int vertex_count,
311 const int knots_count,
312 const int order)
313{
314 auto curve = pxr::UsdGeomNurbsCurves(nurbs_prim);
315
316 pxr::UsdAttribute order_attr = curve.GetOrderAttr();
317 pxr::VtArray<int> orders;
318 order_attr.Get(&orders);
319
320 EXPECT_EQ(orders.size(), 1) << "Prim should contain orders for one curves";
321 EXPECT_EQ(orders[0], order) << "Curve should have order " << order;
322
323 pxr::UsdAttribute knots_attr = curve.GetKnotsAttr();
324 pxr::VtArray<double> knots;
325 knots_attr.Get(&knots);
326
327 EXPECT_EQ(knots.size(), knots_count) << "Curve should have " << knots_count << " knots.";
328
329 EXPECT_EQ(knots[0], knots[1] - (knots[knots.size() - 2] - knots[knots.size() - 3]))
330 << "NURBS curve should satisfy this knots rule for a periodic curve";
331 EXPECT_EQ(knots[knots.size() - 1], knots[knots.size() - 2] + (knots[2] - knots[1]))
332 << "NURBS curve should satisfy this knots rule for a periodic curve";
333
334 auto widths_interp_token = curve.GetWidthsInterpolation();
335 EXPECT_EQ(widths_interp_token, pxr::UsdGeomTokens->vertex)
336 << "Widths interpolation token should be vertex for NURBS curve";
337
338 pxr::UsdAttribute vert_count_attr = curve.GetCurveVertexCountsAttr();
339 pxr::VtArray<int> vert_counts;
340 vert_count_attr.Get(&vert_counts);
341
342 EXPECT_EQ(vert_counts.size(), 1) << "Prim should contain verts for one curve";
343 EXPECT_EQ(vert_counts[0], vertex_count) << "Curve should have " << vertex_count << " verts.";
344}
345
346} // namespace blender::io::usd
void CTX_data_main_set(bContext *C, Main *bmain)
void CTX_free(bContext *C)
void CTX_data_scene_set(bContext *C, Scene *scene)
bContext * CTX_create()
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
external readfile function prototypes.
eEvaluationMode
@ DAG_EVAL_VIEWPORT
virtual void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode)
bool blendfile_load(const char *filepath)
constexpr const char * c_str() const
virtual void TearDown() override
bool load_file_and_depsgraph(const StringRefNull &filepath, const eEvaluationMode eval_mode=DAG_EVAL_VIEWPORT)
virtual void SetUp() override
EvaluationStage stage
Definition deg_eval.cc:83
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static void check_catmullRom_curve(const pxr::UsdPrim prim, const bool is_periodic, const int vertex_count)
static void check_nurbs_circle(const pxr::UsdPrim nurbs_prim, const int vertex_count, const int knots_count, const int order)
bool USD_export(const bContext *C, const char *filepath, const USDExportParams *params, bool as_background_job, ReportList *reports)
const StringRefNull output_filename
TEST_F(UsdCurvesTest, usd_export_curves)
static void check_bezier_curve(const pxr::UsdPrim bezier_prim, const bool is_periodic, const int vertex_count)
static void check_nurbs_curve(const pxr::UsdPrim nurbs_prim, const int vertex_count, const int knots_count, const int order)
const StringRefNull usd_curves_test_filename
float wrap(float value, float max, float min)
Definition node_math.h:71