Blender V5.0
obj_nurbs_io_tests.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023-2025 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4#include "BLI_array_utils.hh"
5#include "BLI_string.h"
6
7#include "BKE_appdir.hh"
8#include "BKE_curves.hh"
10#include "BKE_idtype.hh"
11
13#include "obj_export_nurbs.hh"
14#include "obj_exporter.hh"
15#include "obj_importer.hh"
16
17#include "CLG_log.h"
18#include "testing/testing.h"
19
20namespace blender::io::obj {
21
22static OBJExportParams default_export_params(const std::string &filepath)
23{
25 params.forward_axis = eIOAxis::IO_AXIS_Y;
26 params.up_axis = eIOAxis::IO_AXIS_Z;
27 STRNCPY(params.filepath, filepath.c_str());
28 return params;
29}
30
31static OBJImportParams default_import_params(const std::string &filepath)
32{
34 params.forward_axis = eIOAxis::IO_AXIS_Y;
35 params.up_axis = eIOAxis::IO_AXIS_Z;
36 STRNCPY(params.filepath, filepath.c_str());
37 return params;
38}
39
40class OBJCurvesTest : public testing::Test {
41 public:
42 static void SetUpTestSuite()
43 {
44 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
45 CLG_init();
46
47 /* Might not be necessary but... */
49 }
50
51 static void TearDownTestSuite()
52 {
53 CLG_exit();
54 }
55
56 void write_curves(const Span<std::unique_ptr<IOBJCurve>> curves, OBJExportParams params)
57 {
58 export_objects(params, Span<std::unique_ptr<OBJMesh>>(nullptr, 0), curves, params.filepath);
59 }
60
61 void write_curves(const std::unique_ptr<IOBJCurve> &curve, OBJExportParams params)
62 {
63 Span<std::unique_ptr<IOBJCurve>> span(&curve, 1);
64 write_curves(span, params);
65 }
66
68 {
69 float4x4 identity = float4x4::identity();
70 std::unique_ptr<IOBJCurve> curve_wrapper(new OBJCurves(curve, identity, "test"));
71 write_curves(curve_wrapper, params);
72 }
73
80
82 {
83 bke::CurvesGeometry curves(points.size(), 1);
84 curves.offsets_for_write()[0] = 0;
85 curves.offsets_for_write()[1] = points.size();
86 curves.cyclic_for_write()[0] = cyclic;
87 curves.positions_for_write().copy_from(points);
88 return curves;
89 }
90
92 Span<float3> points, Span<float> weights, bool cyclic, int8_t order, KnotsMode mode)
93 {
94 bke::CurvesGeometry curves = create_curves(points, cyclic);
95 curves.nurbs_orders_for_write()[0] = order;
96 curves.nurbs_knots_modes_for_write()[0] = int8_t(mode);
97 curves.nurbs_weights_for_write().copy_from(weights);
98
99 return curves;
100 }
101
103 bool cyclic,
104 int8_t order,
105 KnotsMode mode)
106 {
107 bke::CurvesGeometry curves = create_curves(points, cyclic);
108 curves.nurbs_orders_for_write()[0] = order;
109 curves.nurbs_knots_modes_for_write()[0] = int8_t(mode);
110 curves.nurbs_weights_for_write().fill(1.0f);
111
112 return curves;
113 }
114
115 void run_nurbs_test(const Span<float3> points,
116 const int8_t order,
117 const KnotsMode mode,
118 const bool cyclic,
119 bke::CurvesGeometry &src_curve,
120 const bke::CurvesGeometry *&result_curve,
121 Span<float3> expected_points = Span<float3>(),
122 const KnotsMode *expected_mode = nullptr,
123 const bool *expected_cyclic = nullptr)
124 {
125 BKE_tempdir_init(nullptr);
126 std::string tempdir = std::string(BKE_tempdir_base());
127 std::string out_file_path = tempdir + BLI_path_basename("io_obj/tmp_6f5273f4.obj");
128
129 /* Write/Read */
130 src_curve = OBJCurvesTest::create_nurbs(points, cyclic, order, mode);
131 ASSERT_TRUE(src_curve.cyclic()[0] == cyclic); /* Validate test function */
132
133 write_curves(src_curve, default_export_params(out_file_path));
134
136
137 ASSERT_TRUE(result.size() == 1);
138 result_curve = &result[0].get_curves()->geometry.wrap();
139
140 /* Validate properties */
141 EXPECT_EQ(result_curve->nurbs_orders()[0], order);
142 EXPECT_EQ(result_curve->cyclic()[0], expected_cyclic ? *expected_cyclic : cyclic);
143 EXPECT_EQ(result_curve->nurbs_knots_modes()[0], int8_t(expected_mode ? *expected_mode : mode));
144
145 const Span<float3> result_points = result_curve->positions();
146 expected_points = expected_points.size() ? expected_points : points;
147 ASSERT_EQ(expected_points.size(), result_points.size());
148 EXPECT_NEAR_ARRAY_ND(
149 expected_points.data(), result_points.data(), expected_points.size(), 3, 1e-4);
150
151 if (result_curve->nurbs_knots_modes()[0] != KnotsMode::NURBS_KNOT_MODE_CUSTOM) {
152 ASSERT_TRUE(result_curve->custom_knots == NULL);
153 }
154 }
155};
156
157const std::array<float3, 13> position_array{float3{1.0f, -1.0f, 2.0f},
158 float3{2.0f, -2.0f, 4.0f},
159 float3{3.0f, -3.0f, 6.0f},
160 float3{4.0f, -4.0f, 8.0f},
161 float3{5.0f, -5.0f, 10.0f},
162 float3{6.0f, -6.0f, 12.0f},
163 float3{7.0f, -7.0f, 14.0f},
164 float3{1.0f / 4.0f, -2.0f, 3.0f / 6.0f},
165 float3{1.0f / 6.0f, -3.0f, 3.0f / 9.0f},
166 float3{1.0f / 8.0f, -4.0f, 3.0f / 12.0f},
167 float3{1.0f / 5.0f, -5.0f, 3.0f / 11.0f},
168 float3{1.0f / 3.0f, -6.0f, 3.0f / 10.0f},
169 float3{1.0f / 2.0f, -7.0f, 3.0f / 9.0f}};
171
172/* -------------------------------------------------------------------- */
175
176TEST_F(OBJCurvesTest, nurbs_io_uniform_polyline)
177{
178 const int8_t order = 2;
179 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_NORMAL;
180 const bool cyclic = false;
181 const Span<float3> positions = position_data.slice(0, 5);
182
183 const KnotsMode expected_mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT;
184
187 run_nurbs_test(positions, order, mode, cyclic, src, result, positions, &expected_mode);
188
189 /* Validate uniform knots, don't do this in general as it only verifies the knot generator
190 * `bke::curves::nurbs::calculate_knots`. */
191 Vector<float> knot_buffer(bke::curves::nurbs::knots_num(positions.size(), order, cyclic));
192 bke::curves::nurbs::calculate_knots(positions.size(), mode, order, cyclic, knot_buffer);
194 knot_buffer);
195
196 std::array<int, 7> expected_mult;
197 std::fill(expected_mult.begin(), expected_mult.end(), 1);
198 EXPECT_EQ_SPAN<int>(multiplicity, expected_mult);
199}
200
201TEST_F(OBJCurvesTest, nurbs_io_uniform_deg5)
202{
203 const int8_t order = 6;
204 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_NORMAL;
205 const Span<float3> positions = position_data.slice(0, 8);
206
209 run_nurbs_test(positions, order, mode, false, src, result);
210}
211
212TEST_F(OBJCurvesTest, nurbs_io_uniform_clamped_polyline)
213{
214 const int8_t order = 2;
215 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT;
216 const Span<float3> positions = position_data.slice(0, 5);
217
220 run_nurbs_test(positions, order, mode, false, src, result);
221}
222
223TEST_F(OBJCurvesTest, nurbs_io_uniform_endpoint_clamped_deg3)
224{
225 const int8_t order = 3;
226 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT;
227 const Span<float3> positions = position_data.slice(0, 5);
228
231 run_nurbs_test(positions, order, mode, false, src, result);
232}
233
234TEST_F(OBJCurvesTest, nurbs_io_uniform_endpoint_clamped_deg5)
235{
236 const int8_t order = 6;
237 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_NORMAL;
238 const Span<float3> positions = position_data.slice(0, 8);
239
242 run_nurbs_test(positions, order, mode, false, src, result);
243}
244
245TEST_F(OBJCurvesTest, nurbs_io_uniform_cyclic_polyline)
246{
247 const int8_t order = 2;
248 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_NORMAL;
249 const Span<float3> positions = position_data.slice(0, 5);
250
251 const KnotsMode expected_mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT;
252
255 run_nurbs_test(positions, order, mode, true, src, result, positions, &expected_mode);
256}
257
258TEST_F(OBJCurvesTest, nurbs_io_uniform_cyclic_deg4)
259{
260 const int8_t order = 5;
261 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_NORMAL;
262 const Span<float3> positions = position_data.slice(0, 8);
263
266 run_nurbs_test(positions, order, mode, true, src, result);
267}
268
269TEST_F(OBJCurvesTest, nurbs_io_uniform_cyclic_clamped_deg4)
270{
271 const int8_t order = 5;
272 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT;
273 const Span<float3> positions = position_data.slice(0, 12);
274
277 run_nurbs_test(positions, order, mode, true, src, result);
278}
279
281
282/* -------------------------------------------------------------------- */
285
286TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_single_segment_deg2)
287{
288 const int8_t order = 3;
289 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
290 const Span<float3> positions = position_data.slice(0, 3);
291
294 run_nurbs_test(positions, order, mode, false, src, result);
295}
296
297TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_single_segment_deg4)
298{
299 const int8_t order = 5;
300 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
301 const Span<float3> positions = position_data.slice(0, 5);
302
305 run_nurbs_test(positions, order, mode, false, src, result);
306}
307
308TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_deg2)
309{
310 const int8_t order = 3;
311 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
312 const Span<float3> positions = position_data.slice(0, 7);
313
316 run_nurbs_test(positions, order, mode, false, src, result);
317}
318
319TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_uneven_deg2)
320{
321 const int8_t order = 3;
322 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
323 const Span<float3> positions = position_data.slice(0, 8);
324
327 run_nurbs_test(positions, order, mode, false, src, result, positions.slice(0, 7));
328}
329
330TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_deg4)
331{
332 const int8_t order = 5;
333 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
334 const Span<float3> positions = position_data.slice(0, 13);
335
336 /* Even (whole Bezier segments). */
337 {
340 run_nurbs_test(positions, order, mode, false, src, result);
341 }
342
343 {
346 run_nurbs_test(positions.slice(0, 9), order, mode, false, src, result);
347 }
348
349 /* Uneven (incomplete segment). */
350
351 {
354 run_nurbs_test(positions.slice(0, 12), order, mode, false, src, result, positions.slice(0, 9));
355 }
356
357 {
360 run_nurbs_test(positions.slice(0, 11), order, mode, false, src, result, positions.slice(0, 9));
361 }
362
363 {
366 run_nurbs_test(positions.slice(0, 10), order, mode, false, src, result, positions.slice(0, 9));
367 }
368}
369
370TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_cyclic_deg4_looped_12)
371{
372 const int8_t order = 5;
373 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
374 const Span<float3> positions = position_data.slice(0, 12);
375
378 run_nurbs_test(positions.slice(0, 12), order, mode, true, src, result);
379}
380
381TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_cyclic_deg4_looped_8)
382{
383 const int8_t order = 5;
384 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
385 const Span<float3> positions = position_data.slice(0, 8);
386
389 run_nurbs_test(positions, order, mode, true, src, result);
390}
391
392TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_cyclic_deg4_discontinous_13)
393{
394 const int8_t order = 5;
395 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
396 const Span<float3> positions = position_data;
397
398 Vector<float3> expected(positions);
399 expected.append(positions[0]);
400 const bool expect_cyclic = false;
401 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_CUSTOM;
402
405
406 run_nurbs_test(
407 positions, order, mode, true, src, result, expected, &expect_mode, &expect_cyclic);
408}
409
410TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_cyclic_deg4_discontinous_11)
411{
412 const int8_t order = 5;
413 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
414 const Span<float3> positions = position_data.slice(0, 11);
415
416 Vector<float3> expected(positions);
417 expected.append(positions[0]);
418 const bool expect_cyclic = false;
419 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_CUSTOM;
420
423
424 run_nurbs_test(
425 positions, order, mode, true, src, result, expected, &expect_mode, &expect_cyclic);
426}
427
428TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_cyclic_deg4_discontinous_10)
429{
430 const int8_t order = 5;
431 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
432 const Span<float3> positions = position_data.slice(0, 10);
433
434 Vector<float3> expected(positions);
435 expected.append(positions[0]);
436 const bool expect_cyclic = false;
437 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_CUSTOM;
438
441
442 run_nurbs_test(
443 positions, order, mode, true, src, result, expected, &expect_mode, &expect_cyclic);
444}
445
446TEST_F(OBJCurvesTest, nurbs_io_bezier_clamped_cyclic_deg4_discontinous_9)
447{
448 const int8_t order = 5;
449 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
450 const Span<float3> positions = position_data.slice(0, 9);
451
452 Vector<float3> expected(positions);
453 expected.append(positions[0]);
454 const bool expect_cyclic = false;
455 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_CUSTOM;
456
459
460 run_nurbs_test(
461 positions, order, mode, true, src, result, expected, &expect_mode, &expect_cyclic);
462}
463
465
466/* -------------------------------------------------------------------- */
469
470TEST_F(OBJCurvesTest, nurbs_io_bezier_cyclic_deg4_looped_12)
471{
472 const int8_t order = 5;
473 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_BEZIER;
474 const Span<float3> positions = position_data.slice(0, 12);
475
476 Vector<float3> expected(positions.size());
477 array_utils::copy(positions.slice(1, 11), expected.as_mutable_span().slice(0, 11));
478 expected.last() = positions.first();
479
480 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
481
484 run_nurbs_test(positions, order, mode, true, src, result, expected, &expect_mode);
485}
486
487TEST_F(OBJCurvesTest, nurbs_io_bezier_cyclic_deg4_looped_discontinous_13)
488{
489 const int8_t order = 5;
490 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_BEZIER;
491 const Span<float3> positions = position_data.slice(0, 13);
492
493 Vector<float3> expected(positions.size() + 1);
494 array_utils::copy(positions.slice(1, 12), expected.as_mutable_span().slice(0, 12));
495 expected.last(1) = positions.first();
496 expected.last() = positions[1];
497
498 const bool expect_cyclic = false;
499 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_CUSTOM;
500
503 run_nurbs_test(
504 positions, order, mode, true, src, result, expected, &expect_mode, &expect_cyclic);
505}
506
507TEST_F(OBJCurvesTest, nurbs_io_bezier_cyclic_deg4_looped_discontinous_11)
508{
509 const int8_t order = 5;
510 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_BEZIER;
511 const Span<float3> positions = position_data.slice(0, 11);
512
513 Vector<float3> expected(positions.size() + 1);
514 array_utils::copy(positions.slice(1, 10), expected.as_mutable_span().slice(0, 10));
515 expected.last(1) = positions.first();
516 expected.last() = positions[1];
517
518 const bool expect_cyclic = false;
519 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_CUSTOM;
520
523 run_nurbs_test(
524 positions, order, mode, true, src, result, expected, &expect_mode, &expect_cyclic);
525}
526
527TEST_F(OBJCurvesTest, nurbs_io_bezier_cyclic_deg4_looped_discontinous_10)
528{
529 const int8_t order = 5;
530 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_BEZIER;
531 const Span<float3> positions = position_data.slice(0, 10);
532
533 Vector<float3> expected(positions.size() + 1);
534 array_utils::copy(positions.slice(1, 9), expected.as_mutable_span().slice(0, 9));
535 expected.last(1) = positions.first();
536 expected.last() = positions[1];
537
538 const bool expect_cyclic = false;
539 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_CUSTOM;
540
543 run_nurbs_test(
544 positions, order, mode, true, src, result, expected, &expect_mode, &expect_cyclic);
545}
546
547TEST_F(OBJCurvesTest, nurbs_io_bezier_cyclic_deg4_looped_discontinous_9)
548{
549 const int8_t order = 5;
550 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_BEZIER;
551 const Span<float3> positions = position_data.slice(0, 9);
552
553 Vector<float3> expected(positions.size() + 1);
554 array_utils::copy(positions.slice(1, 8), expected.as_mutable_span().slice(0, 8));
555 expected.last(1) = positions.first();
556 expected.last() = positions[1];
557
558 const bool expect_cyclic = false;
559 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_CUSTOM;
560
563 run_nurbs_test(
564 positions, order, mode, true, src, result, expected, &expect_mode, &expect_cyclic);
565}
566
567TEST_F(OBJCurvesTest, nurbs_io_bezier_cyclic_deg4_looped_8)
568{
569 const int8_t order = 5;
570 const KnotsMode mode = KnotsMode::NURBS_KNOT_MODE_BEZIER;
571 const Span<float3> positions = position_data.slice(0, 8);
572
573 Vector<float3> expected(positions.size());
574 array_utils::copy(positions.slice(1, 7), expected.as_mutable_span().slice(0, 7));
575 expected.last() = positions.first();
576
577 const KnotsMode expect_mode = KnotsMode::NURBS_KNOT_MODE_ENDPOINT_BEZIER;
578
581 run_nurbs_test(positions, order, mode, true, src, result, expected, &expect_mode);
582}
583
585
586} // namespace blender::io::obj
void BKE_tempdir_init(const char *userdir)
Definition appdir.cc:1200
const char * BKE_tempdir_base() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
Definition appdir.cc:1243
Low-level operations for curves.
void BKE_idtype_init()
Definition idtype.cc:121
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
void CLG_exit()
Definition clog.cc:880
void CLG_init()
Definition clog.cc:873
KnotsMode
@ IO_AXIS_Y
@ IO_AXIS_Z
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
void append(const T &value)
const T & last(const int64_t n=0) const
MutableSpan< T > as_mutable_span()
MutableSpan< float3 > positions_for_write()
MutableSpan< int8_t > nurbs_knots_modes_for_write()
MutableSpan< int8_t > nurbs_orders_for_write()
VArray< int8_t > nurbs_knots_modes() const
Span< float3 > positions() const
MutableSpan< float > nurbs_weights_for_write()
MutableSpan< int > offsets_for_write()
VArray< bool > cyclic() const
MutableSpan< bool > cyclic_for_write()
VArray< int8_t > nurbs_orders() const
Vector< bke::GeometrySet > read_curves(OBJImportParams params)
static bke::CurvesGeometry create_rational_nurbs(Span< float3 > points, Span< float > weights, bool cyclic, int8_t order, KnotsMode mode)
static bke::CurvesGeometry create_nurbs(Span< float3 > points, bool cyclic, int8_t order, KnotsMode mode)
void write_curves(const bke::CurvesGeometry &curve, OBJExportParams params)
void write_curves(const std::unique_ptr< IOBJCurve > &curve, OBJExportParams params)
void write_curves(const Span< std::unique_ptr< IOBJCurve > > curves, OBJExportParams params)
static bke::CurvesGeometry create_curves(Span< float3 > points, bool cyclic)
void run_nurbs_test(const Span< float3 > points, const int8_t order, const KnotsMode mode, const bool cyclic, bke::CurvesGeometry &src_curve, const bke::CurvesGeometry *&result_curve, Span< float3 > expected_points=Span< float3 >(), const KnotsMode *expected_mode=nullptr, const bool *expected_cyclic=nullptr)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
Vector< int > calculate_multiplicity_sequence(Span< float > knots)
void calculate_knots(int points_num, KnotsMode mode, int8_t order, bool cyclic, MutableSpan< float > knots)
int knots_num(int points_num, int8_t order, bool cyclic)
void export_objects(const OBJExportParams &export_params, const Span< std::unique_ptr< OBJMesh > > meshes, const Span< std::unique_ptr< IOBJCurve > > curves, const char *filepath)
static OBJImportParams default_import_params(const std::string &filepath)
static OBJExportParams default_export_params(const std::string &filepath)
TEST_F(OBJExportTest, filter_objects_curves_as_mesh)
const std::array< float3, 13 > position_array
const Span< float3 > position_data
void importer_geometry(const OBJImportParams &import_params, Vector< bke::GeometrySet > &geometries, size_t read_buffer_size)
MatBase< float, 4, 4 > float4x4
VecBase< float, 3 > float3