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/usd/usdGeom/primvarsAPI.h"
25 #include "pxr/usd/usd/schemaRegistry.h"
26 #include "pxr/usd/usd/typed.h"
27 #include "pxr/usd/usd/tokens.h"
28 
29 #include "pxr/usd/sdf/types.h"
30 #include "pxr/usd/sdf/assetPath.h"
31 
32 PXR_NAMESPACE_OPEN_SCOPE
33 
34 // Register the schema with the TfType system.
TF_REGISTRY_FUNCTION(TfType)35 TF_REGISTRY_FUNCTION(TfType)
36 {
37     TfType::Define<UsdGeomPrimvarsAPI,
38         TfType::Bases< UsdAPISchemaBase > >();
39 
40 }
41 
42 TF_DEFINE_PRIVATE_TOKENS(
43     _schemaTokens,
44     (PrimvarsAPI)
45 );
46 
47 /* virtual */
~UsdGeomPrimvarsAPI()48 UsdGeomPrimvarsAPI::~UsdGeomPrimvarsAPI()
49 {
50 }
51 
52 /* static */
53 UsdGeomPrimvarsAPI
Get(const UsdStagePtr & stage,const SdfPath & path)54 UsdGeomPrimvarsAPI::Get(const UsdStagePtr &stage, const SdfPath &path)
55 {
56     if (!stage) {
57         TF_CODING_ERROR("Invalid stage");
58         return UsdGeomPrimvarsAPI();
59     }
60     return UsdGeomPrimvarsAPI(stage->GetPrimAtPath(path));
61 }
62 
63 
64 /* virtual */
_GetSchemaKind() const65 UsdSchemaKind UsdGeomPrimvarsAPI::_GetSchemaKind() const
66 {
67     return UsdGeomPrimvarsAPI::schemaKind;
68 }
69 
70 /* static */
71 const TfType &
_GetStaticTfType()72 UsdGeomPrimvarsAPI::_GetStaticTfType()
73 {
74     static TfType tfType = TfType::Find<UsdGeomPrimvarsAPI>();
75     return tfType;
76 }
77 
78 /* static */
79 bool
_IsTypedSchema()80 UsdGeomPrimvarsAPI::_IsTypedSchema()
81 {
82     static bool isTyped = _GetStaticTfType().IsA<UsdTyped>();
83     return isTyped;
84 }
85 
86 /* virtual */
87 const TfType &
_GetTfType() const88 UsdGeomPrimvarsAPI::_GetTfType() const
89 {
90     return _GetStaticTfType();
91 }
92 
93 /*static*/
94 const TfTokenVector&
GetSchemaAttributeNames(bool includeInherited)95 UsdGeomPrimvarsAPI::GetSchemaAttributeNames(bool includeInherited)
96 {
97     static TfTokenVector localNames;
98     static TfTokenVector allNames =
99         UsdAPISchemaBase::GetSchemaAttributeNames(true);
100 
101     if (includeInherited)
102         return allNames;
103     else
104         return localNames;
105 }
106 
107 PXR_NAMESPACE_CLOSE_SCOPE
108 
109 // ===================================================================== //
110 // Feel free to add custom code below this line. It will be preserved by
111 // the code generator.
112 //
113 // Just remember to wrap code in the appropriate delimiters:
114 // 'PXR_NAMESPACE_OPEN_SCOPE', 'PXR_NAMESPACE_CLOSE_SCOPE'.
115 // ===================================================================== //
116 // --(BEGIN CUSTOM CODE)--
117 
118 PXR_NAMESPACE_OPEN_SCOPE
119 
120 UsdGeomPrimvar
CreatePrimvar(const TfToken & name,const SdfValueTypeName & typeName,const TfToken & interpolation,int elementSize) const121 UsdGeomPrimvarsAPI::CreatePrimvar(const TfToken& name,
122                                 const SdfValueTypeName &typeName,
123                                 const TfToken& interpolation,
124                                 int elementSize) const
125 {
126     const UsdPrim &prim = GetPrim();
127 
128     UsdGeomPrimvar primvar(prim, name, typeName);
129 
130     if (primvar){
131         if (!interpolation.IsEmpty())
132             primvar.SetInterpolation(interpolation);
133         if (elementSize > 0)
134             primvar.SetElementSize(elementSize);
135     }
136     // otherwise, errors have already been issued
137     return primvar;
138 }
139 
140 bool
RemovePrimvar(const TfToken & name)141 UsdGeomPrimvarsAPI::RemovePrimvar(const TfToken& name)
142 {
143     const TfToken& attrName = UsdGeomPrimvar::_MakeNamespaced(name);
144     if (attrName.IsEmpty()) {
145         return false;
146     }
147 
148     UsdPrim prim = GetPrim();
149     if (!prim) {
150         TF_CODING_ERROR("RemovePrimvar called on invalid prim: %s",
151                         UsdDescribe(prim).c_str());
152         return false;
153     }
154 
155     const UsdGeomPrimvar &primvar = UsdGeomPrimvar(prim.GetAttribute(attrName));
156     if (!primvar) {
157         return false;
158     }
159 
160     const UsdAttribute& indexAttr = primvar.GetIndicesAttr();
161     bool success = true;
162     // If the Primvar is an indexed primvar, also remove the indexAttr
163     if (indexAttr) {
164         success = prim.RemoveProperty(indexAttr.GetName());
165     }
166     return prim.RemoveProperty(attrName) && success;
167 }
168 
169 void
BlockPrimvar(const TfToken & name)170 UsdGeomPrimvarsAPI::BlockPrimvar(const TfToken& name)
171 {
172     const TfToken& attrName = UsdGeomPrimvar::_MakeNamespaced(name);
173     if (attrName.IsEmpty()) {
174         return;
175     }
176 
177     const UsdPrim& prim = GetPrim();
178     if (!prim) {
179         TF_CODING_ERROR("RemovePrimvar called on invalid prim: %s",
180                         UsdDescribe(prim).c_str());
181         return;
182     }
183 
184     const UsdGeomPrimvar &primvar = UsdGeomPrimvar(prim.GetAttribute(name));
185     if (!primvar) {
186         return;
187     }
188 
189     // Always block indices attr irrespective of primvar is indexed or not
190     // This prevents leak of indices attr in a composed stage when a stronger
191     // layer has blocked the primvar and at a later time weaker layer adds
192     // indices to the primvar
193     primvar.BlockIndices();
194 
195     primvar.GetAttr().Block();
196 }
197 
198 
199 UsdGeomPrimvar
GetPrimvar(const TfToken & name) const200 UsdGeomPrimvarsAPI::GetPrimvar(const TfToken &name) const
201 {
202     // The getter SHOULD issue an error if 'name' is malformed, which
203     // _MakeNamespaced() will do for us.
204     return UsdGeomPrimvar(GetPrim().GetAttribute
205                            (UsdGeomPrimvar::_MakeNamespaced(name)));
206 }
207 
208 static
209 std::vector<UsdGeomPrimvar>
_MakePrimvars(std::vector<UsdProperty> const & props,bool (filterPass)(UsdGeomPrimvar const &))210 _MakePrimvars(std::vector<UsdProperty> const &props,
211               bool (filterPass)(UsdGeomPrimvar const &))
212 {
213     std::vector<UsdGeomPrimvar> primvars;
214 
215     for (UsdProperty const &prop : props) {
216         // All prefixed properties except the ones that contain extra
217         // namespaces (eg. the ":indices" attributes belonging to indexed
218         // primvars) will be valid primvars.
219         UsdGeomPrimvar primvar = UsdGeomPrimvar(prop.As<UsdAttribute>());
220         if (primvar && filterPass(primvar)){
221             primvars.push_back(std::move(primvar));
222         }
223     }
224     return primvars;
225 }
226 
227 
228 std::vector<UsdGeomPrimvar>
GetPrimvars() const229 UsdGeomPrimvarsAPI::GetPrimvars() const
230 {
231     TRACE_FUNCTION();
232     const UsdPrim &prim = GetPrim();
233     if (!prim){
234         TF_CODING_ERROR("Called GetPrimvars on invalid prim: %s",
235                         UsdDescribe(prim).c_str());
236         return std::vector<UsdGeomPrimvar>();
237     }
238     return _MakePrimvars(prim.GetPropertiesInNamespace(
239                              UsdGeomPrimvar::_GetNamespacePrefix()),
240                          [](UsdGeomPrimvar const &) { return true; });
241 }
242 
243 std::vector<UsdGeomPrimvar>
GetAuthoredPrimvars() const244 UsdGeomPrimvarsAPI::GetAuthoredPrimvars() const
245 {
246     TRACE_FUNCTION();
247     const UsdPrim &prim = GetPrim();
248     if (!prim){
249         TF_CODING_ERROR("Called GetAuthoredPrimvars on invalid prim: %s",
250                         UsdDescribe(prim).c_str());
251         return std::vector<UsdGeomPrimvar>();
252     }
253     return _MakePrimvars(prim.GetAuthoredPropertiesInNamespace(
254                              UsdGeomPrimvar::_GetNamespacePrefix()),
255                          [](UsdGeomPrimvar const &) { return true; });
256 }
257 
258 
259 std::vector<UsdGeomPrimvar>
GetPrimvarsWithValues() const260 UsdGeomPrimvarsAPI::GetPrimvarsWithValues() const
261 {
262     TRACE_FUNCTION();
263     const UsdPrim &prim = GetPrim();
264     if (!prim){
265         TF_CODING_ERROR("Called GetPrimvarsWithValues on invalid prim: %s",
266                         UsdDescribe(prim).c_str());
267         return std::vector<UsdGeomPrimvar>();
268     }
269     return _MakePrimvars(prim.GetAuthoredPropertiesInNamespace(
270                              UsdGeomPrimvar::_GetNamespacePrefix()),
271                          [](UsdGeomPrimvar const &pv) { return pv.HasValue(); });
272 }
273 
274 
275 std::vector<UsdGeomPrimvar>
GetPrimvarsWithAuthoredValues() const276 UsdGeomPrimvarsAPI::GetPrimvarsWithAuthoredValues() const
277 {
278     TRACE_FUNCTION();
279     const UsdPrim &prim = GetPrim();
280     if (!prim){
281         TF_CODING_ERROR("Called GetPrimvarsWithAuthoredValues on invalid prim: %s",
282                         UsdDescribe(prim).c_str());
283         return std::vector<UsdGeomPrimvar>();
284     }
285     return _MakePrimvars(prim.GetAuthoredPropertiesInNamespace(
286                              UsdGeomPrimvar::_GetNamespacePrefix()),
287                          [](UsdGeomPrimvar const &pv) {
288                              return pv.HasAuthoredValue();
289                          });
290 }
291 
292 
293 
294 static
295 void
_AddPrimToInheritedPrimvars(const UsdPrim & prim,const TfToken & pvPrefix,const std::vector<UsdGeomPrimvar> * inputPrimvars,std::vector<UsdGeomPrimvar> * outputPrimvars,bool acceptAll)296 _AddPrimToInheritedPrimvars(const UsdPrim &prim, const TfToken &pvPrefix,
297                             const std::vector<UsdGeomPrimvar> *inputPrimvars,
298                             std::vector<UsdGeomPrimvar> *outputPrimvars,
299                             bool acceptAll)
300 {
301     auto copyPrimvars = [&inputPrimvars,&outputPrimvars]()
302         {
303             if (inputPrimvars != outputPrimvars){
304                 *outputPrimvars = *inputPrimvars;
305                 inputPrimvars = outputPrimvars;
306             }
307         };
308 
309 
310     for (UsdProperty const& prop:
311              prim.GetAuthoredPropertiesInNamespace(pvPrefix)) {
312         if (UsdGeomPrimvar pv = UsdGeomPrimvar(prop.As<UsdAttribute>())) {
313             // If the primvar does not provide a value, then it is as if it
314             // does not exist on prim
315             if (!pv.HasAuthoredValue()) {
316                 continue;
317             }
318 
319             // If pv is constant it will replace an instance already on the list;
320             // if non-constant we'll just remove it.
321             const TfToken &name = pv.GetName();
322             size_t i;
323             bool pvIsConstant = pv.GetInterpolation() == UsdGeomTokens->constant;
324             bool foundMatch = false;
325             for (i=0; i < inputPrimvars->size(); ++i) {
326                 if (name == (*inputPrimvars)[i].GetName()) {
327                     copyPrimvars();
328                     foundMatch = true;
329                     if (pvIsConstant || acceptAll){
330                         (*outputPrimvars)[i] = std::move(pv);
331                         break;
332                     }
333                     else {
334                         // Swap to the end and truncate the vector.
335                         // Don't bother to preserve order.
336                         std::swap((*outputPrimvars)[i], outputPrimvars->back());
337                         outputPrimvars->pop_back();
338                         break;
339                     }
340                 }
341             }
342             if ( !foundMatch && (pvIsConstant || acceptAll)) {
343                 copyPrimvars();
344                 outputPrimvars->push_back(std::move(pv));
345             }
346         }
347     }
348 }
349 
350 static
351 void
_RecurseForInheritablePrimvars(const UsdPrim & prim,const TfToken & pvPrefix,std::vector<UsdGeomPrimvar> * primvars,bool acceptAll=false)352 _RecurseForInheritablePrimvars(const UsdPrim &prim, const TfToken &pvPrefix,
353                                std::vector<UsdGeomPrimvar> *primvars,
354                                bool acceptAll = false)
355 {
356     if (prim.IsPseudoRoot())
357         return;
358 
359     // The `acceptAll` override is only useful for the prim we are actually
360     // querying, i.e. the *first* prim on which this function is called
361     _RecurseForInheritablePrimvars(prim.GetParent(), pvPrefix, primvars);
362     _AddPrimToInheritedPrimvars(prim, pvPrefix, primvars, primvars, acceptAll);
363 }
364 
365 std::vector<UsdGeomPrimvar>
FindInheritablePrimvars() const366 UsdGeomPrimvarsAPI::FindInheritablePrimvars() const
367 {
368     TRACE_FUNCTION();
369     // Assume the number of primvars is relatively bounded and
370     // just use a vector to accumulate primvars up to the root prim.
371     std::vector<UsdGeomPrimvar> primvars;
372     const UsdPrim &prim = GetPrim();
373     if (!prim) {
374         TF_CODING_ERROR("FindInheritablePrimvars called on invalid prim: %s",
375                         UsdDescribe(prim).c_str());
376         return primvars;
377     }
378 
379     TfToken const& prefix = UsdGeomPrimvar::_GetNamespacePrefix();
380     _RecurseForInheritablePrimvars(prim, prefix, &primvars);
381 
382     return primvars;
383 }
384 
385 std::vector<UsdGeomPrimvar>
FindIncrementallyInheritablePrimvars(const std::vector<UsdGeomPrimvar> & inheritedFromAncestors) const386 UsdGeomPrimvarsAPI::FindIncrementallyInheritablePrimvars(
387     const std::vector<UsdGeomPrimvar> &inheritedFromAncestors) const
388 {
389     TRACE_FUNCTION();
390 
391     std::vector<UsdGeomPrimvar> primvars;
392     const UsdPrim &prim = GetPrim();
393     if (!prim) {
394         TF_CODING_ERROR("FindIncrementallyInheritablePrimvars called on invalid prim: %s",
395                         UsdDescribe(prim).c_str());
396         return primvars;
397     }
398 
399     TfToken const& prefix = UsdGeomPrimvar::_GetNamespacePrefix();
400     _AddPrimToInheritedPrimvars(prim, prefix,
401                                 &inheritedFromAncestors,
402                                 &primvars, /* acceptAll = */ false);
403     return primvars;
404 }
405 
406 UsdGeomPrimvar
FindPrimvarWithInheritance(const TfToken & name) const407 UsdGeomPrimvarsAPI::FindPrimvarWithInheritance(const TfToken &name) const
408 {
409     TRACE_FUNCTION();
410 
411     const TfToken attrName = UsdGeomPrimvar::_MakeNamespaced(name);
412     UsdPrim prim = GetPrim();
413     if (!prim) {
414         TF_CODING_ERROR("FindPrimvarWithInheritance called on invalid prim: %s",
415                         UsdDescribe(prim).c_str());
416         return UsdGeomPrimvar();
417     }
418     UsdGeomPrimvar  localPv = GetPrimvar(name);
419     if (localPv.HasAuthoredValue()){
420         return localPv;
421     }
422 
423     for (prim = prim.GetParent(); prim && !prim.IsPseudoRoot();
424          prim = prim.GetParent()) {
425         UsdAttribute attr = prim.GetAttribute(attrName);
426         if (attr.HasAuthoredValue()) {
427             if (UsdGeomPrimvar pv = UsdGeomPrimvar(attr)) {
428                 // Only constant primvars can be inherited.
429                 if (pv.GetInterpolation() == UsdGeomTokens->constant) {
430                     return pv;
431                 } else {
432                     // Non-constant interpolation blocks inheritance.
433                     return UsdGeomPrimvar();
434                 }
435             }
436         }
437     }
438     return localPv;
439 }
440 
441 UsdGeomPrimvar
FindPrimvarWithInheritance(const TfToken & name,const std::vector<UsdGeomPrimvar> & inheritedFromAncestors) const442 UsdGeomPrimvarsAPI::FindPrimvarWithInheritance(const TfToken &name,
443                                                const std::vector<UsdGeomPrimvar>
444                                                &inheritedFromAncestors) const
445 {
446     TRACE_FUNCTION();
447 
448     const UsdPrim &prim = GetPrim();
449     if (!prim) {
450         TF_CODING_ERROR("FindPrimvarWithInheritance called on invalid prim: %s",
451                         UsdDescribe(prim).c_str());
452         return UsdGeomPrimvar();
453     }
454     const TfToken attrName = UsdGeomPrimvar::_MakeNamespaced(name);
455     UsdGeomPrimvar  pv = GetPrimvar(attrName);
456     if (pv.HasAuthoredValue()){
457         return pv;
458     }
459 
460     for (UsdGeomPrimvar const &inherited : inheritedFromAncestors) {
461         if (inherited.GetName() == attrName){
462             return inherited;
463         }
464     }
465 
466     return pv;
467 }
468 
469 std::vector<UsdGeomPrimvar>
FindPrimvarsWithInheritance() const470 UsdGeomPrimvarsAPI::FindPrimvarsWithInheritance() const
471 {
472     TRACE_FUNCTION();
473     // Assume the number of primvars is relatively bounded and
474     // just use a vector to accumulate primvars up to the root prim.
475     std::vector<UsdGeomPrimvar> primvars;
476     const UsdPrim &prim = GetPrim();
477     if (!prim) {
478         TF_CODING_ERROR("FindPrimvarsWithINheritance called on invalid prim: %s",
479                         UsdDescribe(prim).c_str());
480         return primvars;
481     }
482 
483     TfToken const& prefix = UsdGeomPrimvar::_GetNamespacePrefix();
484     _RecurseForInheritablePrimvars(prim, prefix, &primvars,
485                                    /* acceptAll = */ true);
486 
487     return primvars;
488 }
489 
490 std::vector<UsdGeomPrimvar>
FindPrimvarsWithInheritance(const std::vector<UsdGeomPrimvar> & inheritedFromAncestors) const491 UsdGeomPrimvarsAPI::FindPrimvarsWithInheritance(
492         const std::vector<UsdGeomPrimvar> &inheritedFromAncestors) const
493 {
494     TRACE_FUNCTION();
495 
496     std::vector<UsdGeomPrimvar> primvars;
497     const UsdPrim &prim = GetPrim();
498     if (!prim) {
499         TF_CODING_ERROR("FindPrimvarsWithInheritance called on invalid prim: %s",
500                         UsdDescribe(prim).c_str());
501         return primvars;
502     }
503 
504     TfToken const& prefix = UsdGeomPrimvar::_GetNamespacePrefix();
505     _AddPrimToInheritedPrimvars(prim, prefix,
506                                 &inheritedFromAncestors,
507                                 &primvars,
508                                 /* acceptAll = */ true);
509 
510     // If this prim contributed no primvars, then `primvars` won't have
511     // gotten a copy of `inheritedFromAncestors`, so ensure we compensate
512     return primvars.empty() ? inheritedFromAncestors : primvars;
513 }
514 
515 
516 bool
HasPrimvar(const TfToken & name) const517 UsdGeomPrimvarsAPI::HasPrimvar(const TfToken &name) const
518 {
519     TfToken primvarName = UsdGeomPrimvar::_MakeNamespaced(name, /* quiet */true);
520     const UsdPrim &prim = GetPrim();
521     if (!prim) {
522         TF_CODING_ERROR("HasPrimvar called on invalid prim: %s",
523                         UsdDescribe(prim).c_str());
524         return false;
525     }
526     return primvarName.IsEmpty() ? false :
527         UsdGeomPrimvar::IsPrimvar(prim.GetAttribute(primvarName));
528 }
529 
530 bool
HasPossiblyInheritedPrimvar(const TfToken & name) const531 UsdGeomPrimvarsAPI::HasPossiblyInheritedPrimvar(const TfToken &name) const
532 {
533     TRACE_FUNCTION();
534 
535     UsdPrim prim = GetPrim();
536     if (!prim) {
537         TF_CODING_ERROR("HasPossiblyInheritedPrimvar called on invalid prim: %s",
538                         UsdDescribe(prim).c_str());
539         return false;
540     }
541     UsdGeomPrimvar  pv = GetPrimvar(name);
542     if (pv.HasAuthoredValue()){
543         return true;
544     }
545 
546     const TfToken attrName = UsdGeomPrimvar::_MakeNamespaced(name);
547     if (attrName.IsEmpty()) {
548         return false;
549     }
550     for (prim = prim.GetParent(); prim && !prim.IsPseudoRoot();
551          prim = prim.GetParent()) {
552         UsdAttribute attr = prim.GetAttribute(attrName);
553         if (attr.HasAuthoredValue() && UsdGeomPrimvar::IsPrimvar(attr)) {
554             // Only constant primvars can be inherited.
555             // Non-constant interpolation blocks inheritance.
556             return UsdGeomPrimvar(attr).GetInterpolation()
557                 == UsdGeomTokens->constant;
558         }
559     }
560     return false;
561 }
562 
563 /* static */
564 bool
CanContainPropertyName(const TfToken & name)565 UsdGeomPrimvarsAPI::CanContainPropertyName(const TfToken& name)
566 {
567     TfToken const& prefix = UsdGeomPrimvar::_GetNamespacePrefix();
568     return TfStringStartsWith(name, prefix);
569 }
570 
571 PXR_NAMESPACE_CLOSE_SCOPE
572