gprim.h
1 //
2 // Copyright 2019 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 #ifndef EXT_RMANPKG_24_0_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
25 #define EXT_RMANPKG_24_0_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/imaging/hd/enums.h"
29 #include "pxr/usd/sdf/types.h"
30 #include "pxr/base/gf/matrix4d.h"
31 
32 #include "hdPrman/gprimbase.h"
33 #include "hdPrman/renderParam.h"
34 #include "hdPrman/instancer.h"
35 #include "hdPrman/material.h"
36 #include "hdPrman/rixStrings.h"
37 
38 #include "Riley.h"
39 #include "RixShadingUtils.h"
40 #include "RixPredefinedStrings.hpp"
41 
42 PXR_NAMESPACE_OPEN_SCOPE
43 
46 template <typename BASE>
47 class HdPrman_Gprim : public BASE, public HdPrman_GprimBase
48 {
49 public:
50  using BaseType = BASE;
51 
52  HdPrman_Gprim(SdfPath const& id)
53  : BaseType(id)
54  {
55  }
56 
57  ~HdPrman_Gprim() override = default;
58 
59  void
60  Finalize(HdRenderParam *renderParam) override
61  {
62  HdPrman_RenderParam *param =
63  static_cast<HdPrman_RenderParam*>(renderParam);
64 
65  riley::Riley *riley = param->AcquireRiley();
66 
67  // Release retained conversions of coordSys bindings.
68  param->ReleaseCoordSysBindings(BASE::GetId());
69 
70  // Delete instances before deleting the prototypes they use.
71  for (const auto &id: _instanceIds) {
72  if (id != riley::GeometryInstanceId::InvalidId()) {
73  riley->DeleteGeometryInstance(
74  riley::GeometryPrototypeId::InvalidId(), id);
75  }
76  }
77  _instanceIds.clear();
78  for (const auto &id: _prototypeIds) {
79  if (id != riley::GeometryPrototypeId::InvalidId()) {
80  riley->DeleteGeometryPrototype(id);
81  }
82  }
83  _prototypeIds.clear();
84  }
85 
86  void Sync(HdSceneDelegate* sceneDelegate,
87  HdRenderParam* renderParam,
88  HdDirtyBits* dirtyBits,
89  TfToken const &reprToken) override;
90 
91 protected:
92  HdDirtyBits GetInitialDirtyBitsMask() const override = 0;
93 
94  HdDirtyBits
95  _PropagateDirtyBits(HdDirtyBits bits) const override
96  {
97  // XXX This is not ideal. Currently Riley requires us to provide
98  // all the values anytime we edit a volume. To make sure the values
99  // exist in the value cache, we propagte the dirty bits.value cache,
100  // we propagte the dirty bits.value cache, we propagte the dirty
101  // bits.value cache, we propagte the dirty bits.
102  return bits ? (bits | GetInitialDirtyBitsMask()) : bits;
103  }
104 
105  void
106  _InitRepr(TfToken const &reprToken,
107  HdDirtyBits *dirtyBits) override
108  {
109  TF_UNUSED(reprToken);
110  TF_UNUSED(dirtyBits);
111  // No-op
112  }
113 
114  // We override this member function in mesh.cpp to support the creation
115  // of mesh light prototype geometry.
116  virtual bool
117  _PrototypeOnly()
118  {
119  return false;
120  }
121 
122  // Provide a fallback material. Default grabs _fallbackMaterial
123  // from the context.
124  virtual riley::MaterialId
125  _GetFallbackMaterial(HdPrman_RenderParam *renderParam)
126  {
127  return renderParam->GetFallbackMaterialId();
128  }
129 
130  // Populate primType and primvars.
131  virtual RtPrimVarList
132  _ConvertGeometry(HdPrman_RenderParam *renderParam,
133  HdSceneDelegate *sceneDelegate,
134  const SdfPath &id,
135  RtUString *primType,
136  std::vector<HdGeomSubset> *geomSubsets) = 0;
137 
138  // This class does not support copying.
139  HdPrman_Gprim(const HdPrman_Gprim&) = delete;
140  HdPrman_Gprim &operator =(const HdPrman_Gprim&) = delete;
141 
142 };
143 
144 template <typename BASE>
145 void
147  HdRenderParam* renderParam,
148  HdDirtyBits* dirtyBits,
149  TfToken const &reprToken)
150 {
151  HD_TRACE_FUNCTION();
152  HF_MALLOC_TAG_FUNCTION();
153  TF_UNUSED(reprToken);
154 
155  HdPrman_RenderParam *param =
156  static_cast<HdPrman_RenderParam*>(renderParam);
157 
158  // Riley API.
159  riley::Riley *riley = param->AcquireRiley();
160 
161  // Update instance bindings.
162  BASE::_UpdateInstancer(sceneDelegate, dirtyBits);
163 
164  // Prim id
165  SdfPath const& id = BASE::GetId();
166  SdfPath const& instancerId = BASE::GetInstancerId();
167  const bool isHdInstance = !instancerId.IsEmpty();
168  SdfPath primPath = sceneDelegate->GetScenePrimPath(id, 0, nullptr);
169 
170  // Prman has a default value for identifier:id of 0 (in case of ray miss),
171  // while Hydra treats id -1 as the clear value. We map Prman primId as
172  // (Hydra primId + 1) to get around this, here and in
173  // hdPrman/framebuffer.cpp.
174  const int32_t primId = BASE::GetPrimId() + 1;
175 
176  // Sample transform
178  sceneDelegate->SampleTransform(id, &xf);
179 
180  // Update visibility so thet rprim->IsVisible() will work in render pass
181  if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) {
182  BASE::_UpdateVisibility(sceneDelegate, dirtyBits);
183  }
184 
185  // Resolve material binding. Default to fallbackGprimMaterial.
186  if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
187 #if HD_API_VERSION < 37
188  BASE::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(),
189  sceneDelegate->GetMaterialId(id));
190 #else
191  BASE::SetMaterialId(sceneDelegate->GetMaterialId(id));
192 #endif
193  }
194  riley::MaterialId materialId = _GetFallbackMaterial(param);
195  riley::DisplacementId dispId = riley::DisplacementId::InvalidId();
196  const SdfPath & hdMaterialId = BASE::GetMaterialId();
197  HdPrman_ResolveMaterial(sceneDelegate, hdMaterialId, &materialId, &dispId);
198 
199  // Convert (and cache) coordinate systems.
200  riley::CoordinateSystemList coordSysList = {0, nullptr};
201  if (HdPrman_RenderParam::RileyCoordSysIdVecRefPtr convertedCoordSys =
202  param->ConvertAndRetainCoordSysBindings(sceneDelegate, id)) {
203  coordSysList.count = convertedCoordSys->size();
204  coordSysList.ids = convertedCoordSys->data();
205  }
206 
207  // Hydra dirty bits corresponding to PRMan prototype primvars
208  // and instance attributes.
209  const int prmanPrimvarBits =
210  HdChangeTracker::DirtyPrimvar;
211  const int prmanAttrBits =
212  HdChangeTracker::DirtyVisibility |
213  HdChangeTracker::DirtyTransform;
214 
215  //
216  // Create or modify Riley geometry prototype(s).
217  //
218  std::vector<riley::MaterialId> subsetMaterialIds;
219  std::vector<SdfPath> subsetPaths;
220  {
221  RtUString primType;
222  HdGeomSubsets geomSubsets;
223  RtPrimVarList primvars = _ConvertGeometry(param, sceneDelegate, id,
224  &primType, &geomSubsets);
225 
226  // Transfer material opinions of primvars.
227  HdPrman_TransferMaterialPrimvarOpinions(sceneDelegate, hdMaterialId,
228  primvars);
229 
230  // Adjust _prototypeIds array.
231  const size_t oldCount = _prototypeIds.size();
232  const size_t newCount = std::max((size_t) 1, geomSubsets.size());
233  if (newCount != oldCount) {
234  for (const auto &oldPrototypeId: _prototypeIds) {
235  if (oldPrototypeId != riley::GeometryPrototypeId::InvalidId()) {
236  riley->DeleteGeometryPrototype(oldPrototypeId);
237  }
238  }
239  _prototypeIds.resize(newCount,
240  riley::GeometryPrototypeId::InvalidId());
241  }
242 
243  // Update Riley geom prototypes.
244  if (geomSubsets.empty()) {
245  // Common case: no subsets.
246  TF_VERIFY(newCount == 1);
247  TF_VERIFY(_prototypeIds.size() == 1);
248  if (_prototypeIds[0] == riley::GeometryPrototypeId::InvalidId()) {
249  _prototypeIds[0] = riley->CreateGeometryPrototype(
250  riley::UserId(
251  stats::AddDataLocation(primPath.GetText()).GetValue()),
252  primType, dispId, primvars);
253  } else if (*dirtyBits & prmanPrimvarBits) {
254  riley->ModifyGeometryPrototype(primType, _prototypeIds[0],
255  &dispId, &primvars);
256  }
257  } else {
258  // Subsets case.
259  // We resolve materials here, and hold them in subsetMaterialIds:
260  // Displacement networks are passed to the geom prototype;
261  // material networks are passed to the instances.
262  subsetMaterialIds.reserve(geomSubsets.size());
263 
264  // We also cache the subset paths for re-use when creating the instances
265  subsetPaths.reserve(geomSubsets.size());
266 
267  for (size_t j=0; j < geomSubsets.size(); ++j) {
268  auto& prototypeId = _prototypeIds[j];
269  HdGeomSubset &subset = geomSubsets[j];
270 
271  // Convert indices to int32_t and set as k_shade_faceset.
272  std::vector<int32_t> int32Indices(subset.indices.begin(),
273  subset.indices.end());
274  primvars.SetIntegerArray(RixStr.k_shade_faceset,
275  int32Indices.data(),
276  int32Indices.size());
277  // Look up material override for the subset (if any)
278  riley::MaterialId subsetMaterialId = materialId;
279  riley::DisplacementId subsetDispId = dispId;
280  if (subset.materialId.IsEmpty()) {
281  subset.materialId = hdMaterialId;
282  }
283  HdPrman_ResolveMaterial(sceneDelegate, subset.materialId,
284  &subsetMaterialId, &subsetDispId);
285  subsetMaterialIds.push_back(subsetMaterialId);
286 
287  // Look up the path for the subset
288  SdfPath subsetPath = sceneDelegate->GetScenePrimPath(subset.id, 0, nullptr);
289  subsetPaths.push_back(subsetPath);
290 
291  if (prototypeId == riley::GeometryPrototypeId::InvalidId()) {
292  prototypeId =
293  riley->CreateGeometryPrototype(
294  riley::UserId(
295  stats::AddDataLocation(subsetPath.GetText()).GetValue()),
296  primType, dispId, primvars);
297  } else if (*dirtyBits & prmanPrimvarBits) {
298  riley->ModifyGeometryPrototype(primType, prototypeId,
299  &dispId, &primvars);
300  }
301  }
302  }
303  }
304 
305  //
306  // Stop here, or also create geometry instances?
307  //
308  if (_PrototypeOnly()) {
309  *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
310  return;
311  }
312 
313  //
314  // Create or modify Riley geometry instances.
315  //
316  // Resolve attributes.
317  RtParamList attrs = param->ConvertAttributes(sceneDelegate, id, true);
318  if (!isHdInstance) {
319  // Simple case: Singleton instance.
320  // Convert transform.
322  for (size_t i=0; i < xf.count; ++i) {
323  xf_rt[i] = HdPrman_GfMatrixToRtMatrix(xf.values[i]);
324  }
325  const riley::Transform xform = {
326  unsigned(xf.count),
327  xf_rt.data(),
328  xf.times.data()};
329 
330  // Add "identifier:id" with the hydra prim id, and "identifier:id2"
331  // with the instance number.
332  // XXX Do we want to distinguish facesets here?
333  attrs.SetInteger(RixStr.k_identifier_id, primId);
334  attrs.SetInteger(RixStr.k_identifier_id2, 0);
335  // Adjust _instanceIds array.
336  const size_t newNumHdInstances = 1u;
337  const size_t oldCount = _instanceIds.size();
338  const size_t newCount = newNumHdInstances * _prototypeIds.size();
339  if (newCount != oldCount) {
340  for (const auto &oldInstanceId: _instanceIds) {
341  if (oldInstanceId != riley::GeometryInstanceId::InvalidId()) {
342  riley->DeleteGeometryInstance(
343  riley::GeometryPrototypeId::InvalidId(), oldInstanceId);
344  }
345  }
346  _instanceIds.resize(
347  newCount,
348  riley::GeometryInstanceId::InvalidId());
349  }
350 
351  // Create or modify Riley instances corresponding to a
352  // singleton Hydra instance.
353  TF_VERIFY(_instanceIds.size() == _prototypeIds.size());
354  for (size_t j=0; j < _prototypeIds.size(); ++j) {
355  auto const& prototypeId = _prototypeIds[j];
356  auto& instanceId = _instanceIds[j];
357  auto instanceMaterialId = materialId;
358 
359  // If a subset path was cached, use it. If not, use the prim path.
360  SdfPath* subsetPath(&primPath);
361  if (!subsetPaths.empty()) {
362  subsetPath = &subsetPaths[j];
363  }
364 
365  // If a valid subset material was bound, use it.
366  if (!subsetMaterialIds.empty()) {
367  TF_VERIFY(j < subsetMaterialIds.size());
368  instanceMaterialId = subsetMaterialIds[j];
369  }
370 
371  if (instanceId == riley::GeometryInstanceId::InvalidId()) {
372  instanceId = riley->CreateGeometryInstance(
373  riley::UserId(
374  stats::AddDataLocation(subsetPath->GetText()).GetValue()),
375  riley::GeometryPrototypeId::InvalidId(),
376  prototypeId, instanceMaterialId, coordSysList,
377  xform, attrs);
378  } else if (*dirtyBits & prmanAttrBits) {
379  riley->ModifyGeometryInstance(
380  riley::GeometryPrototypeId::InvalidId(),
381  instanceId, &instanceMaterialId, &coordSysList,
382  &xform, &attrs);
383  }
384  }
385  } else {
386  // Hydra Instancer case.
387  HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
388 
389  // Sync the hydra instancer (note: this is transitional code, it should
390  // be done by the render index...)
391  HdInstancer::_SyncInstancerAndParents(renderIndex, instancerId);
392 
393  HdPrmanInstancer *instancer = static_cast<HdPrmanInstancer*>(
394  renderIndex.GetInstancer(instancerId));
395  VtIntArray instanceIndices =
396  sceneDelegate->GetInstanceIndices(instancerId, id);
397 
398  // Sample per-instance transforms.
400  instancer->SampleInstanceTransforms(id, instanceIndices, &ixf);
401 
402  // Adjust _instanceIds array.
403  // Each Hydra instance produces a Riley instance for each
404  // geometry prototype. The number of geometry prototypes is
405  // based on the number of geometry subsets.
406  const size_t newNumHdInstances =
407  (ixf.count > 0) ? ixf.values[0].size() : 0;
408  const size_t oldCount = _instanceIds.size();
409  const size_t newCount = newNumHdInstances * _prototypeIds.size();
410  if (newCount != oldCount) {
411  for (const auto &oldInstanceId: _instanceIds) {
412  riley->DeleteGeometryInstance(
413  riley::GeometryPrototypeId::InvalidId(),
414  oldInstanceId);
415  }
416  // Clear before resize, because above we just deleted
417  // all the instances, so require re-creating them in riley.
418  _instanceIds.clear();
419  _instanceIds.resize(newCount,
420  riley::GeometryInstanceId::InvalidId());
421  }
422 
423  // Add "identifier:id" with the hydra prim id.
424  attrs.SetInteger(RixStr.k_identifier_id, primId);
425 
426  // Retrieve instance categories.
427  std::vector<VtArray<TfToken>> instanceCategories =
428  sceneDelegate->GetInstanceCategories(instancerId);
429 
430  // Process each Hydra instance.
431  for (size_t i=0; i < newNumHdInstances; ++i) {
432  // XXX: Add support for nested instancing instance primvars.
433  size_t instanceIndex = 0;
434  if (i < instanceIndices.size()) {
435  instanceIndex = instanceIndices[i];
436  }
437 
438  // Create a copy of the instancer attrs.
439  RtParamList instanceAttrs = attrs;
440  instancer->GetInstancePrimvars(id, instanceIndex, instanceAttrs);
441  // Add "identifier:id2" with the instance number.
442  instanceAttrs.SetInteger(RixStr.k_identifier_id2, i);
443 
444  // Convert categories.
445  if (instanceIndex < instanceCategories.size()) {
446  param->ConvertCategoriesToAttributes(
447  id, instanceCategories[instanceIndex], instanceAttrs);
448  }
449 
450  // Convert transform.
451  // PRMan does not allow transforms on geometry prototypes,
452  // so we apply that transform (xf) to all the instances, here.
454  rt_xf(ixf.count);
455 
456  if (xf.count == 0 ||
457  (xf.count == 1 && (xf.values[0] == GfMatrix4d(1)))) {
458  // Expected case: prototype xf is constant & exactly identity.
459  for (size_t j=0; j < ixf.count; ++j) {
460  rt_xf[j] = HdPrman_GfMatrixToRtMatrix(ixf.values[j][i]);
461  }
462  } else {
463  // Multiply resampled prototype xf against instance xforms.
464  for (size_t j=0; j < ixf.count; ++j) {
465  GfMatrix4d xf_j = xf.Resample(ixf.times[j]);
466  rt_xf[j] =
467  HdPrman_GfMatrixToRtMatrix(xf_j * ixf.values[j][i]);
468  }
469  }
470  const riley::Transform xform =
471  { unsigned(ixf.count), rt_xf.data(), ixf.times.data() };
472 
473  // Create or modify Riley instances corresponding to this
474  // Hydra instance.
475  for (size_t j=0; j < _prototypeIds.size(); ++j) {
476  auto const& prototypeId = _prototypeIds[j];
477  auto& instanceId = _instanceIds[i*_prototypeIds.size() + j];
478  auto instanceMaterialId = materialId;
479 
480  // If a subset path was cached, use it. If not, use the prim path.
481  SdfPath* subsetPath(&primPath);
482  if (!subsetPaths.empty()) {
483  subsetPath = &subsetPaths[j];
484  }
485 
486  // If a valid subset material was bound, use it.
487  if (!subsetMaterialIds.empty()) {
488  TF_VERIFY(j < subsetMaterialIds.size());
489  instanceMaterialId = subsetMaterialIds[j];
490  }
491 
492  if (instanceId == riley::GeometryInstanceId::InvalidId()) {
493  // Generate an instance path using the subset path (or prim
494  // path as appropriate) as a base
495  SdfPath instancePath =
496  sceneDelegate->GetScenePrimPath(*subsetPath, i, nullptr);
497  instanceId = riley->CreateGeometryInstance(
498  riley::UserId(
499  stats::AddDataLocation(instancePath.GetText())
500  .GetValue()),
501  riley::GeometryPrototypeId::InvalidId(), prototypeId,
502  instanceMaterialId, coordSysList, xform, instanceAttrs);
503  } else if (*dirtyBits & prmanAttrBits) {
504  riley->ModifyGeometryInstance(
505  riley::GeometryPrototypeId::InvalidId(),
506  instanceId, &instanceMaterialId, &coordSysList,
507  &xform, &instanceAttrs);
508  }
509  }
510  }
511  }
512  *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
513 }
514 
515 PXR_NAMESPACE_CLOSE_SCOPE
516 
517 #endif // EXT_RMANPKG_24_0_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
The Hydra render index is a flattened representation of the client scene graph, which may be composed...
Definition: renderIndex.h:120
Tracks changes from the HdSceneDelegate, providing invalidation cues to the render engine.
Definition: changeTracker.h:51
This is a small-vector class with local storage optimization, the local storage can be specified via ...
Definition: smallVector.h:177
value_type * data()
Direct access to the underlying array.
Definition: smallVector.h:768
virtual HD_API SdfPath GetMaterialId(SdfPath const &rprimId)
Returns the material ID bound to the rprim rprimId.
virtual HD_API std::vector< VtArray< TfToken > > GetInstanceCategories(SdfPath const &instancerId)
Returns the categories for all instances in the instancer.
The HdRenderParam is an opaque (to core Hydra) handle, to an object that is obtained from the render ...
SDF_API const char * GetText() const
Returns the string representation of this path as a c string.
#define TF_UNUSED(x)
Stops compiler from producing unused argument or variable warnings.
Definition: tf.h:185
Basic Sdf data types.
A common base class for HdPrman_Gprim types.
Definition: gprimbase.h:37
Token for efficient comparison, assignment, and hashing of known strings.
Definition: token.h:87
HD_API HdInstancer * GetInstancer(SdfPath const &id) const
Returns the instancer of id.
virtual HD_API size_t SampleTransform(SdfPath const &id, size_t maxSampleCount, float *sampleTimes, GfMatrix4d *sampleValues)
Store up to maxSampleCount transform samples in *sampleValues.
Stores a 4x4 matrix of double elements.
Definition: matrix4d.h:88
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition: diagnostic.h:283
Adapter class providing data exchange with the client scene graph.
A mix-in template that adds shared gprim behavior to support various HdRprim types.
Definition: gprim.h:47
TYPE Resample(float u) const
Convience method for invoking HdResampleRawTimeSamples on this HdTimeSampleArray.
A path value used to locate objects in layers or scenegraphs.
Definition: path.h:290
VtIntArray indices
The list of element indices contained in the subset.
Definition: geomSubset.h:56
HD_API bool IsVisibilityDirty(SdfPath const &id)
Returns true if the rprim identified by id has dirty visibility.
SdfPath materialId
The path used to identify this material bound to the subset.
Definition: geomSubset.h:54
virtual HD_API VtIntArray GetInstanceIndices(SdfPath const &instancerId, SdfPath const &prototypeId)
Gets the extracted indices array of the prototype id used in the instancer.
SdfPath id
The path used to identify this subset in the scene.
Definition: geomSubset.h:52
bool IsEmpty() const noexcept
Returns true if this is the empty path (SdfPath::EmptyPath()).
Definition: path.h:419
HdRenderIndex & GetRenderIndex()
Returns the RenderIndex owned by this delegate.
Describes a subset of a piece of geometry as a set of indices.
Definition: geomSubset.h:40
virtual HD_API SdfPath GetScenePrimPath(SdfPath const &rprimId, int instanceIndex, HdInstancerContext *instancerContext=nullptr)
Returns the scene address of the prim corresponding to the given rprim/instance index.