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  // Provide a fallback material. Default grabs _fallbackMaterial
115  // from the context.
116  virtual riley::MaterialId
117  _GetFallbackMaterial(HdPrman_RenderParam *renderParam)
118  {
119  return renderParam->GetFallbackMaterialId();
120  }
121 
122  // Populate primType and primvars.
123  virtual RtPrimVarList
124  _ConvertGeometry(HdPrman_RenderParam *renderParam,
125  HdSceneDelegate *sceneDelegate,
126  const SdfPath &id,
127  RtUString *primType,
128  std::vector<HdGeomSubset> *geomSubsets) = 0;
129 
130  // This class does not support copying.
131  HdPrman_Gprim(const HdPrman_Gprim&) = delete;
132  HdPrman_Gprim &operator =(const HdPrman_Gprim&) = delete;
133 
134 };
135 
136 template <typename BASE>
137 void
139  HdRenderParam* renderParam,
140  HdDirtyBits* dirtyBits,
141  TfToken const &reprToken)
142 {
143  HD_TRACE_FUNCTION();
144  HF_MALLOC_TAG_FUNCTION();
145  TF_UNUSED(reprToken);
146 
147  HdPrman_RenderParam *param =
148  static_cast<HdPrman_RenderParam*>(renderParam);
149 
150  // Riley API.
151  riley::Riley *riley = param->AcquireRiley();
152 
153  // Update instance bindings.
154  BASE::_UpdateInstancer(sceneDelegate, dirtyBits);
155 
156  // Prim id
157  SdfPath const& id = BASE::GetId();
158  SdfPath const& instancerId = BASE::GetInstancerId();
159  const bool isHdInstance = !instancerId.IsEmpty();
160  // Prman has a default value for identifier:id of 0 (in case of ray miss),
161  // while Hydra treats id -1 as the clear value. We map Prman primId as
162  // (Hydra primId + 1) to get around this, here and in
163  // hdPrman/framebuffer.cpp.
164  const int32_t primId = BASE::GetPrimId() + 1;
165 
166  // Sample transform
168  sceneDelegate->SampleTransform(id, &xf);
169 
170  // Update visibility so thet rprim->IsVisible() will work in render pass
171  if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) {
172  BASE::_UpdateVisibility(sceneDelegate, dirtyBits);
173  }
174 
175  // Resolve material binding. Default to fallbackGprimMaterial.
176  if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
177 #if HD_API_VERSION < 37
178  BASE::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(),
179  sceneDelegate->GetMaterialId(id));
180 #else
181  BASE::SetMaterialId(sceneDelegate->GetMaterialId(id));
182 #endif
183  }
184  riley::MaterialId materialId = _GetFallbackMaterial(param);
185  riley::DisplacementId dispId = riley::DisplacementId::InvalidId();
186  const SdfPath & hdMaterialId = BASE::GetMaterialId();
187  HdPrman_ResolveMaterial(sceneDelegate, hdMaterialId, &materialId, &dispId);
188 
189  // Convert (and cache) coordinate systems.
190  riley::CoordinateSystemList coordSysList = {0, nullptr};
191  if (HdPrman_RenderParam::RileyCoordSysIdVecRefPtr convertedCoordSys =
192  param->ConvertAndRetainCoordSysBindings(sceneDelegate, id)) {
193  coordSysList.count = convertedCoordSys->size();
194  coordSysList.ids = convertedCoordSys->data();
195  }
196 
197  // Hydra dirty bits corresponding to PRMan prototype primvars
198  // and instance attributes.
199  const int prmanPrimvarBits =
200  HdChangeTracker::DirtyPrimvar;
201  const int prmanAttrBits =
202  HdChangeTracker::DirtyVisibility |
203  HdChangeTracker::DirtyTransform;
204 
205  //
206  // Create or modify Riley geometry prototype(s).
207  //
208  std::vector<riley::MaterialId> subsetMaterialIds;
209  {
210  RtUString primType;
211  HdGeomSubsets geomSubsets;
212  RtPrimVarList primvars = _ConvertGeometry(param, sceneDelegate, id,
213  &primType, &geomSubsets);
214 
215  // Transfer material opinions of primvars.
216  HdPrman_TransferMaterialPrimvarOpinions(sceneDelegate, hdMaterialId,
217  primvars);
218 
219  // Adjust _prototypeIds array.
220  const size_t oldCount = _prototypeIds.size();
221  const size_t newCount = std::max((size_t) 1, geomSubsets.size());
222  if (newCount != oldCount) {
223  for (const auto &oldPrototypeId: _prototypeIds) {
224  if (oldPrototypeId != riley::GeometryPrototypeId::InvalidId()) {
225  riley->DeleteGeometryPrototype(oldPrototypeId);
226  }
227  }
228  _prototypeIds.resize(newCount,
229  riley::GeometryPrototypeId::InvalidId());
230  }
231 
232  // Update Riley geom prototypes.
233  if (geomSubsets.empty()) {
234  // Common case: no subsets.
235  TF_VERIFY(newCount == 1);
236  TF_VERIFY(_prototypeIds.size() == 1);
237  if (_prototypeIds[0] == riley::GeometryPrototypeId::InvalidId()) {
238  _prototypeIds[0] =
239  riley->CreateGeometryPrototype(riley::UserId::DefaultId(),
240  primType, dispId,
241  primvars);
242  } else if (*dirtyBits & prmanPrimvarBits) {
243  riley->ModifyGeometryPrototype(primType, _prototypeIds[0],
244  &dispId, &primvars);
245  }
246  } else {
247  // Subsets case.
248  // We resolve materials here, and hold them in subsetMaterialIds:
249  // Displacement networks are passed to the geom prototype;
250  // material networks are passed to the instances.
251  subsetMaterialIds.reserve(geomSubsets.size());
252  for (size_t j=0; j < geomSubsets.size(); ++j) {
253  auto& prototypeId = _prototypeIds[j];
254  HdGeomSubset &subset = geomSubsets[j];
255  // Convert indices to int32_t and set as k_shade_faceset.
256  std::vector<int32_t> int32Indices(subset.indices.begin(),
257  subset.indices.end());
258  primvars.SetIntegerArray(RixStr.k_shade_faceset,
259  int32Indices.data(),
260  int32Indices.size());
261  // Look up material override for the subset (if any)
262  riley::MaterialId subsetMaterialId = materialId;
263  riley::DisplacementId subsetDispId = dispId;
264  if (subset.materialId.IsEmpty()) {
265  subset.materialId = hdMaterialId;
266  }
267  HdPrman_ResolveMaterial(sceneDelegate, subset.materialId,
268  &subsetMaterialId, &subsetDispId);
269  subsetMaterialIds.push_back(subsetMaterialId);
270  if (prototypeId == riley::GeometryPrototypeId::InvalidId()) {
271  prototypeId =
272  riley->CreateGeometryPrototype(
273  riley::UserId::DefaultId(),
274  primType, dispId, primvars);
275  } else if (*dirtyBits & prmanPrimvarBits) {
276  riley->ModifyGeometryPrototype(primType, prototypeId,
277  &dispId, &primvars);
278  }
279  }
280  }
281  }
282 
283  //
284  // Create or modify Riley geometry instances.
285  //
286  // Resolve attributes.
287  RtParamList attrs = param->ConvertAttributes(sceneDelegate, id);
288  if (!isHdInstance) {
289  // Simple case: Singleton instance.
290  // Convert transform.
292  for (size_t i=0; i < xf.count; ++i) {
293  xf_rt[i] = HdPrman_GfMatrixToRtMatrix(xf.values[i]);
294  }
295  const riley::Transform xform = {
296  unsigned(xf.count),
297  xf_rt.data(),
298  xf.times.data()};
299 
300  // Add "identifier:id" with the hydra prim id, and "identifier:id2"
301  // with the instance number.
302  // XXX Do we want to distinguish facesets here?
303  attrs.SetInteger(RixStr.k_identifier_id, primId);
304  attrs.SetInteger(RixStr.k_identifier_id2, 0);
305  // Adjust _instanceIds array.
306  const size_t newNumHdInstances = 1u;
307  const size_t oldCount = _instanceIds.size();
308  const size_t newCount = newNumHdInstances * _prototypeIds.size();
309  if (newCount != oldCount) {
310  for (const auto &oldInstanceId: _instanceIds) {
311  if (oldInstanceId != riley::GeometryInstanceId::InvalidId()) {
312  riley->DeleteGeometryInstance(
313  riley::GeometryPrototypeId::InvalidId(), oldInstanceId);
314  }
315  }
316  _instanceIds.resize(
317  newCount,
318  riley::GeometryInstanceId::InvalidId());
319  }
320  // Create or modify Riley instances corresponding to a
321  // singleton Hydra instance.
322  TF_VERIFY(_instanceIds.size() == _prototypeIds.size());
323  for (size_t j=0; j < _prototypeIds.size(); ++j) {
324  auto const& prototypeId = _prototypeIds[j];
325  auto& instanceId = _instanceIds[j];
326  auto instanceMaterialId = materialId;
327  // If a valid subset material was bound, use it.
328  if (!subsetMaterialIds.empty()) {
329  TF_VERIFY(j < subsetMaterialIds.size());
330  instanceMaterialId = subsetMaterialIds[j];
331  }
332  if (instanceId == riley::GeometryInstanceId::InvalidId()) {
333  instanceId = riley->CreateGeometryInstance(
334  riley::UserId::DefaultId(),
335  riley::GeometryPrototypeId::InvalidId(),
336  prototypeId, instanceMaterialId, coordSysList,
337  xform, attrs);
338  } else if (*dirtyBits & prmanAttrBits) {
339  riley->ModifyGeometryInstance(
340  riley::GeometryPrototypeId::InvalidId(),
341  instanceId, &instanceMaterialId, &coordSysList,
342  &xform, &attrs);
343  }
344  }
345  } else {
346  // Hydra Instancer case.
347  HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
348 
349  // Sync the hydra instancer (note: this is transitional code, it should
350  // be done by the render index...)
351  HdInstancer::_SyncInstancerAndParents(renderIndex, instancerId);
352 
353  HdPrmanInstancer *instancer = static_cast<HdPrmanInstancer*>(
354  renderIndex.GetInstancer(instancerId));
355  VtIntArray instanceIndices =
356  sceneDelegate->GetInstanceIndices(instancerId, id);
357 
358  // Sample per-instance transforms.
360  instancer->SampleInstanceTransforms(id, instanceIndices, &ixf);
361 
362  // Adjust _instanceIds array.
363  // Each Hydra instance produces a Riley instance for each
364  // geometry prototype. The number of geometry prototypes is
365  // based on the number of geometry subsets.
366  const size_t newNumHdInstances =
367  (ixf.count > 0) ? ixf.values[0].size() : 0;
368  const size_t oldCount = _instanceIds.size();
369  const size_t newCount = newNumHdInstances * _prototypeIds.size();
370  if (newCount != oldCount) {
371  for (const auto &oldInstanceId: _instanceIds) {
372  riley->DeleteGeometryInstance(
373  riley::GeometryPrototypeId::InvalidId(),
374  oldInstanceId);
375  }
376  // Clear before resize, because above we just deleted
377  // all the instances, so require re-creating them in riley.
378  _instanceIds.clear();
379  _instanceIds.resize(newCount,
380  riley::GeometryInstanceId::InvalidId());
381  }
382 
383  // Add "identifier:id" with the hydra prim id.
384  attrs.SetInteger(RixStr.k_identifier_id, primId);
385 
386  // Retrieve instance categories.
387  std::vector<VtArray<TfToken>> instanceCategories =
388  sceneDelegate->GetInstanceCategories(instancerId);
389 
390  // Process each Hydra instance.
391  for (size_t i=0; i < newNumHdInstances; ++i) {
392  // XXX: Add support for nested instancing instance primvars.
393  size_t instanceIndex = 0;
394  if (i < instanceIndices.size()) {
395  instanceIndex = instanceIndices[i];
396  }
397 
398  // Create a copy of the instancer attrs.
399  RtParamList instanceAttrs = attrs;
400  instancer->GetInstancePrimvars(id, instanceIndex, instanceAttrs);
401  // Add "identifier:id2" with the instance number.
402  instanceAttrs.SetInteger(RixStr.k_identifier_id2, i);
403 
404  // Convert categories.
405  if (instanceIndex < instanceCategories.size()) {
406  param->ConvertCategoriesToAttributes(
407  id, instanceCategories[instanceIndex], instanceAttrs);
408  }
409 
410  // Convert transform.
411  // PRMan does not allow transforms on geometry prototypes,
412  // so we apply that transform (xf) to all the instances, here.
414  rt_xf(ixf.count);
415 
416  if (xf.count == 0 ||
417  (xf.count == 1 && (xf.values[0] == GfMatrix4d(1)))) {
418  // Expected case: prototype xf is constant & exactly identity.
419  for (size_t j=0; j < ixf.count; ++j) {
420  rt_xf[j] = HdPrman_GfMatrixToRtMatrix(ixf.values[j][i]);
421  }
422  } else {
423  // Multiply resampled prototype xf against instance xforms.
424  for (size_t j=0; j < ixf.count; ++j) {
425  GfMatrix4d xf_j = xf.Resample(ixf.times[j]);
426  rt_xf[j] =
427  HdPrman_GfMatrixToRtMatrix(xf_j * ixf.values[j][i]);
428  }
429  }
430  const riley::Transform xform =
431  { unsigned(ixf.count), rt_xf.data(), ixf.times.data() };
432 
433  // Create or modify Riley instances corresponding to this
434  // Hydra instance.
435  for (size_t j=0; j < _prototypeIds.size(); ++j) {
436  auto const& prototypeId = _prototypeIds[j];
437  auto& instanceId = _instanceIds[i*_prototypeIds.size() + j];
438  auto instanceMaterialId = materialId;
439  // If a valid subset material was bound, use it.
440  if (!subsetMaterialIds.empty()) {
441  TF_VERIFY(j < subsetMaterialIds.size());
442  instanceMaterialId = subsetMaterialIds[j];
443  }
444  if (instanceId == riley::GeometryInstanceId::InvalidId()) {
445  instanceId = riley->CreateGeometryInstance(
446  riley::UserId::DefaultId(),
447  riley::GeometryPrototypeId::InvalidId(),
448  prototypeId, instanceMaterialId, coordSysList,
449  xform, instanceAttrs);
450  } else if (*dirtyBits & prmanAttrBits) {
451  riley->ModifyGeometryInstance(
452  riley::GeometryPrototypeId::InvalidId(),
453  instanceId, &instanceMaterialId, &coordSysList,
454  &xform, &instanceAttrs);
455  }
456  }
457  }
458  }
459  *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
460 }
461 
462 PXR_NAMESPACE_CLOSE_SCOPE
463 
464 #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 ...
#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.
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