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