Blender V4.5
usd_hierarchy_iterator.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4#include "usd.hh"
5
9#include "usd_skel_convert.hh"
11#include "usd_utils.hh"
14#include "usd_writer_camera.hh"
15#include "usd_writer_curves.hh"
16#include "usd_writer_hair.hh"
17#include "usd_writer_light.hh"
18#include "usd_writer_mesh.hh"
21#include "usd_writer_points.hh"
22#include "usd_writer_text.hh"
24#include "usd_writer_volume.hh"
25
26#include <string>
27
28#include "BKE_lib_id.hh"
29#include "BKE_main.hh"
30#include "BKE_report.hh"
31
32#include "BLI_assert.h"
33
34#include "DNA_layer_types.h"
35#include "DNA_object_types.h"
36
37namespace blender::io::usd {
38
40 Depsgraph *depsgraph,
41 pxr::UsdStageRefPtr stage,
43 : AbstractHierarchyIterator(bmain, depsgraph), stage_(stage), params_(params)
44{
45}
46
48{
49 if (params_.selected_objects_only && (object->base_flag & BASE_SELECTED) == 0) {
50 return true;
51 }
52
53 switch (object->type) {
54 case OB_EMPTY:
55 /* Always assume empties are being exported intentionally. */
56 return false;
57 case OB_MESH:
58 case OB_MBALL:
59 case OB_FONT:
60 return !params_.export_meshes;
61 case OB_CAMERA:
62 return !params_.export_cameras;
63 case OB_LAMP:
64 return !params_.export_lights;
66 case OB_CURVES:
67 return !params_.export_curves;
68 case OB_VOLUME:
69 return !params_.export_volumes;
70 case OB_ARMATURE:
71 return !params_.export_armatures;
72 case OB_POINTCLOUD:
73 return !params_.export_points;
74
75 default:
76 /* Assume weak for all other types. */
77 return true;
78 }
79}
80
82{
83 delete static_cast<USDAbstractWriter *>(writer);
84}
85
86std::string USDHierarchyIterator::make_valid_name(const std::string &name) const
87{
88 return make_safe_name(name, params_.allow_unicode);
89}
90
92{
93 skel_export_chaser(stage_,
94 armature_export_map_,
95 skinned_mesh_export_map_,
96 shape_key_mesh_export_map_,
98
99 create_skel_roots(stage_, params_);
100}
101
103{
104 /* The USD stage is already set up to have FPS time-codes per frame. */
105 export_time_ = pxr::UsdTimeCode(frame_nr);
106}
107
108USDExporterContext USDHierarchyIterator::create_usd_export_context(const HierarchyContext *context)
109{
110 pxr::SdfPath path;
111 if (params_.root_prim_path[0] != '\0') {
112 path = pxr::SdfPath(params_.root_prim_path + context->export_path);
113 }
114 else {
115 path = pxr::SdfPath(context->export_path);
116 }
117
118 if (params_.merge_parent_xform && context->is_object_data_context && !context->is_parent) {
119 bool can_merge_with_xform = true;
120 if (params_.export_shapekeys && is_mesh_with_shape_keys(context->object)) {
121 can_merge_with_xform = false;
122 }
123
124 if (params_.use_instancing && (context->is_prototype() || context->is_instance())) {
125 can_merge_with_xform = false;
126 }
127
128 if (can_merge_with_xform) {
129 path = path.GetParentPath();
130 }
131 }
132
133 /* Returns the same path that was passed to `stage_` object during it's creation (via
134 * `pxr::UsdStage::CreateNew` function). */
135 const pxr::SdfLayerHandle root_layer = stage_->GetRootLayer();
136 const std::string export_file_path = root_layer->GetRealPath();
137 auto get_time_code = [this]() { return this->export_time_; };
138
139 USDExporterContext exporter_context = USDExporterContext{
140 bmain_, depsgraph_, stage_, path, get_time_code, params_, export_file_path, nullptr};
141
142 /* Provides optional skel mapping hook. Now it's been used in USDPointInstancerWriter for write
143 * base layer. */
144 exporter_context.add_skel_mapping_fn = [this](const Object *obj, const pxr::SdfPath &path) {
145 this->add_usd_skel_export_mapping(obj, path);
146 };
147
148 return exporter_context;
149}
150
152{
153 if (!context) {
154 return;
155 }
156
157 if (context->object->type == OB_ARMATURE) {
158 return;
159 }
160
161 if (context->is_point_instancer()) {
162 /* Mark the point instancer's children as a point instance. */
163 USDExporterContext usd_export_context = create_usd_export_context(context);
164 ExportChildren *children = graph_children(context);
165
166 bool is_referencing_self = false;
167
168 pxr::SdfPath instancer_path;
169 if (strlen(params_.root_prim_path) != 0) {
170 instancer_path = pxr::SdfPath(std::string(params_.root_prim_path) + context->export_path);
171 }
172 else {
173 instancer_path = pxr::SdfPath(context->export_path);
174 }
175
176 if (children != nullptr) {
177 for (HierarchyContext *child_context : *children) {
178 if (!child_context->original_export_path.empty()) {
179 const pxr::SdfPath parent_export_path(context->export_path);
180 const pxr::SdfPath children_original_export_path(child_context->original_export_path);
181
182 /* Detect if the parent is referencing itself via a prototype. */
183 if (parent_export_path.HasPrefix(children_original_export_path)) {
184 is_referencing_self = true;
185 break;
186 }
187 }
188
189 pxr::SdfPath prototype_path;
190 if (child_context->is_instance() && child_context->duplicator != nullptr) {
191 /* When the current child context is point instancer's instance, use reference path
192 * (original_export_path) as the prototype path. */
193 if (strlen(params_.root_prim_path) != 0) {
194 prototype_path = pxr::SdfPath(std::string(params_.root_prim_path) +
195 child_context->original_export_path);
196 }
197 else {
198 prototype_path = pxr::SdfPath(child_context->original_export_path);
199 }
200
201 prototype_paths[instancer_path].insert(
202 std::make_pair(prototype_path, child_context->object));
203 child_context->is_point_instance = true;
204 }
205 else {
206 /* When the current child context is point instancer's prototype, use its own export path
207 * (export_path) as the prototype path. */
208 if (strlen(params_.root_prim_path) != 0) {
209 prototype_path = pxr::SdfPath(std::string(params_.root_prim_path) +
210 child_context->export_path);
211 }
212 else {
213 prototype_path = pxr::SdfPath(child_context->export_path);
214 }
215
216 prototype_paths[instancer_path].insert(
217 std::make_pair(prototype_path, child_context->object));
218 child_context->is_point_proto = true;
219 }
220 }
221 }
222
223 /* MARK: If the "Instance on Points" node uses an Object as a prototype,
224 * but the "Object Info" node has not enabled the "As Instance" option,
225 * then the generated reference path is incorrect and refers to itself. */
226 if (is_referencing_self) {
228 params_.worker_status->reports,
230 "One or more objects used as prototypes in 'Instance on Points' nodes either do not "
231 "have 'As Instance' enabled in their 'Object Info' nodes, or the prototype is the "
232 "base geometry input itself. Both cases prevent valid point instancer export. If it's "
233 "the former, enable 'As Instance' to avoid incorrect self-referencing.");
234
235 prototype_paths[instancer_path].clear();
236 for (HierarchyContext *child_context : *children) {
237 child_context->is_point_instance = false;
238 child_context->is_point_proto = false;
239 }
240 }
241 }
242}
243
245 const HierarchyContext *context)
246{
247 /* The transform writer is always called before data writers,
248 * so determine if the #Xform's children is a point instancer before writing data. */
249 if (params_.use_instancing) {
251 }
252
253 return new USDTransformWriter(create_usd_export_context(context));
254}
255
257{
258 USDExporterContext usd_export_context = create_usd_export_context(context);
259 USDAbstractWriter *data_writer = nullptr;
260 std::set<std::pair<pxr::SdfPath, Object *>> proto_paths =
261 prototype_paths[usd_export_context.usd_path.GetParentPath()];
262
263 switch (context->object->type) {
264 case OB_MESH:
265 if (usd_export_context.export_params.export_meshes) {
266 if (params_.use_instancing && context->is_point_instancer() && !proto_paths.empty()) {
267 USDExporterContext mesh_context = create_point_instancer_context(context,
268 usd_export_context);
269 std::unique_ptr<USDMeshWriter> mesh_writer = std::make_unique<USDMeshWriter>(
270 mesh_context);
271
272 data_writer = new USDPointInstancerWriter(
273 usd_export_context, proto_paths, std::move(mesh_writer));
274 }
275 else {
276 data_writer = new USDMeshWriter(usd_export_context);
277 }
278 }
279 else {
280 return nullptr;
281 }
282 break;
283 case OB_CAMERA:
284 if (usd_export_context.export_params.export_cameras) {
285 data_writer = new USDCameraWriter(usd_export_context);
286 }
287 else {
288 return nullptr;
289 }
290 break;
291 case OB_LAMP:
292 if (usd_export_context.export_params.export_lights) {
293 data_writer = new USDLightWriter(usd_export_context);
294 }
295 else {
296 return nullptr;
297 }
298 break;
299 case OB_MBALL:
300 data_writer = new USDMetaballWriter(usd_export_context);
301 break;
302 case OB_FONT:
303 data_writer = new USDTextWriter(usd_export_context);
304 break;
305 case OB_CURVES_LEGACY:
306 case OB_CURVES:
307 if (usd_export_context.export_params.export_curves) {
308 if (params_.use_instancing && context->is_point_instancer() && !proto_paths.empty()) {
309 USDExporterContext curves_context = create_point_instancer_context(context,
310 usd_export_context);
311 std::unique_ptr<USDCurvesWriter> curves_writer = std::make_unique<USDCurvesWriter>(
312 curves_context);
313
314 data_writer = new USDPointInstancerWriter(
315 usd_export_context, proto_paths, std::move(curves_writer));
316 }
317 else {
318 data_writer = new USDCurvesWriter(usd_export_context);
319 }
320 }
321 else {
322 return nullptr;
323 }
324 break;
325 case OB_VOLUME:
326 if (usd_export_context.export_params.export_volumes) {
327 data_writer = new USDVolumeWriter(usd_export_context);
328 }
329 else {
330 return nullptr;
331 }
332 break;
333 case OB_ARMATURE:
334 if (usd_export_context.export_params.export_armatures) {
335 data_writer = new USDArmatureWriter(usd_export_context);
336 }
337 else {
338 return nullptr;
339 }
340 break;
341 case OB_POINTCLOUD:
342 if (usd_export_context.export_params.export_points) {
343 if (params_.use_instancing && context->is_point_instancer() && !proto_paths.empty()) {
344 USDExporterContext point_cloud_context = create_point_instancer_context(
345 context, usd_export_context);
346 std::unique_ptr<USDPointsWriter> point_cloud_writer = std::make_unique<USDPointsWriter>(
347 point_cloud_context);
348
349 data_writer = new USDPointInstancerWriter(
350 usd_export_context, proto_paths, std::move(point_cloud_writer));
351 }
352 else {
353 data_writer = new USDPointsWriter(usd_export_context);
354 }
355 }
356 else {
357 return nullptr;
358 }
359 break;
360
361 case OB_EMPTY:
362 case OB_SURF:
363 case OB_SPEAKER:
364 case OB_LIGHTPROBE:
365 case OB_LATTICE:
366 case OB_GREASE_PENCIL:
367 return nullptr;
368
369 case OB_TYPE_MAX:
370 BLI_assert_msg(0, "OB_TYPE_MAX should not be used");
371 return nullptr;
372 default:
374 return nullptr;
375 }
376
377 if (data_writer && !data_writer->is_supported(context)) {
378 delete data_writer;
379 return nullptr;
380 }
381
382 if (data_writer && (params_.export_armatures || params_.export_shapekeys)) {
383 add_usd_skel_export_mapping(context->object, data_writer->usd_path());
384 }
385
386 return data_writer;
387}
388
390{
391 if (!params_.export_hair) {
392 return nullptr;
393 }
394 return new USDHairWriter(create_usd_export_context(context));
395}
396
398 const HierarchyContext * /*context*/)
399{
400 return nullptr;
401}
402
404{
405 /* Don't generate data writers for instances. */
406
407 return !(params_.use_instancing && context->is_instance());
408}
409
411{
412 /* Don't generate writers for children of instances. */
413
414 return !(params_.use_instancing && context->is_instance());
415}
416
417void USDHierarchyIterator::add_usd_skel_export_mapping(const Object *obj, const pxr::SdfPath &path)
418{
420 shape_key_mesh_export_map_.add(obj, path);
421 }
422
423 if (params_.export_armatures && obj->type == OB_ARMATURE) {
424 armature_export_map_.add(obj, path);
425 }
426
427 if (params_.export_armatures && obj->type == OB_MESH &&
429 {
430 skinned_mesh_export_map_.add(obj, path);
431 }
432}
433
434USDExporterContext USDHierarchyIterator::create_point_instancer_context(
435 const HierarchyContext *context, const USDExporterContext &usd_export_context)
436{
437 BLI_assert(context && context->object);
438 std::string base_name = std::string(BKE_id_name(context->object->id)).append("_base");
439 std::string safe_name = make_safe_name(base_name,
440 usd_export_context.export_params.allow_unicode);
441
442 pxr::SdfPath base_path = usd_export_context.usd_path.GetParentPath().AppendChild(
443 pxr::TfToken(safe_name));
444
445 USDExporterContext new_context = usd_export_context;
446 *const_cast<pxr::SdfPath *>(&new_context.usd_path) = base_path;
447
448 return new_context;
449}
450
451} // namespace blender::io::usd
const char * BKE_id_name(const ID &id)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
Object is a sort of wrapper for general info.
@ OB_SPEAKER
@ OB_LATTICE
@ OB_MBALL
@ OB_EMPTY
@ OB_SURF
@ OB_CAMERA
@ OB_FONT
@ OB_GREASE_PENCIL
@ OB_TYPE_MAX
@ OB_ARMATURE
@ OB_LAMP
@ OB_MESH
@ OB_POINTCLOUD
@ OB_VOLUME
@ OB_CURVES_LEGACY
@ OB_CURVES
@ OB_LIGHTPROBE
struct Object Object
#define BASE_SELECTED(v3d, base)
BPy_StructRNA * depsgraph
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
ExportChildren * graph_children(const HierarchyContext *context)
AbstractHierarchyIterator(Main *bmain, Depsgraph *depsgraph)
blender::Set< HierarchyContext * > ExportChildren
const pxr::SdfPath & usd_path() const
virtual bool is_supported(const HierarchyContext *context) const
bool mark_as_weak_export(const Object *object) const override
AbstractHierarchyWriter * create_data_writer(const HierarchyContext *context) override
void release_writer(AbstractHierarchyWriter *writer) override
AbstractHierarchyWriter * create_particle_writer(const HierarchyContext *context) override
USDHierarchyIterator(Main *bmain, Depsgraph *depsgraph, pxr::UsdStageRefPtr stage, const USDExportParams &params)
AbstractHierarchyWriter * create_hair_writer(const HierarchyContext *context) override
bool include_child_writers(const HierarchyContext *context) const override
void determine_point_instancers(const HierarchyContext *context)
bool include_data_writers(const HierarchyContext *context) const override
std::string make_valid_name(const std::string &name) const override
AbstractHierarchyWriter * create_transform_writer(const HierarchyContext *context) override
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int context(const bContext *C, const char *member, bContextDataResult *result)
void skel_export_chaser(pxr::UsdStageRefPtr stage, const ObjExportMap &armature_export_map, const ObjExportMap &skinned_mesh_export_map, const ObjExportMap &shape_key_mesh_export_map, const Depsgraph *depsgraph)
std::string make_safe_name(const StringRef name, bool allow_unicode)
Definition usd_utils.cc:18
void create_skel_roots(pxr::UsdStageRefPtr stage, const USDExportParams &params)
bool can_export_skinned_mesh(const Object &obj, const Depsgraph *depsgraph)
bool is_mesh_with_shape_keys(const Object *obj)
short base_flag
std::function< void(const Object *, const pxr::SdfPath &)> add_skel_mapping_fn