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  // By default, just return the same dirty bits we recieved.
98  return bits;
99  }
100 
101  void
102  _InitRepr(TfToken const &reprToken,
103  HdDirtyBits *dirtyBits) override
104  {
105  TF_UNUSED(reprToken);
106  TF_UNUSED(dirtyBits);
107  // No-op
108  }
109 
110  // We override this member function in mesh.cpp to support the creation
111  // of mesh light prototype geometry.
112  virtual bool
113  _PrototypeOnly()
114  {
115  return false;
116  }
117 
118  // Provide a fallback material. Default grabs _fallbackMaterial
119  // from the context.
120  virtual riley::MaterialId
121  _GetFallbackMaterial(HdPrman_RenderParam *renderParam)
122  {
123  return renderParam->GetFallbackMaterialId();
124  }
125 
126  // Populate primType and primvars.
127  virtual RtPrimVarList
128  _ConvertGeometry(HdPrman_RenderParam *renderParam,
129  HdSceneDelegate *sceneDelegate,
130  const SdfPath &id,
131  RtUString *primType,
132  std::vector<HdGeomSubset> *geomSubsets) = 0;
133 
134  // This class does not support copying.
135  HdPrman_Gprim(const HdPrman_Gprim&) = delete;
136  HdPrman_Gprim &operator =(const HdPrman_Gprim&) = delete;
137 
138 };
139 
140 template <typename BASE>
141 void
143  HdRenderParam* renderParam,
144  HdDirtyBits* dirtyBits,
145  TfToken const &reprToken)
146 {
147  HD_TRACE_FUNCTION();
148  HF_MALLOC_TAG_FUNCTION();
149  TF_UNUSED(reprToken);
150 
151  HdPrman_RenderParam *param =
152  static_cast<HdPrman_RenderParam*>(renderParam);
153 
154  // Riley API.
155  riley::Riley *riley = param->AcquireRiley();
156 
157  // Update instance bindings.
158  BASE::_UpdateInstancer(sceneDelegate, dirtyBits);
159 
160  // Prim id
161  SdfPath const& id = BASE::GetId();
162  SdfPath const& instancerId = BASE::GetInstancerId();
163  const bool isHdInstance = !instancerId.IsEmpty();
164  SdfPath primPath = sceneDelegate->GetScenePrimPath(id, 0, nullptr);
165 
166  // Prman has a default value for identifier:id of 0 (in case of ray miss),
167  // while Hydra treats id -1 as the clear value. We map Prman primId as
168  // (Hydra primId + 1) to get around this, here and in
169  // hdPrman/framebuffer.cpp.
170  const int32_t primId = BASE::GetPrimId() + 1;
171 
172  // Sample transform
174  sceneDelegate->SampleTransform(id, &xf);
175 
176  // Update visibility so thet rprim->IsVisible() will work in render pass
177  if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) {
178  BASE::_UpdateVisibility(sceneDelegate, dirtyBits);
179  }
180 
181  // Resolve material binding. Default to fallbackGprimMaterial.
182  if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
183 #if HD_API_VERSION < 37
184  BASE::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(),
185  sceneDelegate->GetMaterialId(id));
186 #else
187  BASE::SetMaterialId(sceneDelegate->GetMaterialId(id));
188 #endif
189  }
190  riley::MaterialId materialId = _GetFallbackMaterial(param);
191  riley::DisplacementId dispId = riley::DisplacementId::InvalidId();
192  const SdfPath & hdMaterialId = BASE::GetMaterialId();
193  HdPrman_ResolveMaterial(sceneDelegate, hdMaterialId, &materialId, &dispId);
194 
195  // Convert (and cache) coordinate systems.
196  riley::CoordinateSystemList coordSysList = {0, nullptr};
197  if (HdPrman_RenderParam::RileyCoordSysIdVecRefPtr convertedCoordSys =
198  param->ConvertAndRetainCoordSysBindings(sceneDelegate, id)) {
199  coordSysList.count = convertedCoordSys->size();
200  coordSysList.ids = convertedCoordSys->data();
201  }
202 
203  // Hydra dirty bits corresponding to PRMan prototype attributes (also called
204  // "primitive variables" but not synonymous with USD primvars). See prman
205  // docs at https://rmanwiki.pixar.com/display/REN24/Primitive+Variables.
206  static const int prmanProtoAttrBits =
207  HdChangeTracker::DirtyPoints |
208  HdChangeTracker::DirtyNormals |
209  HdChangeTracker::DirtyWidths |
210  HdChangeTracker::DirtyTopology;
211 
212  // Hydra dirty bits corresponding to prman instance attributes. See prman
213  // docs at https://rmanwiki.pixar.com/display/REN24/Instance+Attributes.
214  static const int prmanInstAttrBits =
215  HdChangeTracker::DirtyMaterialId |
216  HdChangeTracker::DirtyTransform |
217  HdChangeTracker::DirtyVisibility |
218  HdChangeTracker::DirtyDoubleSided |
219  HdChangeTracker::DirtySubdivTags |
220  HdChangeTracker::DirtyVolumeField |
221  HdChangeTracker::DirtyCategories |
222  HdChangeTracker::DirtyPrimvar;
223 
224  //
225  // Create or modify Riley geometry prototype(s).
226  //
227  std::vector<riley::MaterialId> subsetMaterialIds;
228  std::vector<SdfPath> subsetPaths;
229  {
230  RtUString primType;
231  HdGeomSubsets geomSubsets;
232  RtPrimVarList primvars = _ConvertGeometry(param, sceneDelegate, id,
233  &primType, &geomSubsets);
234 
235  // Transfer material opinions of primvars.
236  HdPrman_TransferMaterialPrimvarOpinions(sceneDelegate, hdMaterialId,
237  primvars);
238 
239  // Adjust _prototypeIds array.
240  const size_t oldCount = _prototypeIds.size();
241  const size_t newCount = std::max((size_t) 1, geomSubsets.size());
242  if (newCount != oldCount) {
243  for (const auto &oldPrototypeId: _prototypeIds) {
244  if (oldPrototypeId != riley::GeometryPrototypeId::InvalidId()) {
245  riley->DeleteGeometryPrototype(oldPrototypeId);
246  }
247  }
248  _prototypeIds.resize(newCount,
249  riley::GeometryPrototypeId::InvalidId());
250  }
251 
252  // Update Riley geom prototypes.
253  if (geomSubsets.empty()) {
254  // Common case: no subsets.
255  TF_VERIFY(newCount == 1);
256  TF_VERIFY(_prototypeIds.size() == 1);
257  primvars.SetString(RixStr.k_stats_prototypeIdentifier,
258  RtUString(primPath.GetText()));
259  if (_prototypeIds[0] == riley::GeometryPrototypeId::InvalidId()) {
260  _prototypeIds[0] = riley->CreateGeometryPrototype(
261  riley::UserId(
262  stats::AddDataLocation(primPath.GetText()).GetValue()),
263  primType, dispId, primvars);
264  } else if (*dirtyBits & prmanProtoAttrBits) {
265  riley->ModifyGeometryPrototype(primType, _prototypeIds[0],
266  &dispId, &primvars);
267  }
268  } else {
269  // Subsets case.
270  // We resolve materials here, and hold them in subsetMaterialIds:
271  // Displacement networks are passed to the geom prototype;
272  // material networks are passed to the instances.
273  subsetMaterialIds.reserve(geomSubsets.size());
274 
275  // We also cache the subset paths for re-use when creating the instances
276  subsetPaths.reserve(geomSubsets.size());
277 
278  for (size_t j=0; j < geomSubsets.size(); ++j) {
279  auto& prototypeId = _prototypeIds[j];
280  HdGeomSubset &subset = geomSubsets[j];
281 
282  // Convert indices to int32_t and set as k_shade_faceset.
283  std::vector<int32_t> int32Indices(subset.indices.begin(),
284  subset.indices.end());
285  primvars.SetIntegerArray(RixStr.k_shade_faceset,
286  int32Indices.data(),
287  int32Indices.size());
288  // Look up material override for the subset (if any)
289  riley::MaterialId subsetMaterialId = materialId;
290  riley::DisplacementId subsetDispId = dispId;
291  if (subset.materialId.IsEmpty()) {
292  subset.materialId = hdMaterialId;
293  }
294  HdPrman_ResolveMaterial(sceneDelegate, subset.materialId,
295  &subsetMaterialId, &subsetDispId);
296  subsetMaterialIds.push_back(subsetMaterialId);
297 
298  // Look up the path for the subset
299  SdfPath subsetPath = sceneDelegate->GetScenePrimPath(subset.id, 0, nullptr);
300  subsetPaths.push_back(subsetPath);
301  primvars.SetString(RixStr.k_stats_prototypeIdentifier,
302  RtUString(subsetPath.GetText()));
303 
304  if (prototypeId == riley::GeometryPrototypeId::InvalidId()) {
305  prototypeId =
306  riley->CreateGeometryPrototype(
307  riley::UserId(
308  stats::AddDataLocation(subsetPath.GetText()).GetValue()),
309  primType, dispId, primvars);
310  } else if (*dirtyBits & prmanProtoAttrBits) {
311  riley->ModifyGeometryPrototype(primType, prototypeId,
312  &subsetDispId, &primvars);
313  }
314  }
315  }
316  *dirtyBits &= ~prmanProtoAttrBits;
317  }
318 
319  //
320  // Stop here, or also create geometry instances?
321  //
322  if (_PrototypeOnly()) {
323  *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
324  return;
325  }
326 
327  //
328  // Create or modify Riley geometry instances.
329  //
330  // Resolve attributes.
331  RtParamList attrs = param->ConvertAttributes(sceneDelegate, id, true);
332  if (!isHdInstance) {
333  // Simple case: Singleton instance.
334  // Convert transform.
336  for (size_t i=0; i < xf.count; ++i) {
337  xf_rt[i] = HdPrman_GfMatrixToRtMatrix(xf.values[i]);
338  }
339  const riley::Transform xform = {
340  unsigned(xf.count),
341  xf_rt.data(),
342  xf.times.data()};
343 
344  // Add "identifier:id" with the hydra prim id, and "identifier:id2"
345  // with the instance number.
346  // XXX Do we want to distinguish facesets here?
347  attrs.SetInteger(RixStr.k_identifier_id, primId);
348  attrs.SetInteger(RixStr.k_identifier_id2, 0);
349  // Adjust _instanceIds array.
350  const size_t newNumHdInstances = 1u;
351  const size_t oldCount = _instanceIds.size();
352  const size_t newCount = newNumHdInstances * _prototypeIds.size();
353  if (newCount != oldCount) {
354  for (const auto &oldInstanceId: _instanceIds) {
355  if (oldInstanceId != riley::GeometryInstanceId::InvalidId()) {
356  riley->DeleteGeometryInstance(
357  riley::GeometryPrototypeId::InvalidId(), oldInstanceId);
358  }
359  }
360  _instanceIds.resize(
361  newCount,
362  riley::GeometryInstanceId::InvalidId());
363  }
364 
365  // Create or modify Riley instances corresponding to a
366  // singleton Hydra instance.
367  TF_VERIFY(_instanceIds.size() == _prototypeIds.size());
368  for (size_t j=0; j < _prototypeIds.size(); ++j) {
369  auto const& prototypeId = _prototypeIds[j];
370  auto& instanceId = _instanceIds[j];
371  auto instanceMaterialId = materialId;
372 
373  // If a subset path was cached, use it. If not, use the prim path.
374  SdfPath* subsetPath(&primPath);
375  if (!subsetPaths.empty()) {
376  subsetPath = &subsetPaths[j];
377  }
378 
379  // If a valid subset material was bound, use it.
380  if (!subsetMaterialIds.empty()) {
381  TF_VERIFY(j < subsetMaterialIds.size());
382  instanceMaterialId = subsetMaterialIds[j];
383  }
384 
385  if (instanceId == riley::GeometryInstanceId::InvalidId()) {
386  instanceId = riley->CreateGeometryInstance(
387  riley::UserId(
388  stats::AddDataLocation(subsetPath->GetText()).GetValue()),
389  riley::GeometryPrototypeId::InvalidId(), prototypeId,
390  instanceMaterialId, coordSysList, xform, attrs);
391  } else if (*dirtyBits & prmanInstAttrBits) {
392  riley->ModifyGeometryInstance(
393  riley::GeometryPrototypeId::InvalidId(),
394  instanceId, &instanceMaterialId, &coordSysList, &xform,
395  &attrs);
396  }
397  }
398  *dirtyBits &= ~prmanInstAttrBits;
399  } else if (HdChangeTracker::IsInstancerDirty(*dirtyBits, instancerId)) {
400  // This gprim is a prototype of a hydra instancer. (It is not itself an
401  // instancer because it is a gprim.) The riley geometry prototypes have
402  // already been synced above, and those are owned by this gprim instance.
403  // We need to tell the hdprman instancer to sync its riley instances for
404  // these riley prototypes.
405  //
406  // We won't make any riley instances here. The hdprman instancer will
407  // own the riley instances instead.
408  //
409  // We only need to do this if dirtyBits says the instancer is dirty.
410 
411  HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
412 
413  // first, sync the hydra instancer and its parents, from the bottom up.
414  // (note: this is transitional code, it should be done by the render index...)
415  HdInstancer::_SyncInstancerAndParents(renderIndex, instancerId);
416 
417  if (subsetMaterialIds.size() == 0) {
418  subsetMaterialIds.push_back(materialId);
419  }
420  if (subsetPaths.size() == 0) {
421  subsetPaths.push_back(primPath);
422  }
423  TF_VERIFY(_prototypeIds.size() == subsetMaterialIds.size() &&
424  _prototypeIds.size() == subsetPaths.size(),
425  "size mismatch (%lu, %lu, %lu)\n", _prototypeIds.size(),
426  subsetMaterialIds.size(), subsetPaths.size());
427 
428  // next, tell the hdprman instancer to sync the riley instances
429  HdPrmanInstancer *instancer = static_cast<HdPrmanInstancer*>(
430  renderIndex.GetInstancer(instancerId));
431  if (instancer) {
432  instancer->Populate(
433  renderParam,
434  dirtyBits,
435  id,
436  _prototypeIds,
437  coordSysList,
438  primId,
439  subsetMaterialIds,
440  subsetPaths);
441  }
442  }
443  *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
444 }
445 
446 PXR_NAMESPACE_CLOSE_SCOPE
447 
448 #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.
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.
#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
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
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.
static HD_API bool IsInstancerDirty(HdDirtyBits dirtyBits, SdfPath const &id)
Returns true if the dirtyBits has a dirty instancer. id is for perflog.