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