Blender V5.0
obj_exporter_tests.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include <gtest/gtest.h>
6#include <memory>
7#include <string>
8#include <system_error>
9
10#include "testing/testing.h"
12
13#include "BKE_appdir.hh"
14#include "BKE_blender_version.h"
15#include "BKE_main.hh"
16
17#include "BLI_fileops.h"
18#include "BLI_string.h"
19
20#include "BLO_readfile.hh"
21
22#include "DEG_depsgraph.hh"
23
25#include "obj_export_nurbs.hh"
26#include "obj_exporter.hh"
27
28namespace blender::io::obj {
29/* Set this true to keep comparison-failing test output in temp file directory. */
30constexpr bool save_failing_test_output = false;
31
32/* This is also the test name. */
34 public:
38 bool load_file_and_depsgraph(const std::string &filepath,
39 const eEvaluationMode eval_mode = DAG_EVAL_VIEWPORT)
40 {
41 if (!blendfile_load(filepath.c_str())) {
42 return false;
43 }
44 depsgraph_create(eval_mode);
45 return true;
46 }
47};
48
49const std::string all_objects_file = "io_tests" SEP_STR "blend_scene" SEP_STR "all_objects.blend";
50
51TEST_F(OBJExportTest, filter_objects_curves_as_mesh)
52{
54 if (!load_file_and_depsgraph(all_objects_file)) {
55 ADD_FAILURE();
56 return;
57 }
58 auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, params)};
59 EXPECT_EQ(objmeshes.size(), 21);
60 EXPECT_EQ(objcurves.size(), 0);
61}
62
63TEST_F(OBJExportTest, filter_objects_curves_as_nurbs)
64{
66 if (!load_file_and_depsgraph(all_objects_file)) {
67 ADD_FAILURE();
68 return;
69 }
70 params.export_curves_as_nurbs = true;
71 auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, params)};
72 EXPECT_EQ(objmeshes.size(), 18);
73 EXPECT_EQ(objcurves.size(), 3);
74}
75
76TEST_F(OBJExportTest, filter_objects_selected)
77{
79 if (!load_file_and_depsgraph(all_objects_file)) {
80 ADD_FAILURE();
81 return;
82 }
83 params.export_selected_objects = true;
84 params.export_curves_as_nurbs = true;
85 auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, params)};
86 EXPECT_EQ(objmeshes.size(), 1);
87 EXPECT_EQ(objcurves.size(), 0);
88}
89
90TEST(obj_exporter_utils, append_negative_frame_to_filename)
91{
92 const char path_original[FILE_MAX] = SEP_STR "my_file.obj";
93 const char path_truth[FILE_MAX] = SEP_STR "my_file-0012.obj";
94 const int frame = -12;
95 char path_with_frame[FILE_MAX] = {0};
96 const bool ok = append_frame_to_filename(path_original, frame, path_with_frame);
97 EXPECT_TRUE(ok);
98 EXPECT_STREQ(path_with_frame, path_truth);
99}
100
101TEST(obj_exporter_utils, append_positive_frame_to_filename)
102{
103 const char path_original[FILE_MAX] = SEP_STR "my_file.obj";
104 const char path_truth[FILE_MAX] = SEP_STR "my_file0012.obj";
105 const int frame = 12;
106 char path_with_frame[FILE_MAX] = {0};
107 const bool ok = append_frame_to_filename(path_original, frame, path_with_frame);
108 EXPECT_TRUE(ok);
109 EXPECT_STREQ(path_with_frame, path_truth);
110}
111
112TEST(obj_exporter_utils, append_large_positive_frame_to_filename)
113{
114 const char path_original[FILE_MAX] = SEP_STR "my_file.obj";
115 const char path_truth[FILE_MAX] = SEP_STR "my_file1234567.obj";
116 const int frame = 1234567;
117 char path_with_frame[FILE_MAX] = {0};
118 const bool ok = append_frame_to_filename(path_original, frame, path_with_frame);
119 EXPECT_TRUE(ok);
120 EXPECT_STREQ(path_with_frame, path_truth);
121}
122
123static std::string read_temp_file_in_string(const std::string &file_path)
124{
125 std::string res;
126 size_t buffer_len;
127 void *buffer = BLI_file_read_text_as_mem(file_path.c_str(), 0, &buffer_len);
128 if (buffer != nullptr) {
129 res.assign((const char *)buffer, buffer_len);
130 MEM_freeN(buffer);
131 }
132 return res;
133}
134
135class ObjExporterWriterTest : public testing::Test {
136 protected:
137 void SetUp() override
138 {
139 BKE_tempdir_init(nullptr);
140 }
141
142 void TearDown() override
143 {
145 }
146
148 {
149 /* Use Latin Capital Letter A with Ogonek, Cyrillic Capital Letter Zhe
150 * at the end, to test I/O on non-English file names. */
151 const char *const temp_file_path = "output\xc4\x84\xd0\x96.OBJ";
152
153 return std::string(BKE_tempdir_session()) + SEP_STR + std::string(temp_file_path);
154 }
155
156 std::unique_ptr<OBJWriter> init_writer(const OBJExportParams &params,
157 const std::string &out_filepath)
158 {
159 try {
160 auto writer = std::make_unique<OBJWriter>(out_filepath.c_str(), params);
161 return writer;
162 }
163 catch (const std::system_error &ex) {
164 fprintf(stderr, "[%s] %s\n", ex.code().category().name(), ex.what());
165 return nullptr;
166 }
167 }
168};
169
171{
172 /* Because testing doesn't fully initialize Blender, we need the following. */
173 BKE_tempdir_init(nullptr);
174 std::string out_file_path = get_temp_obj_filename();
175 {
177 std::unique_ptr<OBJWriter> writer = init_writer(params, out_file_path);
178 if (!writer) {
179 ADD_FAILURE();
180 return;
181 }
182 writer->write_header();
183 }
184 const std::string result = read_temp_file_in_string(out_file_path);
185 using namespace std::string_literals;
186 ASSERT_EQ(result, "# Blender "s + BKE_blender_version_string() + "\n" + "# www.blender.org\n");
187}
188
190{
191 std::string out_file_path = get_temp_obj_filename();
192 {
194 std::unique_ptr<OBJWriter> writer = init_writer(params, out_file_path);
195 if (!writer) {
196 ADD_FAILURE();
197 return;
198 }
199 writer->write_mtllib_name("/Users/blah.mtl");
200 writer->write_mtllib_name("\\C:\\blah.mtl");
201 }
202 const std::string result = read_temp_file_in_string(out_file_path);
203 ASSERT_EQ(result, "mtllib blah.mtl\nmtllib blah.mtl\n");
204}
205
206TEST(obj_exporter_writer, format_handler_buffer_chunking)
207{
208 /* Use a tiny buffer chunk size, so that the test below ends up creating several blocks. */
209 FormatHandler h(16);
210 h.write_obj_object("abc");
211 h.write_obj_object("abcd");
212 h.write_obj_object("abcde");
213 h.write_obj_object("abcdef");
214 h.write_obj_object("012345678901234567890123456789abcd");
215 h.write_obj_object("123");
219 h.write_obj_nurbs_parm(0.0f);
221
222 size_t got_blocks = h.get_block_count();
223 ASSERT_EQ(got_blocks, 6);
224
225 std::string got_string = h.get_as_string();
226 using namespace std::string_literals;
227 const char *expected = R"(o abc
228o abcd
229o abcde
230o abcdef
231o 012345678901234567890123456789abcd
232o 123
233curv
234parm u 0.000000
235)";
236 ASSERT_EQ(got_string, expected);
237}
238
239/* Return true if string #a and string #b are equal after their first newline. */
240static bool strings_equal_after_first_lines(const std::string &a, const std::string &b)
241{
242 const size_t a_len = a.size();
243 const size_t b_len = b.size();
244 const size_t a_next = a.find_first_of('\n');
245 const size_t b_next = b.find_first_of('\n');
246 if (a_next == std::string::npos) {
247 printf("No newline found in evaluated string\n");
248 return false;
249 }
250 if (b_next == std::string::npos) {
251 printf("No newline found in the golden string\n");
252 return false;
253 }
254 const size_t a_sublen = a_len - a_next;
255 const size_t b_sublen = b_len - b_next;
256 if (a_sublen != b_sublen) {
257 printf("Mismatching string length, evaluated contains %zu chars, while golden has %zu\n",
258 a_sublen,
259 b_sublen);
260 }
261 if (a.compare(a_next, a_sublen, b, b_next, b_sublen) != 0) {
262 for (int i = 0; i < std::min(a_sublen, b_sublen); ++i) {
263 if (a[a_next + i] != b[b_next + i]) {
264 printf("Difference found at pos %zu of a\n", a_next + i);
265 printf("a: %s ...\n", a.substr(a_next + i, 100).c_str());
266 printf("b: %s ...\n", b.substr(b_next + i, 100).c_str());
267 return false;
269 }
270 return false;
271 }
272 return true;
273}
274
275/* From here on, tests are whole file tests, testing for golden output. */
276class OBJExportRegressionTest : public OBJExportTest {
277 public:
285 void compare_obj_export_to_golden(const std::string &blendfile,
286 const std::string &golden_obj,
287 const std::string &golden_mtl,
289 {
290 if (!load_file_and_depsgraph(blendfile)) {
291 return;
292 }
293 /* Because testing doesn't fully initialize Blender, we need the following. */
294 BKE_tempdir_init(nullptr);
295 std::string tempdir = std::string(BKE_tempdir_base());
296 std::string out_file_path = tempdir + BLI_path_basename(golden_obj.c_str());
297 STRNCPY(params.filepath, out_file_path.c_str());
298 params.blen_filepath = bfile->main->filepath;
299 std::string golden_file_path = blender::tests::flags_test_asset_dir() + SEP_STR + golden_obj;
301 golden_file_path.c_str(), params.file_base_for_tests, sizeof(params.file_base_for_tests));
302 export_frame(depsgraph, params, out_file_path.c_str());
303 std::string output_str = read_temp_file_in_string(out_file_path);
304
305 std::string golden_str = read_temp_file_in_string(golden_file_path);
306 bool are_equal = strings_equal_after_first_lines(output_str, golden_str);
307 if (!are_equal) {
308 printf("failed test for file: %s\n", golden_file_path.c_str());
310 printf("failing test output in %s\n", out_file_path.c_str());
311 }
312 }
313 ASSERT_TRUE(are_equal);
314 if (!save_failing_test_output || are_equal) {
315 BLI_delete(out_file_path.c_str(), false, false);
316 }
317 if (!golden_mtl.empty()) {
318 std::string out_mtl_file_path = tempdir + BLI_path_basename(golden_mtl.c_str());
319 std::string output_mtl_str = read_temp_file_in_string(out_mtl_file_path);
320 std::string golden_mtl_file_path = blender::tests::flags_test_asset_dir() + SEP_STR +
321 golden_mtl;
322 std::string golden_mtl_str = read_temp_file_in_string(golden_mtl_file_path);
323 are_equal = strings_equal_after_first_lines(output_mtl_str, golden_mtl_str);
324 if (!are_equal) {
325 printf("failed test for mtl file: %s\n", golden_mtl_file_path.c_str());
327 printf("failing test output in %s\n", out_mtl_file_path.c_str());
328 }
329 }
330 ASSERT_TRUE(are_equal);
331 if (!save_failing_test_output || are_equal) {
332 BLI_delete(out_mtl_file_path.c_str(), false, false);
333 }
334 }
335 }
336};
337
341 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "all_tris.blend",
342 "io_tests" SEP_STR "obj" SEP_STR "all_tris.obj",
343 "io_tests" SEP_STR "obj" SEP_STR "all_tris.mtl",
344 params);
345}
346
347TEST_F(OBJExportRegressionTest, all_quads)
348{
350 params.global_scale = 2.0f;
351 params.export_materials = false;
352 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "all_quads.blend",
353 "io_tests" SEP_STR "obj" SEP_STR "all_quads.obj",
354 "",
355 params);
356}
357
358TEST_F(OBJExportRegressionTest, fgons)
359{
361 params.forward_axis = IO_AXIS_Y;
362 params.up_axis = IO_AXIS_Z;
363 params.export_materials = false;
364 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "fgons.blend",
365 "io_tests" SEP_STR "obj" SEP_STR "fgons.obj",
366 "",
367 params);
368}
369
370TEST_F(OBJExportRegressionTest, edges)
371{
373 params.forward_axis = IO_AXIS_Y;
374 params.up_axis = IO_AXIS_Z;
375 params.export_materials = false;
376 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "edges.blend",
377 "io_tests" SEP_STR "obj" SEP_STR "edges.obj",
378 "",
379 params);
380}
381
382TEST_F(OBJExportRegressionTest, vertices)
383{
385 params.forward_axis = IO_AXIS_Y;
386 params.up_axis = IO_AXIS_Z;
387 params.export_materials = false;
388 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
389 "cube_loose_edges_verts.blend",
390 "io_tests" SEP_STR "obj" SEP_STR "cube_loose_edges_verts.obj",
391 "",
392 params);
393}
394
395TEST_F(OBJExportRegressionTest, cube_loose_edges)
396{
398 params.forward_axis = IO_AXIS_Y;
399 params.up_axis = IO_AXIS_Z;
400 params.export_materials = false;
401 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "vertices.blend",
402 "io_tests" SEP_STR "obj" SEP_STR "vertices.obj",
403 "",
404 params);
405}
406
407TEST_F(OBJExportRegressionTest, non_uniform_scale)
408{
410 params.export_materials = false;
411 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
412 "non_uniform_scale.blend",
413 "io_tests" SEP_STR "obj" SEP_STR "non_uniform_scale.obj",
414 "",
415 params);
416}
417
418TEST_F(OBJExportRegressionTest, nurbs_as_nurbs)
419{
421 params.forward_axis = IO_AXIS_Y;
422 params.up_axis = IO_AXIS_Z;
423 params.export_materials = false;
424 params.export_curves_as_nurbs = true;
425 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "nurbs.blend",
426 "io_tests" SEP_STR "obj" SEP_STR "nurbs.obj",
427 "",
428 params);
429}
430
431TEST_F(OBJExportRegressionTest, nurbs_curves_as_nurbs)
432{
434 params.forward_axis = IO_AXIS_Y;
435 params.up_axis = IO_AXIS_Z;
436 params.export_materials = false;
437 params.export_curves_as_nurbs = true;
438 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "nurbs_curves.blend",
439 "io_tests" SEP_STR "obj" SEP_STR "nurbs_curves.obj",
440 "",
441 params);
442}
443
444TEST_F(OBJExportRegressionTest, nurbs_as_mesh)
445{
447 params.forward_axis = IO_AXIS_Y;
448 params.up_axis = IO_AXIS_Z;
449 params.export_materials = false;
450 params.export_curves_as_nurbs = false;
451 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "nurbs.blend",
452 "io_tests" SEP_STR "obj" SEP_STR "nurbs_mesh.obj",
453 "",
454 params);
455}
456
457TEST_F(OBJExportRegressionTest, cube_all_data_triangulated)
458{
460 params.forward_axis = IO_AXIS_Y;
461 params.up_axis = IO_AXIS_Z;
462 params.export_materials = false;
463 params.export_triangulated_mesh = true;
464 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "cube_all_data.blend",
465 "io_tests" SEP_STR "obj" SEP_STR "cube_all_data_triangulated.obj",
466 "",
467 params);
468}
469
470TEST_F(OBJExportRegressionTest, cube_normal_edit)
471{
473 params.forward_axis = IO_AXIS_Y;
474 params.up_axis = IO_AXIS_Z;
475 params.export_materials = false;
476 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
477 "cube_normal_edit.blend",
478 "io_tests" SEP_STR "obj" SEP_STR "cube_normal_edit.obj",
479 "",
480 params);
481}
482
483TEST_F(OBJExportRegressionTest, cube_vertex_groups)
484{
486 params.export_materials = false;
487 params.export_normals = false;
488 params.export_uv = false;
489 params.export_vertex_groups = true;
490 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
491 "cube_vertex_groups.blend",
492 "io_tests" SEP_STR "obj" SEP_STR "cube_vertex_groups.obj",
493 "",
494 params);
495}
496
497TEST_F(OBJExportRegressionTest, cubes_positioned)
498{
500 params.export_materials = false;
501 params.global_scale = 2.0f;
502 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
503 "cubes_positioned.blend",
504 "io_tests" SEP_STR "obj" SEP_STR "cubes_positioned.obj",
505 "",
506 params);
507}
508
509TEST_F(OBJExportRegressionTest, cubes_vertex_colors)
510{
512 params.export_colors = true;
513 params.export_normals = false;
514 params.export_uv = false;
515 params.export_materials = false;
516 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
517 "cubes_vertex_colors.blend",
518 "io_tests" SEP_STR "obj" SEP_STR "cubes_vertex_colors.obj",
519 "",
520 params);
521}
522
523TEST_F(OBJExportRegressionTest, cubes_with_textures_strip)
524{
527 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
528 "cubes_with_textures.blend",
529 "io_tests" SEP_STR "obj" SEP_STR "cubes_with_textures.obj",
530 "io_tests" SEP_STR "obj" SEP_STR "cubes_with_textures.mtl",
531 params);
532}
533
534TEST_F(OBJExportRegressionTest, cubes_with_textures_relative)
535{
538 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
539 "cubes_with_textures.blend",
540 "io_tests" SEP_STR "obj" SEP_STR "cubes_with_textures_rel.obj",
541 "io_tests" SEP_STR "obj" SEP_STR "cubes_with_textures_rel.mtl",
542 params);
543}
544
545TEST_F(OBJExportRegressionTest, suzanne_all_data)
546{
548 params.forward_axis = IO_AXIS_Y;
549 params.up_axis = IO_AXIS_Z;
550 params.export_materials = false;
551 params.export_smooth_groups = true;
552 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR
553 "suzanne_all_data.blend",
554 "io_tests" SEP_STR "obj" SEP_STR "suzanne_all_data.obj",
555 "",
556 params);
557}
558
559TEST_F(OBJExportRegressionTest, all_curves)
560{
562 params.export_materials = false;
563 compare_obj_export_to_golden("io_tests" SEP_STR "blend_scene" SEP_STR "all_curves.blend",
564 "io_tests" SEP_STR "obj" SEP_STR "all_curves.obj",
565 "",
566 params);
567}
568
569TEST_F(OBJExportRegressionTest, all_curves_as_nurbs)
570{
572 params.export_materials = false;
573 params.export_curves_as_nurbs = true;
574 compare_obj_export_to_golden("io_tests" SEP_STR "blend_scene" SEP_STR "all_curves.blend",
575 "io_tests" SEP_STR "obj" SEP_STR "all_curves_as_nurbs.obj",
576 "",
577 params);
578}
579
580TEST_F(OBJExportRegressionTest, all_objects)
581{
583 params.forward_axis = IO_AXIS_Y;
584 params.up_axis = IO_AXIS_Z;
585 params.export_smooth_groups = true;
586 params.export_colors = true;
587 compare_obj_export_to_golden("io_tests" SEP_STR "blend_scene" SEP_STR "all_objects.blend",
588 "io_tests" SEP_STR "obj" SEP_STR "all_objects.obj",
589 "io_tests" SEP_STR "obj" SEP_STR "all_objects.mtl",
590 params);
591}
592
593TEST_F(OBJExportRegressionTest, all_objects_mat_groups)
594{
596 params.forward_axis = IO_AXIS_Y;
597 params.up_axis = IO_AXIS_Z;
598 params.export_smooth_groups = true;
599 params.export_material_groups = true;
600 compare_obj_export_to_golden("io_tests" SEP_STR "blend_scene" SEP_STR "all_objects.blend",
601 "io_tests" SEP_STR "obj" SEP_STR "all_objects_mat_groups.obj",
602 "io_tests" SEP_STR "obj" SEP_STR "all_objects_mat_groups.mtl",
603 params);
604}
605
606TEST_F(OBJExportRegressionTest, materials_without_pbr)
607{
609 params.export_normals = false;
611 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "materials_pbr.blend",
612 "io_tests" SEP_STR "obj" SEP_STR "materials_without_pbr.obj",
613 "io_tests" SEP_STR "obj" SEP_STR "materials_without_pbr.mtl",
614 params);
615}
616
617TEST_F(OBJExportRegressionTest, materials_pbr)
618{
620 params.export_normals = false;
622 params.export_pbr_extensions = true;
623 compare_obj_export_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "materials_pbr.blend",
624 "io_tests" SEP_STR "obj" SEP_STR "materials_pbr.obj",
625 "io_tests" SEP_STR "obj" SEP_STR "materials_pbr.mtl",
626 params);
627}
628
629} // 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
void BKE_tempdir_session_purge()
Definition appdir.cc:1248
const char * BKE_blender_version_string(void)
Definition blender.cc:146
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
File and directory operations.
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
void * BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
Definition storage.cc:511
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAX
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
external readfile function prototypes.
eEvaluationMode
@ DAG_EVAL_VIEWPORT
@ IO_AXIS_Y
@ IO_AXIS_Z
@ PATH_REFERENCE_RELATIVE
@ PATH_REFERENCE_STRIP
BPy_StructRNA * depsgraph
virtual void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode)
bool blendfile_load(const char *filepath)
std::string get_as_string() const
void compare_obj_export_to_golden(const std::string &blendfile, const std::string &golden_obj, const std::string &golden_mtl, OBJExportParams &params)
bool load_file_and_depsgraph(const std::string &filepath, const eEvaluationMode eval_mode=DAG_EVAL_VIEWPORT)
std::unique_ptr< OBJWriter > init_writer(const OBJExportParams &params, const std::string &out_filepath)
#define printf(...)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool append_frame_to_filename(const char *filepath, const int frame, char r_filepath_with_frames[1024])
static bool strings_equal_after_first_lines(const std::string &a, const std::string &b)
std::pair< Vector< std::unique_ptr< OBJMesh > >, Vector< std::unique_ptr< IOBJCurve > > > filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params)
TEST_F(OBJExportTest, filter_objects_curves_as_mesh)
TEST(obj_exporter_utils, append_negative_frame_to_filename)
constexpr bool save_failing_test_output
const std::string all_objects_file
static std::string read_temp_file_in_string(const std::string &file_path)
void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, const char *filepath)
void * BKE_tempdir_session
Definition stubs.c:38
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39