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