1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/usd/usdGeom/pointInstancer.h"
25 #include "pxr/usd/usd/schemaRegistry.h"
26 #include "pxr/usd/usd/typed.h"
27 
28 #include "pxr/usd/sdf/types.h"
29 #include "pxr/usd/sdf/assetPath.h"
30 
31 PXR_NAMESPACE_OPEN_SCOPE
32 
33 // Register the schema with the TfType system.
TF_REGISTRY_FUNCTION(TfType)34 TF_REGISTRY_FUNCTION(TfType)
35 {
36     TfType::Define<UsdGeomPointInstancer,
37         TfType::Bases< UsdGeomBoundable > >();
38 
39     // Register the usd prim typename as an alias under UsdSchemaBase. This
40     // enables one to call
41     // TfType::Find<UsdSchemaBase>().FindDerivedByName("PointInstancer")
42     // to find TfType<UsdGeomPointInstancer>, which is how IsA queries are
43     // answered.
44     TfType::AddAlias<UsdSchemaBase, UsdGeomPointInstancer>("PointInstancer");
45 }
46 
47 /* virtual */
~UsdGeomPointInstancer()48 UsdGeomPointInstancer::~UsdGeomPointInstancer()
49 {
50 }
51 
52 /* static */
53 UsdGeomPointInstancer
Get(const UsdStagePtr & stage,const SdfPath & path)54 UsdGeomPointInstancer::Get(const UsdStagePtr &stage, const SdfPath &path)
55 {
56     if (!stage) {
57         TF_CODING_ERROR("Invalid stage");
58         return UsdGeomPointInstancer();
59     }
60     return UsdGeomPointInstancer(stage->GetPrimAtPath(path));
61 }
62 
63 /* static */
64 UsdGeomPointInstancer
Define(const UsdStagePtr & stage,const SdfPath & path)65 UsdGeomPointInstancer::Define(
66     const UsdStagePtr &stage, const SdfPath &path)
67 {
68     static TfToken usdPrimTypeName("PointInstancer");
69     if (!stage) {
70         TF_CODING_ERROR("Invalid stage");
71         return UsdGeomPointInstancer();
72     }
73     return UsdGeomPointInstancer(
74         stage->DefinePrim(path, usdPrimTypeName));
75 }
76 
77 /* virtual */
_GetSchemaKind() const78 UsdSchemaKind UsdGeomPointInstancer::_GetSchemaKind() const
79 {
80     return UsdGeomPointInstancer::schemaKind;
81 }
82 
83 /* static */
84 const TfType &
_GetStaticTfType()85 UsdGeomPointInstancer::_GetStaticTfType()
86 {
87     static TfType tfType = TfType::Find<UsdGeomPointInstancer>();
88     return tfType;
89 }
90 
91 /* static */
92 bool
_IsTypedSchema()93 UsdGeomPointInstancer::_IsTypedSchema()
94 {
95     static bool isTyped = _GetStaticTfType().IsA<UsdTyped>();
96     return isTyped;
97 }
98 
99 /* virtual */
100 const TfType &
_GetTfType() const101 UsdGeomPointInstancer::_GetTfType() const
102 {
103     return _GetStaticTfType();
104 }
105 
106 UsdAttribute
GetProtoIndicesAttr() const107 UsdGeomPointInstancer::GetProtoIndicesAttr() const
108 {
109     return GetPrim().GetAttribute(UsdGeomTokens->protoIndices);
110 }
111 
112 UsdAttribute
CreateProtoIndicesAttr(VtValue const & defaultValue,bool writeSparsely) const113 UsdGeomPointInstancer::CreateProtoIndicesAttr(VtValue const &defaultValue, bool writeSparsely) const
114 {
115     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->protoIndices,
116                        SdfValueTypeNames->IntArray,
117                        /* custom = */ false,
118                        SdfVariabilityVarying,
119                        defaultValue,
120                        writeSparsely);
121 }
122 
123 UsdAttribute
GetIdsAttr() const124 UsdGeomPointInstancer::GetIdsAttr() const
125 {
126     return GetPrim().GetAttribute(UsdGeomTokens->ids);
127 }
128 
129 UsdAttribute
CreateIdsAttr(VtValue const & defaultValue,bool writeSparsely) const130 UsdGeomPointInstancer::CreateIdsAttr(VtValue const &defaultValue, bool writeSparsely) const
131 {
132     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->ids,
133                        SdfValueTypeNames->Int64Array,
134                        /* custom = */ false,
135                        SdfVariabilityVarying,
136                        defaultValue,
137                        writeSparsely);
138 }
139 
140 UsdAttribute
GetPositionsAttr() const141 UsdGeomPointInstancer::GetPositionsAttr() const
142 {
143     return GetPrim().GetAttribute(UsdGeomTokens->positions);
144 }
145 
146 UsdAttribute
CreatePositionsAttr(VtValue const & defaultValue,bool writeSparsely) const147 UsdGeomPointInstancer::CreatePositionsAttr(VtValue const &defaultValue, bool writeSparsely) const
148 {
149     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->positions,
150                        SdfValueTypeNames->Point3fArray,
151                        /* custom = */ false,
152                        SdfVariabilityVarying,
153                        defaultValue,
154                        writeSparsely);
155 }
156 
157 UsdAttribute
GetOrientationsAttr() const158 UsdGeomPointInstancer::GetOrientationsAttr() const
159 {
160     return GetPrim().GetAttribute(UsdGeomTokens->orientations);
161 }
162 
163 UsdAttribute
CreateOrientationsAttr(VtValue const & defaultValue,bool writeSparsely) const164 UsdGeomPointInstancer::CreateOrientationsAttr(VtValue const &defaultValue, bool writeSparsely) const
165 {
166     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->orientations,
167                        SdfValueTypeNames->QuathArray,
168                        /* custom = */ false,
169                        SdfVariabilityVarying,
170                        defaultValue,
171                        writeSparsely);
172 }
173 
174 UsdAttribute
GetScalesAttr() const175 UsdGeomPointInstancer::GetScalesAttr() const
176 {
177     return GetPrim().GetAttribute(UsdGeomTokens->scales);
178 }
179 
180 UsdAttribute
CreateScalesAttr(VtValue const & defaultValue,bool writeSparsely) const181 UsdGeomPointInstancer::CreateScalesAttr(VtValue const &defaultValue, bool writeSparsely) const
182 {
183     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->scales,
184                        SdfValueTypeNames->Float3Array,
185                        /* custom = */ false,
186                        SdfVariabilityVarying,
187                        defaultValue,
188                        writeSparsely);
189 }
190 
191 UsdAttribute
GetVelocitiesAttr() const192 UsdGeomPointInstancer::GetVelocitiesAttr() const
193 {
194     return GetPrim().GetAttribute(UsdGeomTokens->velocities);
195 }
196 
197 UsdAttribute
CreateVelocitiesAttr(VtValue const & defaultValue,bool writeSparsely) const198 UsdGeomPointInstancer::CreateVelocitiesAttr(VtValue const &defaultValue, bool writeSparsely) const
199 {
200     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->velocities,
201                        SdfValueTypeNames->Vector3fArray,
202                        /* custom = */ false,
203                        SdfVariabilityVarying,
204                        defaultValue,
205                        writeSparsely);
206 }
207 
208 UsdAttribute
GetAccelerationsAttr() const209 UsdGeomPointInstancer::GetAccelerationsAttr() const
210 {
211     return GetPrim().GetAttribute(UsdGeomTokens->accelerations);
212 }
213 
214 UsdAttribute
CreateAccelerationsAttr(VtValue const & defaultValue,bool writeSparsely) const215 UsdGeomPointInstancer::CreateAccelerationsAttr(VtValue const &defaultValue, bool writeSparsely) const
216 {
217     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->accelerations,
218                        SdfValueTypeNames->Vector3fArray,
219                        /* custom = */ false,
220                        SdfVariabilityVarying,
221                        defaultValue,
222                        writeSparsely);
223 }
224 
225 UsdAttribute
GetAngularVelocitiesAttr() const226 UsdGeomPointInstancer::GetAngularVelocitiesAttr() const
227 {
228     return GetPrim().GetAttribute(UsdGeomTokens->angularVelocities);
229 }
230 
231 UsdAttribute
CreateAngularVelocitiesAttr(VtValue const & defaultValue,bool writeSparsely) const232 UsdGeomPointInstancer::CreateAngularVelocitiesAttr(VtValue const &defaultValue, bool writeSparsely) const
233 {
234     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->angularVelocities,
235                        SdfValueTypeNames->Vector3fArray,
236                        /* custom = */ false,
237                        SdfVariabilityVarying,
238                        defaultValue,
239                        writeSparsely);
240 }
241 
242 UsdAttribute
GetInvisibleIdsAttr() const243 UsdGeomPointInstancer::GetInvisibleIdsAttr() const
244 {
245     return GetPrim().GetAttribute(UsdGeomTokens->invisibleIds);
246 }
247 
248 UsdAttribute
CreateInvisibleIdsAttr(VtValue const & defaultValue,bool writeSparsely) const249 UsdGeomPointInstancer::CreateInvisibleIdsAttr(VtValue const &defaultValue, bool writeSparsely) const
250 {
251     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->invisibleIds,
252                        SdfValueTypeNames->Int64Array,
253                        /* custom = */ false,
254                        SdfVariabilityVarying,
255                        defaultValue,
256                        writeSparsely);
257 }
258 
259 UsdRelationship
GetPrototypesRel() const260 UsdGeomPointInstancer::GetPrototypesRel() const
261 {
262     return GetPrim().GetRelationship(UsdGeomTokens->prototypes);
263 }
264 
265 UsdRelationship
CreatePrototypesRel() const266 UsdGeomPointInstancer::CreatePrototypesRel() const
267 {
268     return GetPrim().CreateRelationship(UsdGeomTokens->prototypes,
269                        /* custom = */ false);
270 }
271 
272 namespace {
273 static inline TfTokenVector
_ConcatenateAttributeNames(const TfTokenVector & left,const TfTokenVector & right)274 _ConcatenateAttributeNames(const TfTokenVector& left,const TfTokenVector& right)
275 {
276     TfTokenVector result;
277     result.reserve(left.size() + right.size());
278     result.insert(result.end(), left.begin(), left.end());
279     result.insert(result.end(), right.begin(), right.end());
280     return result;
281 }
282 }
283 
284 /*static*/
285 const TfTokenVector&
GetSchemaAttributeNames(bool includeInherited)286 UsdGeomPointInstancer::GetSchemaAttributeNames(bool includeInherited)
287 {
288     static TfTokenVector localNames = {
289         UsdGeomTokens->protoIndices,
290         UsdGeomTokens->ids,
291         UsdGeomTokens->positions,
292         UsdGeomTokens->orientations,
293         UsdGeomTokens->scales,
294         UsdGeomTokens->velocities,
295         UsdGeomTokens->accelerations,
296         UsdGeomTokens->angularVelocities,
297         UsdGeomTokens->invisibleIds,
298     };
299     static TfTokenVector allNames =
300         _ConcatenateAttributeNames(
301             UsdGeomBoundable::GetSchemaAttributeNames(true),
302             localNames);
303 
304     if (includeInherited)
305         return allNames;
306     else
307         return localNames;
308 }
309 
310 PXR_NAMESPACE_CLOSE_SCOPE
311 
312 // ===================================================================== //
313 // Feel free to add custom code below this line. It will be preserved by
314 // the code generator.
315 //
316 // Just remember to wrap code in the appropriate delimiters:
317 // 'PXR_NAMESPACE_OPEN_SCOPE', 'PXR_NAMESPACE_CLOSE_SCOPE'.
318 // ===================================================================== //
319 // --(BEGIN CUSTOM CODE)--
320 
321 #include "pxr/base/tf/enum.h"
322 #include "pxr/base/tf/envSetting.h"
323 #include "pxr/base/gf/transform.h"
324 #include "pxr/usd/usdGeom/samplingUtils.h"
325 #include "pxr/usd/usdGeom/bboxCache.h"
326 #include "pxr/usd/usdGeom/debugCodes.h"
327 #include "pxr/usd/usdGeom/xformCache.h"
328 #include "pxr/usd/usdGeom/motionAPI.h"
329 
330 #include "pxr/usd/usdGeom/boundableComputeExtent.h"
331 #include "pxr/base/work/loops.h"
332 #include "pxr/base/work/reduce.h"
333 #include "pxr/base/tf/registryManager.h"
334 
335 PXR_NAMESPACE_OPEN_SCOPE
336 
337 // XXX Bug 139215: When we enable this, we can remove
338 // SdfListOp::ComposeOperations().
339 TF_DEFINE_ENV_SETTING(
340     USDGEOM_POINTINSTANCER_NEW_APPLYOPS, true,
341     "Set to true to use SdfListOp::ApplyOperations() instead of "
342     "ComposeOperations().");
343 
TF_REGISTRY_FUNCTION(TfEnum)344 TF_REGISTRY_FUNCTION(TfEnum)
345 {
346     TF_ADD_ENUM_NAME(UsdGeomPointInstancer::IncludeProtoXform);
347     TF_ADD_ENUM_NAME(UsdGeomPointInstancer::ExcludeProtoXform);
348     TF_ADD_ENUM_NAME(UsdGeomPointInstancer::ApplyMask);
349     TF_ADD_ENUM_NAME(UsdGeomPointInstancer::IgnoreMask);
350 }
351 
352 // Convert a list-op to a canonical order, treating it as an
353 // operation on a set rather than a list.  A side effect is
354 // ensuring that it does not use added or ordered items,
355 // and can therefore be used with ApplyOperations().
356 template <typename T>
357 static SdfListOp<T>
_CanonicalizeListOp(const SdfListOp<T> & op)358 _CanonicalizeListOp(const SdfListOp<T> &op) {
359     if (op.IsExplicit()) {
360         return op;
361     } else {
362         std::vector<T> items;
363         op.ApplyOperations(&items);
364         std::sort(items.begin(), items.end());
365         SdfListOp<T> r;
366         r.SetPrependedItems(std::vector<T>(items.begin(), items.end()));
367         r.SetDeletedItems(op.GetDeletedItems());
368         return r;
369     }
370 }
371 
372 bool
UsdGeomPointInstancerApplyNewStyleListOps()373 UsdGeomPointInstancerApplyNewStyleListOps()
374 {
375     return TfGetEnvSetting(USDGEOM_POINTINSTANCER_NEW_APPLYOPS);
376 }
377 
378 bool
UsdGeomPointInstancerSetOrMergeOverOp(std::vector<int64_t> const & items,SdfListOpType op,UsdPrim const & prim,TfToken const & metadataName)379 UsdGeomPointInstancerSetOrMergeOverOp(std::vector<int64_t> const &items,
380                                       SdfListOpType op,
381                                       UsdPrim const &prim,
382                                       TfToken const &metadataName)
383 {
384     SdfInt64ListOp  proposed, current;
385     UsdStagePtr stage = prim.GetStage();
386     UsdEditTarget editTarget = stage->GetEditTarget();
387     SdfPrimSpecHandle  primSpec =
388         editTarget.GetPrimSpecForScenePath(prim.GetPath());
389 
390     if (primSpec){
391         VtValue  existingOp = primSpec->GetInfo(metadataName);
392         if (existingOp.IsHolding<SdfInt64ListOp>()){
393             current = existingOp.UncheckedGet<SdfInt64ListOp>();
394         }
395     }
396 
397     proposed.SetItems(items, op);
398 
399     if (TfGetEnvSetting(USDGEOM_POINTINSTANCER_NEW_APPLYOPS)) {
400         current = _CanonicalizeListOp(current);
401         return prim.SetMetadata(UsdGeomTokens->inactiveIds,
402                                 *proposed.ApplyOperations(current));
403     }
404 
405     if (current.IsExplicit()){
406         std::vector<int64_t> explicitItems = current.GetExplicitItems();
407         proposed.ApplyOperations(&explicitItems);
408         current.SetExplicitItems(explicitItems);
409     }
410     else {
411         // We can't use ApplyOperations on an extant, non-explicit listOp
412         // because the result is always flat and explicit.
413         current.ComposeOperations(proposed, op);
414         // ComposeOperations() is too narrow in functionality - it does not
415         // consider that if we "remove over" an existing set of added items,
416         // we need to additionally ensure the removed items get removed
417         // from the added in current, since when applying ops, we first
418         // remove, then add.  Bug #139215 filed to track; when it gets fixed
419         // we can remove this code!
420         if (op == SdfListOpTypeDeleted){
421             std::vector<int64_t> addedItems = current.GetAddedItems();
422             if (!addedItems.empty()){
423                 std::set<int64_t> toRemove(items.begin(), items.end());
424                 std::vector<int64_t> newAdded;
425                 newAdded.reserve(addedItems.size());
426                 for (auto elt : addedItems){
427                     if (!toRemove.count(elt))
428                         newAdded.push_back(elt);
429                 }
430                 if (newAdded.size() != addedItems.size())
431                     current.SetAddedItems(newAdded);
432             }
433         }
434         else if (op == SdfListOpTypeAdded){
435             std::vector<int64_t> deletedItems = current.GetDeletedItems();
436             if (!deletedItems.empty()){
437                 std::set<int64_t> toAdd(items.begin(), items.end());
438                 std::vector<int64_t> newDeleted;
439                 newDeleted.reserve(deletedItems.size());
440                 for (auto elt : deletedItems){
441                     if (!toAdd.count(elt))
442                         newDeleted.push_back(elt);
443                 }
444                 if (newDeleted.size() != deletedItems.size())
445                     current.SetDeletedItems(newDeleted);
446             }
447         }
448     }
449     return prim.SetMetadata(metadataName, current);
450 }
451 
452 bool
ActivateId(int64_t id) const453 UsdGeomPointInstancer::ActivateId(int64_t id) const
454 {
455     std::vector<int64_t> toRemove(1, id);
456     return UsdGeomPointInstancerSetOrMergeOverOp(
457         toRemove, SdfListOpTypeDeleted, GetPrim(), UsdGeomTokens->inactiveIds);
458 }
459 
460 bool
ActivateIds(VtInt64Array const & ids) const461 UsdGeomPointInstancer::ActivateIds(VtInt64Array const &ids) const
462 {
463     std::vector<int64_t> toRemove(ids.begin(), ids.end());
464     return UsdGeomPointInstancerSetOrMergeOverOp(
465         toRemove, SdfListOpTypeDeleted, GetPrim(), UsdGeomTokens->inactiveIds);
466 }
467 
468 bool
ActivateAllIds() const469 UsdGeomPointInstancer::ActivateAllIds() const
470 {
471     SdfInt64ListOp  op;
472     op.SetExplicitItems(std::vector<int64_t>());
473 
474     return GetPrim().SetMetadata(UsdGeomTokens->inactiveIds, op);
475 }
476 
477 bool
DeactivateId(int64_t id) const478 UsdGeomPointInstancer::DeactivateId(int64_t id) const
479 {
480     std::vector<int64_t> toAdd(1, id);
481     return UsdGeomPointInstancerSetOrMergeOverOp(toAdd,
482         TfGetEnvSetting(USDGEOM_POINTINSTANCER_NEW_APPLYOPS) ?
483         SdfListOpTypeAppended : SdfListOpTypeAdded, GetPrim(),
484         UsdGeomTokens->inactiveIds);
485 }
486 
487 bool
DeactivateIds(VtInt64Array const & ids) const488 UsdGeomPointInstancer::DeactivateIds(VtInt64Array const &ids) const
489 {
490     std::vector<int64_t> toAdd(ids.begin(), ids.end());
491     return UsdGeomPointInstancerSetOrMergeOverOp(toAdd,
492         TfGetEnvSetting(USDGEOM_POINTINSTANCER_NEW_APPLYOPS) ?
493         SdfListOpTypeAppended : SdfListOpTypeAdded, GetPrim(),
494         UsdGeomTokens->inactiveIds);
495 }
496 
497 bool
VisId(int64_t id,UsdTimeCode const & time) const498 UsdGeomPointInstancer::VisId(int64_t id, UsdTimeCode const &time) const
499 {
500     return VisIds({id}, time);
501 }
502 
503 bool
VisIds(VtInt64Array const & ids,UsdTimeCode const & time) const504 UsdGeomPointInstancer::VisIds(VtInt64Array const &ids, UsdTimeCode const &time) const
505 {
506     VtInt64Array invised;
507 
508     if (!GetInvisibleIdsAttr().Get(&invised, time))
509         return true;
510 
511     std::set<int64_t>  invisSet(invised.begin(), invised.end());
512     size_t numRemoved = 0;
513 
514     for (int64_t id : ids){
515         numRemoved += invisSet.erase(id);
516     }
517 
518     if (numRemoved){
519         invised.clear();
520         invised.reserve(invisSet.size());
521         for ( int64_t id : invisSet ) {
522             invised.push_back(id);
523         }
524     }
525 
526     return CreateInvisibleIdsAttr().Set(invised, time);
527 }
528 
529 bool
VisAllIds(UsdTimeCode const & time) const530 UsdGeomPointInstancer::VisAllIds(UsdTimeCode const &time) const
531 {
532     VtInt64Array invised(0);
533 
534     if (GetInvisibleIdsAttr().HasAuthoredValue())
535         // We _could_ just block the attr instead. Better?
536         return CreateInvisibleIdsAttr().Set(invised, time);
537 
538     return true;
539 }
540 
541 bool
InvisId(int64_t id,UsdTimeCode const & time) const542 UsdGeomPointInstancer::InvisId(int64_t id, UsdTimeCode const &time) const
543 {
544     return InvisIds({id}, time);
545 }
546 
547 bool
InvisIds(VtInt64Array const & ids,UsdTimeCode const & time) const548 UsdGeomPointInstancer::InvisIds(VtInt64Array const &ids, UsdTimeCode const &time) const
549 {
550     VtInt64Array invised;
551 
552     if (!GetInvisibleIdsAttr().Get(&invised, time))
553         return true;
554 
555     std::set<int64_t>  invisSet(invised.begin(), invised.end());
556 
557     for (int64_t id : ids){
558         if (invisSet.find(id) == invisSet.end())
559             invised.push_back(id);
560     }
561 
562     return CreateInvisibleIdsAttr().Set(invised, time);
563 }
564 
565 std::vector<bool>
ComputeMaskAtTime(UsdTimeCode time,VtInt64Array const * ids) const566 UsdGeomPointInstancer::ComputeMaskAtTime(UsdTimeCode time,
567                                          VtInt64Array const *ids) const
568 {
569     VtInt64Array       idVals, invisedIds;
570     std::vector<bool>  mask;
571     SdfInt64ListOp     inactiveIdsListOp;
572 
573     // XXX Note we could be doing all three fetches in parallel
574     GetPrim().GetMetadata(UsdGeomTokens->inactiveIds, &inactiveIdsListOp);
575     std::vector<int64_t> inactiveIds = inactiveIdsListOp.GetExplicitItems();
576     GetInvisibleIdsAttr().Get(&invisedIds, time);
577     if (inactiveIds.size() > 0 || invisedIds.size() > 0){
578         bool anyPruned = false;
579         std::set<int64_t>  maskedIds(inactiveIds.begin(), inactiveIds.end());
580         maskedIds.insert(invisedIds.begin(), invisedIds.end());
581         if (!ids){
582             if (GetIdsAttr().Get(&idVals, time)){
583                 ids = &idVals;
584             }
585             if (!ids){
586                 VtIntArray  protoIndices;
587                 if (!GetProtoIndicesAttr().Get(&protoIndices, time)){
588                     // not a functional PointInstancer... just return
589                     // trivial pass
590                     return mask;
591                 }
592                 size_t numInstances = protoIndices.size();
593                 idVals.reserve(numInstances);
594                 for (size_t i = 0; i < numInstances; ++i) {
595                     idVals.push_back(i);
596                 }
597                 ids = &idVals;
598             }
599         }
600 
601         mask.reserve(ids->size());
602         for (int64_t id : *ids){
603             bool pruned = (maskedIds.find(id) != maskedIds.end());
604             anyPruned = anyPruned || pruned;
605             mask.push_back(!pruned);
606         }
607 
608         if (!anyPruned){
609             mask.resize(0);
610         }
611     }
612 
613     return mask;
614 }
615 
616 bool
_GetProtoIndicesForInstanceTransforms(UsdTimeCode baseTime,VtIntArray * protoIndices) const617 UsdGeomPointInstancer::_GetProtoIndicesForInstanceTransforms(
618     UsdTimeCode baseTime,
619     VtIntArray* protoIndices) const
620 {
621     if (baseTime.IsNumeric()) {
622         double sampleTimeValue = 0.0;
623         double upperTimeValue = 0.0;
624         bool hasSamples;
625         if (!GetProtoIndicesAttr().GetBracketingTimeSamples(
626                 baseTime.GetValue(),
627                 &sampleTimeValue,
628                 &upperTimeValue,
629                 &hasSamples)) {
630             return false;
631         }
632 
633         UsdTimeCode sampleTime = UsdTimeCode::Default();
634         if (hasSamples) {
635             sampleTime = UsdTimeCode(sampleTimeValue);
636         }
637 
638         if (!GetProtoIndicesAttr().Get(protoIndices, sampleTime)) {
639             return false;
640         }
641 
642     } else {
643         // baseTime is UsdTimeCode.Default()
644         if (!GetProtoIndicesAttr().Get(protoIndices, baseTime)) {
645             return false;
646         }
647     }
648     return true;
649 }
650 
651 bool
_GetPrototypePathsForInstanceTransforms(const VtIntArray & protoIndices,SdfPathVector * protoPaths) const652 UsdGeomPointInstancer::_GetPrototypePathsForInstanceTransforms(
653     const VtIntArray& protoIndices,
654     SdfPathVector* protoPaths) const
655 {
656     SdfPathVector protoPathData;
657     if (!GetPrototypesRel().GetTargets(&protoPathData) || protoPathData.empty()) {
658         TF_WARN("%s -- no prototypes",
659                 GetPrim().GetPath().GetText());
660         return false;
661     }
662 
663     for (const auto& protoIndex : protoIndices) {
664         if (protoIndex < 0
665                 || static_cast<size_t>(protoIndex) >= protoPathData.size()) {
666             TF_WARN("%s -- invalid prototype index: %d. Should be in [0, %zu)",
667                     GetPrim().GetPath().GetText(),
668                     protoIndex,
669                     protoPathData.size());
670             return false;
671         }
672     }
673 
674     *protoPaths = protoPathData;
675     return true;
676 }
677 
678 bool
_ComputePointInstancerAttributesPreamble(const UsdTimeCode baseTime,const ProtoXformInclusion doProtoXforms,const MaskApplication applyMask,VtIntArray * protoIndices,SdfPathVector * protoPaths,std::vector<bool> * mask) const679 UsdGeomPointInstancer::_ComputePointInstancerAttributesPreamble(
680     const UsdTimeCode baseTime,
681     const ProtoXformInclusion doProtoXforms,
682     const MaskApplication applyMask,
683     VtIntArray* protoIndices,
684     SdfPathVector* protoPaths,
685     std::vector<bool>* mask) const
686 {
687     TRACE_FUNCTION();
688 
689     if (!_GetProtoIndicesForInstanceTransforms(
690             baseTime,
691             protoIndices)) {
692         return false;
693     }
694 
695     size_t numInstances = protoIndices->size();
696 
697     if (doProtoXforms == IncludeProtoXform) {
698         if (!_GetPrototypePathsForInstanceTransforms(
699                 *protoIndices,
700                 protoPaths)) {
701             return false;
702         }
703     }
704 
705     if (applyMask == ApplyMask) {
706         *mask = ComputeMaskAtTime(baseTime);
707         if (!(mask->empty() || mask->size() == numInstances)) {
708             TF_WARN(
709                 "%s -- found mask of size [%zu], but expected size [%zu]",
710                 GetPrim().GetPath().GetText(),
711                 mask->size(),
712                 numInstances);
713             return false;
714         }
715     }
716 
717     return true;
718 }
719 
720 bool
ComputeInstanceTransformsAtTime(VtArray<GfMatrix4d> * xforms,const UsdTimeCode time,const UsdTimeCode baseTime,const ProtoXformInclusion doProtoXforms,const MaskApplication applyMask) const721 UsdGeomPointInstancer::ComputeInstanceTransformsAtTime(
722     VtArray<GfMatrix4d>* xforms,
723     const UsdTimeCode time,
724     const UsdTimeCode baseTime,
725     const ProtoXformInclusion doProtoXforms,
726     const MaskApplication applyMask) const
727 {
728     TRACE_FUNCTION();
729 
730     std::vector<VtArray<GfMatrix4d>> xformsArray;
731     std::vector<UsdTimeCode> times({time});
732     if (!ComputeInstanceTransformsAtTimes(&xformsArray,
733                             times,
734                             baseTime,
735                             doProtoXforms,
736                             applyMask)) {
737         return false;
738     }
739     *xforms = xformsArray.at(0);
740 
741     return true;
742 }
743 
744 bool
ComputeInstanceTransformsAtTimes(std::vector<VtArray<GfMatrix4d>> * xformsArray,const std::vector<UsdTimeCode> & times,const UsdTimeCode baseTime,const ProtoXformInclusion doProtoXforms,const MaskApplication applyMask) const745 UsdGeomPointInstancer::ComputeInstanceTransformsAtTimes(
746     std::vector<VtArray<GfMatrix4d>>* xformsArray,
747     const std::vector<UsdTimeCode>& times,
748     const UsdTimeCode baseTime,
749     const ProtoXformInclusion doProtoXforms,
750     const MaskApplication applyMask) const
751 {
752     size_t numSamples = times.size();
753     for (auto time : times) {
754         if (time.IsNumeric() != baseTime.IsNumeric()) {
755             TF_CODING_ERROR(
756                 "%s -- all sample times in times and baseTime must either all "
757                 "be numeric or all be default",
758                 GetPrim().GetPath().GetText());
759         }
760     }
761 
762     VtIntArray protoIndices;
763     VtVec3fArray positions;
764     VtVec3fArray velocities;
765     UsdTimeCode velocitiesSampleTime;
766     VtVec3fArray accelerations;
767     VtVec3fArray scales;
768     VtQuathArray orientations;
769     VtVec3fArray angularVelocities;
770     UsdTimeCode angularVelocitiesSampleTime;
771     SdfPathVector protoPaths;
772     std::vector<bool> mask;
773     float velocityScale;
774 
775     if (!_ComputePointInstancerAttributesPreamble(
776             baseTime,
777             doProtoXforms,
778             applyMask,
779             &protoIndices,
780             &protoPaths,
781             &mask)) {
782         return false;
783     }
784 
785     size_t numInstances = protoIndices.size();
786 
787     if (!UsdGeom_GetPositionsVelocitiesAndAccelerations(
788             GetPositionsAttr(),
789             GetVelocitiesAttr(),
790             GetAccelerationsAttr(),
791             baseTime,
792             numInstances,
793             &positions,
794             &velocities,
795             &velocitiesSampleTime,
796             &accelerations,
797             &velocityScale,
798             GetPrim())) {
799         return false;
800     }
801 
802     UsdGeom_GetScales(
803             GetScalesAttr(),
804             baseTime,
805             numInstances,
806             &scales,
807             GetPrim());
808 
809     UsdGeom_GetOrientationsAndAngularVelocities(
810             GetOrientationsAttr(),
811             GetAngularVelocitiesAttr(),
812             baseTime,
813             numInstances,
814             &orientations,
815             &angularVelocities,
816             &angularVelocitiesSampleTime,
817             GetPrim());
818 
819     if (numInstances == 0) {
820         xformsArray->clear();
821         xformsArray->resize(numSamples);
822         return true;
823     }
824 
825     UsdStageWeakPtr stage = GetPrim().GetStage();
826 
827     std::vector<VtArray<GfMatrix4d>> xformsArrayData;
828     xformsArrayData.resize(numSamples);
829     bool useInterpolated = (velocities.empty() && angularVelocities.empty());
830     for (size_t i = 0; i < numSamples; i++) {
831 
832         UsdTimeCode time = times[i];
833         VtArray<GfMatrix4d>* xforms = &(xformsArrayData[i]);
834 
835         // If there are no valid velocities or angular velocities, we fallback to
836         // "standard" computation logic (linear interpolation between samples).
837         if (useInterpolated) {
838 
839             // Try to fetch the positions, scales, and orientations at the sample
840             // time. If this fails or the fetched data don't have the correct
841             // topology, we fallback to the data from the base time.
842 
843             VtVec3fArray interpolatedPositions;
844             if (GetPositionsAttr().Get(&interpolatedPositions, time)
845                     && interpolatedPositions.size() == numInstances) {
846                 positions = interpolatedPositions;
847             }
848 
849             VtVec3fArray interpolatedScales;
850             if (GetScalesAttr().Get(&interpolatedScales, time)
851                     && interpolatedScales.size() == numInstances) {
852                 scales = interpolatedScales;
853             }
854 
855             VtQuathArray interpolatedOrientations;
856             if (GetOrientationsAttr().Get(&interpolatedOrientations, time)
857                     && interpolatedOrientations.size() == numInstances) {
858                 orientations = interpolatedOrientations;
859             }
860 
861         }
862 
863         if (!UsdGeomPointInstancer::ComputeInstanceTransformsAtTime(
864                 xforms,
865                 stage,
866                 time,
867                 protoIndices,
868                 positions,
869                 velocities,
870                 velocitiesSampleTime,
871                 accelerations,
872                 scales,
873                 orientations,
874                 angularVelocities,
875                 angularVelocitiesSampleTime,
876                 protoPaths,
877                 mask,
878                 velocityScale)) {
879             return false;
880         }
881     }
882 
883     *xformsArray = xformsArrayData;
884     return true;
885 }
886 
887 bool
ComputeInstanceTransformsAtTime(VtArray<GfMatrix4d> * xforms,UsdStageWeakPtr & stage,UsdTimeCode time,const VtIntArray & protoIndices,const VtVec3fArray & positions,const VtVec3fArray & velocities,UsdTimeCode velocitiesSampleTime,const VtVec3fArray & accelerations,const VtVec3fArray & scales,const VtQuathArray & orientations,const VtVec3fArray & angularVelocities,UsdTimeCode angularVelocitiesSampleTime,const SdfPathVector & protoPaths,const std::vector<bool> & mask,float velocityScale)888 UsdGeomPointInstancer::ComputeInstanceTransformsAtTime(
889     VtArray<GfMatrix4d>* xforms,
890     UsdStageWeakPtr& stage,
891     UsdTimeCode time,
892     const VtIntArray& protoIndices,
893     const VtVec3fArray& positions,
894     const VtVec3fArray& velocities,
895     UsdTimeCode velocitiesSampleTime,
896     const VtVec3fArray& accelerations,
897     const VtVec3fArray& scales,
898     const VtQuathArray& orientations,
899     const VtVec3fArray& angularVelocities,
900     UsdTimeCode angularVelocitiesSampleTime,
901     const SdfPathVector& protoPaths,
902     const std::vector<bool>& mask,
903     float velocityScale)
904 {
905     TRACE_FUNCTION();
906 
907     size_t numInstances = protoIndices.size();
908 
909     const double timeCodesPerSecond = stage->GetTimeCodesPerSecond();
910     const float velocityTimeDelta = UsdGeom_CalculateTimeDelta(
911                                       velocityScale,
912                                       time,
913                                       velocitiesSampleTime,
914                                       timeCodesPerSecond);
915     const float angularVelocityTimeDelta = UsdGeom_CalculateTimeDelta(
916                                       velocityScale,
917                                       time,
918                                       angularVelocitiesSampleTime,
919                                       timeCodesPerSecond);\
920 
921     xforms->resize(numInstances);
922 
923     const GfMatrix4d identity(1.0);
924     std::vector<GfMatrix4d> protoXforms(protoPaths.size(), identity);
925     UsdGeomXformCache xformCache(time);
926     if (protoPaths.size() != 0) {
927         for (size_t protoIndex = 0 ; protoIndex < protoPaths.size() ;
928                 ++protoIndex) {
929             const SdfPath& protoPath = protoPaths[protoIndex];
930             if (const UsdPrim& protoPrim = stage->GetPrimAtPath(protoPath)) {
931                 // Get the prototype's local transformation.
932                 bool resetsXformStack;
933                 protoXforms[protoIndex] = xformCache.GetLocalTransformation(
934                     protoPrim, &resetsXformStack);
935             }
936         }
937     }
938 
939     const auto computeInstanceXforms = [&mask, &velocityTimeDelta,
940         &angularVelocityTimeDelta, &scales,
941         &orientations, &positions, &velocities, &accelerations,
942         &angularVelocities, &protoXforms, &protoIndices,
943         &protoPaths, &xforms] (size_t start, size_t end) {
944         for (size_t instanceId = start ; instanceId < end ; ++instanceId) {
945             if (!mask.empty() && !mask[instanceId]) {
946                 continue;
947             }
948 
949             bool identity = true;
950             GfMatrix4d instanceTransform(1.0);
951 
952             if (!scales.empty()) {
953                 instanceTransform.SetScale(scales[instanceId]);
954                 identity = false;
955             }
956 
957             if (!orientations.empty()) {
958                 if (identity) {
959                     instanceTransform.SetRotate(orientations[instanceId]);
960                     identity = false;
961                 } else {
962                     GfMatrix4d rotation;
963                     rotation.SetRotate(orientations[instanceId]);
964                     instanceTransform *= rotation;
965                 }
966 
967                 if (angularVelocities.size() != 0) {
968                     GfVec3f angularVelocity = angularVelocities[instanceId];
969                     GfMatrix4d rotation;
970                     rotation.SetRotate(GfRotation(angularVelocity,
971                         angularVelocityTimeDelta * angularVelocity.GetLength()));
972                     instanceTransform *= rotation;
973                 }
974             }
975 
976             GfVec3f translation = positions[instanceId];
977             if (velocities.size() != 0) {
978                 GfVec3f velocity = velocities[instanceId];
979                 if (accelerations.size() != 0) {
980                     velocity += velocityTimeDelta * accelerations[instanceId] * 0.5;
981                 }
982                 translation += velocityTimeDelta * velocity;
983             }
984             instanceTransform.SetTranslateOnly(translation);
985 
986             const int protoIndex = protoIndices[instanceId];
987 
988             if (protoPaths.size() != 0) {
989                 (*xforms)[instanceId] =
990                     protoXforms[protoIndex] * instanceTransform;
991             } else {
992                 (*xforms)[instanceId] = instanceTransform;
993             }
994         }
995     };
996 
997     {
998         TRACE_SCOPE("UsdGeomPointInstancer::ComputeInstanceTransformsAtTime (Parallel)");
999         WorkParallelForN(numInstances, computeInstanceXforms);
1000     }
1001 
1002     return ApplyMaskToArray(mask, xforms);
1003 }
1004 
1005 bool
_ComputeExtentAtTimePreamble(UsdTimeCode baseTime,VtIntArray * protoIndices,std::vector<bool> * mask,UsdRelationship * prototypes,SdfPathVector * protoPaths) const1006 UsdGeomPointInstancer::_ComputeExtentAtTimePreamble(
1007     UsdTimeCode baseTime,
1008     VtIntArray* protoIndices,
1009     std::vector<bool>* mask,
1010     UsdRelationship* prototypes,
1011     SdfPathVector* protoPaths) const
1012 {
1013     if (!GetProtoIndicesAttr().Get(protoIndices, baseTime)) {
1014         TF_WARN("%s -- no prototype indices",
1015                 GetPrim().GetPath().GetText());
1016         return false;
1017     }
1018 
1019     *mask = ComputeMaskAtTime(baseTime);
1020     if (!mask->empty() && mask->size() != protoIndices->size()) {
1021         TF_WARN("%s -- mask.size() [%zu] != protoIndices.size() [%zu]",
1022                 GetPrim().GetPath().GetText(),
1023                 mask->size(),
1024                 protoIndices->size());
1025         return false;
1026     }
1027 
1028     *prototypes = GetPrototypesRel();
1029     if (!prototypes->GetTargets(protoPaths) || protoPaths->empty()) {
1030         TF_WARN("%s -- no prototypes",
1031                 GetPrim().GetPath().GetText());
1032         return false;
1033     }
1034 
1035     // verify that all the protoIndices are in bounds.
1036     TF_FOR_ALL(iter, *protoIndices) {
1037         const int protoIndex = *iter;
1038         if (protoIndex < 0 ||
1039             static_cast<size_t>(protoIndex) >= protoPaths->size()) {
1040             TF_WARN("%s -- invalid prototype index: %d. Should be in [0, %zu)",
1041                     GetPrim().GetPath().GetText(),
1042                     protoIndex,
1043                     protoPaths->size());
1044             return false;
1045         }
1046     }
1047 
1048     return true;
1049 }
1050 
1051 bool
_ComputeExtentFromTransforms(VtVec3fArray * extent,const VtIntArray & protoIndices,const std::vector<bool> & mask,const UsdRelationship & prototypes,const SdfPathVector & protoPaths,const VtMatrix4dArray & instanceTransforms,UsdTimeCode time,const GfMatrix4d * transform) const1052 UsdGeomPointInstancer::_ComputeExtentFromTransforms(
1053     VtVec3fArray* extent,
1054     const VtIntArray& protoIndices,
1055     const std::vector<bool>& mask,
1056     const UsdRelationship& prototypes,
1057     const SdfPathVector& protoPaths,
1058     const VtMatrix4dArray& instanceTransforms,
1059     UsdTimeCode time,
1060     const GfMatrix4d* transform) const
1061 {
1062     TRACE_FUNCTION();
1063 
1064     UsdStageWeakPtr stage = GetPrim().GetStage();
1065 
1066     if (protoIndices.size() <= protoPaths.size()) {
1067         TF_DEBUG(USDGEOM_BBOX).Msg("Number of prototypes (%zu) is >= number"
1068             "of instances (%zu). May be inefficient.", protoPaths.size(),
1069             protoIndices.size());
1070     }
1071 
1072     // We might want to precompute prototype bounds only when the number of
1073     // instances is greater than the number of prototypes.
1074     std::vector<GfBBox3d> protoUntransformedBounds;
1075     protoUntransformedBounds.reserve(protoPaths.size());
1076 
1077     UsdGeomBBoxCache bboxCache(time,
1078         /*purposes*/ {UsdGeomTokens->default_,
1079                       UsdGeomTokens->proxy,
1080                       UsdGeomTokens->render });
1081     for (size_t protoId = 0 ; protoId < protoPaths.size() ; ++protoId) {
1082         const SdfPath& protoPath = protoPaths[protoId];
1083         const UsdPrim& protoPrim = stage->GetPrimAtPath(protoPath);
1084         const GfBBox3d protoBounds =
1085                 bboxCache.ComputeUntransformedBound(protoPrim);
1086         protoUntransformedBounds.push_back(protoBounds);
1087     }
1088 
1089     // Compute all the instance aligned ranges.
1090     std::vector<GfRange3d> instanceAlignedRanges(protoIndices.size());
1091     const auto computeInstanceAlignedRange =
1092         [&mask, &protoIndices, &transform, &protoUntransformedBounds,
1093          &instanceTransforms, &instanceAlignedRanges]
1094         (size_t start, size_t end) {
1095             for (size_t instanceId = start ; instanceId < end ; ++instanceId) {
1096                 if (!mask.empty() && !mask[instanceId]) {
1097                     continue;
1098                 }
1099 
1100                 // Get the prototype bounding box.
1101                 const int protoIndex = protoIndices[instanceId];
1102                 GfBBox3d thisBounds = protoUntransformedBounds[protoIndex];
1103 
1104                 // Apply the instance transform.
1105                 thisBounds.Transform(instanceTransforms[instanceId]);
1106 
1107                 // Apply the optional transform.
1108                 if (transform) {
1109                     thisBounds.Transform(*transform);
1110                 }
1111                 instanceAlignedRanges[instanceId] =
1112                         thisBounds.ComputeAlignedRange();
1113             }
1114         };
1115 
1116     WorkParallelForN(protoIndices.size(), computeInstanceAlignedRange);
1117 
1118     GfRange3d extentRange = WorkParallelReduceN(
1119             GfRange3d(),
1120             instanceAlignedRanges.size(),
1121             [&instanceAlignedRanges](size_t b, size_t e, GfRange3d init){
1122                 for (auto i = b; i < e ; ++i) {
1123                     init.UnionWith(instanceAlignedRanges[i]);
1124                 }
1125                 return init;
1126             },
1127             [](GfRange3d lhs, GfRange3d rhs) {
1128                 return GfRange3d::GetUnion(lhs, rhs);
1129             },
1130             /* grainSize */ 500);
1131 
1132     const GfVec3d &extentMin = extentRange.GetMin();
1133     const GfVec3d &extentMax = extentRange.GetMax();
1134 
1135     *extent = VtVec3fArray(2);
1136     (*extent)[0] = GfVec3f(extentMin[0], extentMin[1], extentMin[2]);
1137     (*extent)[1] = GfVec3f(extentMax[0], extentMax[1], extentMax[2]);
1138 
1139     return true;
1140 }
1141 
1142 bool
_ComputeExtentAtTime(VtVec3fArray * extent,const UsdTimeCode time,const UsdTimeCode baseTime,const GfMatrix4d * transform) const1143 UsdGeomPointInstancer::_ComputeExtentAtTime(
1144     VtVec3fArray* extent,
1145     const UsdTimeCode time,
1146     const UsdTimeCode baseTime,
1147     const GfMatrix4d* transform) const
1148 {
1149     if (!extent) {
1150         TF_CODING_ERROR("%s -- null container passed to ComputeExtentAtTime()",
1151                 GetPrim().GetPath().GetText());
1152         return false;
1153     }
1154 
1155     VtIntArray protoIndices;
1156     std::vector<bool> mask;
1157     UsdRelationship prototypes;
1158     SdfPathVector protoPaths;
1159     if (!_ComputeExtentAtTimePreamble(
1160             baseTime,
1161             &protoIndices,
1162             &mask,
1163             &prototypes,
1164             &protoPaths)) {
1165         return false;
1166     }
1167 
1168     // Note that we do NOT apply any masking when computing the instance
1169     // transforms. This is so that for a particular instance we can determine
1170     // both its transform and its prototype. Otherwise, the instanceTransforms
1171     // array would have masked instances culled out and we would lose the
1172     // mapping to the prototypes.
1173     // Masked instances will be culled before being applied to the extent below.
1174     VtMatrix4dArray instanceTransforms;
1175     if (!ComputeInstanceTransformsAtTime(&instanceTransforms,
1176                                          time,
1177                                          baseTime,
1178                                          IncludeProtoXform,
1179                                          IgnoreMask)) {
1180         TF_WARN("%s -- could not compute instance transforms",
1181                 GetPrim().GetPath().GetText());
1182         return false;
1183     }
1184 
1185     return _ComputeExtentFromTransforms(
1186         extent,
1187         protoIndices,
1188         mask,
1189         prototypes,
1190         protoPaths,
1191         instanceTransforms,
1192         time,
1193         transform);
1194 }
1195 
1196 bool
_ComputeExtentAtTimes(std::vector<VtVec3fArray> * extents,const std::vector<UsdTimeCode> & times,const UsdTimeCode baseTime,const GfMatrix4d * transform) const1197 UsdGeomPointInstancer::_ComputeExtentAtTimes(
1198     std::vector<VtVec3fArray>* extents,
1199     const std::vector<UsdTimeCode>& times,
1200     const UsdTimeCode baseTime,
1201     const GfMatrix4d* transform) const
1202 {
1203     if (!extents) {
1204         TF_CODING_ERROR("%s -- null container passed to ComputeExtentAtTimes()",
1205                 GetPrim().GetPath().GetText());
1206         return false;
1207     }
1208 
1209     VtIntArray protoIndices;
1210     std::vector<bool> mask;
1211     UsdRelationship prototypes;
1212     SdfPathVector protoPaths;
1213     if (!_ComputeExtentAtTimePreamble(
1214             baseTime,
1215             &protoIndices,
1216             &mask,
1217             &prototypes,
1218             &protoPaths)) {
1219         return false;
1220     }
1221 
1222     // Note that we do NOT apply any masking when computing the instance
1223     // transforms. This is so that for a particular instance we can determine
1224     // both its transform and its prototype. Otherwise, the instanceTransforms
1225     // array would have masked instances culled out and we would lose the
1226     // mapping to the prototypes.
1227     // Masked instances will be culled before being applied to the extent below.
1228     std::vector<VtMatrix4dArray> instanceTransformsArray;
1229     if (!ComputeInstanceTransformsAtTimes(
1230             &instanceTransformsArray,
1231             times,
1232             baseTime,
1233             IncludeProtoXform,
1234             IgnoreMask)) {
1235         TF_WARN("%s -- could not compute instance transforms",
1236                 GetPrim().GetPath().GetText());
1237         return false;
1238     }
1239 
1240     std::vector<VtVec3fArray> computedExtents;
1241     computedExtents.resize(times.size());
1242 
1243     for (size_t i = 0; i < times.size(); i++) {
1244 
1245         const UsdTimeCode& time = times[i];
1246         const VtMatrix4dArray& instanceTransforms = instanceTransformsArray[i];
1247 
1248         if (!_ComputeExtentFromTransforms(
1249                 &(computedExtents[i]),
1250                 protoIndices,
1251                 mask,
1252                 prototypes,
1253                 protoPaths,
1254                 instanceTransforms,
1255                 time,
1256                 transform)) {
1257             return false;
1258         }
1259     }
1260 
1261     extents->swap(computedExtents);
1262     return true;
1263 }
1264 
1265 bool
ComputeExtentAtTime(VtVec3fArray * extent,const UsdTimeCode time,const UsdTimeCode baseTime) const1266 UsdGeomPointInstancer::ComputeExtentAtTime(
1267     VtVec3fArray* extent,
1268     const UsdTimeCode time,
1269     const UsdTimeCode baseTime) const
1270 {
1271     return _ComputeExtentAtTime(extent, time, baseTime, nullptr);
1272 }
1273 
1274 bool
ComputeExtentAtTime(VtVec3fArray * extent,const UsdTimeCode time,const UsdTimeCode baseTime,const GfMatrix4d & transform) const1275 UsdGeomPointInstancer::ComputeExtentAtTime(
1276     VtVec3fArray* extent,
1277     const UsdTimeCode time,
1278     const UsdTimeCode baseTime,
1279     const GfMatrix4d& transform) const
1280 {
1281     return _ComputeExtentAtTime(extent, time, baseTime, &transform);
1282 }
1283 
1284 bool
ComputeExtentAtTimes(std::vector<VtVec3fArray> * extents,const std::vector<UsdTimeCode> & times,const UsdTimeCode baseTime) const1285 UsdGeomPointInstancer::ComputeExtentAtTimes(
1286     std::vector<VtVec3fArray>* extents,
1287     const std::vector<UsdTimeCode>& times,
1288     const UsdTimeCode baseTime) const
1289 {
1290     return _ComputeExtentAtTimes(extents, times, baseTime, nullptr);
1291 }
1292 
1293 bool
ComputeExtentAtTimes(std::vector<VtVec3fArray> * extents,const std::vector<UsdTimeCode> & times,const UsdTimeCode baseTime,const GfMatrix4d & transform) const1294 UsdGeomPointInstancer::ComputeExtentAtTimes(
1295     std::vector<VtVec3fArray>* extents,
1296     const std::vector<UsdTimeCode>& times,
1297     const UsdTimeCode baseTime,
1298     const GfMatrix4d& transform) const
1299 {
1300     return _ComputeExtentAtTimes(extents, times, baseTime, &transform);
1301 }
1302 
1303 static bool
_ComputeExtentForPointInstancer(const UsdGeomBoundable & boundable,const UsdTimeCode & time,const GfMatrix4d * transform,VtVec3fArray * extent)1304 _ComputeExtentForPointInstancer(
1305     const UsdGeomBoundable& boundable,
1306     const UsdTimeCode& time,
1307     const GfMatrix4d* transform,
1308     VtVec3fArray* extent)
1309 {
1310     TRACE_FUNCTION();
1311 
1312     const UsdGeomPointInstancer pointInstancerSchema(boundable);
1313     if (!TF_VERIFY(pointInstancerSchema)) {
1314         return false;
1315     }
1316 
1317     // We use the input time as the baseTime because we don't care about
1318     // velocity or angularVelocity.
1319     if (transform) {
1320         return pointInstancerSchema.ComputeExtentAtTime(
1321             extent, time, time, *transform);
1322     } else {
1323         return pointInstancerSchema.ComputeExtentAtTime(extent, time, time);
1324     }
1325 }
1326 
1327 size_t
GetInstanceCount(UsdTimeCode timeCode) const1328 UsdGeomPointInstancer::GetInstanceCount(UsdTimeCode timeCode) const
1329 {
1330     UsdAttribute indicesAttr = GetProtoIndicesAttr();
1331     VtIntArray indices;
1332     indicesAttr.Get(&indices, timeCode);
1333     return indices.size();
1334 }
1335 
1336 
TF_REGISTRY_FUNCTION(UsdGeomBoundable)1337 TF_REGISTRY_FUNCTION(UsdGeomBoundable)
1338 {
1339     UsdGeomRegisterComputeExtentFunction<UsdGeomPointInstancer>(
1340         _ComputeExtentForPointInstancer);
1341 }
1342 
1343 PXR_NAMESPACE_CLOSE_SCOPE
1344