Blender  V3.3
abstract_hierarchy_iterator_test.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2019 Blender Foundation. All rights reserved. */
4 
6 
7 #include "BKE_scene.h"
8 #include "BLI_math.h"
9 #include "BLO_readfile.h"
10 #include "DEG_depsgraph.h"
11 #include "DEG_depsgraph_build.h"
12 #include "DNA_object_types.h"
13 
14 #include <map>
15 #include <set>
16 
17 namespace blender::io {
18 
19 namespace {
20 
21 /* Mapping from ID.name to set of export hierarchy path. Duplicated objects can be exported
22  * multiple times with different export paths, hence the set. */
23 using used_writers = std::map<std::string, std::set<std::string>>;
24 
25 class TestHierarchyWriter : public AbstractHierarchyWriter {
26  public:
27  std::string writer_type;
28  used_writers &writers_map;
29 
30  TestHierarchyWriter(const std::string &writer_type, used_writers &writers_map)
32  {
33  }
34 
35  void write(HierarchyContext &context) override
36  {
37  const char *id_name = context.object->id.name;
38  used_writers::mapped_type &writers = writers_map[id_name];
39 
40  if (writers.find(context.export_path) != writers.end()) {
41  ADD_FAILURE() << "Unexpectedly found another " << writer_type << " writer for " << id_name
42  << " to export to " << context.export_path;
43  }
44  writers.insert(context.export_path);
45  }
46 };
47 
48 } // namespace
49 
51  public: /* Public so that the test cases can directly inspect the created writers. */
52  used_writers transform_writers;
53  used_writers data_writers;
54  used_writers hair_writers;
55  used_writers particle_writers;
56 
59  {
60  }
62  {
64  }
65 
66  protected:
68  {
69  return new TestHierarchyWriter("transform", transform_writers);
70  }
72  {
73  return new TestHierarchyWriter("data", data_writers);
74  }
76  {
77  return new TestHierarchyWriter("hair", hair_writers);
78  }
80  {
81  return new TestHierarchyWriter("particle", particle_writers);
82  }
83 
84  void release_writer(AbstractHierarchyWriter *writer) override
85  {
86  delete writer;
87  }
88 };
89 
91  protected:
93 
94  void SetUp() override
95  {
96  BlendfileLoadingBaseTest::SetUp();
97  iterator = nullptr;
98  }
99 
100  void TearDown() override
101  {
102  iterator_free();
104  }
105 
106  /* Create a test iterator. */
108  {
110  }
111  /* Free the test iterator if it is not nullptr. */
113  {
114  if (iterator == nullptr) {
115  return;
116  }
117  delete iterator;
118  iterator = nullptr;
119  }
120 };
121 
123 {
124  /* Load the test blend file. */
125  if (!blendfile_load("usd/usd_hierarchy_export_test.blend")) {
126  return;
127  }
128  depsgraph_create(DAG_EVAL_RENDER);
129  iterator_create();
130 
131  iterator->iterate_and_write();
132 
133  /* Mapping from object name to set of export paths. */
134  used_writers expected_transforms = {
135  {"OBCamera", {"/Camera"}},
136  {"OBDupli1", {"/Dupli1"}},
137  {"OBDupli2", {"/ParentOfDupli2/Dupli2"}},
138  {"OBGEO_Ear_L",
139  {"/Dupli1/GEO_Head-0/GEO_Ear_L-1",
140  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L",
141  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1"}},
142  {"OBGEO_Ear_R",
143  {"/Dupli1/GEO_Head-0/GEO_Ear_R-2",
144  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R",
145  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2"}},
146  {"OBGEO_Head",
147  {"/Dupli1/GEO_Head-0",
148  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head",
149  "/ParentOfDupli2/Dupli2/GEO_Head-0"}},
150  {"OBGEO_Nose",
151  {"/Dupli1/GEO_Head-0/GEO_Nose-3",
152  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose",
153  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3"}},
154  {"OBGround plane", {"/Ground plane"}},
155  {"OBOutsideDupliGrandParent", {"/Ground plane/OutsideDupliGrandParent"}},
156  {"OBOutsideDupliParent", {"/Ground plane/OutsideDupliGrandParent/OutsideDupliParent"}},
157  {"OBParentOfDupli2", {"/ParentOfDupli2"}}};
158  EXPECT_EQ(expected_transforms, iterator->transform_writers);
159 
160  used_writers expected_data = {
161  {"OBCamera", {"/Camera/Camera"}},
162  {"OBGEO_Ear_L",
163  {"/Dupli1/GEO_Head-0/GEO_Ear_L-1/Ear",
164  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L/Ear",
165  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1/Ear"}},
166  {"OBGEO_Ear_R",
167  {"/Dupli1/GEO_Head-0/GEO_Ear_R-2/Ear",
168  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R/Ear",
169  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2/Ear"}},
170  {"OBGEO_Head",
171  {"/Dupli1/GEO_Head-0/Face",
172  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/Face",
173  "/ParentOfDupli2/Dupli2/GEO_Head-0/Face"}},
174  {"OBGEO_Nose",
175  {"/Dupli1/GEO_Head-0/GEO_Nose-3/Nose",
176  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose/Nose",
177  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3/Nose"}},
178  {"OBGround plane", {"/Ground plane/Plane"}},
179  {"OBParentOfDupli2", {"/ParentOfDupli2/Icosphere"}},
180  };
181 
182  EXPECT_EQ(expected_data, iterator->data_writers);
183 
184  /* The scene has no hair or particle systems. */
185  EXPECT_EQ(0, iterator->hair_writers.size());
186  EXPECT_EQ(0, iterator->particle_writers.size());
187 
188  /* On the second iteration, everything should be written as well.
189  * This tests the default value of iterator->export_subset_. */
190  iterator->transform_writers.clear();
191  iterator->data_writers.clear();
192  iterator->iterate_and_write();
193  EXPECT_EQ(expected_transforms, iterator->transform_writers);
194  EXPECT_EQ(expected_data, iterator->data_writers);
195 }
196 
198 {
199  /* The scene has no hair or particle systems, and this is already covered by ExportHierarchyTest,
200  * so not included here. Update this test when hair & particle systems are included. */
201 
202  /* Load the test blend file. */
203  if (!blendfile_load("usd/usd_hierarchy_export_test.blend")) {
204  return;
205  }
206  depsgraph_create(DAG_EVAL_RENDER);
207  iterator_create();
208 
209  /* Mapping from object name to set of export paths. */
210  used_writers expected_transforms = {
211  {"OBCamera", {"/Camera"}},
212  {"OBDupli1", {"/Dupli1"}},
213  {"OBDupli2", {"/ParentOfDupli2/Dupli2"}},
214  {"OBGEO_Ear_L",
215  {"/Dupli1/GEO_Head-0/GEO_Ear_L-1",
216  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L",
217  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1"}},
218  {"OBGEO_Ear_R",
219  {"/Dupli1/GEO_Head-0/GEO_Ear_R-2",
220  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R",
221  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2"}},
222  {"OBGEO_Head",
223  {"/Dupli1/GEO_Head-0",
224  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head",
225  "/ParentOfDupli2/Dupli2/GEO_Head-0"}},
226  {"OBGEO_Nose",
227  {"/Dupli1/GEO_Head-0/GEO_Nose-3",
228  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose",
229  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3"}},
230  {"OBGround plane", {"/Ground plane"}},
231  {"OBOutsideDupliGrandParent", {"/Ground plane/OutsideDupliGrandParent"}},
232  {"OBOutsideDupliParent", {"/Ground plane/OutsideDupliGrandParent/OutsideDupliParent"}},
233  {"OBParentOfDupli2", {"/ParentOfDupli2"}}};
234 
235  used_writers expected_data = {
236  {"OBCamera", {"/Camera/Camera"}},
237  {"OBGEO_Ear_L",
238  {"/Dupli1/GEO_Head-0/GEO_Ear_L-1/Ear",
239  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_L/Ear",
240  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_L-1/Ear"}},
241  {"OBGEO_Ear_R",
242  {"/Dupli1/GEO_Head-0/GEO_Ear_R-2/Ear",
243  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Ear_R/Ear",
244  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Ear_R-2/Ear"}},
245  {"OBGEO_Head",
246  {"/Dupli1/GEO_Head-0/Face",
247  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/Face",
248  "/ParentOfDupli2/Dupli2/GEO_Head-0/Face"}},
249  {"OBGEO_Nose",
250  {"/Dupli1/GEO_Head-0/GEO_Nose-3/Nose",
251  "/Ground plane/OutsideDupliGrandParent/OutsideDupliParent/GEO_Head/GEO_Nose/Nose",
252  "/ParentOfDupli2/Dupli2/GEO_Head-0/GEO_Nose-3/Nose"}},
253  {"OBGround plane", {"/Ground plane/Plane"}},
254  {"OBParentOfDupli2", {"/ParentOfDupli2/Icosphere"}},
255  };
256 
257  /* Even when only asking an export of transforms, on the first frame everything should be
258  * exported. */
259  {
260  ExportSubset export_subset = {false};
261  export_subset.transforms = true;
262  export_subset.shapes = false;
263  iterator->set_export_subset(export_subset);
264  }
265  iterator->iterate_and_write();
266  EXPECT_EQ(expected_transforms, iterator->transform_writers);
267  EXPECT_EQ(expected_data, iterator->data_writers);
268 
269  /* Clear data to prepare for the next iteration. */
270  iterator->transform_writers.clear();
271  iterator->data_writers.clear();
272 
273  /* Second iteration, should only write transforms now. */
274  iterator->iterate_and_write();
275  EXPECT_EQ(expected_transforms, iterator->transform_writers);
276  EXPECT_EQ(0, iterator->data_writers.size());
277 
278  /* Clear data to prepare for the next iteration. */
279  iterator->transform_writers.clear();
280  iterator->data_writers.clear();
281 
282  /* Third iteration, should only write data now. */
283  {
284  ExportSubset export_subset = {false};
285  export_subset.transforms = false;
286  export_subset.shapes = true;
287  iterator->set_export_subset(export_subset);
288  }
289  iterator->iterate_and_write();
290  EXPECT_EQ(0, iterator->transform_writers.size());
291  EXPECT_EQ(expected_data, iterator->data_writers);
292 
293  /* Clear data to prepare for the next iteration. */
294  iterator->transform_writers.clear();
295  iterator->data_writers.clear();
296 
297  /* Fourth iteration, should export everything now. */
298  {
299  ExportSubset export_subset = {false};
300  export_subset.transforms = true;
301  export_subset.shapes = true;
302  iterator->set_export_subset(export_subset);
303  }
304  iterator->iterate_and_write();
305  EXPECT_EQ(expected_transforms, iterator->transform_writers);
306  EXPECT_EQ(expected_data, iterator->data_writers);
307 }
308 
309 /* Test class that constructs a depsgraph in such a way that it includes invisible objects. */
311  protected:
312  void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode) override
313  {
315  bfile->main, bfile->curscene, bfile->cur_view_layer, depsgraph_evaluation_mode);
318  }
319 };
320 
322 {
323  if (!blendfile_load("alembic/visibility.blend")) {
324  return;
325  }
326  depsgraph_create(DAG_EVAL_RENDER);
327  iterator_create();
328 
329  iterator->iterate_and_write();
330 
331  /* Mapping from object name to set of export paths. */
332  used_writers expected_transforms = {{"OBInvisibleAnimatedCube", {"/InvisibleAnimatedCube"}},
333  {"OBInvisibleCube", {"/InvisibleCube"}},
334  {"OBVisibleCube", {"/VisibleCube"}}};
335  EXPECT_EQ(expected_transforms, iterator->transform_writers);
336 
337  used_writers expected_data = {{"OBInvisibleAnimatedCube", {"/InvisibleAnimatedCube/Cube"}},
338  {"OBInvisibleCube", {"/InvisibleCube/Cube"}},
339  {"OBVisibleCube", {"/VisibleCube/Cube"}}};
340 
341  EXPECT_EQ(expected_data, iterator->data_writers);
342 
343  /* The scene has no hair or particle systems. */
344  EXPECT_EQ(0, iterator->hair_writers.size());
345  EXPECT_EQ(0, iterator->particle_writers.size());
346 }
347 
348 } // namespace blender::io
void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bmain)
Definition: scene.cc:2648
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
external readfile function prototypes.
Depsgraph * DEG_graph_new(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, eEvaluationMode mode)
Definition: depsgraph.cc:267
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
eEvaluationMode
Definition: DEG_depsgraph.h:44
@ DAG_EVAL_RENDER
Definition: DEG_depsgraph.h:46
void DEG_graph_build_for_all_objects(struct Depsgraph *graph)
Object is a sort of wrapper for general info.
std::string writer_type
used_writers & writers_map
void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode) override
AbstractHierarchyWriter * create_hair_writer(const HierarchyContext *) override
void release_writer(AbstractHierarchyWriter *writer) override
AbstractHierarchyWriter * create_particle_writer(const HierarchyContext *) override
AbstractHierarchyWriter * create_data_writer(const HierarchyContext *) override
TestingHierarchyIterator(Main *bmain, Depsgraph *depsgraph)
AbstractHierarchyWriter * create_transform_writer(const HierarchyContext *) override
std::string id_name(void *id)
const Depsgraph * depsgraph
TEST_F(AbstractHierarchyIteratorTest, ExportHierarchyTest)
struct Scene * curscene
Definition: BLO_readfile.h:64
struct ViewLayer * cur_view_layer
Definition: BLO_readfile.h:65
struct Main * main
Definition: BLO_readfile.h:56
Definition: BKE_main.h:121