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