HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
primvar.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_USD_USD_GEOM_PRIMVAR_H
8 #define PXR_USD_USD_GEOM_PRIMVAR_H
9 
10 #include "pxr/pxr.h"
11 #include "pxr/usd/usdGeom/api.h"
12 #include "pxr/usd/usd/attribute.h"
13 #include "pxr/usd/usdGeom/tokens.h"
14 
15 #include <atomic>
16 #include <string>
17 #include <vector>
18 
20 
21 
22 /// \class UsdGeomPrimvar
23 ///
24 /// Schema wrapper for UsdAttribute for authoring and introspecting attributes
25 /// that are primvars.
26 ///
27 /// UsdGeomPrimvar provides API for authoring and retrieving the
28 /// additional data required to encode an attribute as a "Primvar",
29 /// which is a convenient contraction of RenderMan's "Primitive Variable"
30 /// concept, which is represented in Alembic as
31 /// "arbitrary geometry parameters" (arbGeomParams).
32 ///
33 /// This includes the attribute's \ref GetInterpolation() "interpolation"
34 /// across the primitive (which RenderMan refers to as its
35 /// \ref Usd_InterpolationVals "class specifier"
36 /// and Alembic as its <A HREF="https://github.com/alembic/alembic/blob/master/lib/Alembic/AbcGeom/GeometryScope.h#L47"> "geometry scope"</A>);
37 /// it also includes the attribute's \ref GetElementSize() "elementSize",
38 /// which states how many values in the value array must be aggregated for
39 /// each element on the primitive. An attribute's \ref
40 /// UsdAttribute::GetTypeName() "TypeName" also factors into the encoding of
41 /// Primvar.
42 ///
43 /// \section Usd_What_Is_Primvar What is the Purpose of a Primvar?
44 ///
45 /// There are three key aspects of Primvar identity:
46 /// \li Primvars define a value that can vary across the primitive on which
47 /// they are defined, via prescribed interpolation rules
48 /// \li Taken collectively on a prim, its Primvars describe the "per-primitive
49 /// overrides" to the material to which the prim is bound. Different
50 /// renderers may communicate the variables to the shaders using different
51 /// mechanisms over which Usd has no control; Primvars simply provide the
52 /// classification that any renderer should use to locate potential
53 /// overrides. Do please note that primvars override parameters on
54 /// UsdShadeShader objects, \em not
55 /// \ref UsdShadeNodeGraph_Interfaces "Interface Attributes" on UsdShadeMaterial
56 /// prims.
57 /// \li *Primvars inherit down scene namespace.* Regular USD attributes only
58 /// apply to the prim on which they are specified, but primvars implicitly
59 /// also apply to any child prims, unless those child prims have their
60 /// own opinions about those primvars. This capability necessarily
61 /// entails added cost to check for inherited values, but the benefit
62 /// is that it allows concise encoding of certain opinions that broadly
63 /// affect large amounts of geometry. See
64 /// UsdGeomImageable::FindInheritedPrimvars().
65 ///
66 /// \section Usd_Creating_and_Accessing_Primvars Creating and Accessing Primvars
67 ///
68 /// The UsdGeomPrimvarsAPI schema provides a complete interface for creating
69 /// and querying prims for primvars.
70 ///
71 /// The <b>only</b> way to create a new Primvar in scene description is by
72 /// calling UsdGeomPrimvarsAPI::CreatePrimvar(). One cannot "enhance" or
73 /// "promote" an already existing attribute into a Primvar, because doing so
74 /// may require a namespace edit to rename the attribute, which cannot, in
75 /// general, be done within a single UsdEditContext. Instead, create a new
76 /// UsdGeomPrimvar of the desired name using
77 /// UsdGeomPrimvarsAPI::CreatePrimvar(), and then copy the existing attribute
78 /// onto the new UsdGeomPrimvar.
79 ///
80 /// Primvar names can contain arbitrary sub-namespaces. The behavior of
81 /// UsdGeomImageable::GetPrimvar(TfToken const &name) is to prepend "primvars:"
82 /// onto 'name' if it is not already a prefix, and return the result, which
83 /// means we do not have any ambiguity between the primvars
84 /// "primvars:nsA:foo" and "primvars:nsB:foo". <b>There are reserved keywords
85 /// that may not be used as the base names of primvars,</b> and attempting to
86 /// create Primvars of these names will result in a coding error. The
87 /// reserved keywords are tokens the Primvar uses internally to encode various
88 /// features, such as the "indices" keyword used by
89 /// \ref UsdGeomPrimvar_Indexed_primvars "Indexed Primvars".
90 ///
91 /// \anchor UsdGeomPrimvar_Using_Primvar
92 /// If a client wishes to access an already-extant attribute as a Primvar,
93 /// (which may or may not actually be valid Primvar), they can use the
94 /// speculative constructor; typically, a primvar is only "interesting" if it
95 /// additionally provides a value. This might look like:
96 /// \code
97 /// UsdGeomPrimvar primvar = UsdGeomPrimvar(usdAttr);
98 /// if (primvar.HasValue()) {
99 /// VtValue values;
100 /// primvar.Get(&values, timeCode);
101 /// TfToken interpolation = primvar.GetInterpolation();
102 /// int elementSize = primvar.GetElementSize();
103 /// ...
104 /// }
105 /// \endcode
106 ///
107 /// or, because Get() returns `true` if and only if it found a value:
108 /// \code
109 /// UsdGeomPrimvar primvar = UsdGeomPrimvar(usdAttr);
110 /// VtValue values;
111 /// if (primvar.Get(&values, timeCode)) {
112 /// TfToken interpolation = primvar.GetInterpolation();
113 /// int elementSize = primvar.GetElementSize();
114 /// ...
115 /// }
116 /// \endcode
117 ///
118 /// \subsection Usd_Handling_Indexed_Primvars Proper Client Handling of "Indexed" Primvars
119 ///
120 /// As discussed in greater detail in
121 /// \ref UsdGeomPrimvar_Indexed_primvars "Indexed Primvars", primvars can
122 /// optionally contain a (possibly time-varying) indexing attribute that
123 /// establishes a sharing topology for elements of the primvar. Consumers
124 /// can always chose to ignore the possibility of indexed data by exclusively
125 /// using the ComputeFlattened() API. If a client wishes to preserve indexing
126 /// in their processing of a primvar, we suggest a pattern like the following,
127 /// which accounts for the fact that a stronger layer can
128 /// \ref UsdAttribute::Block() "block" a primvar's indexing from a weaker
129 /// layer, via UsdGeomPrimvar::BlockIndices():
130 /// \code
131 /// VtValue values;
132 /// VtIntArray indices;
133 ///
134 /// if (primvar.Get(&values, timeCode)){
135 /// if (primvar.GetIndices(&indices, timeCode)){
136 /// // primvar is indexed: validate/process values and indices together
137 /// }
138 /// else {
139 /// // primvar is not indexed: validate/process values as flat array
140 /// }
141 /// }
142 /// \endcode
143 ///
144 /// \subsection Usd_Primvar_As_Attribute UsdGeomPrimvar and UsdAttribute API
145 ///
146 /// UsdGeomPrimvar presents a small slice of the UsdAttribute API - enough to
147 /// extract the data that comprises the "Declaration info", and get/set of
148 /// the attribute value. A UsdGeomPrimvar also auto-converts to UsdAttribute,
149 /// so you can pass a UsdGeomPrimvar to any function that accepts a UsdAttribute
150 /// or const-ref thereto.
151 ///
152 /// \section Usd_Primvar_Types Primvar Allowed Scene Description Types and Plurality
153 /// There are no limitations imposed on the allowable scene description types
154 /// for Primvars; it is the responsibility of each consuming client to perform
155 /// renderer-specific conversions, if need be (the USD distribution will include
156 /// reference RenderMan conversion utilities).
157 ///
158 /// A note about type plurality of Primvars: It is legitimate for a Primvar
159 /// to be of scalar or array type, and again, consuming clients must be
160 /// prepared to accommodate both. However, while it is not possible, in all
161 /// cases, for USD to \em prevent one from \em changing the type of an attribute
162 /// in different layers or variants of an asset, it is never a good idea to
163 /// do so. This is relevant because, except in a few special cases, it is
164 /// not possible to encode an \em interpolation of any value greater than
165 /// \em constant without providing multiple (i.e. array) data values. Therefore,
166 /// if there is any possibility that downstream clients might need to change
167 /// a Primvar's interpolation, the Primvar-creator should encode it as an
168 /// array rather than a scalar.
169 ///
170 /// Why allow scalar values at all, then? First, sometimes it brings clarity
171 /// to (use of) a shader's API to acknowledge that some parameters are meant
172 /// to be single-valued over a shaded primitive. Second, many DCC's provide
173 /// far richer affordances for editing scalars than they do array values, and
174 /// we feel it is safer to let the content creator make the decision/tradeoff
175 /// of which kind of flexibility is more relevant, rather than leaving it to
176 /// an importer/exporter pair to interpret.
177 ///
178 /// Also, like all attributes, Primvars can be time-sampled, and values can
179 /// be authored and consumed just as any other attribute. There is currently
180 /// no validation that the length of value arrays matches to the size
181 /// required by a gprim's topology, interpolation, and elementSize.
182 ///
183 /// For consumer convenience, we provide GetDeclarationInfo(), which returns
184 /// all the type information (other than topology) needed to compute the
185 /// required array size, which is also all the information required to
186 /// prepare the Primvar's value for consumption by a renderer.
187 ///
188 /// \section Usd_UsdGeomPrimvar_Lifetime Lifetime Management and Primvar Validity
189 ///
190 /// UsdGeomPrimvar has an explicit bool operator that validates that
191 /// the attribute IsDefined() and thus valid for querying and authoring
192 /// values and metadata. This is a fairly expensive query that we do
193 /// <b>not</b> cache, so if client code retains UsdGeomPrimvar objects, it should
194 /// manage its object validity closely, for performance. An ideal pattern
195 /// is to listen for UsdNotice::StageContentsChanged notifications, and
196 /// revalidate/refetch its retained UsdGeomPrimvar s only then, and otherwise use
197 /// them without validity checking.
198 ///
199 /// \section Usd_InterpolationVals Interpolation of Geometric Primitive Variables
200 /// In the following explanation of the meaning of the various kinds/levels
201 /// of Primvar interpolation, each bolded bullet gives the name of the token
202 /// in \ref UsdGeomTokens that provides the value. So to set a Primvar's
203 /// interpolation to "varying", one would:
204 /// \code
205 /// primvar.SetInterpolation(UsdGeomTokens->varying);
206 /// \endcode
207 ///
208 /// \em Interpolation describes how the Primvar will be interpolated over the
209 /// uv parameter space of a surface primitive (or Curves, Points, or
210 /// PointInstancer schemas). The possible values are:
211 /// \li <b>constant</b> One value remains constant over the entire surface
212 /// primitive.
213 /// \li <b>uniform</b> One value remains constant for each uv patch segment of
214 /// the surface primitive (which is a \em face for meshes).
215 /// \li <b>varying</b> Four values are interpolated over each uv patch segment
216 /// of the surface. Bilinear interpolation is used for interpolation
217 /// between the four values.
218 /// \li <b>vertex</b> Values are interpolated between each vertex in the
219 /// surface primitive. The basis function of the surface is used for
220 /// interpolation between vertices.
221 /// \li <b>faceVarying</b> For polygons and subdivision surfaces, four values
222 /// are interpolated over each face of the mesh. Bilinear interpolation
223 /// is used for interpolation between the four values.
224 ///
225 /// See <a href="https://openusd.org/release/user_guides/primvars.html">Primvars</a>
226 /// for visual examples and details on the different interpolation modes. Also,
227 /// see the schema documentation for non-surface schemas for details on how
228 /// the interpolation modes are interpreted for those schemas.
229 ///
230 /// \section Usd_Extending_UsdObject_Classes UsdGeomPrimvar As Example of Attribute Schema
231 ///
232 /// Just as UsdSchemaBase and its subclasses provide the pattern for how to
233 /// layer schema onto the generic UsdPrim object, UsdGeomPrimvar provides an
234 /// example of how to layer schema onto a generic UsdAttribute object. In both
235 /// cases, the schema object wraps and contains the UsdObject.
236 ///
237 /// \section Usd_UsdGeomPrimvar_Inheritance Primvar Namespace Inheritance
238 ///
239 /// Constant interpolation primvar values can be inherited down namespace.
240 /// That is, a primvar value set on a prim will also apply to any child
241 /// prims, unless those children have their own opinions about those named
242 /// primvars. For complete details on how primvars inherit, see
243 /// \ref usdGeom_PrimvarInheritance .
244 ///
245 /// \sa UsdGeomImageable::FindInheritablePrimvars().
246 ///
248 {
249 public:
250 
251  // Default constructor returns an invalid Primvar. Exists for
252  // container classes
254  {
255  /* NOTHING */
256  }
257 
258  /// Copy construct.
260  UsdGeomPrimvar(const UsdGeomPrimvar &other);
261 
262  /// Copy assign.
264  UsdGeomPrimvar &operator=(const UsdGeomPrimvar &other);
265 
266  /// Speculative constructor that will produce a valid UsdGeomPrimvar when
267  /// \p attr already represents an attribute that is Primvar, and
268  /// produces an \em invalid Primvar otherwise (i.e.
269  /// \ref UsdGeomPrimvar_bool "operator bool()" will return false).
270  ///
271  /// Calling \c UsdGeomPrimvar::IsPrimvar(attr) will return the same truth
272  /// value as this constructor, but if you plan to subsequently use the
273  /// Primvar anyways, just use this constructor, as demonstrated in the
274  /// \ref UsdGeomPrimvar_Using_Primvar "class documentation".
276  explicit UsdGeomPrimvar(const UsdAttribute &attr);
277 
278  /// Return the Primvar's interpolation, which is
279  /// \ref Usd_InterpolationVals "UsdGeomTokens->constant" if unauthored
280  ///
281  /// Interpolation determines how the Primvar interpolates over
282  /// a geometric primitive. See \ref Usd_InterpolationVals
284  TfToken GetInterpolation() const;
285 
286  /// Set the Primvar's interpolation.
287  ///
288  /// Errors and returns false if \p interpolation is out of range as
289  /// defined by IsValidInterpolation(). No attempt is made to validate
290  /// that the Primvar's value contains the right number of elements
291  /// to match its interpolation to its topology.
292  ///
293  /// \sa GetInterpolation(), \ref Usd_InterpolationVals
295  bool SetInterpolation(const TfToken &interpolation);
296 
297  /// Has interpolation been explicitly authored on this Primvar?
298  ///
299  /// \sa GetInterpolationSize()
301  bool HasAuthoredInterpolation() const;
302 
303  /// Return the "element size" for this Primvar, which is 1 if
304  /// unauthored. If this Primvar's type is \em not an array type,
305  /// (e.g. "Vec3f[]"), then elementSize is irrelevant.
306  ///
307  /// ElementSize does \em not generally encode the length of an array-type
308  /// primvar, and rarely needs to be authored. ElementSize can be thought
309  /// of as a way to create an "aggregate interpolatable type", by
310  /// dictating how many consecutive elements in the value array should be
311  /// taken as an atomic element to be interpolated over a gprim.
312  ///
313  /// For example, spherical harmonics are often represented as a
314  /// collection of nine floating-point coefficients, and the coefficients
315  /// need to be sampled across a gprim's surface: a perfect case for
316  /// primvars. However, USD has no <tt>float9</tt> datatype. But we can
317  /// communicate the aggregation of nine floats successfully to renderers
318  /// by declaring a simple float-array valued primvar, and setting its
319  /// \em elementSize to 9. To author a \em uniform spherical harmonic
320  /// primvar on a Mesh of 42 faces, the primvar's array value would contain
321  /// 9*42 = 378 float elements.
323  int GetElementSize() const;
324 
325  /// Set the elementSize for this Primvar.
326  ///
327  /// Errors and returns false if \p eltSize less than 1.
328  ///
329  /// \sa GetElementSize()
331  bool SetElementSize(int eltSize);
332 
333  /// Has elementSize been explicitly authored on this Primvar?
334  ///
335  /// \sa GetElementSize()
337  bool HasAuthoredElementSize() const;
338 
339 
340  /// Test whether a given UsdAttribute represents valid Primvar, which
341  /// implies that creating a UsdGeomPrimvar from the attribute will succeed.
342  ///
343  /// Success implies that \c attr.IsDefined() is true.
345  static bool IsPrimvar(const UsdAttribute &attr);
346 
347 
348  /// Test whether a given \p name represents a valid name of a primvar,
349  /// which implies that creating a UsdGeomPrimvar with the given name will
350  /// succeed.
351  ///
353  static bool IsValidPrimvarName(const TfToken& name);
354 
355  /// Returns the \p name, devoid of the "primvars:" token if present,
356  /// otherwise returns the \p name unchanged
358  static TfToken StripPrimvarsName(const TfToken& name);
359 
360  /// Validate that the provided \p interpolation is a valid setting for
361  /// interpolation as defined by \ref Usd_InterpolationVals.
363  static bool IsValidInterpolation(const TfToken &interpolation);
364 
365  /// Convenience function for fetching all information required to
366  /// properly declare this Primvar. The \p name returned is the
367  /// "client name", stripped of the "primvars" namespace, i.e. equivalent to
368  /// GetPrimvarName()
369  ///
370  /// May also be more efficient than querying key individually.
373  TfToken *interpolation, int *elementSize) const;
374 
375  // ---------------------------------------------------------------
376  /// \name UsdAttribute API
377  // ---------------------------------------------------------------
378  /// @{
379 
380  /// Allow UsdGeomPrimvar to auto-convert to UsdAttribute, so you can
381  /// pass a UsdGeomPrimvar to any function that accepts a UsdAttribute or
382  /// const-ref thereto.
383  operator UsdAttribute const& () const { return _attr; }
384 
385  /// Explicit UsdAttribute extractor
386  UsdAttribute const &GetAttr() const { return _attr; }
387 
388  /// Return true if the underlying UsdAttribute::IsDefined(), and in
389  /// addition the attribute is identified as a Primvar. Does not imply
390  /// that the primvar provides a value
391  bool IsDefined() const { return IsPrimvar(_attr); }
392 
393  /// Return true if the underlying attribute has a value, either from
394  /// authored scene description or a fallback.
395  bool HasValue() const { return _attr.HasValue(); }
396 
397  /// Return true if the underlying attribute has an unblocked, authored
398  /// value.
399  bool HasAuthoredValue() const { return _attr.HasAuthoredValue(); }
400 
401  /// \anchor UsdGeomPrimvar_bool
402  /// Return true if this Primvar is valid for querying and authoring
403  /// values and metadata, which is identically equivalent to IsDefined().
404  explicit operator bool() const {
405  return IsDefined() ? &UsdGeomPrimvar::_attr : 0;
406  }
407 
408  /// \sa UsdAttribute::GetName()
409  TfToken const &GetName() const { return _attr.GetName(); }
410 
411  /// Returns the primvar's name, devoid of the "primvars:" namespace.
412  /// This is the name by which clients should refer to the primvar, if
413  /// not by its full attribute name - i.e. they should **not**, in general,
414  /// use GetBaseName(). In the error condition in which this Primvar
415  /// object is not backed by a properly namespaced UsdAttribute, return
416  /// an empty TfToken.
418  TfToken GetPrimvarName() const;
419 
420  /// Does this primvar contain any namespaces other than the "primvars:"
421  /// namespace?
422  ///
423  /// Some clients may only wish to consume primvars that have no extra
424  /// namespaces in their names, for ease of translating to other systems
425  /// that do not allow namespaces.
427  bool NameContainsNamespaces() const;
428 
429  /// \sa UsdAttribute::GetBaseName()
430  TfToken GetBaseName() const { return _attr.GetBaseName(); }
431 
432  /// \sa UsdAttribute::GetNamespace()
433  TfToken GetNamespace() const { return _attr.GetNamespace(); }
434 
435  /// \sa UsdAttribute::SplitName()
436  std::vector<std::string> SplitName() const { return _attr.SplitName(); };
437 
438  /// \sa UsdAttribute::GetTypeName()
439  SdfValueTypeName GetTypeName() const { return _attr.GetTypeName(); }
440 
441  /// Get the attribute value of the Primvar at \p time .
442  ///
443  /// \sa Usd_Handling_Indexed_Primvars for proper handling of
444  /// \ref Usd_Handling_Indexed_Primvars "indexed primvars"
445  template <typename T>
447  return _attr.Get(value, time);
448  }
449 
450  /// Set the attribute value of the Primvar at \p time
451  template <typename T>
452  bool Set(const T& value, UsdTimeCode time = UsdTimeCode::Default()) const {
453  return _attr.Set(value, time);
454  }
455 
456  /// Populates a vector with authored sample times for this primvar.
457  /// Returns false on error.
458  ///
459  /// This considers any timeSamples authored on the associated "indices"
460  /// attribute if the primvar is indexed.
461  ///
462  /// \sa UsdAttribute::GetTimeSamples
464  bool GetTimeSamples(std::vector<double>* times) const;
465 
466  /// Populates a vector with authored sample times in \p interval.
467  ///
468  /// This considers any timeSamples authored on the associated "indices"
469  /// attribute if the primvar is indexed.
470  ///
471  /// \sa UsdAttribute::GetTimeSamplesInInterval
473  bool GetTimeSamplesInInterval(const GfInterval& interval,
474  std::vector<double>* times) const;
475 
476  /// Return true if it is possible, but not certain, that this primvar's
477  /// value changes over time, false otherwise.
478  ///
479  /// This considers time-varyingness of the associated "indices" attribute
480  /// if the primvar is indexed.
481  ///
482  /// \sa UsdAttribute::ValueMightBeTimeVarying
484  bool ValueMightBeTimeVarying() const;
485 
486  /// @}
487 
488  // ---------------------------------------------------------------
489  /// @{
490  /// \anchor UsdGeomPrimvar_Indexed_primvars
491  /// \name Indexed primvars API
492  ///
493  /// For non-constant values of interpolation, it is often the case that the
494  /// same value is repeated many times in the array value of a primvar. An
495  /// indexed primvar can be used in such cases to optimize for data storage
496  /// if the primvar's interpolation is uniform, varying, or vertex.
497  /// For **faceVarying primvars**, however, indexing serves a higher
498  /// purpose (and should be used *only* for this purpose, since renderers
499  /// and OpenSubdiv will assume it) of establishing a surface topology
500  /// for the primvar. That is, faceVarying primvars use indexing to
501  /// unambiguously define discontinuities in their functions at edges
502  /// and vertices. Please see the <a href="http://graphics.pixar.com/opensubdiv/docs/subdivision_surfaces.html#face-varying-interpolation-rules">
503  /// OpenSubdiv documentation on FaceVarying Primvars</a> for more
504  /// information.
505  ///
506  /// To create an indexed primvar, the value of the attribute associated with
507  /// the primvar is set to an array consisting of all the unique values that
508  /// appear in the primvar array. A separate namespaced "indices" attribute
509  /// is set to an integer array containing indices into the array with all
510  /// the unique elements. The final value of the primvar is computed using
511  /// the indices array and the attribute value array.
512  ///
513  /// See also \ref Usd_Handling_Indexed_Primvars
514 
515  /// Sets the indices value of the indexed primvar at \p time.
516  ///
517  /// The values in the indices array must be valid indices into the authored
518  /// array returned by Get(). The element numerality of the primvar's
519  /// 'interpolation' metadata applies to the "indices" array, not the attribute
520  /// value array (returned by Get()).
522  bool SetIndices(const VtIntArray &indices,
524 
525  /// Returns the value of the indices array associated with the indexed
526  /// primvar at \p time.
527  ///
528  /// \sa SetIndices(), \ref Usd_Handling_Indexed_Primvars
530  bool GetIndices(VtIntArray *indices,
532 
533  /// Block the indices that were previously set. This effectively makes an
534  /// indexed primvar no longer indexed. This is useful when overriding an
535  /// existing primvar.
537  void BlockIndices() const;
538 
539  /// Returns true if the primvar is indexed, i.e., if it has an associated
540  /// "indices" attribute.
541  ///
542  /// If you are going to query the indices anyways, prefer to simply
543  /// consult the return-value of GetIndices(), which will be more efficient.
545  bool IsIndexed() const;
546 
547  /// Returns a valid indices attribute if the primvar is indexed. Returns
548  /// an invalid attribute otherwise.
551 
552  /// Returns the existing indices attribute if the primvar is indexed
553  /// or creates a new one.
556 
557  /// Set the index that represents unauthored values in the indices array.
558  ///
559  /// Some apps (like Maya) allow you to author primvars sparsely over a
560  /// surface. Since most apps can't handle sparse primvars, Maya needs to
561  /// provide a value even for the elements it didn't author. This metadatum
562  /// provides a way to recover the information in apps that do support
563  /// sparse authoring / representation of primvars.
564  ///
565  /// The fallback value of unauthoredValuesIndex is -1, which indicates that
566  /// there are no unauthored values.
567  ///
568  /// \sa GetUnauthoredValuesIndex()
570  bool SetUnauthoredValuesIndex(int unauthoredValuesIndex) const;
571 
572  /// Returns the index that represents unauthored values in the indices array.
573  ///
574  /// \sa SetUnauthoredValuesIndex()
576  int GetUnauthoredValuesIndex() const;
577 
578  /// Computes the flattened value of the primvar at \p time.
579  ///
580  /// If the primvar is not indexed or if the value type of this primvar is
581  /// a scalar, this returns the authored value, which is the same as
582  /// \ref Get(). Hence, it's safe to call ComputeFlattened() on non-indexed
583  /// primvars.
584  template <typename ScalarType>
587 
588  /// \overload
589  /// Computes the flattened value of the primvar at \p time as a VtValue.
590  ///
591  /// If the primvar is not indexed or if the value type of this primvar is
592  /// a scalar, this returns the authored value, which is the same as
593  /// \ref Get(). Hence, it's safe to call ComputeFlattened() on non-indexed
594  /// primvars.
596  bool ComputeFlattened(VtValue *value,
598 
599  /// Computes the flattened value of \p attrValue given \p indices, assuming
600  /// an elementSize of 1.
601  ///
602  /// This method is a static convenience function that performs the main
603  /// work of ComputeFlattened above without needing an instance of a
604  /// UsdGeomPrimvar.
605  ///
606  /// Returns \c false if the value contained in \p attrVal is not a supported
607  /// type for flattening. Otherwise returns \c true. The output
608  /// \p errString variable may be populated with an error string if an error
609  /// is encountered during flattening.
611  static bool ComputeFlattened(VtValue *value, const VtValue &attrVal,
612  const VtIntArray &indices,
613  std::string *errString);
614 
615  /// Computes the flattened value of \p attrValue given \p indices and
616  /// \p elementSize.
617  ///
618  /// This method is a static convenience function that performs the main
619  /// work of ComputeFlattened above without needing an instance of a
620  /// UsdGeomPrimvar.
621  ///
622  /// Returns \c false if the value contained in \p attrVal is not a supported
623  /// type for flattening. Otherwise returns \c true. The output
624  /// \p errString variable may be populated with an error string if an error
625  /// is encountered during flattening.
627  static bool ComputeFlattened(VtValue *value, const VtValue &attrVal,
628  const VtIntArray &indices,
629  int elementSize,
630  std::string *errString);
631 
632 
633  /// @}
634 
635  // ---------------------------------------------------------------
636  /// @{
637  /// \anchor UsdGeomPrimvar_Id_primvars
638  /// \name Id attribute API
639  ///
640  /// Often there is the need to identify a prim within a scene (e.g. a mesh
641  /// on which a procedural should operate, or a shader to inherit). A
642  /// string or string[] -typed primvar can be turned into an "Id Path" primvar by
643  /// calling SetIdTarget() with the path of any object on the current stage. When
644  /// the primvar is subsequently queried via Get(), the returned value will
645  /// be the stringified value of the targeted object's path in whatever
646  /// namespace is defined by the querying stage's root layer. In other
647  /// words, authoring an Id primvar into a published model will return the
648  /// path-to-target in the model, but when the model is referenced into a
649  /// larger scene, it will return the complete scene path.
650  ///
651  /// This works by adding a paired UsdRelationship in a ":idFrom" namespace
652  /// "below" the string primvar. Get() evaluates
653  /// UsdRelationship::GetForwardedTargets() to determine the id-string.
654  /// Thus, this mechanism will always produce a unique identifier for every
655  /// object in a scene, regardless of how many times an asset is referenced
656  /// into a scene. Providing a mesh with a unique identifier primvar can be
657  /// especially useful for renderers that allow plugins/shaders access to
658  /// processed scene data based on user-provided string identifiers.
659  ///
660  /// If an Id primvar has both an \em authored string value and a SetIdTarget()'d
661  /// target, the target path takes precedence.
662  ///
663  /// Currently Id primvars can have only a single target, so the only useful
664  /// interpolation is constant.
665  // ---------------------------------------------------------------
666 
667  /// Returns true if the primvar is an Id primvar.
668  ///
669  /// \sa \ref UsdGeomPrimvar_Id_primvars
671  bool IsIdTarget() const;
672 
673  /// This primvar must be of String or StringArray type for this method to
674  /// succeed. If not, a coding error is raised.
675  ///
676  /// \sa \ref UsdGeomPrimvar_Id_primvars
678  bool SetIdTarget(const SdfPath& path) const;
679 
680  /// @}
681 
682  /// Equality comparison. Return true if \a lhs and \a rhs represent the
683  /// same UsdGeomPrimvar, false otherwise.
684  friend bool operator==(const UsdGeomPrimvar &lhs, const UsdGeomPrimvar &rhs) {
685  return lhs.GetAttr() == rhs.GetAttr();
686  }
687 
688  /// Inequality comparison. Return false if \a lhs and \a rhs represent the
689  /// same UsdPrimvar, true otherwise.
690  friend bool operator!=(const UsdGeomPrimvar &lhs, const UsdGeomPrimvar &rhs) {
691  return !(lhs == rhs);
692  }
693 
694  /// Less-than operator. Returns true if \a lhs < \a rhs.
695  ///
696  /// This simply compares the paths of the underlyingattributes.
697  friend bool operator<(const UsdGeomPrimvar &lhs, const UsdGeomPrimvar &rhs) {
698  return lhs.GetAttr().GetPath() < rhs.GetAttr().GetPath();
699  }
700 
701  // Specialize TfHashAppend for TfHash
702  template <typename HashState>
703  friend void TfHashAppend(HashState& h, const UsdGeomPrimvar& obj) {
704  h.Append(obj.GetAttr());
705  }
706 
707  // hash_value overload for std/boost hash.
708  friend size_t hash_value(const UsdGeomPrimvar &obj) {
709  return TfHash{}(obj);
710  }
711 
712 
713 private:
714  friend class UsdGeomImageable;
715  friend class UsdGeomPrimvarsAPI;
716 
717  /// Validate that the given \p name contains the primvars namespace.
718  /// Does not validate name as a legal property identifier
719  static bool _IsNamespaced(const TfToken& name);
720 
721  /// Return \p name prepended with the proper primvars namespace, if
722  /// it is not already prefixed.
723  ///
724  /// Does not validate name as a legal property identifier, but will
725  /// verify that \p name contains no reserved keywords, and will return
726  /// an empty TfToken if it does. If \p quiet is true, the verification
727  /// will be silent
728  static TfToken _MakeNamespaced(const TfToken& name, bool quiet=false);
729 
730  static TfToken const &_GetNamespacePrefix();
731 
732  /// Factory for UsdGeomImageable's use, so that we can encapsulate the
733  /// logic of what discriminates Primvar in this class, while
734  /// preserving the pattern that attributes can only be created
735  /// via their container objects.
736  ///
737  /// The name of the created attribute may or may not be the specified
738  /// \p attrName, due to the possible need to apply property namespacing
739  /// for Primvar.
740  ///
741  /// The behavior with respect to the provided \p typeName
742  /// is the same as for UsdAttributes::Create().
743  ///
744  /// \return an invalid UsdGeomPrimvar if we failed to create a valid
745  /// attribute, a valid UsdGeomPrimvar otherwise. It is not an
746  /// error to create over an existing, compatible attribute.
747  ///
748  /// It is a failed verification for \p prim to be invalid/expired
749  ///
750  /// \sa UsdPrim::CreateAttribute()
751  UsdGeomPrimvar(const UsdPrim& prim, const TfToken& attrName,
752  const SdfValueTypeName &typeName);
753 
754  UsdAttribute _attr;
755 
756  // Gets or creates the indices attribute corresponding to the primvar.
757  UsdAttribute _GetIndicesAttr(bool create) const;
758 
759  // Helper method for computing the flattened value of an indexed primvar.
760  template<typename ScalarType>
761  static bool _ComputeFlattenedHelper(const VtArray<ScalarType> &authored,
762  const VtIntArray &indices,
763  int elementSize,
764  VtArray<ScalarType> *value,
765  std::string *errString);
766 
767  // Helper function to evaluate the flattened array value of a primvar given
768  // the attribute value and the indices array.
769  template <typename ArrayType>
770  static bool _ComputeFlattenedArray(const VtValue &attrVal,
771  const VtIntArray &indices,
772  int elementSize,
773  VtValue *value,
774  std::string *errString);
775 
776  // Should only be called if _idTargetRelName is set
777  UsdRelationship _GetIdTargetRel(bool create) const;
778 
779  // Compute & cache whether or not this primvar can be an idtarget. After a
780  // call to this function, _idTargetStatus will be either IdTargetImpossible
781  // or IdTargetPossible. If the result is "possible" then _idTargetRelName
782  // will contain the relationship name. This function returns true if
783  // _idTargetStatus was set to IdTargetPossible, else false.
784  bool _ComputeIdTargetPossibility() const;
785 
786  enum _IdTargetStatus {
787  IdTargetUninitialized,
788  IdTargetInitializing,
789  IdTargetImpossible,
790  IdTargetPossible };
791 
792  mutable TfToken _idTargetRelName;
793  mutable std::atomic<_IdTargetStatus> _idTargetStatus;
794 };
795 
796 // We instantiate the following so we can check and provide the correct value
797 // for Id attributes.
798 template <>
799 USDGEOM_API bool UsdGeomPrimvar::Get(std::string* value, UsdTimeCode time) const;
800 
801 template <>
802 USDGEOM_API bool UsdGeomPrimvar::Get(VtStringArray* value, UsdTimeCode time) const;
803 
804 template <>
806 
807 template <typename ScalarType>
808 bool
810 {
811  VtArray<ScalarType> authored;
812  if (!Get(&authored, time))
813  return false;
814 
815  if (!IsIndexed()) {
816  *value = authored;
817  return true;
818  }
819 
820  VtIntArray indices;
821  if (!GetIndices(&indices, time)) {
822  TF_WARN("No indices authored for indexed primvar <%s>.",
823  _attr.GetPath().GetText());
824  return false;
825  }
826 
827  // If the authored array is empty, there's nothing to do.
828  if (authored.empty())
829  return false;
830 
831  std::string errString;
832  bool res = _ComputeFlattenedHelper(authored, indices, GetElementSize(), value, &errString);
833  if (!errString.empty()) {
834  TF_WARN("For primvar %s: %s",
835  UsdDescribe(_attr).c_str(), errString.c_str());
836  }
837  return res;
838 }
839 
840 template<typename ScalarType>
841 bool
842 UsdGeomPrimvar::_ComputeFlattenedHelper(const VtArray<ScalarType> &authored,
843  const VtIntArray &indices,
844  int elementSize,
845  VtArray<ScalarType> *value,
846  std::string *errString)
847 {
848  TF_VERIFY(elementSize >= 1);
849  value->resize(indices.size() * elementSize);
850  bool success = true;
851 
852  std::vector<size_t> invalidIndexPositions;
853  for (size_t i=0; i < indices.size(); i++) {
854  if (indices[i] < 0 || static_cast<size_t>((indices[i] + 1) * elementSize) > authored.size()) {
855  invalidIndexPositions.push_back(i);
856  success = false;
857  continue;
858  }
859 
860  const size_t indicesIdx = indices[i] * elementSize;
861  const size_t valuesIdx = i * elementSize;
862  for (size_t j=0; j < static_cast<size_t>(elementSize); j++) {
863  size_t index = indicesIdx + j;
864  (*value)[valuesIdx + j] = authored[index];
865  }
866  }
867 
868  if (!invalidIndexPositions.empty() && errString) {
869  *errString = TfStringPrintf(
870  "Found %ld invalid indices into authored array of size %ld with"
871  " element size of %i:",
872  invalidIndexPositions.size(),
873  authored.size(), elementSize);
874 
875  // Print a maximum of 5 invalid index positions.
876  size_t numElementsToPrint = std::min(invalidIndexPositions.size(),
877  size_t(5));
878  for (size_t i = 0; i < numElementsToPrint ; ++i) {
879  int invalidIndex = indices[invalidIndexPositions[i]];
880  int authoredStartIndex = invalidIndex * elementSize;
881 
882  *errString += TfStringPrintf(
883  "\n\t Invalid index %i at position %ld refers to %s of the"
884  " authored array, which is out of bounds",
885  invalidIndex,
886  invalidIndexPositions[i],
887  elementSize == 1 ? TfStringPrintf("index %i", authoredStartIndex).c_str()
888  : TfStringPrintf("indices [%i,...,%i]", authoredStartIndex, authoredStartIndex + elementSize - 1).c_str());
889  }
890  }
891  return success;
892 }
893 
894 
896 
897 #endif // USD_PRIMVAR_H
SDF_API const char * GetText() const
TF_API std::string TfStringPrintf(const char *fmt,...)
bool Get(T *value, UsdTimeCode time=UsdTimeCode::Default()) const
Definition: primvar.h:446
static USDGEOM_API TfToken StripPrimvarsName(const TfToken &name)
GLsizei GLenum const void * indices
Definition: glcorearb.h:406
static USDGEOM_API bool IsValidPrimvarName(const TfToken &name)
static constexpr UsdTimeCode Default()
Definition: timeCode.h:113
bool Get(T *value, UsdTimeCode time=UsdTimeCode::Default()) const
Definition: attribute.h:452
USDGEOM_API UsdAttribute GetIndicesAttr() const
USD_API TfToken GetBaseName() const
friend bool operator!=(const UsdGeomPrimvar &lhs, const UsdGeomPrimvar &rhs)
Definition: primvar.h:690
GT_API const UT_StringHolder time
USDGEOM_API bool ValueMightBeTimeVarying() const
GLsizei const GLfloat * value
Definition: glcorearb.h:824
GLsizei const GLchar *const * path
Definition: glcorearb.h:3341
static USDGEOM_API bool IsPrimvar(const UsdAttribute &attr)
USDGEOM_API TfToken GetPrimvarName() const
USDGEOM_API bool GetIndices(VtIntArray *indices, UsdTimeCode time=UsdTimeCode::Default()) const
USD_API SdfValueTypeName GetTypeName() const
Return the "scene description" value type name for this attribute.
ImageBuf OIIO_API min(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
TfToken GetNamespace() const
Definition: primvar.h:433
USD_API bool HasAuthoredValue() const
OutGridT const XformOp bool bool
TfToken GetBaseName() const
Definition: primvar.h:430
Definition: hash.h:472
USDGEOM_API bool NameContainsNamespaces() const
bool ComputeFlattened(VtArray< ScalarType > *value, UsdTimeCode time=UsdTimeCode::Default()) const
Definition: primvar.h:809
USDGEOM_API int GetUnauthoredValuesIndex() const
Definition: token.h:70
USDGEOM_API bool SetIdTarget(const SdfPath &path) const
USD_API std::vector< std::string > SplitName() const
USDGEOM_API bool SetInterpolation(const TfToken &interpolation)
std::vector< std::string > SplitName() const
Definition: primvar.h:436
USD_API bool HasValue() const
USD_API std::string UsdDescribe(const UsdObject &)
Return a human-readable description.
#define TF_WARN
bool Set(const T &value, UsdTimeCode time=UsdTimeCode::Default()) const
Set the attribute value of the Primvar at time.
Definition: primvar.h:452
USDGEOM_API UsdAttribute CreateIndicesAttr() const
TfToken const & GetName() const
Definition: primvar.h:409
Definition: prim.h:116
bool HasAuthoredValue() const
Definition: primvar.h:399
USDGEOM_API bool IsIndexed() const
GLuint const GLchar * name
Definition: glcorearb.h:786
USD_API TfToken GetNamespace() const
bool Set(const T &value, UsdTimeCode time=UsdTimeCode::Default()) const
Definition: attribute.h:495
Definition: types.h:153
Definition: path.h:273
const TfToken & GetName() const
Definition: object.h:221
static USDGEOM_API bool IsValidInterpolation(const TfToken &interpolation)
GLint j
Definition: glad.h:2733
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:2002
SdfPath GetPath() const
Definition: object.h:186
SdfValueTypeName GetTypeName() const
Definition: primvar.h:439
friend size_t hash_value(const UsdGeomPrimvar &obj)
Definition: primvar.h:708
USDGEOM_API bool IsIdTarget() const
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
GLuint index
Definition: glcorearb.h:786
USDGEOM_API bool HasAuthoredElementSize() const
UsdAttribute const & GetAttr() const
Explicit UsdAttribute extractor.
Definition: primvar.h:386
friend void TfHashAppend(HashState &h, const UsdGeomPrimvar &obj)
Definition: primvar.h:703
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
bool IsDefined() const
Definition: primvar.h:391
USDGEOM_API bool GetTimeSamples(std::vector< double > *times) const
bool HasValue() const
Definition: primvar.h:395
OIIO_UTIL_API const char * c_str(string_view str)
USDGEOM_API bool SetIndices(const VtIntArray &indices, UsdTimeCode time=UsdTimeCode::Default()) const
USDGEOM_API bool HasAuthoredInterpolation() const
#define USDGEOM_API
Definition: api.h:23
friend bool operator==(const UsdGeomPrimvar &lhs, const UsdGeomPrimvar &rhs)
Definition: primvar.h:684
USDGEOM_API bool SetUnauthoredValuesIndex(int unauthoredValuesIndex) const
USDGEOM_API int GetElementSize() const
USDGEOM_API void GetDeclarationInfo(TfToken *name, SdfValueTypeName *typeName, TfToken *interpolation, int *elementSize) const
friend bool operator<(const UsdGeomPrimvar &lhs, const UsdGeomPrimvar &rhs)
Definition: primvar.h:697
Definition: value.h:146
USDGEOM_API TfToken GetInterpolation() const
USDGEOM_API bool GetTimeSamplesInInterval(const GfInterval &interval, std::vector< double > *times) const
USDGEOM_API UsdGeomPrimvar & operator=(const UsdGeomPrimvar &other)
Copy assign.
USDGEOM_API bool SetElementSize(int eltSize)
USDGEOM_API void BlockIndices() const