All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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/matrix4f.h"
31 #include "pxr/base/gf/matrix4d.h"
32 
33 #include "hdPrman/context.h"
34 #include "hdPrman/instancer.h"
35 #include "hdPrman/material.h"
36 #include "hdPrman/renderParam.h"
37 #include "hdPrman/renderPass.h"
38 #include "hdPrman/rixStrings.h"
39 
40 #include "Riley.h"
41 #include "RixShadingUtils.h"
42 #include "RixPredefinedStrings.hpp"
43 
44 PXR_NAMESPACE_OPEN_SCOPE
45 
48 template <typename BASE>
49 class HdPrman_Gprim : public BASE {
50 public:
51  typedef BASE BaseType;
52 
53  HdPrman_Gprim(SdfPath const& id)
54  : BaseType(id)
55  {
56  }
57 
58  virtual ~HdPrman_Gprim() = default;
59 
60  void
61  Finalize(HdRenderParam *renderParam) override
62  {
63  HdPrman_Context *context =
64  static_cast<HdPrman_RenderParam*>(renderParam)->AcquireContext();
65 
66  riley::Riley *riley = context->riley;
67 
68  // Release retained conversions of coordSys bindings.
69  context->ReleaseCoordSysBindings(BASE::GetId());
70 
71  // Delete instances before deleting the prototypes they use.
72  for (const auto &id: _instanceIds) {
73  if (id != riley::GeometryInstanceId::InvalidId()) {
74  riley->DeleteGeometryInstance(
75  riley::GeometryPrototypeId::InvalidId(), id);
76  }
77  }
78  _instanceIds.clear();
79  for (const auto &id: _prototypeIds) {
80  if (id != riley::GeometryPrototypeId::InvalidId()) {
81  riley->DeleteGeometryPrototype(id);
82  }
83  }
84  _prototypeIds.clear();
85  }
86 
87  void Sync(HdSceneDelegate* sceneDelegate,
88  HdRenderParam* renderParam,
89  HdDirtyBits* dirtyBits,
90  TfToken const &reprToken) override;
91 
92 protected:
93  HdDirtyBits GetInitialDirtyBitsMask() const override = 0;
94 
95  HdDirtyBits
96  _PropagateDirtyBits(HdDirtyBits bits) const override
97  {
98  // XXX This is not ideal. Currently Riley requires us to provide
99  // all the values anytime we edit a volume. To make sure the values
100  // exist in the value cache, we propagte the dirty bits.value cache,
101  // we propagte the dirty bits.value cache, we propagte the dirty
102  // bits.value cache, we propagte the dirty bits.
103  return bits ? (bits | GetInitialDirtyBitsMask()) : bits;
104  }
105 
106  void
107  _InitRepr(TfToken const &reprToken,
108  HdDirtyBits *dirtyBits) override
109  {
110  TF_UNUSED(reprToken);
111  TF_UNUSED(dirtyBits);
112  // No-op
113  }
114 
115  // Provide a fallback material. Default grabs _fallbackMaterial
116  // from the context.
117  virtual riley::MaterialId
118  _GetFallbackMaterial(HdPrman_Context *context)
119  {
120  return context->fallbackMaterial;
121  }
122 
123  // Populate primType and primvars.
124  virtual RtPrimVarList
125  _ConvertGeometry(HdPrman_Context *context,
126  HdSceneDelegate *sceneDelegate,
127  const SdfPath &id,
128  RtUString *primType,
129  std::vector<HdGeomSubset> *geomSubsets) = 0;
130 
131  // This class does not support copying.
132  HdPrman_Gprim(const HdPrman_Gprim&) = delete;
133  HdPrman_Gprim &operator =(const HdPrman_Gprim&) = delete;
134 
135 protected:
136  std::vector<riley::GeometryPrototypeId> _prototypeIds;
137  std::vector<riley::GeometryInstanceId> _instanceIds;
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_Context *context =
152  static_cast<HdPrman_RenderParam*>(renderParam)->AcquireContext();
153 
154  // Update instance bindings.
155  BASE::_UpdateInstancer(sceneDelegate, dirtyBits);
156 
157  // Prim id
158  SdfPath const& id = BASE::GetId();
159  SdfPath const& instancerId = BASE::GetInstancerId();
160  const bool isHdInstance = !instancerId.IsEmpty();
161  // Prman has a default value for identifier:id of 0 (in case of ray miss),
162  // while Hydra treats id -1 as the clear value. We map Prman primId as
163  // (Hydra primId + 1) to get around this, here and in
164  // hdPrman/framebuffer.cpp.
165  const int32_t primId = BASE::GetPrimId() + 1;
166 
167  // Sample transform
169  sceneDelegate->SampleTransform(id, &xf);
170 
171  // Riley API.
172  riley::Riley *riley = context->riley;
173 
174  // Resolve material binding. Default to fallbackGprimMaterial.
175  if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
176 #if HD_API_VERSION < 37
177  BASE::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(),
178  sceneDelegate->GetMaterialId(id));
179 #else
180  BASE::SetMaterialId(sceneDelegate->GetMaterialId(id));
181 #endif
182  }
183  riley::MaterialId materialId = _GetFallbackMaterial(context);
184  riley::DisplacementId dispId = riley::DisplacementId::InvalidId();
185  const SdfPath & hdMaterialId = BASE::GetMaterialId();
186  HdPrman_ResolveMaterial(sceneDelegate, hdMaterialId, &materialId, &dispId);
187 
188  // Convert (and cache) coordinate systems.
189  riley::CoordinateSystemList coordSysList = {0, nullptr};
190  if (HdPrman_Context::RileyCoordSysIdVecRefPtr convertedCoordSys =
191  context->ConvertAndRetainCoordSysBindings(sceneDelegate, id)) {
192  coordSysList.count = convertedCoordSys->size();
193  coordSysList.ids = &(*convertedCoordSys)[0];
194  }
195 
196  // Hydra dirty bits corresponding to PRMan prototype primvars
197  // and instance attributes.
198  const int prmanPrimvarBits =
199  HdChangeTracker::DirtyPrimvar;
200  const int prmanAttrBits =
201  HdChangeTracker::DirtyVisibility |
202  HdChangeTracker::DirtyTransform;
203 
204  //
205  // Create or modify Riley geometry prototype(s).
206  //
207  std::vector<riley::MaterialId> subsetMaterialIds;
208  {
209  RtUString primType;
210  HdGeomSubsets geomSubsets;
211  RtPrimVarList primvars = _ConvertGeometry(context, sceneDelegate, id,
212  &primType, &geomSubsets);
213 
214  // Adjust _prototypeIds array.
215  const size_t oldCount = _prototypeIds.size();
216  const size_t newCount = std::max((size_t) 1, geomSubsets.size());
217  if (newCount != oldCount) {
218  for (const auto &oldPrototypeId: _prototypeIds) {
219  if (oldPrototypeId != riley::GeometryPrototypeId::InvalidId()) {
220  riley->DeleteGeometryPrototype(oldPrototypeId);
221  }
222  }
223  _prototypeIds.resize(newCount,
224  riley::GeometryPrototypeId::InvalidId());
225  }
226 
227  // Update Riley geom prototypes.
228  if (geomSubsets.empty()) {
229  // Common case: no subsets.
230  TF_VERIFY(newCount == 1);
231  TF_VERIFY(_prototypeIds.size() == 1);
232  if (_prototypeIds[0] == riley::GeometryPrototypeId::InvalidId()) {
233  _prototypeIds[0] =
234  riley->CreateGeometryPrototype(riley::UserId::DefaultId(),
235  primType, dispId,
236  primvars);
237  } else if (*dirtyBits & prmanPrimvarBits) {
238  riley->ModifyGeometryPrototype(primType, _prototypeIds[0],
239  &dispId, &primvars);
240  }
241  } else {
242  // Subsets case.
243  // We resolve materials here, and hold them in subsetMaterialIds:
244  // Displacement networks are passed to the geom prototype;
245  // material networks are passed to the instances.
246  subsetMaterialIds.reserve(geomSubsets.size());
247  for (size_t j=0; j < geomSubsets.size(); ++j) {
248  auto& prototypeId = _prototypeIds[j];
249  HdGeomSubset &subset = geomSubsets[j];
250  // Convert indices to int32_t and set as k_shade_faceset.
251  std::vector<int32_t> int32Indices(subset.indices.begin(),
252  subset.indices.end());
253  primvars.SetIntegerArray(RixStr.k_shade_faceset,
254  &int32Indices[0],
255  int32Indices.size());
256  // Look up material override for the subset (if any)
257  riley::MaterialId subsetMaterialId = materialId;
258  riley::DisplacementId subsetDispId = dispId;
259  if (subset.materialId.IsEmpty()) {
260  subset.materialId = hdMaterialId;
261  }
262  HdPrman_ResolveMaterial(sceneDelegate, subset.materialId,
263  &subsetMaterialId, &subsetDispId);
264  subsetMaterialIds.push_back(subsetMaterialId);
265  if (prototypeId == riley::GeometryPrototypeId::InvalidId()) {
266  prototypeId =
267  riley->CreateGeometryPrototype(
268  riley::UserId::DefaultId(),
269  primType, dispId, primvars);
270  } else if (*dirtyBits & prmanPrimvarBits) {
271  riley->ModifyGeometryPrototype(primType, prototypeId,
272  &dispId, &primvars);
273  }
274  }
275  }
276  }
277 
278  //
279  // Create or modify Riley geometry instances.
280  //
281  // Resolve attributes.
282  RtParamList attrs = context->ConvertAttributes(sceneDelegate, id);
283  if (!isHdInstance) {
284  // Simple case: Singleton instance.
285  // Convert transform.
287  for (size_t i=0; i < xf.count; ++i) {
288  xf_rt[i] = HdPrman_GfMatrixToRtMatrix(xf.values[i]);
289  }
290  const riley::Transform xform = {
291  unsigned(xf.count),
292  xf_rt.data(),
293  xf.times.data()};
294 
295  // Add "identifier:id" with the hydra prim id, and "identifier:id2"
296  // with the instance number.
297  // XXX Do we want to distinguish facesets here?
298  attrs.SetInteger(RixStr.k_identifier_id, primId);
299  attrs.SetInteger(RixStr.k_identifier_id2, 0);
300  // Adjust _instanceIds array.
301  const size_t newNumHdInstances = 1u;
302  const size_t oldCount = _instanceIds.size();
303  const size_t newCount = newNumHdInstances * _prototypeIds.size();
304  if (newCount != oldCount) {
305  for (const auto &oldInstanceId: _instanceIds) {
306  if (oldInstanceId != riley::GeometryInstanceId::InvalidId()) {
307  riley->DeleteGeometryInstance(
308  riley::GeometryPrototypeId::InvalidId(), oldInstanceId);
309  }
310  }
311  _instanceIds.resize(
312  newCount,
313  riley::GeometryInstanceId::InvalidId());
314  }
315  // Create or modify Riley instances corresponding to a
316  // singleton Hydra instance.
317  TF_VERIFY(_instanceIds.size() == _prototypeIds.size());
318  for (size_t j=0; j < _prototypeIds.size(); ++j) {
319  auto const& prototypeId = _prototypeIds[j];
320  auto& instanceId = _instanceIds[j];
321  auto instanceMaterialId = materialId;
322  // If a valid subset material was bound, use it.
323  if (!subsetMaterialIds.empty()) {
324  TF_VERIFY(j < subsetMaterialIds.size());
325  instanceMaterialId = subsetMaterialIds[j];
326  }
327  if (instanceId == riley::GeometryInstanceId::InvalidId()) {
328  instanceId = riley->CreateGeometryInstance(
329  riley::UserId::DefaultId(),
330  riley::GeometryPrototypeId::InvalidId(),
331  prototypeId, instanceMaterialId, coordSysList,
332  xform, attrs);
333  } else if (*dirtyBits & prmanAttrBits) {
334  riley->ModifyGeometryInstance(
335  riley::GeometryPrototypeId::InvalidId(),
336  instanceId, &instanceMaterialId, &coordSysList,
337  &xform, &attrs);
338  }
339  }
340  } else {
341  // Hydra Instancer case.
342  HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
343 
344  // Sync the hydra instancer (note: this is transitional code, it should
345  // be done by the render index...)
346  HdInstancer::_SyncInstancerAndParents(renderIndex, instancerId);
347 
348  HdPrmanInstancer *instancer = static_cast<HdPrmanInstancer*>(
349  renderIndex.GetInstancer(instancerId));
350  VtIntArray instanceIndices =
351  sceneDelegate->GetInstanceIndices(instancerId, id);
352 
353  // Sample per-instance transforms.
355  instancer->SampleInstanceTransforms(id, instanceIndices, &ixf);
356 
357  // Adjust _instanceIds array.
358  // Each Hydra instance produces a Riley instance for each
359  // geometry prototype. The number of geometry prototypes is
360  // based on the number of geometry subsets.
361  const size_t newNumHdInstances =
362  (ixf.count > 0) ? ixf.values[0].size() : 0;
363  const size_t oldCount = _instanceIds.size();
364  const size_t newCount = newNumHdInstances * _prototypeIds.size();
365  if (newCount != oldCount) {
366  for (const auto &oldInstanceId: _instanceIds) {
367  riley->DeleteGeometryInstance(
368  riley::GeometryPrototypeId::InvalidId(),
369  oldInstanceId);
370  }
371  _instanceIds.resize(newCount,
372  riley::GeometryInstanceId::InvalidId());
373  }
374 
375  // Add "identifier:id" with the hydra prim id.
376  attrs.SetInteger(RixStr.k_identifier_id, primId);
377 
378  // Retrieve instance categories.
379  std::vector<VtArray<TfToken>> instanceCategories =
380  sceneDelegate->GetInstanceCategories(instancerId);
381 
382  // Process each Hydra instance.
383  for (size_t i=0; i < newNumHdInstances; ++i) {
384  // XXX: Add support for nested instancing instance primvars.
385  size_t instanceIndex = 0;
386  if (i < instanceIndices.size()) {
387  instanceIndex = instanceIndices[i];
388  }
389 
390  // Create a copy of the instancer attrs.
391  RtParamList instanceAttrs = attrs;
392  instancer->GetInstancePrimvars(id, instanceIndex, instanceAttrs);
393  // Add "identifier:id2" with the instance number.
394  instanceAttrs.SetInteger(RixStr.k_identifier_id2, i);
395 
396  // Convert categories.
397  if (instanceIndex < instanceCategories.size()) {
398  context->ConvertCategoriesToAttributes(
399  id, instanceCategories[instanceIndex], instanceAttrs);
400  }
401 
402  // Convert transform.
403  // PRMan does not allow transforms on geometry prototypes,
404  // so we apply that transform (xf) to all the instances, here.
406  rt_xf(ixf.count);
407 
408  if (xf.count == 0 ||
409  (xf.count == 1 && (xf.values[0] == GfMatrix4d(1)))) {
410  // Expected case: prototype xf is constant & exactly identity.
411  for (size_t j=0; j < ixf.count; ++j) {
412  rt_xf[j] = HdPrman_GfMatrixToRtMatrix(ixf.values[j][i]);
413  }
414  } else {
415  // Multiply resampled prototype xf against instance xforms.
416  for (size_t j=0; j < ixf.count; ++j) {
417  GfMatrix4d xf_j = xf.Resample(ixf.times[j]);
418  rt_xf[j] =
419  HdPrman_GfMatrixToRtMatrix(xf_j * ixf.values[j][i]);
420  }
421  }
422  const riley::Transform xform =
423  { unsigned(ixf.count), rt_xf.data(), ixf.times.data() };
424 
425  // Create or modify Riley instances corresponding to this
426  // Hydra instance.
427  for (size_t j=0; j < _prototypeIds.size(); ++j) {
428  auto const& prototypeId = _prototypeIds[j];
429  auto& instanceId = _instanceIds[i*_prototypeIds.size() + j];
430  auto instanceMaterialId = materialId;
431  // If a valid subset material was bound, use it.
432  if (!subsetMaterialIds.empty()) {
433  TF_VERIFY(j < subsetMaterialIds.size());
434  instanceMaterialId = subsetMaterialIds[j];
435  }
436  if (instanceId == riley::GeometryInstanceId::InvalidId()) {
437  instanceId = riley->CreateGeometryInstance(
438  riley::UserId::DefaultId(),
439  riley::GeometryPrototypeId::InvalidId(),
440  prototypeId, instanceMaterialId, coordSysList,
441  xform, instanceAttrs);
442  } else if (*dirtyBits & prmanAttrBits) {
443  riley->ModifyGeometryInstance(
444  riley::GeometryPrototypeId::InvalidId(),
445  instanceId, &instanceMaterialId, &coordSysList,
446  &xform, &instanceAttrs);
447  }
448  }
449  }
450  }
451  *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
452 }
453 
454 PXR_NAMESPACE_CLOSE_SCOPE
455 
456 #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:116
Tracks changes from the HdSceneDelegate, providing invalidation cues to the render engine...
Definition: changeTracker.h:50
This is a small-vector class with local storage optimization, the local storage can be specified via ...
Definition: smallVector.h:177
HD_API HdInstancer * GetInstancer(SdfPath const &id) const
Returns the instancer of id.
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 ...
TYPE Resample(float u) const
Convience method for invoking HdResampleRawTimeSamples on this HdTimeSampleArray. ...
#define TF_UNUSED(x)
Stops compiler from producing unused argument or variable warnings.
Definition: tf.h:185
Basic Sdf data types.
Token for efficient comparison, assignment, and hashing of known strings.
Definition: token.h:87
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:49
A path value used to locate objects in layers or scenegraphs.
Definition: path.h:288
VtIntArray indices
The list of element indices contained in the subset.
Definition: geomSubset.h:56
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:417
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