Blender V5.0
usd_instancing_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 NVIDIA Corporation. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "usd.hh"
8#include "usd_hash_types.hh"
9#include "usd_utils.hh"
10
11#include "BLI_map.hh"
12#include "BLI_set.hh"
13
14#include <pxr/usd/sdf/copyUtils.h>
15#include <pxr/usd/sdf/path.h>
16#include <pxr/usd/usd/primCompositionQuery.h>
17#include <pxr/usd/usd/primRange.h>
18#include <pxr/usd/usd/references.h>
19
20#include <string>
21
22#include "CLG_log.h"
23static CLG_LogRef LOG = {"io.usd"};
24
25namespace blender::io::usd {
26
27/* We need an ordered map so we use std::map. */
28using PathMap = std::map<pxr::SdfPath, pxr::SdfPath>;
30
31/* Map an instanceable prim path to a list of prototype prim paths. */
33
34/* Convert the given prototype prim to an instance by deleting its children and making
35 * it an instanceable reference to the prim at ref_path. */
36static void convert_proto_to_instance(pxr::UsdStageRefPtr stage,
37 const pxr::SdfPath &proto_path,
38 const pxr::SdfPath &ref_path)
39{
40 pxr::UsdPrim proto_prim = stage->GetPrimAtPath(proto_path);
41
42 if (!proto_prim) {
43 CLOG_ERROR(&LOG, "Couldn't find prototype prim %s", proto_path.GetAsString().c_str());
44 return;
45 }
46
47 /* Collect child paths. */
48 pxr::SdfPathVector child_paths;
49 pxr::UsdPrimSiblingRange children = proto_prim.GetFilteredChildren(
50 pxr::Usd_PrimFlagsPredicate());
51 for (const auto &child_prim : children) {
52 child_paths.push_back(child_prim.GetPath());
53 }
54
55 /* Remove children from the sage. */
56 for (const pxr::SdfPath &child_path : child_paths) {
57 stage->RemovePrim(child_path);
58 }
59
60 proto_prim.GetReferences().AddInternalReference(pxr::SdfPath(ref_path));
61 proto_prim.SetInstanceable(true);
62}
63
64void process_scene_graph_instances(const USDExportParams &export_params, pxr::UsdStageRefPtr stage)
65{
66 if (!stage) {
67 return;
68 }
69
70 /* Collect paths to instanceable references and prototypes. */
71 PathSet protos;
72 /* Map an instance to the prototypes it references. */
73 ReferencesMap references_map;
74
75 pxr::UsdPrimRange range(stage->GetPseudoRoot());
76 for (pxr::UsdPrim prim : range) {
77 if (prim.IsInstanceable()) {
78 /* Get the prototypes referenced by this prim. */
79 pxr::UsdPrimCompositionQuery query = pxr::UsdPrimCompositionQuery::GetDirectReferences(prim);
80 Vector<pxr::SdfPath> references;
81 for (const auto &arc : query.GetCompositionArcs()) {
82 pxr::SdfPath target_prim_path = arc.GetTargetPrimPath();
83 protos.add(target_prim_path);
84 references.append(target_prim_path);
85 }
86 references_map.add(prim.GetPath(), references);
87 }
88 }
89
90 if (protos.is_empty()) {
91 /* No prototypes to move. */
92 return;
93 }
94
95 /* Map an original prototype path to the location where it will be copied. */
96 PathMap proto_to_copy_map;
97
98 std::string protos_root_str(export_params.root_prim_path);
99 protos_root_str += "/prototypes";
100 pxr::SdfPath protos_root_path = get_unique_path(stage, protos_root_str);
101
102 /* Create the abstract prim under which prototypes will be copied. */
103 if (!stage->CreateClassPrim(protos_root_path)) {
104 CLOG_ERROR(&LOG, "Couldn't create class prim %s.", protos_root_path.GetAsString().c_str());
105 return;
106 }
107
108 /*
109 * For each original prototype, create a placeholder Xform prim under the protos root
110 * which will be the new location where the prototype will be copied.
111 */
112 for (const pxr::SdfPath &proto_path : protos) {
113 pxr::SdfPath copy_path = protos_root_path;
114
115 copy_path = copy_path.AppendChild(proto_path.GetNameToken());
116 copy_path = get_unique_path(stage, copy_path.GetAsString());
117
118 /* Create the placeholder prim. */
119 static pxr::TfToken xform_type_tok("Xform");
120 pxr::UsdPrim dest_prim = stage->DefinePrim(copy_path, xform_type_tok);
121 if (!dest_prim) {
123 "Couldn't create destination prim %s for copying prototype %s",
124 copy_path.GetAsString().c_str(),
125 proto_path.GetAsString().c_str());
126 continue;
127 }
128
129 /* Record where original prototype path will be copied. */
130 proto_to_copy_map.insert(std::make_pair(proto_path, dest_prim.GetPath()));
131 }
132
133 /* Update all references to point to new prototype locations. */
134 for (const auto item : references_map.items()) {
135 pxr::SdfPath inst_path = item.key;
136 pxr::UsdPrim inst_prim = stage->GetPrimAtPath(item.key);
137 if (!inst_prim) {
138 CLOG_ERROR(&LOG, "Couldn't get prim for instance %s.", inst_path.GetAsString().c_str());
139 continue;
140 }
141
142 /* Updated references pointing to new prototype locations. */
143 Vector<pxr::SdfPath> new_ref_targets;
144 const Vector<pxr::SdfPath> &ref_targets = item.value;
145 for (const pxr::SdfPath &target_path : ref_targets) {
146 PathMap::const_iterator iter = proto_to_copy_map.find(target_path);
147 if (iter != proto_to_copy_map.end()) {
148 new_ref_targets.append(iter->second);
149 }
150 }
151
152 /* Replace existing references with the updated ones. */
153 if (!new_ref_targets.is_empty()) {
154 pxr::UsdReferences refs = inst_prim.GetReferences();
155 refs.ClearReferences();
156 for (const pxr::SdfPath &target : new_ref_targets) {
157 refs.AddInternalReference(target);
158 }
159 }
160 }
161
162 /*
163 * Copy the original prototypes to their new locations and update
164 * the original prototype roots to be references to the new locations.
165 * Since prototypes may be nested, we must copy the most nested prototypes
166 * first by iterating backwards through the sorted prototype map.
167 */
168 for (PathMap::reverse_iterator riter = proto_to_copy_map.rbegin();
169 riter != proto_to_copy_map.rend();
170 ++riter)
171 {
172 const pxr::SdfPath &src_path = riter->first;
173 const pxr::SdfPath &dst_path = riter->second;
174 if (!pxr::SdfCopySpec(
175 stage->GetRootLayer(), riter->first, stage->GetRootLayer(), riter->second))
176 {
177 CLOG_WARN(&LOG,
178 "Couldn't copy prim %s to %s",
179 src_path.GetAsString().c_str(),
180 dst_path.GetAsString().c_str());
181 continue;
182 }
183
184 convert_proto_to_instance(stage, src_path, dst_path);
185 }
186}
187
188} // namespace blender::io::usd
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
ItemIterator items() const &
Definition BLI_map.hh:902
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_empty() const
Definition BLI_set.hh:595
void append(const T &value)
bool is_empty() const
#define LOG(level)
Definition log.h:97
pxr::SdfPath get_unique_path(pxr::UsdStageRefPtr stage, const std::string &path)
Definition usd_utils.cc:61
Map< pxr::SdfPath, Vector< pxr::SdfPath > > ReferencesMap
static void convert_proto_to_instance(pxr::UsdStageRefPtr stage, const pxr::SdfPath &proto_path, const pxr::SdfPath &ref_path)
void process_scene_graph_instances(const USDExportParams &export_params, pxr::UsdStageRefPtr stage)
Set< pxr::SdfPath > PathSet
std::map< pxr::SdfPath, pxr::SdfPath > PathMap