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/usdImaging/usdImaging/pointInstancerAdapter.h"
25 
26 #include "pxr/usdImaging/usdImaging/debugCodes.h"
27 #include "pxr/usdImaging/usdImaging/delegate.h"
28 #include "pxr/usdImaging/usdImaging/debugCodes.h"
29 #include "pxr/usdImaging/usdImaging/indexProxy.h"
30 #include "pxr/usdImaging/usdImaging/instancerContext.h"
31 #include "pxr/usdImaging/usdImaging/tokens.h"
32 #include "pxr/usdImaging/usdImaging/version.h"
33 
34 #include "pxr/imaging/hd/perfLog.h"
35 #include "pxr/imaging/hd/renderIndex.h"
36 #include "pxr/usd/sdf/schema.h"
37 #include "pxr/usd/usd/primRange.h"
38 #include "pxr/usd/usdGeom/imageable.h"
39 #include "pxr/usd/usdGeom/pointInstancer.h"
40 #include "pxr/usd/usdGeom/primvarsAPI.h"
41 #include "pxr/usd/usdGeom/tokens.h"
42 #include "pxr/usd/usdGeom/xformable.h"
43 
44 #include "pxr/base/tf/staticTokens.h"
45 #include "pxr/base/tf/stringUtils.h"
46 #include "pxr/base/tf/type.h"
47 #include "pxr/base/gf/quath.h"
48 
49 #include <limits>
50 #include <atomic>
51 
52 PXR_NAMESPACE_OPEN_SCOPE
53 
54 
55 // XXX: These should come from Hd or UsdImaging
56 TF_DEFINE_PRIVATE_TOKENS(
57     _tokens,
58     (instance)
59     (instancer)
60     (rotate)
61     (scale)
62     (translate)
63 );
64 
TF_REGISTRY_FUNCTION(TfType)65 TF_REGISTRY_FUNCTION(TfType)
66 {
67     typedef UsdImagingPointInstancerAdapter Adapter;
68     TfType t = TfType::Define<Adapter, TfType::Bases<Adapter::BaseAdapter> >();
69     t.SetFactory< UsdImagingPrimAdapterFactory<Adapter> >();
70 }
71 
~UsdImagingPointInstancerAdapter()72 UsdImagingPointInstancerAdapter::~UsdImagingPointInstancerAdapter()
73 {
74 }
75 
76 bool
ShouldCullChildren() const77 UsdImagingPointInstancerAdapter::ShouldCullChildren() const
78 {
79     return true;
80 }
81 
82 bool
IsInstancerAdapter() const83 UsdImagingPointInstancerAdapter::IsInstancerAdapter() const
84 {
85     return true;
86 }
87 
88 SdfPath
Populate(UsdPrim const & prim,UsdImagingIndexProxy * index,UsdImagingInstancerContext const * instancerContext)89 UsdImagingPointInstancerAdapter::Populate(UsdPrim const& prim,
90                             UsdImagingIndexProxy* index,
91                             UsdImagingInstancerContext const* instancerContext)
92 {
93     return _Populate(prim, index, instancerContext);
94 }
95 
96 SdfPath
_Populate(UsdPrim const & prim,UsdImagingIndexProxy * index,UsdImagingInstancerContext const * instancerContext)97 UsdImagingPointInstancerAdapter::_Populate(UsdPrim const& prim,
98                             UsdImagingIndexProxy* index,
99                             UsdImagingInstancerContext const* instancerContext)
100 {
101     SdfPath const& parentInstancerCachePath =
102         instancerContext ? instancerContext->instancerCachePath : SdfPath();
103     SdfPath instancerCachePath = prim.GetPath();
104     UsdGeomPointInstancer inst(prim);
105 
106     if (!inst) {
107         TF_WARN("Invalid instancer prim <%s>, instancer scheme was not valid\n",
108                 instancerCachePath.GetText());
109         return SdfPath();
110     }
111 
112     // for the case we happen to process the same instancer more than once,
113     // use variant selection path to make a unique index path (e.g. NI-PI)
114     if (_instancerData.find(instancerCachePath) != _instancerData.end()) {
115         static std::atomic_int ctr(0);
116         std::string name = TfStringify(++ctr);
117         instancerCachePath = instancerCachePath.AppendVariantSelection(
118                                                 "instance",name);
119     }
120 
121     // ---------------------------------------------------------------------- //
122     // Init instancer and fetch authored data needed to drive population
123     // ---------------------------------------------------------------------- //
124 
125     // Get the prototype target paths. These paths target subgraphs that are to
126     // be instanced. As a result, a single path here may result in many rprims
127     // for a single declared "prototype".
128     SdfPathVector usdProtoPaths;
129     UsdRelationship protosRel = inst.GetPrototypesRel();
130     if (!protosRel.GetForwardedTargets(&usdProtoPaths)) {
131         TF_WARN("Point instancer %s does not have a valid 'prototypes' "
132                 "relationship. Not adding it to the render index."
133                 , instancerCachePath.GetText());
134         return SdfPath();
135     }
136 
137     // protoIndices is a required property; it is allowed to be empty if
138     // time-varying data is provided via protoIndices.timeSamples. we only
139     // check for its definition  since USD doesn't have a cheap mechanism to
140     // check if an attribute has data
141     UsdAttribute protoIndicesAttr = inst.GetProtoIndicesAttr();
142     if (!protoIndicesAttr.HasValue()) {
143         TF_WARN("Point instancer %s does not have a 'protoIndices'"
144                 "attribute. Not adding it to the render index.",
145                 instancerCachePath.GetText());
146         return SdfPath();
147     }
148 
149     // positions is a required property; it is allowed to be empty if
150     // time-varying data is provided via positions.timeSamples. we only
151     // check for its definition  since USD doesn't have a cheap mechanism to
152     // check if an attribute has data
153     UsdAttribute positionsAttr = inst.GetPositionsAttr();
154     if (!positionsAttr.HasValue()) {
155         TF_WARN("Point instancer %s does not have a 'positions' attribute. "
156                 "Not adding it to the render index.",
157                 instancerCachePath.GetText());
158         return SdfPath();
159     }
160 
161     // Erase any data that we may have accumulated for a previous instancer at
162     // the same path (given that we should get a PrimResync notice before
163     // population, perhaps this is unnecessary?).
164     if (!TF_VERIFY(_instancerData.find(instancerCachePath)
165                         == _instancerData.end(), "<%s>\n",
166                         instancerCachePath.GetText())) {
167         _UnloadInstancer(instancerCachePath, index);
168     }
169 
170     // Init instancer data for this point instancer.
171     _InstancerData& instrData = _instancerData[instancerCachePath];
172     // myself. we want to grab PI adapter even if the PI itself is NI
173     // so that the children are bound to the PI adapter.
174     UsdImagingPrimAdapterSharedPtr instancerAdapter
175         = _GetPrimAdapter(prim, /*ignoreInstancing=*/true);
176 
177     // PERFORMANCE: We may allocate more pools than are actually used, so if
178     // we're squeezing memory in the future, we could be a little more efficient
179     // here.
180     instrData.prototypePaths.resize(usdProtoPaths.size());
181     instrData.prototypePathIndices.clear();
182     instrData.visible = true;
183     instrData.variableVisibility = true;
184     instrData.parentInstancerCachePath = parentInstancerCachePath;
185     instrData.visibleTime = std::numeric_limits<double>::infinity();
186 
187     TF_DEBUG(USDIMAGING_INSTANCER)
188         .Msg("[Add PI] %s, parentInstancerCachePath <%s>\n",
189              instancerCachePath.GetText(), parentInstancerCachePath.GetText());
190 
191     // Need to use GetAbsoluteRootOrPrimPath() on instancerCachePath to drop
192     // {instance=X} from the path, so usd can find the prim.
193     index->InsertInstancer(
194         instancerCachePath,
195         _GetPrim(instancerCachePath.GetAbsoluteRootOrPrimPath()),
196         instancerContext ? instancerContext->instancerAdapter
197                          : UsdImagingPrimAdapterSharedPtr());
198 
199     // ---------------------------------------------------------------------- //
200     // Main Prototype allocation loop.
201     // ---------------------------------------------------------------------- //
202 
203     // Iterate over all prototypes to allocate the Rprims in the Hydra
204     // RenderIndex.
205     size_t prototypeCount = instrData.prototypePaths.size();
206 
207     // For each prototype, allocate the Rprims.
208     for (size_t protoIndex = 0; protoIndex < prototypeCount; ++protoIndex) {
209 
210         // -------------------------------------------------------------- //
211         // Initialize this prototype.
212         // -------------------------------------------------------------- //
213         const SdfPath & prototypePath = usdProtoPaths[protoIndex];
214         instrData.prototypePaths[protoIndex] = prototypePath;
215         instrData.prototypePathIndices[prototypePath] = protoIndex;
216         UsdPrim protoRootPrim = _GetPrim(instrData.prototypePaths[protoIndex]);
217         if (!protoRootPrim) {
218             TF_WARN("Targeted prototype was not found <%s>\n",
219                     instrData.prototypePaths[protoIndex].GetText());
220             continue;
221         }
222 
223         // -------------------------------------------------------------- //
224         // Traverse the subtree and allocate the Rprims
225         // -------------------------------------------------------------- //
226         UsdImagingInstancerContext ctx = { instancerCachePath,
227                                            /*childName=*/TfToken(),
228                                            SdfPath(),
229                                            TfToken(),
230                                            TfToken(),
231                                            instancerAdapter};
232         _PopulatePrototype(protoIndex, instrData, protoRootPrim, index, &ctx);
233     }
234 
235     return instancerCachePath;
236 }
237 
238 void
_PopulatePrototype(int protoIndex,_InstancerData & instrData,UsdPrim const & protoRootPrim,UsdImagingIndexProxy * index,UsdImagingInstancerContext const * instancerContext)239 UsdImagingPointInstancerAdapter::_PopulatePrototype(
240     int protoIndex,
241     _InstancerData& instrData,
242     UsdPrim const& protoRootPrim,
243     UsdImagingIndexProxy* index,
244     UsdImagingInstancerContext const *instancerContext)
245 {
246     int protoID = 0;
247     size_t primCount = 0;
248     size_t instantiatedPrimCount = 0;
249 
250     std::vector<UsdPrimRange> treeStack;
251     treeStack.push_back(
252         UsdPrimRange(protoRootPrim, _GetDisplayPredicateForPrototypes()));
253     while (!treeStack.empty()) {
254         if (!treeStack.back()) {
255             treeStack.pop_back();
256             if (!treeStack.empty() && treeStack.back()) {
257                 // whenever we push a new tree iterator, we leave the
258                 // last one un-incremented intentionally so we have the
259                 // residual path. That also means that whenever we pop,
260                 // must increment the last iterator.
261                 treeStack.back().increment_begin();
262             }
263             if (treeStack.empty() || !treeStack.back()) {
264                 continue;
265             }
266         }
267         UsdPrimRange &range = treeStack.back();
268         UsdPrimRange::iterator iter = range.begin();
269 
270         // If we encounter native instances, continue traversing inside them.
271         // XXX: Should we delegate to instanceAdapter here?
272         if (iter->IsInstance()) {
273             UsdPrim prototype = iter->GetPrototype();
274             UsdPrimRange prototypeRange(
275                 prototype, _GetDisplayPredicateForPrototypes());
276             treeStack.push_back(prototypeRange);
277 
278             // Make sure to register a dependency on this instancer with the
279             // parent PI.
280             index->AddDependency(instancerContext->instancerCachePath, *iter);
281             continue;
282         }
283 
284         // construct instance chain
285         // note: paths is stored in the backward of treeStack
286         //       (prototype, prototype, ... , instance path)
287         //       to get the UsdPrim, use paths.front()
288         //
289         // for example:
290         //
291         // ProtoCube   <----+
292         //   +-- cube       | (native instance)
293         // ProtoA           |  <--+
294         //   +-- ProtoCube--+     | (native instance)
295         // PointInstancer         |
296         //   +-- ProtoA ----------+
297         //
298         // paths =
299         //    /__Prototype_1/cube
300         //    /__Prototype_2/ProtoCube
301         //    /PointInstancer/ProtoA
302 
303         SdfPathVector instancerChain;
304         for (int i = treeStack.size()-1; i >= 0; i--) {
305             instancerChain.push_back(treeStack[i].front().GetPath());
306         }
307         // make sure instancerChain is not empty
308         TF_VERIFY(instancerChain.size() > 0);
309 
310         // _GetPrimAdapter requires the instance proxy prim path, so:
311         UsdPrim instanceProxyPrim = _GetPrim(_GetPrimPathFromInstancerChain(
312                 instancerChain));
313 
314         if (!instanceProxyPrim) {
315             range.set_begin(++iter);
316             continue;
317         }
318 
319         // Skip population of non-imageable prims.
320         if (UsdImagingPrimAdapter::ShouldCullSubtree(instanceProxyPrim)) {
321             TF_DEBUG(USDIMAGING_INSTANCER).Msg("[Instance PI] Discovery of new "
322                 "prims at or below <%s> pruned by prim type (%s)\n",
323                 iter->GetPath().GetText(), iter->GetTypeName().GetText());
324             iter.PruneChildren();
325             range.set_begin(++iter);
326             continue;
327         }
328 
329         UsdImagingPrimAdapterSharedPtr adapter =
330             _GetPrimAdapter(instanceProxyPrim, /* ignoreInstancing = */ true);
331 
332         // Usd prohibits directly instancing gprims so if the current prim is
333         // an instance and has an adapter, warn and skip the prim. Prim types
334         // (such as cards) that can be directly instanced can opt out of this
335         // via CanPopulateUsdInstance().
336         if (instanceProxyPrim.IsInstance() && adapter &&
337             !adapter->CanPopulateUsdInstance()) {
338             TF_WARN("The gprim at path <%s> was directly instanced. "
339                     "In order to instance this prim, put the prim under an "
340                     "Xform, and instance the Xform parent.",
341                     iter->GetPath().GetText());
342             range.set_begin(++iter);
343             continue;
344         }
345 
346         if (adapter) {
347             primCount++;
348 
349             //
350             // prototype allocation.
351             //
352 
353             SdfPath protoPath;
354             if (adapter->IsInstancerAdapter()) {
355                 // if the prim is handled by some kind of multiplexing adapter
356                 // (e.g. another nested PointInstancer)
357                 // we'll relocate its children to itself, then no longer need to
358                 // traverse for this instancer.
359                 //
360                 // note that this condition should be tested after IsInstance()
361                 // above, since UsdImagingInstanceAdapter also returns true for
362                 // IsInstancerAdapter but it could be instancing something else.
363                 UsdImagingInstancerContext ctx = {
364                     instancerContext->instancerCachePath,
365                     instancerContext->childName,
366                     instancerContext->instancerMaterialUsdPath,
367                     instancerContext->instanceDrawMode,
368                     instancerContext->instanceInheritablePurpose,
369                     UsdImagingPrimAdapterSharedPtr() };
370                 protoPath = adapter->Populate(*iter, index, &ctx);
371             } else {
372                 TfToken protoName(
373                     TfStringPrintf(
374                         "proto%d_%s_id%d", protoIndex,
375                         iter->GetPath().GetName().c_str(), protoID++));
376 
377                 UsdPrim populatePrim = *iter;
378                 if (iter->IsPrototype() &&
379                     TF_VERIFY(instancerChain.size() > 1)) {
380                     populatePrim = _GetPrim(instancerChain.at(1));
381                 }
382 
383                 SdfPath const& materialId = GetMaterialUsdPath(instanceProxyPrim);
384                 TfToken const& drawMode = GetModelDrawMode(instanceProxyPrim);
385                 TfToken const& inheritablePurpose =
386                     GetInheritablePurpose(instanceProxyPrim);
387                 UsdImagingInstancerContext ctx = {
388                     instancerContext->instancerCachePath,
389                     /*childName=*/protoName,
390                     materialId,
391                     drawMode,
392                     inheritablePurpose,
393                     instancerContext->instancerAdapter };
394                 protoPath = adapter->Populate(populatePrim, index, &ctx);
395             }
396 
397             if (adapter->ShouldCullChildren()) {
398                 iter.PruneChildren();
399             }
400 
401             if (protoPath.IsEmpty()) {
402                 // Dont track this prototype if it wasn't actually
403                 // added.
404                 range.set_begin(++iter);
405                 continue;
406             }
407 
408             TF_DEBUG(USDIMAGING_INSTANCER).Msg(
409                 "[Add Instance PI] <%s>  %s\n",
410                 instancerContext->instancerCachePath.GetText(),
411                 protoPath.GetText());
412 
413             //
414             // Update instancer data.
415             //
416             _ProtoPrim& proto = instrData.protoPrimMap[protoPath];
417             proto.adapter = adapter;
418             proto.protoRootPath = instrData.prototypePaths[protoIndex];
419             proto.paths = instancerChain;
420 
421             // Book keeping, for debugging.
422             instantiatedPrimCount++;
423         }
424         range.set_begin(++iter);
425     }
426 
427     TF_DEBUG(USDIMAGING_POINT_INSTANCER_PROTO_CREATED).Msg(
428         "Prototype[%d]: <%s>, primCount: %lu, instantiatedPrimCount: %lu\n",
429         protoIndex,
430         protoRootPrim.GetPath().GetText(),
431         primCount,
432         instantiatedPrimCount);
433 }
434 
435 void
TrackVariability(UsdPrim const & prim,SdfPath const & cachePath,HdDirtyBits * timeVaryingBits,UsdImagingInstancerContext const * instancerContext) const436 UsdImagingPointInstancerAdapter::TrackVariability(UsdPrim const& prim,
437                                   SdfPath const& cachePath,
438                                   HdDirtyBits* timeVaryingBits,
439                                   UsdImagingInstancerContext const*
440                                       instancerContext) const
441 {
442     // XXX: This is no good: if an attribute has exactly one time sample, the
443     // default value will get cached and never updated. However, if we use an
444     // arbitrary time here, attributes which have valid default values and 1
445     // time sample will get cached with the wrong result. The solution is to
446     // stop guessing about what time to read, which will be done in a future
447     // change, which requires a much larger structure change to UsdImaging.
448     //
449     // Here we choose to favor correctness of the time sample, since we must
450     // ensure the correct image is produced for final render.
451     UsdTimeCode time(1.0);
452 
453     if (IsChildPath(cachePath)) {
454         _ProtoPrim& proto =
455             const_cast<_ProtoPrim&>(_GetProtoPrim(prim.GetPath(), cachePath));
456         if (!TF_VERIFY(proto.adapter, "%s", cachePath.GetText())) {
457             return;
458         }
459         if (!TF_VERIFY(proto.paths.size() > 0, "%s", cachePath.GetText())) {
460             return;
461         }
462 
463         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
464         proto.adapter->TrackVariability(protoPrim, cachePath,
465                                         &proto.variabilityBits);
466         *timeVaryingBits |= proto.variabilityBits;
467 
468         if (!(proto.variabilityBits & HdChangeTracker::DirtyVisibility)) {
469             // Pre-cache visibility, because we now know that it is static for
470             // the populated prototype over all time.  protoPrim may be across
471             // an instance boundary from protoRootPrim, so compute visibility
472             // for each prototype subtree, and then for the final path relative
473             // to the proto root.
474             UsdPrim protoRootPrim = _GetPrim(proto.protoRootPath);
475             for (size_t i = 0; i < proto.paths.size()-1; ++i) {
476                 _ComputeProtoVisibility(
477                     _GetPrim(proto.paths[i+1]).GetPrototype(),
478                     _GetPrim(proto.paths[i+0]),
479                     time, &proto.visible);
480             }
481             _ComputeProtoVisibility(protoRootPrim, _GetPrim(proto.paths.back()),
482                                     time, &proto.visible);
483         }
484 
485         // XXX: We handle PI visibility by pushing it onto the prototype;
486         // we should fix this.
487         _IsVarying(prim,
488             UsdGeomTokens->visibility,
489             HdChangeTracker::DirtyVisibility,
490             UsdImagingTokens->usdVaryingVisibility,
491             timeVaryingBits,
492             true);
493 
494         return;
495     } else  if (_InstancerData const* instrData =
496                 TfMapLookupPtr(_instancerData, cachePath)) {
497 
498         // Mark instance indices as time varying if any of the following is
499         // time varying : protoIndices, invisibleIds
500         _IsVarying(prim,
501             UsdGeomTokens->invisibleIds,
502             HdChangeTracker::DirtyInstanceIndex,
503             _tokens->instancer,
504             timeVaryingBits,
505             false) || _IsVarying(prim,
506                 UsdGeomTokens->protoIndices,
507                 HdChangeTracker::DirtyInstanceIndex,
508                 _tokens->instancer,
509                 timeVaryingBits,
510                 false);
511 
512         // this is for instancer transform.
513         _IsTransformVarying(prim,
514                             HdChangeTracker::DirtyTransform,
515                             UsdImagingTokens->usdVaryingXform,
516                             timeVaryingBits);
517 
518         // instancer visibility
519         HdDirtyBits visBits;
520         if (!_IsVarying(prim,
521                     UsdGeomTokens->visibility,
522                     HdChangeTracker::DirtyVisibility,
523                     UsdImagingTokens->usdVaryingVisibility,
524                     &visBits,
525                     true))
526         {
527             // When the instancer visibility doesn't vary over time, pre-cache
528             // visibility to avoid fetching it on frame change.
529             // XXX: The usage of _GetTimeWithOffset here is super-sketch, but
530             // it avoids blowing up the inherited visibility cache...
531             // We should let this be initialized by the first UpdateForTime...
532             instrData->visible = _GetInstancerVisible(cachePath,
533                     _GetTimeWithOffset(0.0));
534             instrData->variableVisibility = false;
535         } else {
536             instrData->variableVisibility = true;
537         }
538 
539         // Check per-instance transform primvars
540         _IsVarying(prim,
541                 UsdGeomTokens->positions,
542                 HdChangeTracker::DirtyPrimvar,
543                 _tokens->instancer,
544                 timeVaryingBits,
545                 false) ||
546             _IsVarying(prim,
547                     UsdGeomTokens->orientations,
548                     HdChangeTracker::DirtyPrimvar,
549                     _tokens->instancer,
550                     timeVaryingBits,
551                     false) ||
552             _IsVarying(prim,
553                     UsdGeomTokens->scales,
554                     HdChangeTracker::DirtyPrimvar,
555                     _tokens->instancer,
556                     timeVaryingBits,
557                     false);
558 
559         if (!(*timeVaryingBits & HdChangeTracker::DirtyPrimvar)) {
560             UsdGeomPrimvarsAPI primvars(prim);
561             for (auto const &pv: primvars.GetPrimvarsWithValues()) {
562                 TfToken const& interp = pv.GetInterpolation();
563                 if (interp != UsdGeomTokens->constant &&
564                     interp != UsdGeomTokens->uniform &&
565                     pv.ValueMightBeTimeVarying()) {
566                     *timeVaryingBits |= HdChangeTracker::DirtyPrimvar;
567                     HD_PERF_COUNTER_INCR(_tokens->instancer);
568                     break;
569                 }
570             }
571         }
572     }
573 }
574 
575 void
UpdateForTime(UsdPrim const & prim,SdfPath const & cachePath,UsdTimeCode time,HdDirtyBits requestedBits,UsdImagingInstancerContext const * instancerContext) const576 UsdImagingPointInstancerAdapter::UpdateForTime(UsdPrim const& prim,
577                                SdfPath const& cachePath,
578                                UsdTimeCode time,
579                                HdDirtyBits requestedBits,
580                                UsdImagingInstancerContext const*
581                                    instancerContext) const
582 {
583     UsdImagingPrimvarDescCache* primvarDescCache = _GetPrimvarDescCache();
584 
585     if (IsChildPath(cachePath)) {
586         // Allow the prototype's adapter to update, if there's anything left
587         // to do.
588         if (requestedBits != HdChangeTracker::Clean) {
589             // cachePath : /path/instancerPath.proto_*
590             // instancerPath : /path/instancerPath
591             SdfPath instancerPath = cachePath.GetParentPath();
592             _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
593             if (!TF_VERIFY(proto.adapter, "%s", cachePath.GetText())) {
594                 return;
595             }
596             if (!TF_VERIFY(proto.paths.size() > 0, "%s", cachePath.GetText())) {
597                 return;
598             }
599 
600             UsdPrim protoPrim = _GetProtoUsdPrim(proto);
601             proto.adapter->UpdateForTime(
602                 protoPrim, cachePath, time, requestedBits);
603         }
604     } else if (_instancerData.find(cachePath) != _instancerData.end()) {
605         // For the instancer itself, we only send translate, rotate and scale
606         // back as primvars, which all fall into the DirtyPrimvar bucket
607         // currently.
608         if (requestedBits & HdChangeTracker::DirtyPrimvar) {
609             UsdGeomPointInstancer instancer(prim);
610 
611             HdPrimvarDescriptorVector& vPrimvars =
612                 primvarDescCache->GetPrimvars(cachePath);
613 
614             // PERFORMANCE: It would be nice to track variability of individual
615             // primvars separately, since uniform values will  needlessly be
616             // sent to the GPU on every frame.
617             VtVec3fArray positions;
618             if (instancer.GetPositionsAttr().Get(&positions, time)) {
619                 _MergePrimvar(
620                     &vPrimvars,
621                     _tokens->translate,
622                     HdInterpolationInstance,
623                     HdPrimvarRoleTokens->vector);
624             }
625 
626             VtQuathArray orientations;
627             if (instancer.GetOrientationsAttr().Get(&orientations, time)) {
628                 _MergePrimvar(
629                     &vPrimvars,
630                     _tokens->rotate,
631                     HdInterpolationInstance);
632             }
633 
634             VtVec3fArray scales;
635             if (instancer.GetScalesAttr().Get(&scales, time)) {
636                 _MergePrimvar(
637                     &vPrimvars,
638                     _tokens->scale,
639                     HdInterpolationInstance);
640             }
641 
642             // Convert non-constant primvars on UsdGeomPointInstancer
643             // into instance-rate primvars. Note: this only gets local primvars.
644             // Inherited primvars don't vary per-instance, so we let the
645             // prototypes pick them up.
646             UsdGeomPrimvarsAPI primvars(instancer);
647             for (auto const &pv: primvars.GetPrimvarsWithValues()) {
648                 TfToken const& interp = pv.GetInterpolation();
649                 if (interp != UsdGeomTokens->constant &&
650                     interp != UsdGeomTokens->uniform) {
651                     HdInterpolation interp = HdInterpolationInstance;
652                     _ComputeAndMergePrimvar(prim, pv, time, &vPrimvars, &interp);
653                 }
654             }
655         }
656     }
657 }
658 
659 HdDirtyBits
ProcessPropertyChange(UsdPrim const & prim,SdfPath const & cachePath,TfToken const & propertyName)660 UsdImagingPointInstancerAdapter::ProcessPropertyChange(UsdPrim const& prim,
661                                       SdfPath const& cachePath,
662                                       TfToken const& propertyName)
663 {
664     if (IsChildPath(cachePath)) {
665         _ProtoPrim const& proto = _GetProtoPrim(prim.GetPath(), cachePath);
666         if (!proto.adapter || (proto.paths.size() <= 0)) {
667             // It's possible we'll get multiple USD edits for the same
668             // prototype, one of which will cause a resync.  On resync,
669             // we immediately remove the instancer data, but primInfo
670             // deletion is deferred until the end of the edit batch.
671             // That means, if GetProtoPrim fails we've already
672             // queued the prototype for resync and we can safely
673             // return clean (no-work).
674             return HdChangeTracker::Clean;
675         }
676 
677         UsdPrim protoUsdPrim = _GetProtoUsdPrim(proto);
678         if (!protoUsdPrim) {
679             // It's possible that we will get a property change that was
680             // actually directed at a parent primitive for an inherited
681             // primvar. We need to verify that the prototype UsdPrim still
682             // exists, as it may have been deactivated or otherwise removed,
683             // in which case we can return clean (no-work).
684             return HdChangeTracker::Clean;
685         }
686 
687         // XXX: Specifically disallow visibility and transform updates: in
688         // these cases, it's hard to tell which prims we should dirty but
689         // probably we need to dirty both prototype & instancer. This is a
690         // project for later. In the meantime, returning AllDirty causes
691         // a re-sync.
692         HdDirtyBits dirtyBits = proto.adapter->ProcessPropertyChange(
693             protoUsdPrim, cachePath, propertyName);
694 
695         if (dirtyBits & (HdChangeTracker::DirtyTransform |
696                          HdChangeTracker::DirtyVisibility)) {
697             return HdChangeTracker::AllDirty;
698         }
699         return dirtyBits;
700     }
701 
702     if (propertyName == UsdGeomTokens->positions ||
703         propertyName == UsdGeomTokens->orientations ||
704         propertyName == UsdGeomTokens->scales) {
705 
706         TfToken primvarName = propertyName;
707         if (propertyName == UsdGeomTokens->positions) {
708             primvarName = _tokens->translate;
709         } else if (propertyName == UsdGeomTokens->orientations) {
710             primvarName = _tokens->rotate;
711         } else if (propertyName == UsdGeomTokens->scales) {
712             primvarName = _tokens->scale;
713         }
714 
715         return _ProcessNonPrefixedPrimvarPropertyChange(
716                 prim, cachePath, propertyName, primvarName,
717                 HdInterpolationInstance);
718     }
719 
720     if (propertyName == UsdGeomTokens->protoIndices ||
721         propertyName == UsdGeomTokens->invisibleIds) {
722         return HdChangeTracker::DirtyInstanceIndex;
723     }
724 
725     // Is the property a primvar?
726     if (UsdGeomPrimvarsAPI::CanContainPropertyName(propertyName)) {
727         // Ignore local constant/uniform primvars.
728         UsdGeomPrimvar pv = UsdGeomPrimvarsAPI(prim).GetPrimvar(propertyName);
729         if (pv && (pv.GetInterpolation() == UsdGeomTokens->constant ||
730                    pv.GetInterpolation() == UsdGeomTokens->uniform)) {
731             return HdChangeTracker::Clean;
732         }
733 
734 
735         return UsdImagingPrimAdapter::_ProcessPrefixedPrimvarPropertyChange(
736             prim, cachePath, propertyName,
737             /*valueChangeDirtyBit*/HdChangeTracker::DirtyPrimvar,
738             /*inherited=*/false);
739     }
740 
741     // XXX: Treat transform & visibility changes as re-sync, until we untangle
742     // instancer vs proto data.
743     if (propertyName == UsdGeomTokens->visibility ||
744         UsdGeomXformable::IsTransformationAffectedByAttrNamed(propertyName)) {
745         return HdChangeTracker::AllDirty;
746     }
747 
748     return HdChangeTracker::Clean;
749 }
750 
751 void
_ProcessPrimRemoval(SdfPath const & cachePath,UsdImagingIndexProxy * index,SdfPathVector * instancersToReload)752 UsdImagingPointInstancerAdapter::_ProcessPrimRemoval(SdfPath const& cachePath,
753                                              UsdImagingIndexProxy* index,
754                                              SdfPathVector* instancersToReload)
755 {
756     SdfPath affectedInstancer;
757 
758     // cachePath is from the _dependencyInfo map in the delegate, and points to
759     // either a hydra instancer or a hydra prototype (the latter in the case of
760     // adapter forwarding).  For hydra prototypes, their name is mangled by the
761     // immediate instancer parent: if /World/PI/PI2 has cache path
762     // /World/PI/PI2{0}, then /World/PI/PI2/cube will have cache path
763     // /World/PI/PI2{0}.proto0_cube_id0 (see _PopulatePrototype). This, then,
764     // gives us an easy route to the affected instancer.
765     if (IsChildPath(cachePath)) {
766         affectedInstancer = cachePath.GetParentPath();
767     } else {
768         affectedInstancer = cachePath;
769     }
770 
771     // If the affected instancer is populated, delete it by finding the
772     // top-level instancer and calling _UnloadInstancer on that.
773     // XXX: It would be nice if we could just remove *this* prim and rely on
774     // the resync code to propertly resync it with the right parent instancer.
775 
776     _InstancerDataMap::iterator instIt = _instancerData.find(affectedInstancer);
777 
778     if (instIt == _instancerData.end()) {
779         // Invalid cache path.
780         return;
781     }
782 
783     while (instIt != _instancerData.end()) {
784         affectedInstancer = instIt->first;
785         SdfPath parentInstancerCachePath =
786             instIt->second.parentInstancerCachePath;
787         if (parentInstancerCachePath.IsEmpty()) {
788             break;
789         }
790         instIt = _instancerData.find(parentInstancerCachePath);
791     }
792 
793     // Should we reload affected instancer?
794     if (instancersToReload) {
795         UsdPrim p = _GetPrim(affectedInstancer.GetPrimPath());
796         if (p && p.IsActive()) {
797             instancersToReload->push_back(affectedInstancer);
798         }
799     }
800     _UnloadInstancer(affectedInstancer, index);
801 }
802 
803 void
ProcessPrimResync(SdfPath const & cachePath,UsdImagingIndexProxy * index)804 UsdImagingPointInstancerAdapter::ProcessPrimResync(SdfPath const& cachePath,
805                                              UsdImagingIndexProxy* index)
806 {
807     // _ProcesPrimRemoval does the heavy lifting, returning a set of instancers
808     // to repopulate. Note that the child/prototype prims need not be in the
809     // "toReload" list, as they will be discovered in the process of reloading
810     // the root instancer prim.
811     SdfPathVector toReload;
812     _ProcessPrimRemoval(cachePath, index, &toReload);
813     for (SdfPath const& instancerRootPath : toReload) {
814         index->Repopulate(instancerRootPath);
815     }
816 }
817 
818 /*virtual*/
819 void
ProcessPrimRemoval(SdfPath const & cachePath,UsdImagingIndexProxy * index)820 UsdImagingPointInstancerAdapter::ProcessPrimRemoval(SdfPath const& cachePath,
821                                                     UsdImagingIndexProxy* index)
822 {
823     // Process removals, but do not repopulate.
824     _ProcessPrimRemoval(cachePath, index, /*instancersToRepopulate*/nullptr);
825 }
826 
827 void
MarkDirty(UsdPrim const & prim,SdfPath const & cachePath,HdDirtyBits dirty,UsdImagingIndexProxy * index)828 UsdImagingPointInstancerAdapter::MarkDirty(UsdPrim const& prim,
829                                            SdfPath const& cachePath,
830                                            HdDirtyBits dirty,
831                                            UsdImagingIndexProxy* index)
832 {
833     if (IsChildPath(cachePath)) {
834         // cachePath : /path/instancerPath.proto_*
835         // instancerPath : /path/instancerPath
836         SdfPath instancerPath = cachePath.GetParentPath();
837         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
838 
839         proto.adapter->MarkDirty(prim, cachePath, dirty, index);
840     } else {
841         index->MarkInstancerDirty(cachePath, dirty);
842     }
843 }
844 
845 void
MarkRefineLevelDirty(UsdPrim const & prim,SdfPath const & cachePath,UsdImagingIndexProxy * index)846 UsdImagingPointInstancerAdapter::MarkRefineLevelDirty(
847                                                    UsdPrim const& prim,
848                                                    SdfPath const& cachePath,
849                                                    UsdImagingIndexProxy* index)
850 {
851     if (IsChildPath(cachePath)) {
852         // cachePath : /path/instancerPath.proto_*
853         // instancerPath : /path/instancerPath
854         SdfPath instancerPath = cachePath.GetParentPath();
855         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
856 
857         proto.adapter->MarkRefineLevelDirty(prim, cachePath, index);
858     }
859 }
860 
861 void
MarkReprDirty(UsdPrim const & prim,SdfPath const & cachePath,UsdImagingIndexProxy * index)862 UsdImagingPointInstancerAdapter::MarkReprDirty(UsdPrim const& prim,
863                                                SdfPath const& cachePath,
864                                                UsdImagingIndexProxy* index)
865 {
866     if (IsChildPath(cachePath)) {
867         // cachePath : /path/instancerPath.proto_*
868         // instancerPath : /path/instancerPath
869         SdfPath instancerPath = cachePath.GetParentPath();
870         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
871 
872         proto.adapter->MarkReprDirty(prim, cachePath, index);
873     }
874 }
875 
876 void
MarkCullStyleDirty(UsdPrim const & prim,SdfPath const & cachePath,UsdImagingIndexProxy * index)877 UsdImagingPointInstancerAdapter::MarkCullStyleDirty(UsdPrim const& prim,
878                                                     SdfPath const& cachePath,
879                                                     UsdImagingIndexProxy* index)
880 {
881     if (IsChildPath(cachePath)) {
882         // cachePath : /path/instancerPath.proto_*
883         // instancerPath : /path/instancerPath
884         SdfPath instancerPath = cachePath.GetParentPath();
885         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
886 
887         proto.adapter->MarkCullStyleDirty(prim, cachePath, index);
888     }
889 }
890 
891 void
MarkRenderTagDirty(UsdPrim const & prim,SdfPath const & cachePath,UsdImagingIndexProxy * index)892 UsdImagingPointInstancerAdapter::MarkRenderTagDirty(UsdPrim const& prim,
893                                                     SdfPath const& cachePath,
894                                                     UsdImagingIndexProxy* index)
895 {
896     if (IsChildPath(cachePath)) {
897         // cachePath : /path/instancerPath.proto_*
898         // instancerPath : /path/instancerPath
899         SdfPath instancerPath = cachePath.GetParentPath();
900         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
901 
902         proto.adapter->MarkRenderTagDirty(prim, cachePath, index);
903     }
904 }
905 
906 void
MarkTransformDirty(UsdPrim const & prim,SdfPath const & cachePath,UsdImagingIndexProxy * index)907 UsdImagingPointInstancerAdapter::MarkTransformDirty(UsdPrim const& prim,
908                                                     SdfPath const& cachePath,
909                                                     UsdImagingIndexProxy* index)
910 {
911     if (IsChildPath(cachePath)) {
912         // cachePath : /path/instancerPath.proto_*
913         // instancerPath : /path/instancerPath
914         SdfPath instancerPath = cachePath.GetParentPath();
915         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
916 
917         proto.adapter->MarkTransformDirty(prim, cachePath, index);
918     } else {
919         static const HdDirtyBits transformDirty =
920                                                 HdChangeTracker::DirtyTransform;
921 
922         index->MarkInstancerDirty(cachePath, transformDirty);
923     }
924 }
925 
926 void
MarkVisibilityDirty(UsdPrim const & prim,SdfPath const & cachePath,UsdImagingIndexProxy * index)927 UsdImagingPointInstancerAdapter::MarkVisibilityDirty(
928                                                     UsdPrim const& prim,
929                                                     SdfPath const& cachePath,
930                                                     UsdImagingIndexProxy* index)
931 {
932     if (IsChildPath(cachePath)) {
933         // cachePath : /path/instancerPath.proto_*
934         // instancerPath : /path/instancerPath
935         SdfPath instancerPath = cachePath.GetParentPath();
936         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
937 
938         proto.adapter->MarkVisibilityDirty(prim, cachePath, index);
939     } else {
940         static const HdDirtyBits visibilityDirty =
941                                                HdChangeTracker::DirtyVisibility;
942 
943         index->MarkInstancerDirty(cachePath, visibilityDirty);
944     }
945 }
946 
947 void
_UnloadInstancer(SdfPath const & instancerPath,UsdImagingIndexProxy * index)948 UsdImagingPointInstancerAdapter::_UnloadInstancer(SdfPath const& instancerPath,
949                                             UsdImagingIndexProxy* index)
950 {
951     _InstancerDataMap::iterator instIt = _instancerData.find(instancerPath);
952 
953     // Note: If any of the prototype children is a point instancer, their
954     // ProcessPrimRemoval will try to forward the removal call to the
955     // top-level instancer that has an entry in _instancerData.  This means,
956     // to avoid infinite loops, that we need to remove the _instancerData
957     // entry for this instancer before removing prototypes.
958 
959     const _ProtoPrimMap protoPrimMap = instIt->second.protoPrimMap;
960     _instancerData.erase(instIt);
961 
962     // First, we need to make sure all proto rprims are removed.
963     for (auto const& pair : protoPrimMap) {
964         // pair: <cache path, _ProtoPrim>
965         pair.second.adapter->ProcessPrimRemoval(pair.first, index);
966     }
967 
968     // Blow away the instancer and the associated local data.
969     index->RemoveInstancer(instancerPath);
970 }
971 
972 // -------------------------------------------------------------------------- //
973 // Private IO Helpers
974 // -------------------------------------------------------------------------- //
975 
976 UsdImagingPointInstancerAdapter::_ProtoPrim const&
_GetProtoPrim(SdfPath const & instrPath,SdfPath const & cachePath) const977 UsdImagingPointInstancerAdapter::_GetProtoPrim(SdfPath const& instrPath,
978                                                  SdfPath const& cachePath) const
979 {
980     static _ProtoPrim const EMPTY;
981     SdfPath const& instancerPath =
982         (cachePath.GetParentPath().IsPrimVariantSelectionPath())
983             ? cachePath.GetParentPath()
984             : instrPath;
985 
986     _InstancerDataMap::const_iterator it = _instancerData.find(instancerPath);
987     if (it == _instancerData.end()) {
988         return EMPTY;
989     }
990     _ProtoPrimMap::const_iterator protoPrimIt
991         = it->second.protoPrimMap.find(cachePath);
992     if (protoPrimIt == it->second.protoPrimMap.end()) {
993         return EMPTY;
994     }
995     return protoPrimIt->second;
996 }
997 
998 bool
_GetProtoPrimForChild(UsdPrim const & usdPrim,SdfPath const & cachePath,_ProtoPrim const ** proto,UsdImagingInstancerContext * ctx) const999 UsdImagingPointInstancerAdapter::_GetProtoPrimForChild(
1000     UsdPrim const& usdPrim,
1001     SdfPath const& cachePath,
1002     _ProtoPrim const** proto,
1003     UsdImagingInstancerContext* ctx) const
1004 {
1005     if (IsChildPath(cachePath)) {
1006         *proto = &_GetProtoPrim(usdPrim.GetPath(), cachePath);
1007         if (!TF_VERIFY(*proto)) {
1008             return false;
1009         }
1010         UsdPrim protoPrim = _GetProtoUsdPrim(**proto);
1011 
1012         // The instancer path since IsChildPath is true
1013         const SdfPath instancerPath = cachePath.GetParentPath();
1014 
1015         ctx->instancerCachePath = instancerPath;
1016         ctx->childName = cachePath.GetNameToken();
1017         ctx->instancerMaterialUsdPath = SdfPath();
1018         ctx->instanceDrawMode = TfToken();
1019         ctx->instanceInheritablePurpose = TfToken();
1020         ctx->instancerAdapter = const_cast<UsdImagingPointInstancerAdapter *>
1021             (this)->shared_from_this();
1022         return true;
1023     } else {
1024         return false;
1025     }
1026 }
1027 
1028 const UsdPrim
_GetProtoUsdPrim(_ProtoPrim const & proto) const1029 UsdImagingPointInstancerAdapter::_GetProtoUsdPrim(
1030     _ProtoPrim const& proto) const
1031 {
1032     // proto.paths.front() is the most local path for the rprim.
1033     // If it's not native-instanced, proto.paths will be size 1.
1034     // If it is native-instanced, proto.paths may look like
1035     //   /__Prototype_1/prim
1036     //   /Instance
1037     // where /__Prototype_1/prim is the pointer to the actual prim in question.
1038     UsdPrim prim = _GetPrim(proto.paths.front());
1039 
1040     // One exception: if the prototype is an instance, proto.paths looks like
1041     //   /__Prototype_1
1042     //   /Instance
1043     // ... in which case, we want to return /Instance since prototypes drop all
1044     // attributes.
1045     if (prim && prim.IsPrototype() && TF_VERIFY(proto.paths.size() > 1)) {
1046         prim = _GetPrim(proto.paths.at(1));
1047     }
1048     return prim;
1049 }
1050 
1051 bool
_GetInstancerVisible(SdfPath const & instancerPath,UsdTimeCode time) const1052 UsdImagingPointInstancerAdapter::_GetInstancerVisible(
1053     SdfPath const &instancerPath, UsdTimeCode time) const
1054 {
1055     bool visible = UsdImagingPrimAdapter::GetVisible(
1056         _GetPrim(instancerPath.GetPrimPath()),
1057         instancerPath,
1058         time);
1059 
1060     if (visible) {
1061         _InstancerDataMap::const_iterator it
1062             = _instancerData.find(instancerPath);
1063         if (it != _instancerData.end()) {
1064             // note that parent instancer may not be a namespace parent
1065             // (e.g. prototype -> instance)
1066             SdfPath const &parentInstancerCachePath =
1067                 it->second.parentInstancerCachePath;
1068             if (!parentInstancerCachePath.IsEmpty()) {
1069                 return _GetInstancerVisible(parentInstancerCachePath, time);
1070             }
1071         }
1072     }
1073 
1074     return visible;
1075 }
1076 
1077 void
_UpdateInstancerVisibility(SdfPath const & instancerPath,_InstancerData const & instrData,UsdTimeCode time) const1078 UsdImagingPointInstancerAdapter::_UpdateInstancerVisibility(
1079         SdfPath const& instancerPath,
1080         _InstancerData const& instrData,
1081         UsdTimeCode time) const
1082 {
1083     TF_DEBUG(USDIMAGING_INSTANCER).Msg(
1084         "[PointInstancer::_UpdateInstancerVisibility] %s\n",
1085         instancerPath.GetText());
1086 
1087     if (instrData.variableVisibility) {
1088         // If visibility is variable, each prototype will be updating the
1089         // instancer visibility here, so make sure we lock before retrieving
1090         // or computing it.
1091         std::lock_guard<std::mutex> lock(instrData.mutex);
1092 
1093         // Grab the instancer visibility, if it varies over time.
1094         bool upToDate = instrData.visibleTime == time;
1095         if (!upToDate) {
1096             instrData.visible = _GetInstancerVisible(instancerPath, time);
1097             instrData.visibleTime = time;
1098         }
1099     }
1100 }
1101 
1102 GfMatrix4d
_CorrectTransform(UsdPrim const & instancer,UsdPrim const & protoRoot,SdfPath const & cachePath,SdfPathVector const & protoPathChain,GfMatrix4d const & inTransform,UsdTimeCode time) const1103 UsdImagingPointInstancerAdapter::_CorrectTransform(
1104     UsdPrim const& instancer,
1105     UsdPrim const& protoRoot,
1106     SdfPath const& cachePath,
1107     SdfPathVector const& protoPathChain,
1108     GfMatrix4d const& inTransform,
1109     UsdTimeCode time) const
1110 {
1111     // Subtract out the parent transform from prototypes (in prototype time).
1112     //
1113     // Need to track instancer transform variability (this should be
1114     // fine, as long as the prototypes live under the instancer).
1115 
1116     // - delegate-root-transform
1117     //      root transform applied to entire prims in a delegate.
1118     // - proto-root-transform
1119     //      transform of the each prototype root usd-prim
1120     // - proto-gprim-transform
1121     //      transform of the each prototype Rprim
1122 
1123     // Our Hydra convention applies the delegate-root-transform to instancer,
1124     // not to a prototype (required for nested instancing).
1125     // Compute inverse to extract root transform from prototypes too.
1126     GfMatrix4d inverseRootTransform = GetRootTransform().GetInverse();
1127 
1128     // First, GprimAdapter has already populated transform of the protoPrim into
1129     // value cache, including the delegate-root-transform, because GprimAdapter
1130     // doesn't know if it's a prototype of point instancer or not.
1131     //
1132     //  (see UsdImagingGprimAdapter::UpdateForTime. GetTransform() doesn't
1133     //   specify ignoreRootTransform parameter)
1134     //
1135     // We want to store the relative transform for each prototype rprim.
1136     // Subtract the delegate-root-transform.
1137     GfMatrix4d protoGprimToWorld = inTransform;
1138     protoGprimToWorld = protoGprimToWorld * inverseRootTransform;
1139 
1140     // If this is nested instancer (has parent),
1141     for (size_t i = 1; i < protoPathChain.size(); ++i) {
1142         // ignore root transform of nested instancer chain
1143         //
1144         // PI ---(protoRoot)--- NI:XFM
1145         //                          ^
1146         //                       This matrix, we're applying
1147         protoGprimToWorld *= BaseAdapter::GetTransform(
1148             _GetPrim(protoPathChain[i]),
1149             protoPathChain[i],
1150             time,
1151             /*ignoreRootTransform=*/true);
1152     }
1153 
1154     // Then, we also need to subtract transform above the proto root to avoid
1155     // double transform of instancer and prototypes.
1156     // Compute the transform of the proto root,
1157     // excluding delegate-root-transform.
1158     //
1159     // PI(or whatever):XFM---(protoRoot)--- NI (or whatever)
1160     //                 ^
1161     //      This matrix, we're subtracting
1162     UsdPrim parent = protoRoot.GetParent();
1163     if (parent) {
1164         GfMatrix4d parentToWorld = GetTransform(
1165             parent, parent.GetPath(), time, /*ignoreRootTransform=*/true);
1166 
1167         // protoRootToWorld includes its own transform AND root transform,
1168         // GetInverse() extracts both transforms.
1169         protoGprimToWorld = protoGprimToWorld * parentToWorld.GetInverse();
1170     }
1171     return protoGprimToWorld;
1172 }
1173 
1174 void
_ComputeProtoVisibility(UsdPrim const & protoRoot,UsdPrim const & protoGprim,UsdTimeCode time,bool * vis) const1175 UsdImagingPointInstancerAdapter::_ComputeProtoVisibility(
1176                                  UsdPrim const& protoRoot,
1177                                  UsdPrim const& protoGprim,
1178                                  UsdTimeCode time,
1179                                  bool* vis) const
1180 {
1181     if (!TF_VERIFY(vis)) { return; }
1182     if (!protoGprim.GetPath().HasPrefix(protoRoot.GetPath())) {
1183         TF_CODING_ERROR("Prototype <%s> is not prefixed under "
1184                 "proto root <%s>\n",
1185                 protoGprim.GetPath().GetText(),
1186                 protoRoot.GetPath().GetText());
1187         return;
1188     }
1189 
1190     // if it's in invised list, set vis to false
1191     if (_IsInInvisedPaths(protoGprim.GetPath())) {
1192         *vis = false;
1193         return;
1194     }
1195 
1196     // Recurse until we get to the protoRoot. With this recursion, we'll
1197     // process the protoRoot first, then a child, down to the protoGprim.
1198     //
1199     // Skip all prototypes, since they can't have an opinion.
1200     if (!protoGprim.IsPrototype() &&
1201         protoRoot != protoGprim && protoGprim.GetParent()) {
1202         _ComputeProtoVisibility(protoRoot, protoGprim.GetParent(), time, vis);
1203     }
1204 
1205     // If an ancestor set vis to false, we need not check any other prims.
1206     if (!*vis)
1207         return;
1208 
1209     // Check visibility of this prim.
1210     TfToken visToken;
1211     if (UsdGeomImageable(protoGprim).GetVisibilityAttr().Get(&visToken, time)
1212                                     && visToken == UsdGeomTokens->invisible) {
1213         *vis = false;
1214         return;
1215     }
1216 }
1217 
1218 /* virtual */
1219 SdfPath
GetScenePrimPath(SdfPath const & cachePath,int instanceIndex,HdInstancerContext * instancerContext) const1220 UsdImagingPointInstancerAdapter::GetScenePrimPath(
1221     SdfPath const& cachePath,
1222     int instanceIndex,
1223     HdInstancerContext *instancerContext) const
1224 {
1225     HD_TRACE_FUNCTION();
1226 
1227     TF_DEBUG(USDIMAGING_SELECTION).Msg(
1228         "GetScenePrimPath: proto = %s\n", cachePath.GetText());
1229 
1230     SdfPath instancerPath;
1231     SdfPath protoPath = cachePath;
1232 
1233     // If the prototype is an rprim, the instancer path is just the parent path.
1234     if (IsChildPath(cachePath)) {
1235         instancerPath = cachePath.GetParentPath();
1236     } else {
1237         // If the prototype is a PI, then cache path will be in the prim map
1238         // of one of the instancers.  If it's a UsdGeomPointInstancer, we can
1239         // look it up directly, and get the parent path that way. Otherwise,
1240         // we need to loop all instancers.
1241         _InstancerDataMap::const_iterator it =
1242             _instancerData.find(cachePath);
1243         if (it != _instancerData.end()) {
1244             instancerPath = it->second.parentInstancerCachePath;
1245         } else {
1246             for (auto const& pair : _instancerData) {
1247                 if (pair.second.protoPrimMap.count(cachePath) > 0) {
1248                     instancerPath = pair.first;
1249                     break;
1250                 }
1251             }
1252         }
1253     }
1254 
1255     // If we couldn't determine instancerPath, bail.
1256     if (instancerPath == SdfPath()) {
1257         return SdfPath();
1258     }
1259 
1260     _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
1261     if (!proto.adapter) {
1262         // If proto.adapter is null, _GetProtoPrim failed.
1263         return SdfPath();
1264     }
1265     SdfPath primPath = _GetPrimPathFromInstancerChain(proto.paths);
1266 
1267     // If the prim path is in prototype, we need the help of the parent
1268     // instancer to figure out what the right instance is.  We assume:
1269     // 1.) primPath and instancerPath are inside the same prototype.
1270     // 2.) recursing gives us the fully-qualified version of instancerPath.
1271     //
1272     // If:
1273     // - primPath is /__Prototype_1/Instancer/protos/A
1274     // - instancerPath is /__Prototype_1/Instancer
1275     // - parentPath is /World/Foo/Instance
1276     //
1277     // Let fqInstancerPath = parentAdapter->GetScenePrimPath(instancerPath);
1278     // - fqInstancerPath = /World/Bar/Instance/Instancer
1279     // Then instancePath = /World/Bar/Instance
1280     // instancePath = fqInstancerPath - instancerPath, or traversing up
1281     // until we hit the first instance.
1282     //
1283     // Finally:
1284     // - fqPrimPath = instancePath + primPath
1285     //   = /World/Bar/Instance/Instancer/protos/A
1286     //
1287     // We also recurse here to fill in instancerContext.
1288 
1289     // Look up the parent instancer of this instancer.
1290     _InstancerDataMap::const_iterator it = _instancerData.find(instancerPath);
1291     if (it == _instancerData.end()) {
1292         return SdfPath();
1293     }
1294     SdfPath parentPath = it->second.parentInstancerCachePath;
1295 
1296     // Compute the local & parent instance index.
1297     VtValue indicesValue = GetInstanceIndices(_GetPrim(instancerPath),
1298             instancerPath, cachePath, _GetTimeWithOffset(0.0));
1299 
1300     if (!indicesValue.IsHolding<VtIntArray>()) {
1301         return SdfPath();
1302     }
1303     VtIntArray const & indices = indicesValue.UncheckedGet<VtIntArray>();
1304 
1305     // instanceIndex = parentIndex * indices.size() + i.
1306     int parentIndex = instanceIndex / indices.size();
1307     // indices[i] gives the offset into the index buffers (i.e. protoIndices).
1308     int localIndex = indices[instanceIndex % indices.size()];
1309 
1310     // Find out the fully-qualified parent path. If there is none, the
1311     // one we have is fully qualified.
1312     SdfPath fqInstancerPath = instancerPath;
1313     UsdPrim parentInstancerUsdPrim =
1314         _GetPrim(parentPath.GetAbsoluteRootOrPrimPath());
1315     if (parentInstancerUsdPrim) {
1316         UsdImagingPrimAdapterSharedPtr parentAdapter =
1317             _GetPrimAdapter(parentInstancerUsdPrim);
1318         if (!TF_VERIFY(parentAdapter, "%s",
1319                        parentPath.GetAbsoluteRootOrPrimPath().GetText())) {
1320             return SdfPath();
1321         }
1322         fqInstancerPath =
1323             parentAdapter->GetScenePrimPath(instancerPath, parentIndex,
1324                                             instancerContext);
1325     }
1326 
1327     // Append to the instancer context.
1328     if (instancerContext != nullptr) {
1329         instancerContext->push_back(
1330             std::make_pair(fqInstancerPath, localIndex));
1331     }
1332 
1333     // Check if primPath is in prototype, and if so check if the instancer
1334     // is in the same prototype...
1335     UsdPrim prim = _GetPrim(primPath);
1336     if (!prim || !prim.IsInPrototype()) {
1337         return primPath;
1338     }
1339     UsdPrim instancer = _GetPrim(instancerPath.GetAbsoluteRootOrPrimPath());
1340     if (!instancer || !instancer.IsInPrototype() ||
1341         prim.GetPrototype() != instancer.GetPrototype()) {
1342         TF_CODING_ERROR("primPath <%s> and instancerPath <%s> are not in "
1343                         "the same prototype", primPath.GetText(),
1344                         instancerPath.GetText());
1345         return SdfPath();
1346     }
1347 
1348     // Stitch the paths together.
1349     UsdPrim fqInstancer = _GetPrim(fqInstancerPath);
1350     if (!fqInstancer || !fqInstancer.IsInstanceProxy()) {
1351         return SdfPath();
1352     }
1353     while (fqInstancer && !fqInstancer.IsInstance()) {
1354         fqInstancer = fqInstancer.GetParent();
1355     }
1356     SdfPath instancePath = fqInstancer.GetPath();
1357 
1358     SdfPathVector paths = { primPath, instancePath };
1359     return _GetPrimPathFromInstancerChain(paths);
1360 }
1361 
1362 static
1363 size_t
_GatherAuthoredTransformTimeSamples(UsdPrim const & prim,GfInterval interval,std::vector<double> * timeSamples)1364 _GatherAuthoredTransformTimeSamples(
1365     UsdPrim const& prim,
1366     GfInterval interval,
1367     std::vector<double>* timeSamples)
1368 {
1369     UsdPrim p = prim;
1370     while (p) {
1371         // XXX We could do some caching here
1372         if (UsdGeomXformable xf = UsdGeomXformable(p)) {
1373             std::vector<double> localTimeSamples;
1374             xf.GetTimeSamplesInInterval(interval, &localTimeSamples);
1375 
1376             // Join timesamples
1377             timeSamples->insert(
1378                 timeSamples->end(),
1379                 localTimeSamples.begin(),
1380                 localTimeSamples.end());
1381         }
1382         p = p.GetParent();
1383     }
1384 
1385     // Sort here
1386     std::sort(timeSamples->begin(), timeSamples->end());
1387     timeSamples->erase(
1388         std::unique(timeSamples->begin(),
1389             timeSamples->end()),
1390             timeSamples->end());
1391 
1392     return timeSamples->size();
1393 }
1394 
1395 /*virtual*/
1396 GfMatrix4d
GetInstancerTransform(UsdPrim const & instancerPrim,SdfPath const & instancerPath,UsdTimeCode time) const1397 UsdImagingPointInstancerAdapter::GetInstancerTransform(
1398     UsdPrim const& instancerPrim,
1399     SdfPath const& instancerPath,
1400     UsdTimeCode time) const
1401 {
1402     TRACE_FUNCTION();
1403 
1404     _InstancerDataMap::const_iterator inst = _instancerData.find(instancerPath);
1405     if (!TF_VERIFY(inst != _instancerData.end(),
1406                    "Unknown instancer %s", instancerPath.GetText())) {
1407         return GfMatrix4d(1);
1408     }
1409 
1410     SdfPath parentInstancerCachePath = inst->second.parentInstancerCachePath;
1411     if (!parentInstancerCachePath.IsEmpty()) {
1412         // If nested, double transformation should be avoided.
1413         SdfPath parentInstancerUsdPath =
1414             parentInstancerCachePath.GetAbsoluteRootOrPrimPath();
1415         UsdPrim parentInstancerUsdPrim = _GetPrim(parentInstancerUsdPath);
1416         UsdImagingPrimAdapterSharedPtr adapter =
1417             _GetPrimAdapter(parentInstancerUsdPrim);
1418 
1419         // ParentInstancer doesn't necessarily be UsdGeomPointInstancer.
1420         // lookup and delegate adapter to compute the instancer
1421         // transform.
1422         return adapter->GetRelativeInstancerTransform(
1423                 parentInstancerCachePath,
1424                 instancerPath,
1425                 time);
1426     } else {
1427         // If not nested, simply output the transform of the instancer.
1428         return GetRelativeInstancerTransform(parentInstancerCachePath,
1429                                              instancerPath,
1430                                              time);
1431     }
1432 }
1433 
1434 /*virtual*/
1435 SdfPath
GetInstancerId(UsdPrim const & usdPrim,SdfPath const & cachePath) const1436 UsdImagingPointInstancerAdapter::GetInstancerId(
1437     UsdPrim const& usdPrim,
1438     SdfPath const& cachePath) const
1439 {
1440     if (IsChildPath(cachePath)) {
1441         // If this is called on behalf of an rprim, the rprim's name will be
1442         // /path/to/instancer.name_of_proto, so just take the parent path.
1443         return cachePath.GetParentPath();
1444     } else if (_InstancerData const* instrData =
1445             TfMapLookupPtr(_instancerData, cachePath)) {
1446         // Otherwise, look up the parent in the instancer data.
1447         return instrData->parentInstancerCachePath;
1448     } else {
1449         TF_CODING_ERROR("Unexpected path <%s>", cachePath.GetText());
1450         return SdfPath::EmptyPath();
1451     }
1452 }
1453 
1454 /*virtual*/
1455 SdfPathVector
GetInstancerPrototypes(UsdPrim const & usdPrim,SdfPath const & cachePath) const1456 UsdImagingPointInstancerAdapter::GetInstancerPrototypes(
1457         UsdPrim const& usdPrim,
1458         SdfPath const& cachePath) const
1459 {
1460     HD_TRACE_FUNCTION();
1461 
1462     if (IsChildPath(cachePath)) {
1463         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1464         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1465         return proto.adapter->GetInstancerPrototypes(protoPrim, cachePath);
1466     } else {
1467         SdfPathVector prototypes;
1468         if (const _InstancerData* instancerData =
1469                 TfMapLookupPtr(_instancerData, cachePath)) {
1470             for (_ProtoPrimMap::const_iterator i =
1471                     instancerData->protoPrimMap.cbegin();
1472                     i != instancerData->protoPrimMap.cend(); ++i) {
1473                 prototypes.push_back(i->first);
1474             }
1475         }
1476         return prototypes;
1477     }
1478 }
1479 
1480 /*virtual*/
1481 size_t
SampleInstancerTransform(UsdPrim const & instancerPrim,SdfPath const & instancerPath,UsdTimeCode time,size_t maxNumSamples,float * sampleTimes,GfMatrix4d * sampleValues)1482 UsdImagingPointInstancerAdapter::SampleInstancerTransform(
1483     UsdPrim const& instancerPrim,
1484     SdfPath const& instancerPath,
1485     UsdTimeCode time,
1486     size_t maxNumSamples,
1487     float *sampleTimes,
1488     GfMatrix4d *sampleValues)
1489 {
1490     HD_TRACE_FUNCTION();
1491 
1492     if (maxNumSamples == 0) {
1493         return 0;
1494     }
1495 
1496     // This code must match how UpdateForTime() computes instancerTransform.
1497     _InstancerDataMap::iterator inst = _instancerData.find(instancerPath);
1498     if (!TF_VERIFY(inst != _instancerData.end(),
1499                    "Unknown instancer %s", instancerPath.GetText())) {
1500         return 0;
1501     }
1502     SdfPath parentInstancerCachePath = inst->second.parentInstancerCachePath;
1503     GfInterval interval = _GetCurrentTimeSamplingInterval();
1504 
1505     // Add time samples at the boudary conditions
1506     size_t numSamples = 0;
1507     std::vector<double> timeSamples;
1508     timeSamples.push_back(interval.GetMin());
1509     timeSamples.push_back(interval.GetMax());
1510 
1511     if (!parentInstancerCachePath.IsEmpty()) {
1512         // if nested, double transformation should be avoided.
1513         SdfPath parentInstancerUsdPath =
1514             parentInstancerCachePath.GetAbsoluteRootOrPrimPath();
1515         UsdPrim parentInstancerUsdPrim = _GetPrim(parentInstancerUsdPath);
1516         UsdImagingPrimAdapterSharedPtr adapter =
1517             _GetPrimAdapter(parentInstancerUsdPrim);
1518 
1519         numSamples = _GatherAuthoredTransformTimeSamples(
1520             parentInstancerUsdPrim,
1521             interval,
1522             &timeSamples);
1523 
1524         size_t numSamplesToEvaluate = std::min(maxNumSamples, numSamples);
1525         for (size_t i=0; i < numSamplesToEvaluate; ++i) {
1526             sampleTimes[i] = timeSamples[i] - time.GetValue();
1527             sampleValues[i] = adapter->GetRelativeInstancerTransform(
1528                 parentInstancerCachePath,
1529                 instancerPath,
1530                 timeSamples[i]);
1531         }
1532     } else {
1533         numSamples = _GatherAuthoredTransformTimeSamples(
1534             _GetPrim(instancerPath),
1535             interval,
1536             &timeSamples);
1537 
1538         size_t numSamplesToEvaluate = std::min(maxNumSamples, numSamples);
1539         for (size_t i=0; i < numSamplesToEvaluate; ++i) {
1540             sampleTimes[i] = timeSamples[i] - time.GetValue();
1541             sampleValues[i] = GetRelativeInstancerTransform(
1542                 parentInstancerCachePath,
1543                 instancerPath,
1544                 timeSamples[i]);
1545         }
1546     }
1547     return numSamples;
1548 }
1549 
1550 GfMatrix4d
GetTransform(UsdPrim const & prim,SdfPath const & cachePath,UsdTimeCode time,bool ignoreRootTransform) const1551 UsdImagingPointInstancerAdapter::GetTransform(UsdPrim const& prim,
1552                                               SdfPath const& cachePath,
1553                                               UsdTimeCode time,
1554                                               bool ignoreRootTransform) const
1555 {
1556     GfMatrix4d output(1.0);
1557 
1558     if (!IsChildPath(cachePath)) {
1559         return BaseAdapter::GetTransform(prim,
1560                                         cachePath,
1561                                         time,
1562                                         ignoreRootTransform);
1563     }
1564 
1565     // cachePath : /path/instancerPath.proto_*
1566     // instancerPath : /path/instancerPath
1567     SdfPath instancerPath = cachePath.GetParentPath();
1568     _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
1569     if (!TF_VERIFY(proto.adapter, "%s", cachePath.GetText())) {
1570         return output;
1571     }
1572     if (!TF_VERIFY(proto.paths.size() > 0, "%s", cachePath.GetText())) {
1573         return output;
1574     }
1575 
1576     UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1577 
1578     output = proto.adapter->GetTransform(
1579         protoPrim,
1580         cachePath,
1581         time,
1582         ignoreRootTransform);
1583 
1584     // If the prototype we're processing is a prototype, _GetProtoUsdPrim
1585     // will return us the instance for attribute lookup; but the
1586     // instance transform for that instance is already accounted for in
1587     // _CorrectTransform.  Prototypes don't have any transform aside from
1588     // the root transform, so override the result of UpdateForTime.
1589     if (protoPrim.IsInstance()) {
1590         output = GetRootTransform();
1591     }
1592 
1593     // Correct the transform for various shenanigans: NI transforms,
1594     // delegate root transform, proto root transform.
1595     return _CorrectTransform(prim, _GetPrim(proto.protoRootPath), cachePath,
1596         proto.paths, output, time);
1597 }
1598 
1599 size_t
SampleTransform(UsdPrim const & usdPrim,SdfPath const & cachePath,UsdTimeCode time,size_t maxNumSamples,float * sampleTimes,GfMatrix4d * sampleValues)1600 UsdImagingPointInstancerAdapter::SampleTransform(
1601     UsdPrim const& usdPrim,
1602     SdfPath const& cachePath,
1603     UsdTimeCode time,
1604     size_t maxNumSamples,
1605     float *sampleTimes,
1606     GfMatrix4d *sampleValues)
1607 {
1608     if (maxNumSamples == 0) {
1609         return 0;
1610     }
1611 
1612     // Pull a single sample from the value-cached transform.
1613     // This makes the (hopefully safe) assumption that we do not need
1614     // motion blur on the underlying prototypes.
1615     sampleTimes[0] = 0.0;
1616     sampleValues[0] = GetTransform(usdPrim, cachePath, time);
1617     return 1;
1618 }
1619 
1620 size_t
SamplePrimvar(UsdPrim const & usdPrim,SdfPath const & cachePath,TfToken const & key,UsdTimeCode time,size_t maxNumSamples,float * sampleTimes,VtValue * sampleValues,VtIntArray * sampleIndices)1621 UsdImagingPointInstancerAdapter::SamplePrimvar(
1622     UsdPrim const& usdPrim,
1623     SdfPath const& cachePath,
1624     TfToken const& key,
1625     UsdTimeCode time,
1626     size_t maxNumSamples,
1627     float *sampleTimes,
1628     VtValue *sampleValues,
1629     VtIntArray *sampleIndices)
1630 {
1631     HD_TRACE_FUNCTION();
1632 
1633     if (maxNumSamples == 0) {
1634         return 0;
1635     }
1636 
1637     if (IsChildPath(cachePath)) {
1638         // Delegate to prototype adapter and USD prim.
1639         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(),
1640                                                    cachePath);
1641         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1642         return proto.adapter->SamplePrimvar(
1643             protoPrim, cachePath, key, time,
1644             maxNumSamples, sampleTimes, sampleValues, sampleIndices);
1645     } else {
1646         // Map Hydra-PI transform keys to their USD equivalents.
1647         TfToken usdKey = key;
1648         if (key == _tokens->translate) {
1649             usdKey = UsdGeomTokens->positions;
1650         } else if (key == _tokens->scale) {
1651             usdKey = UsdGeomTokens->scales;
1652         } else if (key == _tokens->rotate) {
1653             usdKey = UsdGeomTokens->orientations;
1654         }
1655         return UsdImagingPrimAdapter::SamplePrimvar(
1656             usdPrim, cachePath, usdKey, time,
1657             maxNumSamples, sampleTimes, sampleValues, sampleIndices);
1658     }
1659 }
1660 
1661 /*virtual*/
1662 bool
GetVisible(UsdPrim const & prim,SdfPath const & cachePath,UsdTimeCode time) const1663 UsdImagingPointInstancerAdapter::GetVisible(UsdPrim const& prim,
1664                                             SdfPath const& cachePath,
1665                                             UsdTimeCode time) const
1666 {
1667     // Apply the instancer visibility at the current time to the
1668     // instance. Notice that the instance will also pickup the instancer
1669     // visibility at the time offset.
1670 
1671     if (IsChildPath(cachePath)) {
1672         bool vis = false;
1673 
1674         // cachePath : /path/instancerPath.proto_*
1675         // instancerPath : /path/instancerPath
1676         SdfPath instancerPath = cachePath.GetParentPath();
1677         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
1678         if (!TF_VERIFY(proto.adapter, "%s", cachePath.GetText())) {
1679             return vis;
1680         }
1681         if (!TF_VERIFY(proto.paths.size() > 0, "%s", cachePath.GetText())) {
1682             return vis;
1683         }
1684 
1685         bool protoHasFixedVis = !(proto.variabilityBits
1686                 & HdChangeTracker::DirtyVisibility);
1687         _InstancerDataMap::const_iterator it =
1688             _instancerData.find(instancerPath);
1689         if (TF_VERIFY(it != _instancerData.end())) {
1690             _InstancerData const& instrData = it->second;
1691             _UpdateInstancerVisibility(instancerPath, instrData, time);
1692             vis = instrData.visible;
1693         }
1694 
1695         if (protoHasFixedVis) {
1696             // The instancer is visible and the proto prim has fixed
1697             // visibility (it does not vary over time), we can use the
1698             // pre-cached visibility.
1699             vis = vis && proto.visible;
1700         } else if (vis) {
1701             // The instancer is visible and the prototype has varying
1702             // visibility, we must compute visibility from the proto
1703             // prim to the model instance root.
1704             for (size_t i = 0; i < proto.paths.size()-1; ++i) {
1705                 _ComputeProtoVisibility(
1706                     _GetPrim(proto.paths[i+1]).GetPrototype(),
1707                     _GetPrim(proto.paths[i+0]),
1708                     time, &vis);
1709             }
1710             _ComputeProtoVisibility(
1711                 _GetPrim(proto.protoRootPath),
1712                 _GetPrim(proto.paths.back()),
1713                 time, &vis);
1714         }
1715 
1716         return vis;
1717     }
1718 
1719     return BaseAdapter::GetVisible(prim, cachePath, time);
1720 }
1721 
1722 /*virtual*/
1723 TfToken
GetPurpose(UsdPrim const & usdPrim,SdfPath const & cachePath,TfToken const & instanceInheritablePurpose) const1724 UsdImagingPointInstancerAdapter::GetPurpose(
1725     UsdPrim const& usdPrim,
1726     SdfPath const& cachePath,
1727     TfToken const& instanceInheritablePurpose) const
1728 {
1729     if (IsChildPath(cachePath)) {
1730         // Delegate to prototype adapter and USD prim
1731         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1732         UsdPrim protoUsdPrim = _GetProtoUsdPrim(proto);
1733 
1734         UsdPrim instanceProxyPrim = _GetPrim(_GetPrimPathFromInstancerChain(
1735                 proto.paths));
1736 
1737         TfToken const& inheritablePurpose =
1738                     GetInheritablePurpose(instanceProxyPrim);
1739 
1740         return proto.adapter->GetPurpose(protoUsdPrim, cachePath,
1741                                          inheritablePurpose);
1742     }
1743     return BaseAdapter::GetPurpose(usdPrim, cachePath, TfToken());
1744 
1745 }
1746 
1747 /*virtual*/
1748 PxOsdSubdivTags
GetSubdivTags(UsdPrim const & usdPrim,SdfPath const & cachePath,UsdTimeCode time) const1749 UsdImagingPointInstancerAdapter::GetSubdivTags(UsdPrim const& usdPrim,
1750                                                SdfPath const& cachePath,
1751                                                UsdTimeCode time) const
1752 {
1753     if (IsChildPath(cachePath)) {
1754         // Delegate to prototype adapter and USD prim.
1755         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1756         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1757         return proto.adapter->GetSubdivTags(protoPrim, cachePath, time);
1758     }
1759     return BaseAdapter::GetSubdivTags(usdPrim, cachePath, time);
1760 }
1761 
1762 /*virtual*/
1763 VtValue
GetTopology(UsdPrim const & usdPrim,SdfPath const & cachePath,UsdTimeCode time) const1764 UsdImagingPointInstancerAdapter::GetTopology(UsdPrim const& usdPrim,
1765                                              SdfPath const& cachePath,
1766                                              UsdTimeCode time) const
1767 {
1768     if (IsChildPath(cachePath)) {
1769         // Delegate to prototype adapter and USD prim.
1770         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1771         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1772         return proto.adapter->GetTopology(protoPrim, cachePath, time);
1773     }
1774     return BaseAdapter::GetTopology(usdPrim, cachePath, time);
1775 }
1776 
1777 /*virtual*/
1778 HdCullStyle
GetCullStyle(UsdPrim const & usdPrim,SdfPath const & cachePath,UsdTimeCode time) const1779 UsdImagingPointInstancerAdapter::GetCullStyle(UsdPrim const& usdPrim,
1780                                              SdfPath const& cachePath,
1781                                              UsdTimeCode time) const
1782 {
1783     if (IsChildPath(cachePath)) {
1784         // Delegate to prototype adapter and USD prim.
1785         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1786         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1787         return proto.adapter->GetCullStyle(protoPrim, cachePath, time);
1788     }
1789     return BaseAdapter::GetCullStyle(usdPrim, cachePath, time);
1790 }
1791 
1792 /*virtual*/
1793 GfRange3d
GetExtent(UsdPrim const & usdPrim,SdfPath const & cachePath,UsdTimeCode time) const1794 UsdImagingPointInstancerAdapter::GetExtent(UsdPrim const& usdPrim,
1795                                            SdfPath const& cachePath,
1796                                            UsdTimeCode time) const
1797 {
1798     if (IsChildPath(cachePath)) {
1799         // Delegate to prototype adapter and USD prim.
1800         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1801         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1802         return proto.adapter->GetExtent(protoPrim, cachePath, time);
1803     }
1804     return BaseAdapter::GetExtent(usdPrim, cachePath, time);
1805 }
1806 
1807 
1808 /*virtual*/
1809 bool
GetDoubleSided(UsdPrim const & usdPrim,SdfPath const & cachePath,UsdTimeCode time) const1810 UsdImagingPointInstancerAdapter::GetDoubleSided(UsdPrim const& usdPrim,
1811                                                 SdfPath const& cachePath,
1812                                                 UsdTimeCode time) const
1813 {
1814     if (IsChildPath(cachePath)) {
1815         // Delegate to prototype adapter and USD prim.
1816         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1817         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1818         return proto.adapter->GetDoubleSided(protoPrim, cachePath, time);
1819     }
1820     return BaseAdapter::GetDoubleSided(usdPrim, cachePath, time);
1821 }
1822 
1823 /*virtual*/
1824 SdfPath
GetMaterialId(UsdPrim const & usdPrim,SdfPath const & cachePath,UsdTimeCode time) const1825 UsdImagingPointInstancerAdapter::GetMaterialId(UsdPrim const& usdPrim,
1826                       SdfPath const& cachePath,
1827                       UsdTimeCode time) const
1828 {
1829     if (IsChildPath(cachePath)) {
1830         // Delegate to prototype adapter and USD prim.
1831         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1832         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1833         SdfPath materialId =
1834             proto.adapter->GetMaterialId(protoPrim, cachePath, time);
1835         if (!materialId.IsEmpty()) {
1836             return materialId;
1837         }
1838         // If the child prim doesn't have a material ID, see if there's
1839         // an instancer material path...
1840         UsdPrim instanceProxyPrim = _GetPrim(_GetPrimPathFromInstancerChain(
1841                     proto.paths));
1842         return GetMaterialUsdPath(instanceProxyPrim);
1843     }
1844     return BaseAdapter::GetMaterialId(usdPrim, cachePath, time);
1845 }
1846 
1847 /*virtual*/
1848 VtValue
Get(UsdPrim const & usdPrim,SdfPath const & cachePath,TfToken const & key,UsdTimeCode time,VtIntArray * outIndices) const1849 UsdImagingPointInstancerAdapter::Get(UsdPrim const& usdPrim,
1850                                      SdfPath const& cachePath,
1851                                      TfToken const& key,
1852                                      UsdTimeCode time,
1853                                      VtIntArray *outIndices) const
1854 {
1855     TRACE_FUNCTION();
1856 
1857     if (IsChildPath(cachePath)) {
1858         // Delegate to prototype adapter and USD prim.
1859         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), cachePath);
1860         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
1861         return proto.adapter->Get(protoPrim, cachePath, key, time, outIndices);
1862 
1863     } else  if (_InstancerData const* instrData =
1864                 TfMapLookupPtr(_instancerData, cachePath)) {
1865         TF_UNUSED(instrData);
1866 
1867         if (key == _tokens->translate) {
1868             UsdGeomPointInstancer instancer(usdPrim);
1869             VtVec3fArray positions;
1870             if (instancer.GetPositionsAttr().Get(&positions, time)) {
1871                 return VtValue(positions);
1872             }
1873 
1874         } else if (key == _tokens->rotate) {
1875             UsdGeomPointInstancer instancer(usdPrim);
1876             VtQuathArray orientations;
1877             if (instancer.GetOrientationsAttr().Get(&orientations, time)) {
1878                 return VtValue(orientations);
1879             }
1880 
1881         } else if (key == _tokens->scale) {
1882             UsdGeomPointInstancer instancer(usdPrim);
1883             VtVec3fArray scales;
1884             if (instancer.GetScalesAttr().Get(&scales, time)) {
1885                 return VtValue(scales);
1886             }
1887 
1888         } else {
1889             UsdGeomPrimvarsAPI primvars(usdPrim);
1890             if (UsdGeomPrimvar pv = primvars.GetPrimvar(key)) {
1891                 VtValue value;
1892                 if (outIndices) {
1893                     if (pv && pv.Get(&value, time)) {
1894                         pv.GetIndices(outIndices, time);
1895                         return value;
1896                     }
1897                 } else if (pv && pv.ComputeFlattened(&value, time)) {
1898                     return value;
1899                 }
1900             }
1901         }
1902     }
1903 
1904     return BaseAdapter::Get(usdPrim, cachePath, key, time, outIndices);
1905 }
1906 
1907 /*virtual*/
1908 HdExtComputationInputDescriptorVector
GetExtComputationInputs(UsdPrim const & usdPrim,SdfPath const & cachePath,const UsdImagingInstancerContext *) const1909 UsdImagingPointInstancerAdapter::GetExtComputationInputs(
1910     UsdPrim const& usdPrim,
1911     SdfPath const& cachePath,
1912     const UsdImagingInstancerContext* /*unused*/) const
1913 {
1914     UsdImagingInstancerContext ctx;
1915     _ProtoPrim const *proto;
1916     if (_GetProtoPrimForChild(usdPrim, cachePath, &proto, &ctx)) {
1917 
1918         return proto->adapter->GetExtComputationInputs(
1919                 _GetProtoUsdPrim(*proto), cachePath, &ctx);
1920     }
1921     return BaseAdapter::GetExtComputationInputs(usdPrim, cachePath, nullptr);
1922 }
1923 
1924 /*virtual*/
1925 HdExtComputationOutputDescriptorVector
GetExtComputationOutputs(UsdPrim const & usdPrim,SdfPath const & cachePath,const UsdImagingInstancerContext *) const1926 UsdImagingPointInstancerAdapter::GetExtComputationOutputs(
1927     UsdPrim const& usdPrim,
1928     SdfPath const& cachePath,
1929     const UsdImagingInstancerContext* /*unused*/) const
1930 
1931 {
1932     UsdImagingInstancerContext ctx;
1933     _ProtoPrim const *proto;
1934     if (_GetProtoPrimForChild(usdPrim, cachePath, &proto, &ctx)) {
1935 
1936         return proto->adapter->GetExtComputationOutputs(
1937                 _GetProtoUsdPrim(*proto), cachePath, &ctx);
1938     }
1939     return BaseAdapter::GetExtComputationOutputs(usdPrim, cachePath, nullptr);
1940 }
1941 
1942 /*virtual*/
1943 HdExtComputationPrimvarDescriptorVector
GetExtComputationPrimvars(UsdPrim const & usdPrim,SdfPath const & cachePath,HdInterpolation interpolation,const UsdImagingInstancerContext *) const1944 UsdImagingPointInstancerAdapter::GetExtComputationPrimvars(
1945     UsdPrim const& usdPrim,
1946     SdfPath const& cachePath,
1947     HdInterpolation interpolation,
1948     const UsdImagingInstancerContext* /*unused*/) const
1949 {
1950     UsdImagingInstancerContext ctx;
1951     _ProtoPrim const *proto;
1952     if (_GetProtoPrimForChild(usdPrim, cachePath, &proto, &ctx)) {
1953 
1954         return proto->adapter->GetExtComputationPrimvars(
1955                 _GetProtoUsdPrim(*proto), cachePath, interpolation, &ctx);
1956     }
1957     return BaseAdapter::GetExtComputationPrimvars(usdPrim, cachePath,
1958             interpolation, nullptr);
1959 }
1960 
1961 /*virtual*/
1962 VtValue
GetExtComputationInput(UsdPrim const & usdPrim,SdfPath const & cachePath,TfToken const & name,UsdTimeCode time,const UsdImagingInstancerContext *) const1963 UsdImagingPointInstancerAdapter::GetExtComputationInput(
1964     UsdPrim const& usdPrim,
1965     SdfPath const& cachePath,
1966     TfToken const& name,
1967     UsdTimeCode time,
1968     const UsdImagingInstancerContext* /*unused*/) const
1969 {
1970     UsdImagingInstancerContext ctx;
1971     _ProtoPrim const *proto;
1972     if (_GetProtoPrimForChild(usdPrim, cachePath, &proto, &ctx)) {
1973 
1974         return proto->adapter->GetExtComputationInput(
1975                 _GetProtoUsdPrim(*proto), cachePath, name, time, &ctx);
1976     }
1977     return BaseAdapter::GetExtComputationInput(usdPrim, cachePath, name, time,
1978                 nullptr);
1979 }
1980 
1981 /*virtual*/
1982 VtValue
GetInstanceIndices(UsdPrim const & instancerPrim,SdfPath const & instancerCachePath,SdfPath const & prototypeCachePath,UsdTimeCode time) const1983 UsdImagingPointInstancerAdapter::GetInstanceIndices(
1984     UsdPrim const& instancerPrim,
1985     SdfPath const& instancerCachePath,
1986     SdfPath const& prototypeCachePath,
1987     UsdTimeCode time) const
1988 {
1989     if (IsChildPath(instancerCachePath)) {
1990         UsdImagingInstancerContext ctx;
1991         _ProtoPrim const *proto;
1992         if (_GetProtoPrimForChild(
1993                 instancerPrim, instancerCachePath, &proto, &ctx)) {
1994              return proto->adapter->GetInstanceIndices(
1995                     _GetProtoUsdPrim(*proto), instancerCachePath,
1996                             prototypeCachePath, time);
1997         }
1998     }
1999 
2000     if (_InstancerData const* instrData =
2001                 TfMapLookupPtr(_instancerData, instancerCachePath)) {
2002 
2003         // need to find the prototypeRootPath for this prototypeCachePath
2004         const auto protoPrimIt =
2005                 instrData->protoPrimMap.find(prototypeCachePath);
2006         if (protoPrimIt != instrData->protoPrimMap.end()) {
2007             const SdfPath & prototypeRootPath =
2008                     protoPrimIt->second.protoRootPath;
2009 
2010             // find index of prototypeRootPath within expected array-of-arrays
2011             const auto pathIndexIt =
2012                     instrData->prototypePathIndices.find(prototypeRootPath);
2013             if (pathIndexIt != instrData->prototypePathIndices.end()) {
2014                 size_t pathIndex = (*pathIndexIt).second;
2015 
2016                 UsdPrim instancerPrim = _GetPrim(
2017                         instancerCachePath.GetPrimPath());
2018                 VtArray<VtIntArray> indices = GetPerPrototypeIndices(
2019                         instancerPrim, time);
2020 
2021                 if (pathIndex >= indices.size()) {
2022                     TF_WARN("ProtoIndex %lu out of bounds "
2023                             "(prototypes size = %lu) for (%s, %s)",
2024                                     pathIndex,
2025                                     indices.size(),
2026                                     instancerCachePath.GetText(),
2027                                     prototypeCachePath.GetText());
2028 
2029                     return VtValue();
2030                 }
2031                 return VtValue(indices[pathIndex]);
2032             }
2033         }
2034 
2035         TF_WARN("No matching ProtoRootPath found for (%s, %s)",
2036                 instancerCachePath.GetText(), prototypeCachePath.GetText());
2037     }
2038 
2039     return VtValue();
2040 }
2041 
2042 /*virtual*/
2043 std::string
GetExtComputationKernel(UsdPrim const & usdPrim,SdfPath const & cachePath,const UsdImagingInstancerContext *) const2044 UsdImagingPointInstancerAdapter::GetExtComputationKernel(
2045     UsdPrim const& usdPrim,
2046     SdfPath const& cachePath,
2047     const UsdImagingInstancerContext* /*unused*/) const
2048 {
2049     UsdImagingInstancerContext ctx;
2050     _ProtoPrim const *proto;
2051     if (_GetProtoPrimForChild(usdPrim, cachePath, &proto, &ctx)) {
2052 
2053         return proto->adapter->GetExtComputationKernel(
2054                 _GetProtoUsdPrim(*proto), cachePath, &ctx);
2055     }
2056     return BaseAdapter::GetExtComputationKernel(usdPrim, cachePath, nullptr);
2057 }
2058 
2059 
2060 /*virtual*/
2061 bool
PopulateSelection(HdSelection::HighlightMode const & highlightMode,SdfPath const & cachePath,UsdPrim const & usdPrim,int const hydraInstanceIndex,VtIntArray const & parentInstanceIndices,HdSelectionSharedPtr const & result) const2062 UsdImagingPointInstancerAdapter::PopulateSelection(
2063     HdSelection::HighlightMode const& highlightMode,
2064     SdfPath const &cachePath,
2065     UsdPrim const &usdPrim,
2066     int const hydraInstanceIndex,
2067     VtIntArray const &parentInstanceIndices,
2068     HdSelectionSharedPtr const &result) const
2069 {
2070     HD_TRACE_FUNCTION();
2071 
2072     if (IsChildPath(cachePath)) {
2073         SdfPath instancerPath = cachePath.GetParentPath();
2074         _ProtoPrim const& proto = _GetProtoPrim(instancerPath, cachePath);
2075         if (!proto.adapter) {
2076             return false;
2077         }
2078 
2079         TF_DEBUG(USDIMAGING_SELECTION).Msg(
2080             "PopulateSelection: proto = %s pi = %s\n",
2081             cachePath.GetText(), instancerPath.GetText());
2082 
2083         // Make sure one of the prototype paths is a suffix of "usdPrim".
2084         // "paths" has the location of the populated proto; for native
2085         // instancing, it will have N-1 paths to instances and
2086         // 1 path to the prototype.
2087         // If there's no native instancing, paths will have size 1.
2088         // If "usdPrim" is a parent of any of these paths, that counts
2089         // as a selection of this prototype.  e.g.
2090         // - /World/Instancer/protos (-> /__Prototype_1)
2091         // - /__Prototype_1/trees/tree_1 (a gprim)
2092         bool foundPrefix = false;
2093         SdfPath usdPath = usdPrim.GetPath();
2094         for (auto const& path : proto.paths) {
2095             if (path.HasPrefix(usdPath)) {
2096                 foundPrefix = true;
2097                 break;
2098             }
2099         }
2100         if (!foundPrefix) {
2101             return false;
2102         }
2103 
2104         // Compose instance indices, if we don't have an explicit index.
2105         // UsdImaging only adds instance indices in the case of usd instances,
2106         // so if we don't have an explicit index we want to add all PI
2107         // instances of the given prototype.
2108         VtIntArray instanceIndices;
2109         if (hydraInstanceIndex == -1 && parentInstanceIndices.size() != 0) {
2110             _InstancerData const* instrData =
2111                 TfMapLookupPtr(_instancerData, instancerPath);
2112             if (instrData == nullptr) {
2113                 return false;
2114             }
2115 
2116             // XXX: Using _GetTimeWithOffset here is a bit of a hack?
2117             VtValue indicesValue = GetInstanceIndices(_GetPrim(instancerPath),
2118                     instancerPath, cachePath, _GetTimeWithOffset(0.0));
2119             if (!indicesValue.IsHolding<VtIntArray>()) {
2120                 return false;
2121             }
2122 
2123             VtIntArray const & indices =
2124                     indicesValue.UncheckedGet<VtIntArray>();
2125 
2126             for (const int pi : parentInstanceIndices) {
2127                 for (size_t i = 0; i < indices.size(); ++i) {
2128                     instanceIndices.push_back(pi * indices.size() + i);
2129                 }
2130             }
2131         }
2132 
2133         // We want the path comparison in PopulateSelection to always succeed
2134         // (since we've verified the path above), so take the prim at
2135         // cachePath.GetPrimPath, since that's guaranteed to exist and be a
2136         // prefix...
2137         UsdPrim prefixPrim = _GetPrim(cachePath.GetAbsoluteRootOrPrimPath());
2138         return proto.adapter->PopulateSelection(
2139             highlightMode, cachePath, prefixPrim,
2140             hydraInstanceIndex, instanceIndices, result);
2141     } else {
2142         _InstancerData const* instrData =
2143             TfMapLookupPtr(_instancerData, cachePath);
2144         if (instrData == nullptr) {
2145             return false;
2146         }
2147 
2148         TF_DEBUG(USDIMAGING_SELECTION).Msg(
2149             "PopulateSelection: pi = %s\n", cachePath.GetText());
2150 
2151         // For non-gprim selections, selectionPath might be pointing to a
2152         // point instancer, or it might be pointing to a usd native instance
2153         // (which is a dependency of the PI, used for calculating prototype
2154         // transform).  If there's native instancing involved, we need to
2155         // break the selection path down to a path context, and zipper compare
2156         // it against the proto paths of each proto prim.  This is a very
2157         // similar implementation to the one in instanceAdapter.cpp...
2158         std::deque<SdfPath> selectionPathVec;
2159         UsdPrim p = usdPrim;
2160         while (p.IsInstanceProxy()) {
2161             selectionPathVec.push_front(p.GetPrimInPrototype().GetPath());
2162             do {
2163                 p = p.GetParent();
2164             } while (!p.IsInstance());
2165         }
2166         selectionPathVec.push_front(p.GetPath());
2167 
2168         // If "cachePath" and "usdPrim" are equal, and hydraInstanceIndex
2169         // has a value, we're responding to "AddSelected(/World/PI, N)";
2170         // we can treat it as an instance index for this PI, rather than
2171         // treating it as an absolute instance index for an rprim.
2172         // (/World/PI, -1) still corresponds to select-all-instances.
2173         if (usdPrim.GetPath() == cachePath.GetAbsoluteRootOrPrimPath() &&
2174             hydraInstanceIndex != -1) {
2175             // "N" here refers to the instance index in the protoIndices array,
2176             // which may be different than the actual hydra index, so we need
2177             // to find the correct prototype/instance pair.
2178 
2179             bool added = false;
2180             for (auto const& pair : instrData->protoPrimMap) {
2181                 VtValue indicesValue =  GetInstanceIndices(_GetPrim(cachePath),
2182                         cachePath, pair.first, _GetTimeWithOffset(0.0));
2183 
2184                 if (!indicesValue.IsHolding<VtIntArray>()) {
2185                     continue;
2186                 }
2187                 VtIntArray const & indices =
2188                         indicesValue.UncheckedGet<VtIntArray>();
2189 
2190                 int foundIndex = -1;
2191                 for (size_t i = 0; i < indices.size(); ++i) {
2192                     if (indices[i] == hydraInstanceIndex) {
2193                         foundIndex = int(i);
2194                         break;
2195                     }
2196                 }
2197                 if (foundIndex == -1) {
2198                     continue;
2199                 }
2200                 VtIntArray instanceIndices;
2201                 if (parentInstanceIndices.size() > 0) {
2202                     for (const int pi : parentInstanceIndices) {
2203                         instanceIndices.push_back(pi * indices.size() +
2204                             foundIndex);
2205                     }
2206                 } else {
2207                     instanceIndices.push_back(foundIndex);
2208                 }
2209                 UsdPrim selectionPrim =
2210                     _GetPrim(pair.first.GetAbsoluteRootOrPrimPath());
2211 
2212                 added |= pair.second.adapter->PopulateSelection(
2213                     highlightMode, pair.first, selectionPrim,
2214                     -1, instanceIndices, result);
2215             }
2216             return added;
2217         }
2218 
2219         bool added = false;
2220         for (auto const& pair : instrData->protoPrimMap) {
2221 
2222             // Zipper compare the instance paths and the selection paths.
2223             size_t instanceCount, selectionCount;
2224             for (instanceCount = 0, selectionCount = 0;
2225                  instanceCount < pair.second.paths.size() &&
2226                  selectionCount < selectionPathVec.size();
2227                  ++instanceCount) {
2228                 // pair.second.paths is innermost-first, and selectionPathVec
2229                 // outermost-first, so we need to flip the paths index.
2230                 size_t instanceIdx =
2231                     pair.second.paths.size() - instanceCount - 1;
2232                 if (pair.second.paths[instanceIdx].HasPrefix(
2233                         selectionPathVec[selectionCount])) {
2234                     ++selectionCount;
2235                 } else if (selectionPathVec[selectionCount].HasPrefix(
2236                             pair.second.paths[instanceIdx])) {
2237                     // If the selection path is a suffix of the prototype path,
2238                     // leave the rest of the selection path to be used as
2239                     // a selection prim for the child adapter.
2240                     ++instanceCount;
2241                     break;
2242                 } else if (selectionCount != 0) {
2243                     // The paths don't match; setting "selectionCount = 0"
2244                     // is telling the below code "no match", which is an
2245                     // ok proxy for "partial match, partial mismatch".
2246                     selectionCount = 0;
2247                     break;
2248                 }
2249             }
2250 
2251             UsdPrim selectionPrim;
2252             if (selectionCount == selectionPathVec.size()) {
2253                 // If we've accounted for the whole selection path, fully
2254                 // populate this prototype.
2255                 selectionPrim =
2256                     _GetPrim(pair.first.GetAbsoluteRootOrPrimPath());
2257             }
2258             else if (selectionCount != 0 &&
2259                      instanceCount == pair.second.paths.size()) {
2260                 // If the selection path goes past the end of the instance path,
2261                 // compose the remainder of the selection path into a
2262                 // (possibly instance proxy) usd prim and use that as the
2263                 // selection prim.
2264                 SdfPathVector residualPathVec(
2265                     selectionPathVec.rbegin(),
2266                     selectionPathVec.rend() - selectionCount);
2267                 SdfPath residualPath =
2268                     _GetPrimPathFromInstancerChain(residualPathVec);
2269                 selectionPrim = _GetPrim(residualPath);
2270             } else {
2271                 continue;
2272             }
2273 
2274             // Compose instance indices.
2275             VtIntArray instanceIndices;
2276 
2277             VtValue indicesValue =
2278                     GetInstanceIndices(_GetPrim(cachePath), cachePath,
2279                             pair.first, _GetTimeWithOffset(0.0));
2280 
2281             if (!indicesValue.IsHolding<VtIntArray>()) {
2282                 continue;
2283             }
2284             VtIntArray const & indices =
2285                     indicesValue.UncheckedGet<VtIntArray>();
2286 
2287 
2288             if (parentInstanceIndices.size() > 0) {
2289                 for (const int pi : parentInstanceIndices) {
2290                     for (size_t i = 0; i < indices.size(); ++i) {
2291                         instanceIndices.push_back(pi * indices.size() + i);
2292                     }
2293                 }
2294             } else {
2295                 for (size_t i = 0; i < indices.size(); ++i) {
2296                     instanceIndices.push_back(i);
2297                 }
2298             }
2299 
2300             added |= pair.second.adapter->PopulateSelection(
2301                 highlightMode, pair.first, selectionPrim,
2302                 hydraInstanceIndex, instanceIndices, result);
2303         }
2304 
2305         return added;
2306     }
2307 
2308     return false;
2309 }
2310 
2311 /*virtual*/
2312 HdVolumeFieldDescriptorVector
GetVolumeFieldDescriptors(UsdPrim const & usdPrim,SdfPath const & id,UsdTimeCode time) const2313 UsdImagingPointInstancerAdapter::GetVolumeFieldDescriptors(
2314     UsdPrim const& usdPrim,
2315     SdfPath const &id,
2316     UsdTimeCode time) const
2317 {
2318     if (IsChildPath(id)) {
2319         // Delegate to prototype adapter and USD prim.
2320         _ProtoPrim const& proto = _GetProtoPrim(usdPrim.GetPath(), id);
2321         UsdPrim protoPrim = _GetProtoUsdPrim(proto);
2322         return proto.adapter->GetVolumeFieldDescriptors(
2323             protoPrim, id, time);
2324     } else {
2325         return UsdImagingPrimAdapter::GetVolumeFieldDescriptors(
2326             usdPrim, id, time);
2327     }
2328 }
2329 
2330 /*virtual*/
2331 void
_RemovePrim(SdfPath const & cachePath,UsdImagingIndexProxy * index)2332 UsdImagingPointInstancerAdapter::_RemovePrim(SdfPath const& cachePath,
2333                                              UsdImagingIndexProxy* index)
2334 {
2335     TF_CODING_ERROR("Should use overidden ProcessPrimResync/ProcessPrimRemoval");
2336 }
2337 
2338 /*virtual*/
2339 GfMatrix4d
GetRelativeInstancerTransform(SdfPath const & parentInstancerCachePath,SdfPath const & cachePath,UsdTimeCode time) const2340 UsdImagingPointInstancerAdapter::GetRelativeInstancerTransform(
2341     SdfPath const &parentInstancerCachePath,
2342     SdfPath const &cachePath,
2343     UsdTimeCode time) const
2344 {
2345     GfMatrix4d transformRoot(1); // target to world.
2346 
2347     // XXX: isProtoRoot detection shouldn't be needed since UsdGeomPointInstaner
2348     // doesn't have a convention of ignoring protoRoot transform unlike the ones
2349     // in PxUsdGeomGL.
2350     // 2 test cases in testUsdImagingGLPointInstancer
2351     //   pi_pi_usda, time=1 and 2
2352     // are wrongly configured, and we need to be updated together when fixing.
2353     //
2354     bool isProtoRoot = false;
2355     UsdPrim prim = _GetPrim(cachePath.GetPrimPath());
2356     bool inPrototype = prim.IsInPrototype();
2357 
2358     if (!parentInstancerCachePath.IsEmpty()) {
2359         // this instancer has a parent instancer. see if this instancer
2360         // is a protoRoot or not.
2361         _ProtoPrim const& proto
2362             = _GetProtoPrim(parentInstancerCachePath, cachePath);
2363         if (proto.protoRootPath == cachePath) {
2364             // this instancer is a proto root.
2365             isProtoRoot = true;
2366         } else {
2367             // this means instancer(cachePath) is a member of a
2368             // prototype of the parent instacer, but not a proto root.
2369             //
2370             // we need to extract relative transform to root.
2371             //
2372             if (inPrototype) {
2373                 // if the instancer is in prototype, set the target
2374                 // root transform to world, since the parent
2375                 // instancer (if the parent is also in prototype,
2376                 // native instancer which instances that parent)
2377                 // has delegate's root transform.
2378                 transformRoot = GetRootTransform();
2379             } else {
2380                 // set the target root to proto root.
2381                 transformRoot
2382                     = BaseAdapter::GetTransform(
2383                         _GetPrim(proto.protoRootPath),
2384                         proto.protoRootPath,
2385                         time);
2386             }
2387         }
2388     }
2389 
2390     if (isProtoRoot) {
2391         // instancer is a protoroot of parent instancer.
2392         // ignore instancer transform.
2393         return GfMatrix4d(1);
2394     } else {
2395         // set protoRoot-to-instancer relative transform
2396 
2397         // note that GetTransform() includes GetRootTransform().
2398         //   GetTransform(prim) : InstancerXfm * RootTransform
2399         //
2400         // 1. If the instancer doesn't have a parent,
2401         //    transformRoot is identity.
2402         //
2403         //    val = InstancerXfm * RootTransform * 1^-1
2404         //        = InstancerXfm * RootTransform
2405         //
2406         // 2. If the instancer has a parent and in prototype,
2407         //    transformRoot is RootTransform.
2408         //
2409         //    val = InstancerXfm * RootTransform * (RootTransform)^-1
2410         //        = InstancerXfm
2411         //
2412         // 3. If the instaner has a parent but not in prototype,
2413         //    transformRoot is (ProtoRoot * RootTransform).
2414         //
2415         //    val = InstancerXfm * RootTransform * (ProtoRoot * RootTransform)^-1
2416         //        = InstancerXfm * (ProtoRoot)^-1
2417         //
2418         // in case 2 and 3, RootTransform will be applied on the parent
2419         // instancer.
2420         //
2421         return BaseAdapter::GetTransform(prim, prim.GetPath(), time) *
2422             transformRoot.GetInverse();
2423     }
2424 }
2425 
2426 PXR_NAMESPACE_CLOSE_SCOPE
2427 
2428