1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/pxr.h"
25 #include "pxr/usd/usd/prim.h"
26
27 #include "pxr/usd/usd/apiSchemaBase.h"
28 #include "pxr/usd/usd/inherits.h"
29 #include "pxr/usd/usd/instanceCache.h"
30 #include "pxr/usd/usd/payloads.h"
31 #include "pxr/usd/usd/primRange.h"
32 #include "pxr/usd/usd/relationship.h"
33 #include "pxr/usd/usd/references.h"
34 #include "pxr/usd/usd/resolver.h"
35 #include "pxr/usd/usd/schemaBase.h"
36 #include "pxr/usd/usd/schemaRegistry.h"
37 #include "pxr/usd/usd/specializes.h"
38 #include "pxr/usd/usd/stage.h"
39 #include "pxr/usd/usd/tokens.h"
40 #include "pxr/usd/usd/variantSets.h"
41
42 #include "pxr/usd/pcp/layerStack.h"
43 #include "pxr/usd/pcp/primIndex.h"
44 #include "pxr/usd/sdf/primSpec.h"
45
46 #include "pxr/base/plug/registry.h"
47 #include "pxr/base/work/dispatcher.h"
48 #include "pxr/base/work/loops.h"
49 #include "pxr/base/work/singularTask.h"
50 #include "pxr/base/work/withScopedParallelism.h"
51
52 #include "pxr/base/tf/ostreamMethods.h"
53
54 #include <boost/functional/hash.hpp>
55
56 #include <tbb/concurrent_queue.h>
57 #include <tbb/concurrent_unordered_set.h>
58 #include <tbb/parallel_sort.h>
59
60 #include <algorithm>
61 #include <functional>
62 #include <vector>
63
64 PXR_NAMESPACE_OPEN_SCOPE
65
66 UsdPrim
GetChild(const TfToken & name) const67 UsdPrim::GetChild(const TfToken &name) const
68 {
69 return GetStage()->GetPrimAtPath(GetPath().AppendChild(name));
70 }
71
72 bool
_IsA(const TfType & schemaType,bool validateSchemaType) const73 UsdPrim::_IsA(const TfType& schemaType, bool validateSchemaType) const
74 {
75 if (validateSchemaType) {
76 // Check Schema TfType
77 if (schemaType.IsUnknown()) {
78 TF_CODING_ERROR("Unknown schema type (%s) is invalid for IsA query",
79 schemaType.GetTypeName().c_str());
80 return false;
81 }
82 }
83
84 // Check that the actual schema type of the prim (accounts for fallback
85 // types for types with no schema) is or derives from the passed in type.
86 return GetPrimTypeInfo().GetSchemaType().IsA(schemaType);
87 }
88
89 bool
IsA(const TfType & schemaType) const90 UsdPrim::IsA(const TfType& schemaType) const
91 {
92 return _IsA(schemaType, true);
93 }
94
95 bool
_HasSingleApplyAPI(const TfType & schemaType) const96 UsdPrim::_HasSingleApplyAPI(const TfType& schemaType) const
97 {
98 TRACE_FUNCTION();
99
100 // Get our composed set of all applied schemas.
101 auto appliedSchemas = GetAppliedSchemas();
102 if (appliedSchemas.empty()) {
103 return false;
104 }
105
106 // The compile time and runtime schema type validation of HasAPI should
107 // ensure that this schemaName won't be empty so we don't check for that
108 // here.
109 const TfToken schemaName =
110 UsdSchemaRegistry::GetAPISchemaTypeName(schemaType);
111
112 // Since this is a single apply API we're just looking for schemaName being
113 // in the list.
114 return std::find(appliedSchemas.begin(), appliedSchemas.end(), schemaName)
115 != appliedSchemas.end();
116 }
117
118 bool
_HasMultiApplyAPI(const TfType & schemaType,const TfToken & instanceName) const119 UsdPrim::_HasMultiApplyAPI(const TfType& schemaType,
120 const TfToken &instanceName) const
121 {
122 TRACE_FUNCTION();
123
124 // Get our composed set of all applied schemas.
125 auto appliedSchemas = GetAppliedSchemas();
126 if (appliedSchemas.empty()) {
127 return false;
128 }
129
130 // The compile time and runtime schema type validation of HasAPI should
131 // ensure that this schemaName won't be empty so we don't check for that
132 // here.
133 const TfToken schemaName =
134 UsdSchemaRegistry::GetAPISchemaTypeName(schemaType);
135
136 // If instance name is empty, we're looking for any instance of a multiple
137 // apply schema of the schemaType. Thus we search for name in the list that
138 // starts with the "<schemaName>:" prefix.
139 if (instanceName.IsEmpty()) {
140 const std::string schemaPrefix =
141 schemaName.GetString() + UsdObject::GetNamespaceDelimiter();
142 return std::any_of(appliedSchemas.begin(), appliedSchemas.end(),
143 [&schemaPrefix](const TfToken &appliedSchema) {
144 return TfStringStartsWith(appliedSchema, schemaPrefix);
145 });
146 }
147
148 // Otherwise we have an instance name so we're looking for exact match of
149 // "<schemaType>:<instanceName>"
150 const TfToken apiName(SdfPath::JoinIdentifier(schemaName, instanceName));
151 return std::find(appliedSchemas.begin(), appliedSchemas.end(), apiName)
152 != appliedSchemas.end();
153 }
154
155 bool
HasAPI(const TfType & schemaType,const TfToken & instanceName) const156 UsdPrim::HasAPI(const TfType& schemaType, const TfToken& instanceName) const
157 {
158 if (schemaType.IsUnknown()) {
159 TF_CODING_ERROR("HasAPI: Invalid unknown schema type (%s) ",
160 schemaType.GetTypeName().c_str());
161 return false;
162 }
163
164 if (!UsdSchemaRegistry::GetInstance().IsAppliedAPISchema(schemaType)) {
165 TF_CODING_ERROR("HasAPI: provided schema type ( %s ) is not an "
166 "applied API schema type.", schemaType.GetTypeName().c_str());
167 return false;
168 }
169
170 static const auto apiSchemaBaseType = TfType::Find<UsdAPISchemaBase>();
171 if (!schemaType.IsA(apiSchemaBaseType) || schemaType == apiSchemaBaseType) {
172 TF_CODING_ERROR("HasAPI: provided schema type ( %s ) does not "
173 "derive from UsdAPISchemaBase.",
174 schemaType.GetTypeName().c_str());
175 return false;
176 }
177
178 // If the type is a multi apply API call the multi apply implementation.
179 if (UsdSchemaRegistry::GetInstance().IsMultipleApplyAPISchema(schemaType)) {
180 return _HasMultiApplyAPI(schemaType, instanceName);
181 }
182
183 // Otherwise it's a single apply API
184 if (!instanceName.IsEmpty()) {
185 TF_CODING_ERROR("HasAPI: single application API schemas like %s do "
186 "not contain an application instanceName ( %s ).",
187 schemaType.GetTypeName().c_str(), instanceName.GetText());
188 return false;
189 }
190 return _HasSingleApplyAPI(schemaType);
191 }
192
193 // Runtime validation for the single apply schema non-templated
194 // ApplyAPI/CanApplyAPI/RemoveAPI functions. The templated versions perform this
195 // verification through compile time asserts.
196 static bool
_ValidateSingleApplySchemaType(const TfType & schemaType,std::string * reason)197 _ValidateSingleApplySchemaType(
198 const TfType& schemaType, std::string *reason)
199 {
200 if (UsdSchemaRegistry::GetSchemaKind(schemaType) !=
201 UsdSchemaKind::SingleApplyAPI) {
202 if (reason) {
203 *reason = TfStringPrintf(
204 "Provided schema type '%s' is not a single-apply API schema "
205 "type.",
206 schemaType.GetTypeName().c_str());
207 }
208 return false;
209 }
210 return true;
211 }
212
213 // Runtime validation for the multiple apply schema non-templated
214 // ApplyAPI/CanApplyAPI/RemoveAPI functions. The templated versions perform this
215 // verification through compile time asserts.
216 static bool
_ValidateMultipleApplySchemaType(const TfType & schemaType,std::string * reason)217 _ValidateMultipleApplySchemaType(
218 const TfType& schemaType, std::string *reason)
219 {
220 if (UsdSchemaRegistry::GetSchemaKind(schemaType) !=
221 UsdSchemaKind::MultipleApplyAPI) {
222 if (reason) {
223 *reason = TfStringPrintf(
224 "Provided schema type '%s' is not a mutiple-apply API schema "
225 "type.",
226 schemaType.GetTypeName().c_str());
227 }
228 return false;
229 }
230 return true;
231 }
232
233 // Determines whether the given prim type can have the given API schema applied
234 // to it based on a list of types the API "can only be applied to". If the
235 // list is empty, this always returns true, otherwise the prim type must be in
236 // the list or derived from a type in the list.
237 static bool
_IsPrimTypeValidApplyToTarget(const TfType & primType,const TfToken & apiSchemaTypeName,const TfToken & instanceName,std::string * whyNot)238 _IsPrimTypeValidApplyToTarget(const TfType &primType,
239 const TfToken &apiSchemaTypeName,
240 const TfToken &instanceName,
241 std::string *whyNot)
242 {
243 // Get the list of prim types this API "can only apply to" if any.
244 const TfTokenVector &canOnlyApplyToTypes =
245 UsdSchemaRegistry::GetAPISchemaCanOnlyApplyToTypeNames(
246 apiSchemaTypeName, instanceName);
247
248 // If no "can only apply to" types are found, the schema can be
249 // applied to any prim type (including empty or invalid prims types)
250 if (canOnlyApplyToTypes.empty()) {
251 return true;
252 }
253
254 // If the prim type or any of its ancestor types are in the list, then it's
255 // valid!
256 if (!primType.IsUnknown()) {
257 for (const TfToken &allowedPrimTypeName : canOnlyApplyToTypes) {
258 const TfType allowedPrimType =
259 UsdSchemaRegistry::GetTypeFromSchemaTypeName(
260 allowedPrimTypeName);
261 if (primType.IsA(allowedPrimType)) {
262 return true;
263 }
264 }
265 }
266
267 // Otherwise, it wasn't in the list and can't be applied to.
268 if (whyNot) {
269 *whyNot = TfStringPrintf(
270 "API schema '%s' can only be applied to prims of the following "
271 "types: %s.",
272 SdfPath::JoinIdentifier(apiSchemaTypeName, instanceName).c_str(),
273 TfStringJoin(canOnlyApplyToTypes.begin(),
274 canOnlyApplyToTypes.end(), ", ").c_str());
275 }
276 return false;
277 }
278
279
280 bool
_CanApplyAPI(const TfType & apiSchemaType,std::string * whyNot) const281 UsdPrim::_CanApplyAPI(const TfType& apiSchemaType,
282 std::string *whyNot) const
283 {
284 // The callers of this function will have already validated the schema is
285 // single apply either through static asserts or runtime validation
286 // _ValidateSingleApplySchemaType.
287
288 // Can't apply API schemas to an invalid prim
289 if (!IsValid()) {
290 if (whyNot) {
291 *whyNot = "Prim is not valid.";
292 }
293 return false;
294 }
295
296 // Return whether this prim's type is a valid target for applying the given
297 // API schema.
298 const TfToken apiSchemaTypeName =
299 UsdSchemaRegistry::GetSchemaTypeName(apiSchemaType);
300 return _IsPrimTypeValidApplyToTarget(
301 GetPrimTypeInfo().GetSchemaType(),
302 apiSchemaTypeName,
303 /*instanceName=*/ TfToken(),
304 whyNot);
305 }
306
307 bool
_CanApplyAPI(const TfType & apiSchemaType,const TfToken & instanceName,std::string * whyNot) const308 UsdPrim::_CanApplyAPI(const TfType& apiSchemaType,
309 const TfToken& instanceName,
310 std::string *whyNot) const
311 {
312 // The callers of this function will have already validated the schema is
313 // mulitple apply either through static asserts or runtime validation
314 // _ValidateMultipleApplySchemaType.
315
316 // Instance name can only be validated at runtime. All API schema functions
317 // treat an empty instance for a multiple apply schema as a coding error.
318 if (instanceName.IsEmpty()) {
319 TF_CODING_ERROR("CanApplyAPI: for multiple apply API schema %s, a "
320 "non-empty instance name must be provided.",
321 apiSchemaType.GetTypeName().c_str());
322 return false;
323 }
324
325 // Can't apply API schemas to an invalid prim
326 if (!IsValid()) {
327 if (whyNot) {
328 *whyNot = "Prim is not valid.";
329 }
330 return false;
331 }
332
333 const TfToken apiSchemaTypeName =
334 UsdSchemaRegistry::GetSchemaTypeName(apiSchemaType);
335
336 // Multiple apply API schemas may have limitations on what instance names
337 // are allowed to be used. Check if the requested instance name is valid.
338 if (!UsdSchemaRegistry::IsAllowedAPISchemaInstanceName(
339 apiSchemaTypeName, instanceName)) {
340 if (whyNot) {
341 *whyNot = TfStringPrintf(
342 "'%s' is not an allowed instance name for multiple apply API "
343 "schema '%s'.",
344 instanceName.GetText(), apiSchemaTypeName.GetText());
345 }
346 return false;
347 }
348
349 // Return whether this prim's type is a valid target for applying the given
350 // API schema and instance name.
351 return _IsPrimTypeValidApplyToTarget(
352 GetPrimTypeInfo().GetSchemaType(),
353 apiSchemaTypeName,
354 instanceName,
355 whyNot);
356 }
357
358 bool
CanApplyAPI(const TfType & schemaType,std::string * whyNot) const359 UsdPrim::CanApplyAPI(const TfType& schemaType,
360 std::string *whyNot) const
361 {
362 // Validate that this function is being called on a single apply API schema.
363 // Failure is a coding error as the matching template function would fail
364 // to compile.
365 std::string errorMsg;
366 if (!_ValidateSingleApplySchemaType(schemaType, &errorMsg)) {
367 TF_CODING_ERROR("CanApplyAPI: %s", errorMsg.c_str());
368 if (whyNot) {
369 *whyNot = std::move(errorMsg);
370 }
371 return false;
372 }
373 return _CanApplyAPI(schemaType, whyNot);
374 }
375
376 bool
CanApplyAPI(const TfType & schemaType,const TfToken & instanceName,std::string * whyNot) const377 UsdPrim::CanApplyAPI(const TfType& schemaType,
378 const TfToken& instanceName,
379 std::string *whyNot) const
380 {
381 // Validate that this function is being called on a multiple apply API
382 // schema. Failure is a coding error as the matching template function would
383 // fail to compile.
384 std::string errorMsg;
385 if (!_ValidateMultipleApplySchemaType(schemaType, &errorMsg)) {
386 TF_CODING_ERROR("CanApplyAPI: %s", errorMsg.c_str());
387 if (whyNot) {
388 *whyNot = std::move(errorMsg);
389 }
390 return false;
391 }
392 return _CanApplyAPI(schemaType, instanceName, whyNot);
393 }
394
395 bool
_ApplyAPI(const TfType & schemaType) const396 UsdPrim::_ApplyAPI(const TfType& schemaType) const
397 {
398 // The callers of this function will have already validated the schema is
399 // single apply either through static asserts or runtime validation
400 // _ValidateSingleApplySchemaType.
401
402 // Validate the prim to protect against crashes in the schema generated
403 // SchemaClass::Apply(const UsdPrim &prim) functions when called with a null
404 // prim as these generated functions call prim.ApplyAPI<SchemaClass>
405 //
406 // While we don't typically validate "this" prim for public UsdPrim C++ API,
407 // for performance reasons, we opt to do so here since ApplyAPI isn't
408 // performance critical. If ApplyAPI becomes performance critical in the
409 // future, we may have to move this validation elsewhere if this validation
410 // is problematic.
411 if (!IsValid()) {
412 TF_CODING_ERROR("Invalid prim '%s'", GetDescription().c_str());
413 return false;
414 }
415
416 const TfToken typeName = UsdSchemaRegistry::GetSchemaTypeName(schemaType);
417 return AddAppliedSchema(typeName);
418 }
419
420 bool
_ApplyAPI(const TfType & schemaType,const TfToken & instanceName) const421 UsdPrim::_ApplyAPI(const TfType& schemaType, const TfToken& instanceName) const
422 {
423 // The callers of this function will have already validated the schema is
424 // mulitple apply either through static asserts or runtime validation
425 // _ValidateMultipleApplySchemaType.
426
427 // Instance name can only be validated at runtime. All API schema functions
428 // treat an empty instance for a multiple apply schema as a coding error.
429 if (instanceName.IsEmpty()) {
430 TF_CODING_ERROR("ApplyAPI: for mutiple apply API schema %s, a "
431 "non-empty instance name must be provided.",
432 schemaType.GetTypeName().c_str());
433 return false;
434 }
435
436 // Validate the prim to protect against crashes in the schema generated
437 // SchemaClass::Apply(const UsdPrim &prim) functions when called with a null
438 // prim as these generated functions call prim.ApplyAPI<SchemaClass>
439 //
440 // While we don't typically validate "this" prim for public UsdPrim C++ API,
441 // for performance reasons, we opt to do so here since ApplyAPI isn't
442 // performance critical. If ApplyAPI becomes performance critical in the
443 // future, we may have to move this validation elsewhere if this validation
444 // is problematic.
445 if (!IsValid()) {
446 TF_CODING_ERROR("Invalid prim '%s'", GetDescription().c_str());
447 return false;
448 }
449
450 const TfToken typeName = UsdSchemaRegistry::GetSchemaTypeName(schemaType);
451 TfToken apiName(SdfPath::JoinIdentifier(typeName, instanceName));
452 return AddAppliedSchema(apiName);
453 }
454
455 bool
ApplyAPI(const TfType & schemaType) const456 UsdPrim::ApplyAPI(const TfType& schemaType) const
457 {
458 // Validate that this function is being called on a single apply API schema.
459 // Failure is a coding error as the matching template function would fail
460 // to compile.
461 std::string errorMsg;
462 if (!_ValidateSingleApplySchemaType(schemaType, &errorMsg)) {
463 TF_CODING_ERROR("ApplyAPI: %s", errorMsg.c_str());
464 return false;
465 }
466 return _ApplyAPI(schemaType);
467 }
468
469 bool
ApplyAPI(const TfType & schemaType,const TfToken & instanceName) const470 UsdPrim::ApplyAPI(const TfType& schemaType, const TfToken& instanceName) const
471 {
472 // Validate that this function is being called on a multiple apply API
473 // schema. Failure is a coding error as the matching template function would
474 // fail to compile.
475 std::string errorMsg;
476 if (!_ValidateMultipleApplySchemaType(schemaType, &errorMsg)) {
477 TF_CODING_ERROR("ApplyAPI: %s", errorMsg.c_str());
478 return false;
479 }
480 return _ApplyAPI(schemaType, instanceName);
481 }
482
483 bool
_RemoveAPI(const TfType & schemaType) const484 UsdPrim::_RemoveAPI(const TfType& schemaType) const
485 {
486 // The callers of this function will have already validated the schema is
487 // single apply either through static asserts or runtime validation
488 // _ValidateSingleApplySchemaType.
489
490 const TfToken typeName = UsdSchemaRegistry::GetSchemaTypeName(schemaType);
491 return RemoveAppliedSchema(typeName);
492 }
493
494 bool
_RemoveAPI(const TfType & schemaType,const TfToken & instanceName) const495 UsdPrim::_RemoveAPI(const TfType& schemaType, const TfToken& instanceName) const
496 {
497 // The callers of this function will have already validated the schema is
498 // mulitple apply either through static asserts or runtime validation
499 // _ValidateMultipleApplySchemaType.
500
501 // Instance name can only be validated at runtime. All API schema functions
502 // treat an empty instance for a multiple apply schema as a coding error.
503 if (instanceName.IsEmpty()) {
504 TF_CODING_ERROR("RemoveAPI: for mutiple apply API schema %s, a "
505 "non-empty instance name must be provided.",
506 schemaType.GetTypeName().c_str());
507 return false;
508 }
509
510 const TfToken typeName = UsdSchemaRegistry::GetSchemaTypeName(schemaType);
511 TfToken apiName(SdfPath::JoinIdentifier(typeName, instanceName));
512 return RemoveAppliedSchema(apiName);
513 }
514
515 bool
RemoveAPI(const TfType & schemaType) const516 UsdPrim::RemoveAPI(const TfType& schemaType) const
517 {
518 // Validate that this function is being called on a single apply API schema.
519 // Failure is a coding error as the matching template function would fail
520 // to compile.
521 std::string errorMsg;
522 if (!_ValidateSingleApplySchemaType(schemaType, &errorMsg)) {
523 TF_CODING_ERROR("RemoveAPI: %s", errorMsg.c_str());
524 return false;
525 }
526 return _RemoveAPI(schemaType);
527 }
528
529 bool
RemoveAPI(const TfType & schemaType,const TfToken & instanceName) const530 UsdPrim::RemoveAPI(const TfType& schemaType, const TfToken& instanceName) const
531 {
532 // Validate that this function is being called on a multiple apply API
533 // schema. Failure is a coding error as the matching template function would
534 // fail to compile.
535 std::string errorMsg;
536 if (!_ValidateMultipleApplySchemaType(schemaType, &errorMsg)) {
537 TF_CODING_ERROR("RemoveAPI: %s", errorMsg.c_str());
538 return false;
539 }
540 return _RemoveAPI(schemaType, instanceName);
541 }
542
543 bool
AddAppliedSchema(const TfToken & appliedSchemaName) const544 UsdPrim::AddAppliedSchema(const TfToken &appliedSchemaName) const
545 {
546 // This should find or create the primSpec in the current edit target.
547 // It will also issue an error if it's unable to.
548 SdfPrimSpecHandle primSpec = _GetStage()->_CreatePrimSpecForEditing(*this);
549
550 // _CreatePrimSpecForEditing would have already issued a runtime error
551 // in case of a failure.
552 if (!primSpec) {
553 TF_WARN("Unable to create primSpec at path <%s> in edit target '%s'. "
554 "Failed to add applied API schema.",
555 GetPath().GetText(),
556 _GetStage()->GetEditTarget().GetLayer()->GetIdentifier().c_str());
557 return false;
558 }
559
560 auto _HasItem = [](const TfTokenVector &items, const TfToken &item) {
561 return std::find(items.begin(), items.end(), item) != items.end();
562 };
563
564 SdfTokenListOp listOp =
565 primSpec->GetInfo(UsdTokens->apiSchemas).Get<SdfTokenListOp>();
566
567 if (listOp.IsExplicit()) {
568 // If the list op is explicit we check if the explicit item to see if
569 // our name is already in it. We'll add it to the end of the explicit
570 // list if it is not.
571 const TfTokenVector &items = listOp.GetExplicitItems();
572 if (_HasItem(items, appliedSchemaName)) {
573 return true;
574 }
575 // Use ReplaceOperations to append in place.
576 if (!listOp.ReplaceOperations(SdfListOpTypeExplicit,
577 items.size(), 0, {appliedSchemaName})) {
578 return false;
579 }
580 } else {
581 // Otherwise our name could be in the append or prepend list (we
582 // purposefully ignore the "add" list which is deprecated) so we check
583 // both before adding it to the end of prepends.
584 const TfTokenVector &preItems = listOp.GetPrependedItems();
585 const TfTokenVector &appItems = listOp.GetAppendedItems();
586 if (_HasItem(preItems, appliedSchemaName) ||
587 _HasItem(appItems, appliedSchemaName)) {
588 return true;
589 }
590 // Use ReplaceOperations to append in place.
591 if (!listOp.ReplaceOperations(SdfListOpTypePrepended,
592 preItems.size(), 0, {appliedSchemaName})) {
593 return false;
594 }
595 }
596
597 // If we got here, we edited the list op, so author it back to the spec.
598 primSpec->SetInfo(UsdTokens->apiSchemas, VtValue::Take(listOp));
599 return true;
600 }
601
602 bool
RemoveAppliedSchema(const TfToken & appliedSchemaName) const603 UsdPrim::RemoveAppliedSchema(const TfToken &appliedSchemaName) const
604 {
605 // This should create the primSpec in the current edit target.
606 // It will also issue an error if it's unable to.
607 SdfPrimSpecHandle primSpec = _GetStage()->_CreatePrimSpecForEditing(*this);
608
609 // _CreatePrimSpecForEditing would have already issued a runtime error
610 // in case of a failure.
611 if (!primSpec) {
612 TF_WARN("Unable to create primSpec at path <%s> in edit target '%s'. "
613 "Failed to remove applied API schema.",
614 GetPath().GetText(),
615 _GetStage()->GetEditTarget().GetLayer()->GetIdentifier().c_str());
616 return false;
617 }
618
619 SdfTokenListOp listOp =
620 primSpec->GetInfo(UsdTokens->apiSchemas).Get<SdfTokenListOp>();
621
622 // Create a list op that deletes our schema name and apply it to the current
623 // apiSchemas list op for the edit prim spec. This will take care of making
624 // sure it ends up in the deletes list (for non-explicit list ops) and is
625 // removed from any other items list that would add it back.
626 SdfTokenListOp editListOp;
627 editListOp.SetDeletedItems({appliedSchemaName});
628 if (auto result = editListOp.ApplyOperations(listOp)) {
629 primSpec->SetInfo(UsdTokens->apiSchemas, VtValue(*result));
630 return true;
631 } else {
632 TF_CODING_ERROR("Failed to apply list op edits to 'apiSchemas' on spec "
633 "at path <%s> in layer '%s'",
634 primSpec->GetLayer()->GetIdentifier().c_str(),
635 primSpec->GetPath().GetText());
636 return false;
637 }
638 }
639
640 std::vector<UsdProperty>
_MakeProperties(const TfTokenVector & names) const641 UsdPrim::_MakeProperties(const TfTokenVector &names) const
642 {
643 std::vector<UsdProperty> props;
644 UsdStage *stage = _GetStage();
645 props.reserve(names.size());
646 for (auto const &propName : names) {
647 SdfSpecType specType =
648 stage->_GetDefiningSpecType(get_pointer(_Prim()), propName);
649 if (specType == SdfSpecTypeAttribute) {
650 props.push_back(GetAttribute(propName));
651 } else if (TF_VERIFY(specType == SdfSpecTypeRelationship)) {
652 props.push_back(GetRelationship(propName));
653 }
654 }
655 return props;
656 }
657
658 // Change the order of items in 'names' so that all the things in 'order' that
659 // are also in 'names' are at the beginning in the order that they appear in
660 // 'order', followed by any remaining items in 'names' in their existing order.
661 static void
_ApplyOrdering(const TfTokenVector & order,TfTokenVector * names)662 _ApplyOrdering(const TfTokenVector &order, TfTokenVector *names)
663 {
664 // If order is empty or names is empty, nothing to do.
665 if (order.empty() || names->empty())
666 return;
667
668 // Perf note: this walks 'order' and linear searches 'names' to find each
669 // element, for O(M*N) operations, where M and N are the lengths of 'order'
670 // and 'names'. We hope 1) that propertyOrder stmts are relatively rare and
671 // 2) that property lists are relatively short. If those assumptions fail,
672 // this may need revisiting. In some quick microbenchmarking, this linear
673 // search seems to outperform binary search up to about 5000 names. We
674 // suspect this is because linear search does TfToken pointer comparisons,
675 // while binary search has to dereference and do string comparisons.
676
677 typedef TfTokenVector::iterator Iter;
678
679 Iter namesRest = names->begin(), namesEnd = names->end();
680 for (const TfToken &oName: order) {
681 // Look for this name from 'order' in the rest of 'names'.
682 Iter i = std::find(namesRest, namesEnd, oName);
683 if (i != namesEnd) {
684 // Found. Move to the front by rotating the sub-range. Using
685 // std::rotate invokes swap(), which avoids TfToken refcounting.
686 // Also advance 'namesRest' to the next element.
687 std::rotate(namesRest++, i, i+1);
688 }
689 }
690 }
691
692 bool
RemoveProperty(const TfToken & propName)693 UsdPrim::RemoveProperty(const TfToken &propName)
694 {
695 SdfPath propPath = GetPath().AppendProperty(propName);
696 return _GetStage()->_RemoveProperty(propPath);
697 }
698
699 UsdProperty
GetProperty(const TfToken & propName) const700 UsdPrim::GetProperty(const TfToken &propName) const
701 {
702 SdfSpecType specType =
703 _GetStage()->_GetDefiningSpecType(get_pointer(_Prim()), propName);
704 if (specType == SdfSpecTypeAttribute) {
705 return GetAttribute(propName);
706 }
707 else if (specType == SdfSpecTypeRelationship) {
708 return GetRelationship(propName);
709 }
710 return UsdProperty(UsdTypeProperty, _Prim(), _ProxyPrimPath(), propName);
711 }
712
713 bool
HasProperty(const TfToken & propName) const714 UsdPrim::HasProperty(const TfToken &propName) const
715 {
716 return static_cast<bool>(GetProperty(propName));
717 }
718
719 TfTokenVector
GetPropertyOrder() const720 UsdPrim::GetPropertyOrder() const
721 {
722 TfTokenVector order;
723 GetMetadata(SdfFieldKeys->PropertyOrder, &order);
724 return order;
725 }
726
727 static void
_ComposePrimPropertyNames(const PcpPrimIndex & primIndex,const PcpNodeRef & node,const UsdPrim::PropertyPredicateFunc & predicate,TfTokenVector * names,TfTokenVector * localNames)728 _ComposePrimPropertyNames(
729 const PcpPrimIndex& primIndex,
730 const PcpNodeRef& node,
731 const UsdPrim::PropertyPredicateFunc &predicate,
732 TfTokenVector *names,
733 TfTokenVector *localNames)
734 {
735 if (node.IsCulled()) {
736 return;
737 }
738
739 // Strength-order does not matter here, since we're just collecting all
740 // names.
741 TF_FOR_ALL(child, node.GetChildrenRange()) {
742 _ComposePrimPropertyNames(primIndex, *child, predicate, names,
743 localNames);
744 }
745
746 // Compose the site's local names over the current result.
747 if (node.CanContributeSpecs()) {
748 for (auto &layer : node.GetLayerStack()->GetLayers()) {
749 if (layer->HasField<TfTokenVector>(node.GetPath(),
750 SdfChildrenKeys->PropertyChildren, localNames)) {
751 // If predicate is valid, then append only the names that pass
752 // the predicate. If not, add all names (including duplicates).
753 if (predicate) {
754 for(auto &name: *localNames) {
755 if (predicate(name)) {
756 names->push_back(name);
757 }
758 }
759 } else {
760 names->insert(names->end(), localNames->begin(),
761 localNames->end());
762 }
763 }
764 }
765 }
766 }
767
768 // This function and the one above (_ComposePrimPropertyNames) were copied
769 // from Pcp/{PrimIndex,ComposeSite} and optimized for Usd.
770 static void
_ComputePrimPropertyNames(const PcpPrimIndex & primIndex,const UsdPrim::PropertyPredicateFunc & predicate,TfTokenVector * names)771 _ComputePrimPropertyNames(
772 const PcpPrimIndex &primIndex,
773 const UsdPrim::PropertyPredicateFunc &predicate,
774 TfTokenVector *names)
775 {
776 if (!primIndex.IsValid()) {
777 return;
778 }
779
780 TRACE_FUNCTION();
781
782 // Temporary shared vector for collecting local property names.
783 // This is used to re-use storage allocated for the local property
784 // names in each layer.
785 TfTokenVector localNames;
786
787 // Walk the graph to compose prim child names.
788 _ComposePrimPropertyNames(primIndex, primIndex.GetRootNode(), predicate,
789 names, &localNames);
790 }
791
792 TfTokenVector
_GetPropertyNames(bool onlyAuthored,bool applyOrder,const UsdPrim::PropertyPredicateFunc & predicate) const793 UsdPrim::_GetPropertyNames(
794 bool onlyAuthored,
795 bool applyOrder,
796 const UsdPrim::PropertyPredicateFunc &predicate) const
797 {
798 TfTokenVector names;
799
800 // If we're including unauthored properties, take names from definition, if
801 // present.
802 const UsdPrimDefinition &primDef = _Prim()->GetPrimDefinition();
803 if (!onlyAuthored) {
804 if (predicate) {
805 const TfTokenVector &builtInNames = primDef.GetPropertyNames();
806 for (const auto &builtInName : builtInNames) {
807 if (predicate(builtInName)) {
808 names.push_back(builtInName);
809 }
810 }
811 } else {
812 names = primDef.GetPropertyNames();
813 }
814 }
815
816 // Add authored names, then sort and apply ordering.
817 _ComputePrimPropertyNames(GetPrimIndex(), predicate, &names);
818
819 if (!names.empty()) {
820 // Sort and uniquify the names.
821 sort(names.begin(), names.end(), TfDictionaryLessThan());
822 names.erase(std::unique(names.begin(), names.end()), names.end());
823 if (applyOrder) {
824 _ApplyOrdering(GetPropertyOrder(), &names);
825 }
826 }
827
828 return names;
829 }
830
831 TfTokenVector
GetAppliedSchemas() const832 UsdPrim::GetAppliedSchemas() const
833 {
834 return GetPrimDefinition().GetAppliedAPISchemas();
835 }
836
837 TfTokenVector
GetPropertyNames(const UsdPrim::PropertyPredicateFunc & predicate) const838 UsdPrim::GetPropertyNames(
839 const UsdPrim::PropertyPredicateFunc &predicate) const
840 {
841 return _GetPropertyNames(/*onlyAuthored=*/ false,
842 /*applyOrder*/ true,
843 predicate);
844 }
845
846 TfTokenVector
GetAuthoredPropertyNames(const UsdPrim::PropertyPredicateFunc & predicate) const847 UsdPrim::GetAuthoredPropertyNames(
848 const UsdPrim::PropertyPredicateFunc &predicate) const
849 {
850 return _GetPropertyNames(/*onlyAuthored=*/ true,
851 /*applyOrder*/ true,
852 predicate);
853 }
854
855 std::vector<UsdProperty>
GetProperties(const UsdPrim::PropertyPredicateFunc & predicate) const856 UsdPrim::GetProperties(const UsdPrim::PropertyPredicateFunc &predicate) const
857 {
858 return _MakeProperties(GetPropertyNames(predicate));
859 }
860
861 std::vector<UsdProperty>
GetAuthoredProperties(const UsdPrim::PropertyPredicateFunc & predicate) const862 UsdPrim::GetAuthoredProperties(
863 const UsdPrim::PropertyPredicateFunc &predicate) const
864 {
865 return _MakeProperties(GetAuthoredPropertyNames(predicate));
866 }
867
868 std::vector<UsdProperty>
_GetPropertiesInNamespace(const std::string & namespaces,bool onlyAuthored) const869 UsdPrim::_GetPropertiesInNamespace(
870 const std::string &namespaces,
871 bool onlyAuthored) const
872 {
873 if (namespaces.empty())
874 return onlyAuthored ? GetAuthoredProperties() : GetProperties();
875
876 const char delim = UsdObject::GetNamespaceDelimiter();
877
878 // Set terminator to the expected position of the delimiter after all the
879 // supplied namespaces. We perform an explicit test for this char below
880 // so that we don't need to allocate a new string if namespaces does not
881 // already end with the delimiter
882 const size_t terminator = namespaces.size() -
883 (*namespaces.rbegin() == delim);
884
885 TfTokenVector names = _GetPropertyNames(onlyAuthored,
886 /*applyOrder=*/ true,
887 /*predicate*/ [&namespaces, terminator, delim](const TfToken &name) {
888 const std::string &s = name.GetString();
889 return s.size() > terminator &&
890 TfStringStartsWith(s, namespaces) &&
891 s[terminator] == delim;
892 });
893
894 return _MakeProperties(names);
895 }
896
897 std::vector<UsdProperty>
GetPropertiesInNamespace(const std::string & namespaces) const898 UsdPrim::GetPropertiesInNamespace(const std::string &namespaces) const
899 {
900 return _GetPropertiesInNamespace(namespaces, /*onlyAuthored=*/false);
901 }
902
903 std::vector<UsdProperty>
GetAuthoredPropertiesInNamespace(const std::string & namespaces) const904 UsdPrim::GetAuthoredPropertiesInNamespace(const std::string &namespaces) const
905 {
906 return _GetPropertiesInNamespace(namespaces, /*onlyAuthored=*/true);
907 }
908
909 std::vector<UsdProperty>
GetPropertiesInNamespace(const std::vector<std::string> & namespaces) const910 UsdPrim::GetPropertiesInNamespace(const std::vector<std::string> &namespaces) const
911 {
912 return GetPropertiesInNamespace(SdfPath::JoinIdentifier(namespaces));
913 }
914
915 std::vector<UsdProperty>
GetAuthoredPropertiesInNamespace(const std::vector<std::string> & namespaces) const916 UsdPrim::GetAuthoredPropertiesInNamespace(
917 const std::vector<std::string> &namespaces) const
918 {
919 return GetAuthoredPropertiesInNamespace(
920 SdfPath::JoinIdentifier(namespaces));
921 }
922
923 UsdAttribute
CreateAttribute(const TfToken & name,const SdfValueTypeName & typeName,bool custom,SdfVariability variability) const924 UsdPrim::CreateAttribute(const TfToken& name,
925 const SdfValueTypeName &typeName,
926 bool custom,
927 SdfVariability variability) const
928 {
929 UsdAttribute attr = GetAttribute(name);
930 attr._Create(typeName, custom, variability);
931 return attr;
932 }
933
934 UsdAttribute
CreateAttribute(const TfToken & name,const SdfValueTypeName & typeName,SdfVariability variability) const935 UsdPrim::CreateAttribute(const TfToken& name,
936 const SdfValueTypeName &typeName,
937 SdfVariability variability) const
938 {
939 return CreateAttribute(name, typeName, /*custom=*/true, variability);
940 }
941
942 UsdAttribute
CreateAttribute(const std::vector<std::string> & nameElts,const SdfValueTypeName & typeName,bool custom,SdfVariability variability) const943 UsdPrim::CreateAttribute(const std::vector<std::string> &nameElts,
944 const SdfValueTypeName &typeName,
945 bool custom,
946 SdfVariability variability) const
947 {
948 return CreateAttribute(TfToken(SdfPath::JoinIdentifier(nameElts)),
949 typeName, custom, variability);
950 }
951
952 UsdAttribute
CreateAttribute(const std::vector<std::string> & nameElts,const SdfValueTypeName & typeName,SdfVariability variability) const953 UsdPrim::CreateAttribute(const std::vector<std::string> &nameElts,
954 const SdfValueTypeName &typeName,
955 SdfVariability variability) const
956 {
957 return CreateAttribute(nameElts, typeName, /*custom=*/true, variability);
958 }
959
960 UsdAttributeVector
_GetAttributes(bool onlyAuthored,bool applyOrder) const961 UsdPrim::_GetAttributes(bool onlyAuthored, bool applyOrder) const
962 {
963 const TfTokenVector names = _GetPropertyNames(onlyAuthored, applyOrder);
964 UsdAttributeVector attrs;
965
966 // PERFORMANCE: This is sloppy, since property names are a superset of
967 // attribute names, however this vector is likely short lived and worth the
968 // trade off of repeated reallocation.
969 attrs.reserve(names.size());
970 for (const auto& propName : names) {
971 if (UsdAttribute attr = GetAttribute(propName)) {
972 attrs.push_back(attr);
973 }
974 }
975
976 return attrs;
977 }
978
979 UsdAttributeVector
GetAttributes() const980 UsdPrim::GetAttributes() const
981 {
982 return _GetAttributes(/*onlyAuthored=*/false, /*applyOrder=*/true);
983 }
984
985 UsdAttributeVector
GetAuthoredAttributes() const986 UsdPrim::GetAuthoredAttributes() const
987 {
988 return _GetAttributes(/*onlyAuthored=*/true, /*applyOrder=*/true);
989 }
990
991 UsdAttribute
GetAttribute(const TfToken & attrName) const992 UsdPrim::GetAttribute(const TfToken& attrName) const
993 {
994 // An invalid prim will present a coding error, and then return an
995 // invalid attribute
996 return UsdAttribute(_Prim(), _ProxyPrimPath(), attrName);
997 }
998
999 bool
HasAttribute(const TfToken & attrName) const1000 UsdPrim::HasAttribute(const TfToken& attrName) const
1001 {
1002 return static_cast<bool>(GetAttribute(attrName));
1003 }
1004
1005 UsdRelationship
CreateRelationship(const TfToken & name,bool custom) const1006 UsdPrim::CreateRelationship(const TfToken& name, bool custom) const
1007 {
1008 UsdRelationship rel = GetRelationship(name);
1009 rel._Create(custom);
1010 return rel;
1011 }
1012
1013 UsdRelationship
CreateRelationship(const std::vector<std::string> & nameElts,bool custom) const1014 UsdPrim::CreateRelationship(const std::vector<std::string> &nameElts,
1015 bool custom) const
1016 {
1017 return CreateRelationship(TfToken(SdfPath::JoinIdentifier(nameElts)),
1018 custom);
1019 }
1020
1021 UsdRelationshipVector
_GetRelationships(bool onlyAuthored,bool applyOrder) const1022 UsdPrim::_GetRelationships(bool onlyAuthored, bool applyOrder) const
1023 {
1024 const TfTokenVector names = _GetPropertyNames(onlyAuthored, applyOrder);
1025 UsdRelationshipVector rels;
1026
1027 // PERFORMANCE: This is sloppy, since property names are a superset of
1028 // relationship names, however this vector is likely short lived and worth
1029 // the trade off of repeated reallocation.
1030 rels.reserve(names.size());
1031 for (const auto& propName : names) {
1032 if (UsdRelationship rel = GetRelationship(propName)) {
1033 rels.push_back(rel);
1034 }
1035 }
1036
1037 return rels;
1038 }
1039
1040 UsdRelationshipVector
GetRelationships() const1041 UsdPrim::GetRelationships() const
1042 {
1043 return _GetRelationships(/*onlyAuthored=*/false, /*applyOrder=*/true);
1044 }
1045
1046 UsdRelationshipVector
GetAuthoredRelationships() const1047 UsdPrim::GetAuthoredRelationships() const
1048 {
1049 return _GetRelationships(/*onlyAuthored=*/true, /*applyOrder=*/true);
1050 }
1051
1052 UsdRelationship
GetRelationship(const TfToken & relName) const1053 UsdPrim::GetRelationship(const TfToken& relName) const
1054 {
1055 return UsdRelationship(_Prim(), _ProxyPrimPath(), relName);
1056 }
1057
1058 bool
HasRelationship(const TfToken & relName) const1059 UsdPrim::HasRelationship(const TfToken& relName) const
1060 {
1061 return static_cast<bool>(GetRelationship(relName));
1062 }
1063
1064 template <class PropertyType, class Derived>
1065 struct UsdPrim_TargetFinder
1066 {
1067 using Predicate = std::function<bool (PropertyType const &)>;
1068
1069 static SdfPathVector
FindUsdPrim_TargetFinder1070 Find(UsdPrim const &prim, Predicate const &pred, bool recurse) {
1071 UsdPrim_TargetFinder tf(prim, pred, recurse);
1072 tf._Find();
1073 return std::move(tf._result);
1074 }
1075
1076 private:
UsdPrim_TargetFinderUsdPrim_TargetFinder1077 explicit UsdPrim_TargetFinder(
1078 UsdPrim const &prim, Predicate const &pred, bool recurse)
1079 : _prim(prim)
1080 , _consumerTask(_dispatcher, [this]() { _ConsumerTask(); })
1081 , _predicate(pred)
1082 , _recurse(recurse) {}
1083
_VisitUsdPrim_TargetFinder1084 void _Visit(UsdRelationship const &rel) {
1085 SdfPathVector targets;
1086 rel._GetForwardedTargets(&targets,
1087 /*includeForwardingRels=*/true);
1088 _VisitImpl(targets);
1089 }
1090
_VisitUsdPrim_TargetFinder1091 void _Visit(UsdAttribute const &attr) {
1092 SdfPathVector sources;
1093 attr.GetConnections(&sources);
1094 _VisitImpl(sources);
1095 }
1096
_VisitImplUsdPrim_TargetFinder1097 void _VisitImpl(SdfPathVector const &paths) {
1098 if (!paths.empty()) {
1099 for (SdfPath const &p: paths) {
1100 _workQueue.push(p);
1101 }
1102 _consumerTask.Wake();
1103 }
1104
1105 if (_recurse) {
1106 WorkParallelForEach(
1107 paths.begin(), paths.end(),
1108 [this](SdfPath const &path) {
1109 if (!path.HasPrefix(_prim.GetPath())) {
1110 if (UsdPrim owningPrim = _prim.GetStage()->
1111 GetPrimAtPath(path.GetPrimPath())) {
1112 _VisitSubtree(owningPrim);
1113 }
1114 }
1115 });
1116 }
1117 }
1118
_VisitPrimUsdPrim_TargetFinder1119 void _VisitPrim(UsdPrim const &prim) {
1120 if (_seenPrims.insert(prim).second) {
1121 auto props = static_cast<Derived *>(this)->_GetProperties(prim);
1122 for (auto const &prop: props) {
1123 if (!_predicate || _predicate(prop)) {
1124 _dispatcher.Run([this, prop]() { _Visit(prop); });
1125 }
1126 }
1127 }
1128 };
1129
_VisitSubtreeUsdPrim_TargetFinder1130 void _VisitSubtree(UsdPrim const &prim) {
1131 _VisitPrim(prim);
1132 auto range = prim.GetDescendants();
1133 WorkParallelForEach(range.begin(), range.end(),
1134 [this](UsdPrim const &desc) { _VisitPrim(desc); });
1135 }
1136
_FindUsdPrim_TargetFinder1137 void _Find() {
1138 TF_PY_ALLOW_THREADS_IN_SCOPE();
1139
1140 WorkWithScopedParallelism([this]() {
1141 _VisitSubtree(_prim);
1142 _dispatcher.Wait();
1143 tbb::parallel_sort(_result.begin(), _result.end(),
1144 SdfPath::FastLessThan());
1145 });
1146
1147 _result.erase(unique(_result.begin(), _result.end()), _result.end());
1148 }
1149
_ConsumerTaskUsdPrim_TargetFinder1150 void _ConsumerTask() {
1151 SdfPath path;
1152 while (_workQueue.try_pop(path)) {
1153 _result.push_back(path);
1154 }
1155 }
1156
1157 UsdPrim _prim;
1158 WorkDispatcher _dispatcher;
1159 WorkSingularTask _consumerTask;
1160 Predicate const &_predicate;
1161 tbb::concurrent_queue<SdfPath> _workQueue;
1162 tbb::concurrent_unordered_set<UsdPrim, boost::hash<UsdPrim> > _seenPrims;
1163 SdfPathVector _result;
1164 bool _recurse;
1165 };
1166
1167 struct UsdPrim_RelTargetFinder
1168 : public UsdPrim_TargetFinder<UsdRelationship, UsdPrim_RelTargetFinder>
1169 {
_GetPropertiesUsdPrim_RelTargetFinder1170 std::vector<UsdRelationship> _GetProperties(UsdPrim const &prim) const {
1171 return prim._GetRelationships(/*onlyAuthored=*/true,
1172 /*applyOrder=*/false);
1173 }
1174 };
1175
1176 struct UsdPrim_AttrConnectionFinder
1177 : public UsdPrim_TargetFinder<UsdAttribute, UsdPrim_AttrConnectionFinder>
1178 {
_GetPropertiesUsdPrim_AttrConnectionFinder1179 std::vector<UsdAttribute> _GetProperties(UsdPrim const &prim) const {
1180 return prim._GetAttributes(/*onlyAuthored=*/true,
1181 /*applyOrder=*/false);
1182 }
1183 };
1184
1185 USD_API
1186 SdfPathVector
FindAllAttributeConnectionPaths(std::function<bool (UsdAttribute const &)> const & predicate,bool recurseOnSources) const1187 UsdPrim::FindAllAttributeConnectionPaths(
1188 std::function<bool (UsdAttribute const &)> const &predicate,
1189 bool recurseOnSources) const
1190 {
1191 return UsdPrim_AttrConnectionFinder
1192 ::Find(*this, predicate, recurseOnSources);
1193 }
1194
1195 SdfPathVector
FindAllRelationshipTargetPaths(std::function<bool (UsdRelationship const &)> const & predicate,bool recurseOnTargets) const1196 UsdPrim::FindAllRelationshipTargetPaths(
1197 std::function<bool (UsdRelationship const &)> const &predicate,
1198 bool recurseOnTargets) const
1199 {
1200 return UsdPrim_RelTargetFinder::Find(*this, predicate, recurseOnTargets);
1201 }
1202
1203 bool
HasVariantSets() const1204 UsdPrim::HasVariantSets() const
1205 {
1206 // Variant sets can't be defined in schema fallbacks as of yet so we only
1207 // need to check for authored variant sets.
1208 return HasAuthoredMetadata(SdfFieldKeys->VariantSetNames);
1209 }
1210
1211 UsdVariantSets
GetVariantSets() const1212 UsdPrim::GetVariantSets() const
1213 {
1214 return UsdVariantSets(*this);
1215 }
1216
1217 UsdVariantSet
GetVariantSet(const std::string & variantSetName) const1218 UsdPrim::GetVariantSet(const std::string& variantSetName) const
1219 {
1220 return UsdVariantSet(*this, variantSetName);
1221 }
1222
1223
1224 UsdInherits
GetInherits() const1225 UsdPrim::GetInherits() const
1226 {
1227 return UsdInherits(*this);
1228 }
1229
1230 bool
HasAuthoredInherits() const1231 UsdPrim::HasAuthoredInherits() const
1232 {
1233 return HasAuthoredMetadata(SdfFieldKeys->InheritPaths);
1234 }
1235
1236 UsdSpecializes
GetSpecializes() const1237 UsdPrim::GetSpecializes() const
1238 {
1239 return UsdSpecializes(*this);
1240 }
1241
1242 bool
HasAuthoredSpecializes() const1243 UsdPrim::HasAuthoredSpecializes() const
1244 {
1245 return HasAuthoredMetadata(SdfFieldKeys->Specializes);
1246 }
1247
1248 UsdReferences
GetReferences() const1249 UsdPrim::GetReferences() const
1250 {
1251 return UsdReferences(*this);
1252 }
1253
1254 bool
HasAuthoredReferences() const1255 UsdPrim::HasAuthoredReferences() const
1256 {
1257 return HasAuthoredMetadata(SdfFieldKeys->References);
1258 }
1259
1260 // --------------------------------------------------------------------- //
1261 /// \name Payloads, Load and Unload
1262 // --------------------------------------------------------------------- //
1263
1264 bool
HasPayload() const1265 UsdPrim::HasPayload() const
1266 {
1267 return HasAuthoredPayloads();
1268 }
1269
1270 bool
SetPayload(const SdfPayload & payload) const1271 UsdPrim::SetPayload(const SdfPayload& payload) const
1272 {
1273 UsdPayloads payloads = GetPayloads();
1274 payloads.ClearPayloads();
1275 return payloads.SetPayloads(SdfPayloadVector{payload});
1276 }
1277
1278 bool
SetPayload(const std::string & assetPath,const SdfPath & primPath) const1279 UsdPrim::SetPayload(const std::string& assetPath, const SdfPath& primPath) const
1280 {
1281 return SetPayload(SdfPayload(assetPath, primPath));
1282 }
1283
1284 bool
SetPayload(const SdfLayerHandle & layer,const SdfPath & primPath) const1285 UsdPrim::SetPayload(const SdfLayerHandle& layer, const SdfPath& primPath) const
1286 {
1287 return SetPayload(SdfPayload(layer->GetIdentifier(), primPath));
1288 }
1289
1290 bool
ClearPayload() const1291 UsdPrim::ClearPayload() const
1292 {
1293 return GetPayloads().ClearPayloads();
1294 }
1295
1296 UsdPayloads
GetPayloads() const1297 UsdPrim::GetPayloads() const
1298 {
1299 return UsdPayloads(*this);
1300 }
1301
1302 bool
HasAuthoredPayloads() const1303 UsdPrim::HasAuthoredPayloads() const
1304 {
1305 // Unlike the equivalent function for references, we query the prim data
1306 // for the cached value of HasPayload computed by Pcp instead of querying
1307 // the composed metadata. This is necessary as this function is called by
1308 // _IncludeNewlyDiscoveredPayloadsPredicate in UsdStage which can't safely
1309 // call back into the querying the composed metatdata.
1310 return _Prim()->HasPayload();
1311 }
1312
1313 void
Load(UsdLoadPolicy policy) const1314 UsdPrim::Load(UsdLoadPolicy policy) const
1315 {
1316 if (IsInPrototype()) {
1317 TF_CODING_ERROR("Attempted to load a prim in a prototype <%s>",
1318 GetPath().GetText());
1319 return;
1320 }
1321 _GetStage()->Load(GetPath(), policy);
1322 }
1323
1324 void
Unload() const1325 UsdPrim::Unload() const
1326 {
1327 if (IsInPrototype()) {
1328 TF_CODING_ERROR("Attempted to unload a prim in a prototype <%s>",
1329 GetPath().GetText());
1330 return;
1331 }
1332 _GetStage()->Unload(GetPath());
1333 }
1334
1335 TfTokenVector
GetChildrenNames() const1336 UsdPrim::GetChildrenNames() const
1337 {
1338 TfTokenVector names;
1339 for (const auto &child : GetChildren()) {
1340 names.push_back(child.GetName());
1341 }
1342 return names;
1343 }
1344
1345 TfTokenVector
GetAllChildrenNames() const1346 UsdPrim::GetAllChildrenNames() const
1347 {
1348 TfTokenVector names;
1349 for (const auto &child : GetAllChildren()) {
1350 names.push_back(child.GetName());
1351 }
1352 return names;
1353 }
1354
1355 TfTokenVector
GetFilteredChildrenNames(const Usd_PrimFlagsPredicate & predicate) const1356 UsdPrim::GetFilteredChildrenNames(const Usd_PrimFlagsPredicate &predicate) const
1357 {
1358 TfTokenVector names;
1359 for (const auto &child : GetFilteredChildren(predicate)) {
1360 names.push_back(child.GetName());
1361 }
1362 return names;
1363 }
1364
1365 TfTokenVector
GetChildrenReorder() const1366 UsdPrim::GetChildrenReorder() const
1367 {
1368 TfTokenVector reorder;
1369 GetMetadata(SdfFieldKeys->PrimOrder, &reorder);
1370 return reorder;
1371 }
1372
1373 UsdPrim
GetNextSibling() const1374 UsdPrim::GetNextSibling() const
1375 {
1376 return GetFilteredNextSibling(UsdPrimDefaultPredicate);
1377 }
1378
1379 UsdPrim
GetFilteredNextSibling(const Usd_PrimFlagsPredicate & inPred) const1380 UsdPrim::GetFilteredNextSibling(const Usd_PrimFlagsPredicate &inPred) const
1381 {
1382 Usd_PrimDataConstPtr sibling = get_pointer(_Prim());
1383 SdfPath siblingPath = _ProxyPrimPath();
1384 const Usd_PrimFlagsPredicate pred =
1385 Usd_CreatePredicateForTraversal(sibling, siblingPath, inPred);
1386
1387 if (Usd_MoveToNextSiblingOrParent(sibling, siblingPath, pred)) {
1388 return UsdPrim();
1389 }
1390 return UsdPrim(sibling, siblingPath);
1391 }
1392
1393 bool
IsPseudoRoot() const1394 UsdPrim::IsPseudoRoot() const
1395 {
1396 return GetPath() == SdfPath::AbsoluteRootPath();
1397 }
1398
1399 bool
IsPrototypePath(const SdfPath & path)1400 UsdPrim::IsPrototypePath(const SdfPath& path)
1401 {
1402 return Usd_InstanceCache::IsPrototypePath(path);
1403 }
1404
1405 bool
IsPathInPrototype(const SdfPath & path)1406 UsdPrim::IsPathInPrototype(const SdfPath& path)
1407 {
1408 return Usd_InstanceCache::IsPathInPrototype(path);
1409 }
1410
1411 UsdPrim
GetPrototype() const1412 UsdPrim::GetPrototype() const
1413 {
1414 Usd_PrimDataConstPtr protoPrimData =
1415 _GetStage()->_GetPrototypeForInstance(get_pointer(_Prim()));
1416 return UsdPrim(protoPrimData, SdfPath());
1417 }
1418
1419 std::vector<UsdPrim>
GetInstances() const1420 UsdPrim::GetInstances() const
1421 {
1422 return _GetStage()->_GetInstancesForPrototype(*this);
1423 }
1424
1425 SdfPrimSpecHandleVector
GetPrimStack() const1426 UsdPrim::GetPrimStack() const
1427 {
1428 SdfPrimSpecHandleVector primStack;
1429
1430 for (Usd_Resolver resolver(&(_Prim()->GetPrimIndex()));
1431 resolver.IsValid(); resolver.NextLayer()) {
1432
1433 auto primSpec = resolver.GetLayer()
1434 ->GetPrimAtPath(resolver.GetLocalPath());
1435
1436 if (primSpec) {
1437 primStack.push_back(primSpec);
1438 }
1439 }
1440
1441 return primStack;
1442 }
1443
1444 PcpPrimIndex
ComputeExpandedPrimIndex() const1445 UsdPrim::ComputeExpandedPrimIndex() const
1446 {
1447 // Get the prim index path to compute from the index stored in the prim
1448 // data. This ensures we get consistent behavior when dealing with
1449 // instancing and instance proxies.
1450 const PcpPrimIndex& cachedPrimIndex = _Prim()->GetPrimIndex();
1451 if (!cachedPrimIndex.IsValid()) {
1452 return PcpPrimIndex();
1453 }
1454
1455 const SdfPath& primIndexPath = cachedPrimIndex.GetPath();
1456 PcpCache* cache = _GetStage()->_GetPcpCache();
1457
1458 PcpPrimIndexOutputs outputs;
1459 PcpComputePrimIndex(
1460 primIndexPath, cache->GetLayerStack(),
1461 cache->GetPrimIndexInputs().Cull(false),
1462 &outputs);
1463
1464 _GetStage()->_ReportPcpErrors(
1465 outputs.allErrors,
1466 TfStringPrintf(
1467 "computing expanded prim index for <%s>", GetPath().GetText()));
1468
1469 return outputs.primIndex;
1470 }
1471
1472 UsdPrim
GetPrimAtPath(const SdfPath & path) const1473 UsdPrim::GetPrimAtPath(const SdfPath& path) const{
1474 const SdfPath absolutePath = path.MakeAbsolutePath(GetPath());
1475 return GetStage()->GetPrimAtPath(absolutePath);
1476 }
1477
1478 UsdObject
GetObjectAtPath(const SdfPath & path) const1479 UsdPrim::GetObjectAtPath(const SdfPath& path) const{
1480 const SdfPath absolutePath = path.MakeAbsolutePath(GetPath());
1481 return GetStage()->GetObjectAtPath(absolutePath);
1482 }
1483
1484 UsdProperty
GetPropertyAtPath(const SdfPath & path) const1485 UsdPrim::GetPropertyAtPath(const SdfPath& path) const{
1486 return GetObjectAtPath(path).As<UsdProperty>();
1487 }
1488
1489 UsdAttribute
GetAttributeAtPath(const SdfPath & path) const1490 UsdPrim::GetAttributeAtPath(const SdfPath& path) const{
1491 return GetObjectAtPath(path).As<UsdAttribute>();
1492 }
1493
1494
1495 UsdRelationship
GetRelationshipAtPath(const SdfPath & path) const1496 UsdPrim::GetRelationshipAtPath(const SdfPath& path) const{
1497 return GetObjectAtPath(path).As<UsdRelationship>();
1498 }
1499
1500 PXR_NAMESPACE_CLOSE_SCOPE
1501
1502