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/attribute.h"
26 #include "pxr/usd/usd/attributeQuery.h"
27 #include "pxr/usd/usd/common.h"
28 #include "pxr/usd/usd/instanceCache.h"
29
30 #include "pxr/usd/usd/interpolators.h"
31 #include "pxr/usd/usd/stage.h"
32 #include "pxr/usd/usd/valueUtils.h"
33
34 #include "pxr/usd/ar/resolver.h"
35 #include "pxr/usd/ar/resolverContextBinder.h"
36 #include "pxr/usd/sdf/attributeSpec.h"
37 #include "pxr/usd/sdf/layer.h"
38 #include "pxr/usd/sdf/primSpec.h"
39 #include "pxr/usd/sdf/schema.h"
40
41 // NOTE: this is not actually used, but AttributeSpec requires it
42 #include "pxr/usd/sdf/relationshipSpec.h"
43
44 #include <boost/preprocessor/seq/for_each.hpp>
45 #include <vector>
46
47 PXR_NAMESPACE_OPEN_SCOPE
48
49
50 // ------------------------------------------------------------------------- //
51 // UsdAttribute
52 // ------------------------------------------------------------------------- //
53
54 SdfVariability
GetVariability() const55 UsdAttribute::GetVariability() const
56 {
57 return _GetStage()->_GetVariability(*this);
58 }
59
60 bool
SetVariability(SdfVariability variability) const61 UsdAttribute::SetVariability(SdfVariability variability) const
62 {
63 return SetMetadata(SdfFieldKeys->Variability, variability);
64 }
65
66 SdfValueTypeName
GetTypeName() const67 UsdAttribute::GetTypeName() const
68 {
69 TfToken typeName;
70 GetMetadata(SdfFieldKeys->TypeName, &typeName);
71 return SdfSchema::GetInstance().FindType(typeName);
72 }
73
74 TfToken
GetRoleName() const75 UsdAttribute::GetRoleName() const
76 {
77 return GetTypeName().GetRole();
78 }
79
80 bool
SetTypeName(const SdfValueTypeName & typeName) const81 UsdAttribute::SetTypeName(const SdfValueTypeName& typeName) const
82 {
83 return SetMetadata(SdfFieldKeys->TypeName, typeName.GetAsToken());
84 }
85
86 void
Block() const87 UsdAttribute::Block() const
88 {
89 Clear();
90 Set(VtValue(SdfValueBlock()), UsdTimeCode::Default());
91 }
92
93 bool
GetTimeSamples(std::vector<double> * times) const94 UsdAttribute::GetTimeSamples(std::vector<double>* times) const
95 {
96 return _GetStage()->_GetTimeSamplesInInterval(*this,
97 GfInterval::GetFullInterval(), times);
98 }
99
100 size_t
GetNumTimeSamples() const101 UsdAttribute::GetNumTimeSamples() const
102 {
103 return _GetStage()->_GetNumTimeSamples(*this);
104 }
105
106 bool
GetBracketingTimeSamples(double desiredTime,double * lower,double * upper,bool * hasTimeSamples) const107 UsdAttribute::GetBracketingTimeSamples(double desiredTime,
108 double* lower,
109 double* upper,
110 bool* hasTimeSamples) const
111 {
112 return _GetStage()->_GetBracketingTimeSamples(
113 *this, desiredTime, /*requireAuthored*/ false,
114 lower, upper, hasTimeSamples);
115 }
116
117 bool
GetTimeSamplesInInterval(const GfInterval & interval,std::vector<double> * times) const118 UsdAttribute::GetTimeSamplesInInterval(const GfInterval& interval,
119 std::vector<double>* times) const
120 {
121 return _GetStage()->_GetTimeSamplesInInterval(*this, interval, times);
122 }
123
124 /* static */
125 bool
GetUnionedTimeSamples(const std::vector<UsdAttribute> & attrs,std::vector<double> * times)126 UsdAttribute::GetUnionedTimeSamples(
127 const std::vector<UsdAttribute> &attrs,
128 std::vector<double> *times)
129 {
130 return GetUnionedTimeSamplesInInterval(attrs,
131 GfInterval::GetFullInterval(), times);
132 }
133
134 /* static */
135 bool
GetUnionedTimeSamplesInInterval(const std::vector<UsdAttribute> & attrs,const GfInterval & interval,std::vector<double> * times)136 UsdAttribute::GetUnionedTimeSamplesInInterval(
137 const std::vector<UsdAttribute> &attrs,
138 const GfInterval &interval,
139 std::vector<double> *times)
140 {
141 // Clear the vector first before proceeding to accumulate sample times.
142 times->clear();
143
144 if (attrs.empty()) {
145 return true;
146 }
147
148 bool success = true;
149
150 // Vector that holds the per-attribute sample times.
151 std::vector<double> attrSampleTimes;
152
153 // Temporary vector used to hold the union of two time-sample vectors.
154 std::vector<double> tempUnionSampleTimes;
155
156 for (const auto &attr : attrs) {
157 if (!attr) {
158 success = false;
159 continue;
160 }
161
162 // This will work even if the attributes belong to different
163 // USD stages.
164 success = attr.GetStage()->_GetTimeSamplesInInterval(attr, interval,
165 &attrSampleTimes) && success;
166
167 // Merge attrSamplesTimes into the times vector.
168 Usd_MergeTimeSamples(times, attrSampleTimes, &tempUnionSampleTimes);
169 }
170
171 return success;
172 }
173
174 bool
HasAuthoredValueOpinion() const175 UsdAttribute::HasAuthoredValueOpinion() const
176 {
177 UsdResolveInfo resolveInfo;
178 _GetStage()->_GetResolveInfo(*this, &resolveInfo);
179 return resolveInfo.HasAuthoredValueOpinion();
180 }
181
182 bool
HasAuthoredValue() const183 UsdAttribute::HasAuthoredValue() const
184 {
185 UsdResolveInfo resolveInfo;
186 _GetStage()->_GetResolveInfo(*this, &resolveInfo);
187 return resolveInfo.HasAuthoredValue();
188 }
189
190 bool
HasValue() const191 UsdAttribute::HasValue() const
192 {
193 UsdResolveInfo resolveInfo;
194 _GetStage()->_GetResolveInfo(*this, &resolveInfo);
195 return resolveInfo._source != UsdResolveInfoSourceNone;
196 }
197
198 bool
HasFallbackValue() const199 UsdAttribute::HasFallbackValue() const
200 {
201 SdfAttributeSpecHandle attrDef =
202 _GetStage()->_GetSchemaAttributeSpec(*this);
203 return attrDef && attrDef->HasDefaultValue();
204 }
205
206 bool
ValueMightBeTimeVarying() const207 UsdAttribute::ValueMightBeTimeVarying() const
208 {
209 return _GetStage()->_ValueMightBeTimeVarying(*this);
210 }
211
212 template <typename T>
213 bool
_Get(T * value,UsdTimeCode time) const214 UsdAttribute::_Get(T* value, UsdTimeCode time) const
215 {
216 return _GetStage()->_GetValue(time, *this, value);
217 }
218
219 bool
Get(VtValue * value,UsdTimeCode time) const220 UsdAttribute::Get(VtValue* value, UsdTimeCode time) const
221 {
222 return _GetStage()->_GetValue(time, *this, value);
223 }
224
225 UsdResolveInfo
GetResolveInfo(UsdTimeCode time) const226 UsdAttribute::GetResolveInfo(UsdTimeCode time) const
227 {
228 UsdResolveInfo resolveInfo;
229 _GetStage()->_GetResolveInfo(*this, &resolveInfo, &time);
230 return resolveInfo;
231 }
232
233 template <typename T>
234 bool
_Set(const T & value,UsdTimeCode time) const235 UsdAttribute::_Set(const T& value, UsdTimeCode time) const
236 {
237 return _GetStage()->_SetValue(time, *this, value);
238 }
239
240 bool
Set(const char * value,UsdTimeCode time) const241 UsdAttribute::Set(const char* value, UsdTimeCode time) const {
242 std::string strVal(value);
243 return _Set(strVal, time);
244 }
245
246 bool
Set(const VtValue & value,UsdTimeCode time) const247 UsdAttribute::Set(const VtValue& value, UsdTimeCode time) const
248 {
249 return _GetStage()->_SetValue(time, *this, value);
250 }
251
252 bool
Clear() const253 UsdAttribute::Clear() const
254 {
255 return ClearDefault()
256 && ClearMetadata(SdfFieldKeys->TimeSamples);
257 }
258
259 bool
ClearAtTime(UsdTimeCode time) const260 UsdAttribute::ClearAtTime(UsdTimeCode time) const
261 {
262 return _GetStage()->_ClearValue(time, *this);
263 }
264
265 bool
ClearDefault() const266 UsdAttribute::ClearDefault() const
267 {
268 return ClearAtTime(UsdTimeCode::Default());
269 }
270
271 TfToken
GetColorSpace() const272 UsdAttribute::GetColorSpace() const
273 {
274 TfToken colorSpace;
275 GetMetadata(SdfFieldKeys->ColorSpace, &colorSpace);
276 return colorSpace;
277 }
278
279 void
SetColorSpace(const TfToken & colorSpace) const280 UsdAttribute::SetColorSpace(const TfToken &colorSpace) const
281 {
282 SetMetadata(SdfFieldKeys->ColorSpace, colorSpace);
283 }
284
285 bool
HasColorSpace() const286 UsdAttribute::HasColorSpace() const
287 {
288 return HasMetadata(SdfFieldKeys->ColorSpace);
289 }
290
291 bool
ClearColorSpace() const292 UsdAttribute::ClearColorSpace() const
293 {
294 return ClearMetadata(SdfFieldKeys->ColorSpace);
295 }
296
297 SdfAttributeSpecHandle
_CreateSpec(const SdfValueTypeName & typeName,bool custom,const SdfVariability & variability) const298 UsdAttribute::_CreateSpec(const SdfValueTypeName& typeName, bool custom,
299 const SdfVariability &variability) const
300 {
301 UsdStage *stage = _GetStage();
302
303 // Try to create a spec for editing either from the definition or from
304 // copying existing spec info.
305 TfErrorMark m;
306 if (SdfAttributeSpecHandle attrSpec =
307 stage->_CreateAttributeSpecForEditing(*this))
308 return attrSpec;
309
310 // If creating the spec on the stage failed without issuing an error, that
311 // means there was no existing authored scene description to go on (i.e. no
312 // builtin info from prim type, and no existing authored spec). Stamp a
313 // spec with the provided default values.
314 if (m.IsClean()) {
315 SdfChangeBlock block;
316 return SdfAttributeSpec::New(
317 stage->_CreatePrimSpecForEditing(GetPrim()),
318 _PropName(), typeName, variability, custom);
319 }
320 return TfNullPtr;
321 }
322
323 SdfAttributeSpecHandle
_CreateSpec() const324 UsdAttribute::_CreateSpec() const
325 {
326 return _GetStage()->_CreateAttributeSpecForEditing(*this);
327 }
328
329 bool
_Create(const SdfValueTypeName & typeName,bool custom,const SdfVariability & variability) const330 UsdAttribute::_Create(const SdfValueTypeName& typeName, bool custom,
331 const SdfVariability &variability) const
332 {
333 return _CreateSpec(typeName, custom, variability);
334 }
335
336
337 ARCH_PRAGMA_PUSH
338 ARCH_PRAGMA_INSTANTIATION_AFTER_SPECIALIZATION
339
340 // Explicitly instantiate templated getters and setters for all Sdf value
341 // types.
342 #define _INSTANTIATE_GET(r, unused, elem) \
343 template USD_API bool UsdAttribute::_Get( \
344 SDF_VALUE_CPP_TYPE(elem)*, UsdTimeCode) const; \
345 template USD_API bool UsdAttribute::_Get( \
346 SDF_VALUE_CPP_ARRAY_TYPE(elem)*, UsdTimeCode) const; \
347 template USD_API bool UsdAttribute::_Set( \
348 const SDF_VALUE_CPP_TYPE(elem)&, UsdTimeCode) const; \
349 template USD_API bool UsdAttribute::_Set( \
350 const SDF_VALUE_CPP_ARRAY_TYPE(elem)&, UsdTimeCode) const;
351
352 BOOST_PP_SEQ_FOR_EACH(_INSTANTIATE_GET, ~, SDF_VALUE_TYPES)
353 #undef _INSTANTIATE_GET
354
355 // In addition to the Sdf value types, _Set can also be called with an
356 // SdfValueBlock.
357 template USD_API bool UsdAttribute::_Set(
358 const SdfValueBlock &, UsdTimeCode) const;
359
360 ARCH_PRAGMA_POP
361
362 SdfPath
_GetPathForAuthoring(const SdfPath & path,std::string * whyNot) const363 UsdAttribute::_GetPathForAuthoring(const SdfPath &path,
364 std::string* whyNot) const
365 {
366 SdfPath result;
367 if (!path.IsEmpty()) {
368 SdfPath absPath =
369 path.MakeAbsolutePath(GetPath().GetAbsoluteRootOrPrimPath());
370 if (Usd_InstanceCache::IsPathInPrototype(absPath)) {
371 if (whyNot) {
372 *whyNot = "Cannot refer to a prototype or an object within a "
373 "prototype.";
374 }
375 return result;
376 }
377 }
378
379 // If this is a relative target path, we have to map both the anchor
380 // and target path and then re-relativize them.
381 const UsdEditTarget &editTarget = _GetStage()->GetEditTarget();
382 if (path.IsAbsolutePath()) {
383 result = editTarget.MapToSpecPath(path).StripAllVariantSelections();
384 } else {
385 const SdfPath anchorPrim = GetPath().GetPrimPath();
386 const SdfPath translatedAnchorPrim =
387 editTarget.MapToSpecPath(anchorPrim)
388 .StripAllVariantSelections();
389 const SdfPath translatedPath =
390 editTarget.MapToSpecPath(path.MakeAbsolutePath(anchorPrim))
391 .StripAllVariantSelections();
392 result = translatedPath.MakeRelativePath(translatedAnchorPrim);
393 }
394
395 if (result.IsEmpty()) {
396 if (whyNot) {
397 *whyNot = TfStringPrintf(
398 "Cannot map <%s> to layer @%s@ via stage's EditTarget",
399 path.GetText(), _GetStage()->GetEditTarget().
400 GetLayer()->GetIdentifier().c_str());
401 }
402 }
403
404 return result;
405 }
406
407
408 bool
AddConnection(const SdfPath & source,UsdListPosition position) const409 UsdAttribute::AddConnection(const SdfPath& source,
410 UsdListPosition position) const
411 {
412 std::string errMsg;
413 const SdfPath pathToAuthor = _GetPathForAuthoring(source, &errMsg);
414 if (pathToAuthor.IsEmpty()) {
415 TF_CODING_ERROR("Cannot append connection <%s> to attribute <%s>: %s",
416 source.GetText(), GetPath().GetText(), errMsg.c_str());
417 return false;
418 }
419
420 // NOTE! Do not insert any code that modifies scene description between the
421 // changeblock and the call to _CreateSpec! Explanation: _CreateSpec calls
422 // code that inspects the composition graph and then does some authoring.
423 // We want that authoring to be inside the change block, but if any scene
424 // description changes are made after the block is created but before we
425 // call _CreateSpec, the composition structure may be invalidated.
426 SdfChangeBlock block;
427 SdfAttributeSpecHandle attrSpec = _CreateSpec();
428
429 if (!attrSpec)
430 return false;
431
432 Usd_InsertListItem( attrSpec->GetConnectionPathList(), pathToAuthor,
433 position );
434 return true;
435 }
436
437 bool
RemoveConnection(const SdfPath & source) const438 UsdAttribute::RemoveConnection(const SdfPath& source) const
439 {
440 std::string errMsg;
441 const SdfPath pathToAuthor = _GetPathForAuthoring(source, &errMsg);
442 if (pathToAuthor.IsEmpty()) {
443 TF_CODING_ERROR("Cannot remove connection <%s> from attribute <%s>: %s",
444 source.GetText(), GetPath().GetText(), errMsg.c_str());
445 return false;
446 }
447
448 // NOTE! Do not insert any code that modifies scene description between the
449 // changeblock and the call to _CreateSpec! Explanation: _CreateSpec calls
450 // code that inspects the composition graph and then does some authoring.
451 // We want that authoring to be inside the change block, but if any scene
452 // description changes are made after the block is created but before we
453 // call _CreateSpec, the composition structure may be invalidated.
454 SdfChangeBlock block;
455 SdfAttributeSpecHandle attrSpec = _CreateSpec();
456
457 if (!attrSpec)
458 return false;
459
460 attrSpec->GetConnectionPathList().Remove(pathToAuthor);
461 return true;
462 }
463
464 bool
SetConnections(const SdfPathVector & sources) const465 UsdAttribute::SetConnections(const SdfPathVector& sources) const
466 {
467 SdfPathVector mappedPaths;
468 mappedPaths.reserve(sources.size());
469 for (const SdfPath &path: sources) {
470 std::string errMsg;
471 mappedPaths.push_back(_GetPathForAuthoring(path, &errMsg));
472 if (mappedPaths.back().IsEmpty()) {
473 TF_CODING_ERROR("Cannot set connection <%s> on attribute <%s>: %s",
474 path.GetText(), GetPath().GetText(),
475 errMsg.c_str());
476 return false;
477 }
478 }
479
480 // NOTE! Do not insert any code that modifies scene description between the
481 // changeblock and the call to _CreateSpec! Explanation: _CreateSpec calls
482 // code that inspects the composition graph and then does some authoring.
483 // We want that authoring to be inside the change block, but if any scene
484 // description changes are made after the block is created but before we
485 // call _CreateSpec, the composition structure may be invalidated.
486 SdfChangeBlock block;
487 SdfAttributeSpecHandle attrSpec = _CreateSpec();
488
489 if (!attrSpec)
490 return false;
491
492 attrSpec->GetConnectionPathList().ClearEditsAndMakeExplicit();
493 attrSpec->GetConnectionPathList().GetExplicitItems() = mappedPaths;
494
495 return true;
496 }
497
498 bool
ClearConnections() const499 UsdAttribute::ClearConnections() const
500 {
501 // NOTE! Do not insert any code that modifies scene description between the
502 // changeblock and the call to _CreateSpec! Explanation: _CreateSpec calls
503 // code that inspects the composition graph and then does some authoring.
504 // We want that authoring to be inside the change block, but if any scene
505 // description changes are made after the block is created but before we
506 // call _CreateSpec, the composition structure may be invalidated.
507 SdfChangeBlock block;
508 SdfAttributeSpecHandle attrSpec = _CreateSpec();
509
510 if (!attrSpec)
511 return false;
512
513 attrSpec->GetConnectionPathList().ClearEdits();
514 return true;
515 }
516
517 bool
GetConnections(SdfPathVector * sources) const518 UsdAttribute::GetConnections(SdfPathVector *sources) const
519 {
520 TRACE_FUNCTION();
521 return _GetTargets(SdfSpecTypeAttribute, sources);
522 }
523
524 bool
HasAuthoredConnections() const525 UsdAttribute::HasAuthoredConnections() const
526 {
527 return HasAuthoredMetadata(SdfFieldKeys->ConnectionPaths);
528 }
529
530 PXR_NAMESPACE_CLOSE_SCOPE
531
532