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/usdGeom/primvar.h"
26 #include "pxr/usd/usd/prim.h"
27 #include "pxr/usd/usd/relationship.h"
28
29 #include "pxr/base/tf/registryManager.h"
30 #include "pxr/base/tf/staticTokens.h"
31
32 #include <vector>
33
34 PXR_NAMESPACE_OPEN_SCOPE
35
36
37 TF_DEFINE_PRIVATE_TOKENS(
38 _tokens,
39 ((primvarsPrefix, "primvars:"))
40 ((idFrom, ":idFrom"))
41 ((indicesSuffix, ":indices"))
42 );
43
UsdGeomPrimvar(const UsdAttribute & attr)44 UsdGeomPrimvar::UsdGeomPrimvar(const UsdAttribute &attr)
45 : _attr(attr)
46 {
47 _SetIdTargetRelName();
48 }
49
50 /* static */
51 bool
IsPrimvar(const UsdAttribute & attr)52 UsdGeomPrimvar::IsPrimvar(const UsdAttribute &attr)
53 {
54 if (!attr)
55 return false;
56
57 return IsValidPrimvarName(attr.GetName());
58 }
59
60 /* static */
61 bool
IsValidPrimvarName(const TfToken & name)62 UsdGeomPrimvar::IsValidPrimvarName(const TfToken& name)
63 {
64 // All properly namespaced attributes are legal primvars, *except*
65 // the "sidecar" attributes we create as part of the schema, like
66 // "primvars:foo:indices". We do not need to worry about the idFrom
67 // suffix because it only appears on relationships.
68 return (TfStringStartsWith(name, _tokens->primvarsPrefix) &&
69 !TfStringEndsWith(name, _tokens->indicesSuffix));
70 }
71
72 /* static */
73 TfToken
StripPrimvarsName(const TfToken & name)74 UsdGeomPrimvar::StripPrimvarsName(const TfToken& name)
75 {
76 std::string const & fullName = name.GetString();
77
78 std::pair<std::string, bool> res =
79 SdfPath::StripPrefixNamespace(fullName, _tokens->primvarsPrefix);
80
81 return res.second ? TfToken(res.first) : name;
82 }
83
84 /* static */
85 bool
_IsNamespaced(const TfToken & name)86 UsdGeomPrimvar::_IsNamespaced(const TfToken& name)
87 {
88 return TfStringStartsWith(name, _tokens->primvarsPrefix);
89 }
90
91 /* static */
92 TfToken
_MakeNamespaced(const TfToken & name,bool quiet)93 UsdGeomPrimvar::_MakeNamespaced(const TfToken& name, bool quiet)
94 {
95 TfToken result;
96 if (_IsNamespaced(name)){
97 result = name;
98 }
99 else {
100 result = TfToken(_tokens->primvarsPrefix.GetString() + name.GetString());
101 }
102
103 if (!IsValidPrimvarName(result)){
104 result = TfToken();
105 if (!quiet){
106 // XXX if we add more reserved keywords we'll need to extract
107 // the offending keyword rather than assume it is "indices".
108 TF_CODING_ERROR("%s is not a valid name for a Primvar, because"
109 " it contains the reserved name \"indices\"",
110 name.GetText());
111 }
112 }
113
114 return result;
115 }
116
117 /* static */
118 TfToken const&
_GetNamespacePrefix()119 UsdGeomPrimvar::_GetNamespacePrefix()
120 {
121 return _tokens->primvarsPrefix;
122 }
123
124 TfToken
GetInterpolation() const125 UsdGeomPrimvar::GetInterpolation() const
126 {
127 TfToken interpolation;
128
129 if (!_attr.GetMetadata(UsdGeomTokens->interpolation, &interpolation)){
130 interpolation = UsdGeomTokens->constant;
131 }
132
133 return interpolation;
134 }
135
136 bool
HasAuthoredInterpolation() const137 UsdGeomPrimvar::HasAuthoredInterpolation() const
138 {
139 return _attr.HasAuthoredMetadata(UsdGeomTokens->interpolation);
140 }
141
142 bool
IsValidInterpolation(const TfToken & interpolation)143 UsdGeomPrimvar::IsValidInterpolation(const TfToken &interpolation)
144 {
145 return ((interpolation == UsdGeomTokens->constant) ||
146 (interpolation == UsdGeomTokens->uniform) ||
147 (interpolation == UsdGeomTokens->vertex) ||
148 (interpolation == UsdGeomTokens->varying) ||
149 (interpolation == UsdGeomTokens->faceVarying));
150 }
151
152 bool
SetInterpolation(const TfToken & interpolation)153 UsdGeomPrimvar::SetInterpolation(const TfToken &interpolation)
154 {
155 if (!IsValidInterpolation(interpolation)){
156 TF_CODING_ERROR("Attempt to set invalid primvar interpolation "
157 "\"%s\" for attribute %s",
158 interpolation.GetText(),
159 _attr.GetPath().GetString().c_str());
160 return false;
161 }
162 return _attr.SetMetadata(UsdGeomTokens->interpolation, interpolation);
163 }
164
165 int
GetElementSize() const166 UsdGeomPrimvar::GetElementSize() const
167 {
168 int eltSize = 1;
169 _attr.GetMetadata(UsdGeomTokens->elementSize, &eltSize);
170
171 return eltSize;
172 }
173
174 bool
SetElementSize(int eltSize)175 UsdGeomPrimvar::SetElementSize(int eltSize)
176 {
177 if (eltSize < 1){
178 TF_CODING_ERROR("Attempt to set elementSize to %d for attribute "
179 "%s (must be a positive, non-zero value)",
180 eltSize,
181 _attr.GetPath().GetString().c_str());
182 return false;
183 }
184 return _attr.SetMetadata(UsdGeomTokens->elementSize, eltSize);
185 }
186
187 bool
HasAuthoredElementSize() const188 UsdGeomPrimvar::HasAuthoredElementSize() const
189 {
190 return _attr.HasAuthoredMetadata(UsdGeomTokens->elementSize);
191 }
192
193
194 void
GetDeclarationInfo(TfToken * name,SdfValueTypeName * typeName,TfToken * interpolation,int * elementSize) const195 UsdGeomPrimvar::GetDeclarationInfo(TfToken *name, SdfValueTypeName *typeName,
196 TfToken *interpolation,
197 int *elementSize) const
198 {
199 TF_VERIFY(name && typeName && interpolation && elementSize);
200
201 // We don't have any more efficient access pattern yet, but at least
202 // we're still saving client some code
203 *name = GetPrimvarName();
204 *typeName = GetTypeName();
205 *interpolation = GetInterpolation();
206 *elementSize = GetElementSize();
207 }
208
209 UsdAttribute
_GetIndicesAttr(bool create) const210 UsdGeomPrimvar::_GetIndicesAttr(bool create) const
211 {
212 TfToken indicesAttrName(GetName().GetString() +
213 _tokens->indicesSuffix.GetString());
214
215 if (create) {
216 return _attr.GetPrim().CreateAttribute(indicesAttrName,
217 SdfValueTypeNames->IntArray, /*custom*/ false,
218 SdfVariabilityVarying);
219 }
220 else {
221 return _attr.GetPrim().GetAttribute(indicesAttrName);
222 }
223 }
224
225 UsdAttribute
GetIndicesAttr() const226 UsdGeomPrimvar::GetIndicesAttr() const
227 {
228 return _GetIndicesAttr(/*create*/ false);
229 }
230
231 UsdAttribute
CreateIndicesAttr() const232 UsdGeomPrimvar::CreateIndicesAttr() const
233 {
234 return _GetIndicesAttr(/*create*/ true);
235 }
236
237 bool
SetIndices(const VtIntArray & indices,UsdTimeCode time) const238 UsdGeomPrimvar::SetIndices(const VtIntArray &indices,
239 UsdTimeCode time) const
240 {
241 // Check if the typeName is array valued here and issue a warning
242 // if it's not.
243 SdfValueTypeName typeName = GetTypeName();
244 if (!typeName.IsArray()) {
245 TF_CODING_ERROR("Setting indices on non-array valued primvar of type "
246 "'%s'.", typeName.GetAsToken().GetText());
247 return false;
248 }
249 return _GetIndicesAttr(/*create*/ true).Set(indices, time);
250
251 }
252
253 void
BlockIndices() const254 UsdGeomPrimvar::BlockIndices() const
255 {
256 // Check if the typeName is array valued here and issue a warning
257 // if it's not.
258 SdfValueTypeName typeName = GetTypeName();
259 if (!typeName.IsArray()) {
260 TF_CODING_ERROR("Setting indices on non-array valued primvar of type "
261 "'%s'.", typeName.GetAsToken().GetText());
262 return;
263 }
264 _GetIndicesAttr(/*create*/ true).Block();
265 }
266
267 bool
GetIndices(VtIntArray * indices,UsdTimeCode time) const268 UsdGeomPrimvar::GetIndices(VtIntArray *indices,
269 UsdTimeCode time) const
270 {
271 UsdAttribute indicesAttr = _GetIndicesAttr(/*create*/ false);
272 if (indicesAttr)
273 return indicesAttr.Get(indices, time);
274
275 return false;
276 }
277
278 bool
IsIndexed() const279 UsdGeomPrimvar::IsIndexed() const
280 {
281 return _GetIndicesAttr(/*create*/ false).HasAuthoredValue();
282 }
283
284 bool
SetUnauthoredValuesIndex(int unauthoredValuesIndex) const285 UsdGeomPrimvar::SetUnauthoredValuesIndex(int unauthoredValuesIndex) const
286 {
287 return _attr.SetMetadata(UsdGeomTokens->unauthoredValuesIndex,
288 unauthoredValuesIndex);
289 }
290
291 int
GetUnauthoredValuesIndex() const292 UsdGeomPrimvar::GetUnauthoredValuesIndex() const
293 {
294 int unauthoredValuesIndex = -1;
295 _attr.GetMetadata(UsdGeomTokens->unauthoredValuesIndex,
296 &unauthoredValuesIndex);
297
298 return unauthoredValuesIndex;
299 }
300
301 // Helper function to evaluate the flattened array value of a primvar given
302 // the attribute value and the indices array.
303 template <typename ArrayType>
304 bool
_ComputeFlattenedArray(const VtValue & attrVal,const VtIntArray & indices,VtValue * value,std::string * errorString)305 UsdGeomPrimvar::_ComputeFlattenedArray(const VtValue &attrVal,
306 const VtIntArray &indices,
307 VtValue *value,
308 std::string *errorString)
309 {
310 if (!attrVal.IsHolding<ArrayType>())
311 return false;
312
313 ArrayType result;
314 if (_ComputeFlattenedHelper(attrVal.UncheckedGet<ArrayType>(), indices,
315 &result, errorString)) {
316 *value = VtValue::Take(result);
317 }
318
319 return true;
320 }
321
322 bool
ComputeFlattened(VtValue * value,UsdTimeCode time) const323 UsdGeomPrimvar::ComputeFlattened(VtValue *value, UsdTimeCode time) const
324 {
325 VtValue attrVal;
326 if (!Get(&attrVal, time)) {
327 return false;
328 }
329
330 // If the primvar attr value is not an array or if the primvar isn't
331 // indexed, simply return the attribute value.
332 if (!attrVal.IsArrayValued() || !IsIndexed()) {
333 *value = VtValue::Take(attrVal);
334 return true;
335 }
336
337 VtIntArray indices;
338 if (!GetIndices(&indices, time)) {
339 TF_CODING_ERROR("No indices authored for indexed primvar <%s>.",
340 _attr.GetPath().GetText());
341 return false;
342 }
343
344 std::string errStr;
345 bool res = ComputeFlattened(value, attrVal, indices, &errStr);
346 if (!errStr.empty()) {
347 TF_WARN("For primvar %s: %s",
348 UsdDescribe(_attr).c_str(), errStr.c_str());
349 }
350 return res;
351 }
352
353 bool
ComputeFlattened(VtValue * value,const VtValue & attrVal,const VtIntArray & indices,std::string * errStr)354 UsdGeomPrimvar::ComputeFlattened(
355 VtValue *value,
356 const VtValue &attrVal,
357 const VtIntArray &indices,
358 std::string *errStr)
359 {
360 // If the primvar attr value is not an array simply return the
361 // attribute value.
362 if (!attrVal.IsArrayValued()) {
363 *value = attrVal;
364 return true;
365 }
366
367 // Handle all known supported array value types.
368 bool foundSupportedType =
369 _ComputeFlattenedArray<VtVec2fArray>(attrVal, indices, value, errStr) ||
370 _ComputeFlattenedArray<VtVec2dArray>(attrVal, indices, value, errStr) ||
371 _ComputeFlattenedArray<VtVec2iArray>(attrVal, indices, value, errStr) ||
372 _ComputeFlattenedArray<VtVec2hArray>(attrVal, indices, value, errStr) ||
373 _ComputeFlattenedArray<VtVec3fArray>(attrVal, indices, value, errStr) ||
374 _ComputeFlattenedArray<VtVec3dArray>(attrVal, indices, value, errStr) ||
375 _ComputeFlattenedArray<VtVec3iArray>(attrVal, indices, value, errStr) ||
376 _ComputeFlattenedArray<VtVec3hArray>(attrVal, indices, value, errStr) ||
377 _ComputeFlattenedArray<VtVec4fArray>(attrVal, indices, value, errStr) ||
378 _ComputeFlattenedArray<VtVec4dArray>(attrVal, indices, value, errStr) ||
379 _ComputeFlattenedArray<VtVec4iArray>(attrVal, indices, value, errStr) ||
380 _ComputeFlattenedArray<VtVec4hArray>(attrVal, indices, value, errStr) ||
381 _ComputeFlattenedArray<VtMatrix3dArray>(attrVal, indices, value,
382 errStr) ||
383 _ComputeFlattenedArray<VtMatrix4dArray>(attrVal, indices, value,
384 errStr) ||
385 _ComputeFlattenedArray<VtStringArray>(attrVal, indices, value, errStr)||
386 _ComputeFlattenedArray<VtDoubleArray>(attrVal, indices, value, errStr)||
387 _ComputeFlattenedArray<VtIntArray>(attrVal, indices, value, errStr) ||
388 _ComputeFlattenedArray<VtUIntArray>(attrVal, indices, value, errStr) ||
389 _ComputeFlattenedArray<VtFloatArray>(attrVal, indices, value, errStr) ||
390 _ComputeFlattenedArray<VtHalfArray>(attrVal, indices, value, errStr);
391
392 if (!foundSupportedType && errStr) {
393 std::string thisErr = TfStringPrintf(
394 "Unsupported indexed primvar value type %s.",
395 attrVal.GetTypeName().c_str());
396 *errStr = errStr->empty() ? thisErr : *errStr + "\n" + thisErr;
397 }
398
399 return !value->IsEmpty();
400 }
401
UsdGeomPrimvar(const UsdPrim & prim,const TfToken & primvarName,const SdfValueTypeName & typeName)402 UsdGeomPrimvar::UsdGeomPrimvar(const UsdPrim& prim,
403 const TfToken& primvarName,
404 const SdfValueTypeName &typeName)
405 {
406 TF_VERIFY(prim);
407
408 TfToken attrName = _MakeNamespaced(primvarName);
409
410 if (!attrName.IsEmpty()){
411 _attr = prim.CreateAttribute(attrName, typeName, /* custom = */ false);
412 }
413 // If a problem occurred, an error should already have been issued,
414 // and _attr will be invalid, which is what we want
415
416 _SetIdTargetRelName();
417 }
418
419 void
_SetIdTargetRelName()420 UsdGeomPrimvar::_SetIdTargetRelName()
421 {
422 if (!_attr) {
423 return;
424 }
425
426 const SdfValueTypeName& typeName = _attr.GetTypeName();
427 if (typeName == SdfValueTypeNames->String ||
428 typeName == SdfValueTypeNames->StringArray) {
429 std::string name(_attr.GetName().GetString());
430 _idTargetRelName = TfToken(name.append(_tokens->idFrom.GetText()));
431 }
432 }
433
434 UsdRelationship
_GetIdTargetRel(bool create) const435 UsdGeomPrimvar::_GetIdTargetRel(bool create) const
436 {
437 if (create) {
438 return _attr.GetPrim().CreateRelationship(_idTargetRelName);
439 }
440 else {
441 return _attr.GetPrim().GetRelationship(_idTargetRelName);
442 }
443 }
444
445 bool
IsIdTarget() const446 UsdGeomPrimvar::IsIdTarget() const
447 {
448 return !_idTargetRelName.IsEmpty() && _GetIdTargetRel(false);
449 }
450
451 bool
SetIdTarget(const SdfPath & path) const452 UsdGeomPrimvar::SetIdTarget(
453 const SdfPath& path) const
454 {
455 if (_idTargetRelName.IsEmpty()) {
456 TF_CODING_ERROR("Can only set ID Target for string or string[] typed"
457 " primvars (primvar type is '%s')",
458 _attr.GetTypeName().GetAsToken().GetText());
459 return false;
460 }
461
462 if (UsdRelationship rel = _GetIdTargetRel(true)) {
463 SdfPathVector targets;
464 targets.push_back(path.IsEmpty() ? _attr.GetPrimPath() :
465 path);
466 return rel.SetTargets(targets);
467 }
468 return false;
469 }
470
471 template <>
472 bool
Get(std::string * value,UsdTimeCode time) const473 UsdGeomPrimvar::Get(
474 std::string* value,
475 UsdTimeCode time) const
476 {
477 // check if there is a relationship and if so use the target path string to
478 // get the string value.
479 if (!_idTargetRelName.IsEmpty()) {
480 if (UsdRelationship rel = _GetIdTargetRel(false)) {
481 SdfPathVector targets;
482 if (rel.GetForwardedTargets(&targets) &&
483 targets.size() == 1) {
484 *value = targets[0].GetString();
485 return true;
486 }
487 return false;
488 }
489 }
490
491 return _attr.Get(value, time);
492 }
493
494 // XXX: for now we just take the first. here's an idea for how
495 // it'd work for multiple targets:
496 // string[] primvars:handleids (interpolation = "uniform")
497 // int[] primvars:handleids:indices = [0, 1, 1, 1, 0, ...., 1]
498 // rel primvars:handleids:idFrom = [</a/t1>, </a/t2>]
499 template <>
500 bool
Get(VtStringArray * value,UsdTimeCode time) const501 UsdGeomPrimvar::Get(
502 VtStringArray* value,
503 UsdTimeCode time) const
504 {
505 // check if there is a relationship and if so use the target path string to
506 // get the string value... Just take the first target, for now.
507 if (!_idTargetRelName.IsEmpty()) {
508 if (UsdRelationship rel = _GetIdTargetRel(false)) {
509 value->clear();
510 SdfPathVector targets;
511 if (rel.GetForwardedTargets(&targets) &&
512 targets.size() > 1) {
513 value->push_back(targets[0].GetString());
514 return true;
515 }
516 return false;
517 }
518 }
519
520 return _attr.Get(value, time);
521 }
522
523 template <>
524 bool
Get(VtValue * value,UsdTimeCode time) const525 UsdGeomPrimvar::Get(
526 VtValue* value,
527 UsdTimeCode time) const
528 {
529 if (!_idTargetRelName.IsEmpty()) {
530 const SdfValueTypeName& typeName = _attr.GetTypeName();
531 if (typeName == SdfValueTypeNames->String) {
532 std::string s;
533 bool ret = Get(&s, time);
534 if (ret) {
535 *value = VtValue(s);
536 }
537 return ret;
538 }
539 else if (typeName == SdfValueTypeNames->StringArray) {
540 VtStringArray s;
541 bool ret = Get(&s, time);
542 if (ret) {
543 *value = VtValue(s);
544 }
545 return ret;
546 }
547 }
548
549 return _attr.Get(value, time);
550 }
551
552 bool
GetTimeSamples(std::vector<double> * times) const553 UsdGeomPrimvar::GetTimeSamples(std::vector<double>* times) const
554 {
555 return GetTimeSamplesInInterval(GfInterval::GetFullInterval(), times);
556 }
557
558 bool
GetTimeSamplesInInterval(const GfInterval & interval,std::vector<double> * times) const559 UsdGeomPrimvar::GetTimeSamplesInInterval(
560 const GfInterval& interval,
561 std::vector<double>* times) const
562 {
563 if (IsIndexed()) {
564 if (UsdAttribute indicesAttr = _GetIndicesAttr(false)) {
565 return UsdAttribute::GetUnionedTimeSamplesInInterval(
566 {_attr, indicesAttr}, interval, times);
567 }
568 }
569
570 return _attr.GetTimeSamplesInInterval(interval, times);
571 }
572
573 bool
ValueMightBeTimeVarying() const574 UsdGeomPrimvar::ValueMightBeTimeVarying() const
575 {
576 if (IsIndexed()) {
577 if (UsdAttribute indicesAttr = _GetIndicesAttr(false)) {
578 if (indicesAttr.ValueMightBeTimeVarying()) {
579 return true;
580 }
581 }
582 }
583
584 return _attr.ValueMightBeTimeVarying();
585 }
586
587 TfToken
GetPrimvarName() const588 UsdGeomPrimvar::GetPrimvarName() const
589 {
590 std::string const & fullName = _attr.GetName().GetString();
591
592 std::pair<std::string, bool> res =
593 SdfPath::StripPrefixNamespace(fullName, _tokens->primvarsPrefix);
594
595 return res.second ? TfToken(res.first) : TfToken();
596 }
597
598 bool
NameContainsNamespaces() const599 UsdGeomPrimvar::NameContainsNamespaces() const
600 {
601 static const size_t primvarsPrefixLen = _tokens->primvarsPrefix.GetString().size();
602 return (_attr.GetName().GetString().find(':', primvarsPrefixLen)
603 != std::string::npos);
604 }
605
606
607 PXR_NAMESPACE_CLOSE_SCOPE
608
609