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
46 /// A mix-in template that adds shared gprim behavior to support
47 /// various HdRprim types.
48 template <typename BASE>
49 class HdPrman_Gprim : public BASE {
50 public:
51 typedef BASE BaseType;
52
HdPrman_Gprim(SdfPath const & id)53 HdPrman_Gprim(SdfPath const& id)
54 : BaseType(id)
55 {
56 }
57
58 virtual ~HdPrman_Gprim() = default;
59
60 void
Finalize(HdRenderParam * renderParam)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
_PropagateDirtyBits(HdDirtyBits bits)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
_InitRepr(TfToken const & reprToken,HdDirtyBits * dirtyBits)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
_GetFallbackMaterial(HdPrman_Context * context)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
Sync(HdSceneDelegate * sceneDelegate,HdRenderParam * renderParam,HdDirtyBits * dirtyBits,TfToken const & reprToken)142 HdPrman_Gprim<BASE>::Sync(HdSceneDelegate* sceneDelegate,
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
168 HdTimeSampleArray<GfMatrix4d, HDPRMAN_MAX_TIME_SAMPLES> xf;
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 // Transfer material opinions of primvars.
215 HdPrman_TransferMaterialPrimvarOpinions(sceneDelegate, hdMaterialId,
216 primvars);
217
218 // Adjust _prototypeIds array.
219 const size_t oldCount = _prototypeIds.size();
220 const size_t newCount = std::max((size_t) 1, geomSubsets.size());
221 if (newCount != oldCount) {
222 for (const auto &oldPrototypeId: _prototypeIds) {
223 if (oldPrototypeId != riley::GeometryPrototypeId::InvalidId()) {
224 riley->DeleteGeometryPrototype(oldPrototypeId);
225 }
226 }
227 _prototypeIds.resize(newCount,
228 riley::GeometryPrototypeId::InvalidId());
229 }
230
231 // Update Riley geom prototypes.
232 if (geomSubsets.empty()) {
233 // Common case: no subsets.
234 TF_VERIFY(newCount == 1);
235 TF_VERIFY(_prototypeIds.size() == 1);
236 if (_prototypeIds[0] == riley::GeometryPrototypeId::InvalidId()) {
237 _prototypeIds[0] =
238 riley->CreateGeometryPrototype(riley::UserId::DefaultId(),
239 primType, dispId,
240 primvars);
241 } else if (*dirtyBits & prmanPrimvarBits) {
242 riley->ModifyGeometryPrototype(primType, _prototypeIds[0],
243 &dispId, &primvars);
244 }
245 } else {
246 // Subsets case.
247 // We resolve materials here, and hold them in subsetMaterialIds:
248 // Displacement networks are passed to the geom prototype;
249 // material networks are passed to the instances.
250 subsetMaterialIds.reserve(geomSubsets.size());
251 for (size_t j=0; j < geomSubsets.size(); ++j) {
252 auto& prototypeId = _prototypeIds[j];
253 HdGeomSubset &subset = geomSubsets[j];
254 // Convert indices to int32_t and set as k_shade_faceset.
255 std::vector<int32_t> int32Indices(subset.indices.begin(),
256 subset.indices.end());
257 primvars.SetIntegerArray(RixStr.k_shade_faceset,
258 &int32Indices[0],
259 int32Indices.size());
260 // Look up material override for the subset (if any)
261 riley::MaterialId subsetMaterialId = materialId;
262 riley::DisplacementId subsetDispId = dispId;
263 if (subset.materialId.IsEmpty()) {
264 subset.materialId = hdMaterialId;
265 }
266 HdPrman_ResolveMaterial(sceneDelegate, subset.materialId,
267 &subsetMaterialId, &subsetDispId);
268 subsetMaterialIds.push_back(subsetMaterialId);
269 if (prototypeId == riley::GeometryPrototypeId::InvalidId()) {
270 prototypeId =
271 riley->CreateGeometryPrototype(
272 riley::UserId::DefaultId(),
273 primType, dispId, primvars);
274 } else if (*dirtyBits & prmanPrimvarBits) {
275 riley->ModifyGeometryPrototype(primType, prototypeId,
276 &dispId, &primvars);
277 }
278 }
279 }
280 }
281
282 //
283 // Create or modify Riley geometry instances.
284 //
285 // Resolve attributes.
286 RtParamList attrs = context->ConvertAttributes(sceneDelegate, id);
287 if (!isHdInstance) {
288 // Simple case: Singleton instance.
289 // Convert transform.
290 TfSmallVector<RtMatrix4x4, HDPRMAN_MAX_TIME_SAMPLES> xf_rt(xf.count);
291 for (size_t i=0; i < xf.count; ++i) {
292 xf_rt[i] = HdPrman_GfMatrixToRtMatrix(xf.values[i]);
293 }
294 const riley::Transform xform = {
295 unsigned(xf.count),
296 xf_rt.data(),
297 xf.times.data()};
298
299 // Add "identifier:id" with the hydra prim id, and "identifier:id2"
300 // with the instance number.
301 // XXX Do we want to distinguish facesets here?
302 attrs.SetInteger(RixStr.k_identifier_id, primId);
303 attrs.SetInteger(RixStr.k_identifier_id2, 0);
304 // Adjust _instanceIds array.
305 const size_t newNumHdInstances = 1u;
306 const size_t oldCount = _instanceIds.size();
307 const size_t newCount = newNumHdInstances * _prototypeIds.size();
308 if (newCount != oldCount) {
309 for (const auto &oldInstanceId: _instanceIds) {
310 if (oldInstanceId != riley::GeometryInstanceId::InvalidId()) {
311 riley->DeleteGeometryInstance(
312 riley::GeometryPrototypeId::InvalidId(), oldInstanceId);
313 }
314 }
315 _instanceIds.resize(
316 newCount,
317 riley::GeometryInstanceId::InvalidId());
318 }
319 // Create or modify Riley instances corresponding to a
320 // singleton Hydra instance.
321 TF_VERIFY(_instanceIds.size() == _prototypeIds.size());
322 for (size_t j=0; j < _prototypeIds.size(); ++j) {
323 auto const& prototypeId = _prototypeIds[j];
324 auto& instanceId = _instanceIds[j];
325 auto instanceMaterialId = materialId;
326 // If a valid subset material was bound, use it.
327 if (!subsetMaterialIds.empty()) {
328 TF_VERIFY(j < subsetMaterialIds.size());
329 instanceMaterialId = subsetMaterialIds[j];
330 }
331 if (instanceId == riley::GeometryInstanceId::InvalidId()) {
332 instanceId = riley->CreateGeometryInstance(
333 riley::UserId::DefaultId(),
334 riley::GeometryPrototypeId::InvalidId(),
335 prototypeId, instanceMaterialId, coordSysList,
336 xform, attrs);
337 } else if (*dirtyBits & prmanAttrBits) {
338 riley->ModifyGeometryInstance(
339 riley::GeometryPrototypeId::InvalidId(),
340 instanceId, &instanceMaterialId, &coordSysList,
341 &xform, &attrs);
342 }
343 }
344 } else {
345 // Hydra Instancer case.
346 HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
347
348 // Sync the hydra instancer (note: this is transitional code, it should
349 // be done by the render index...)
350 HdInstancer::_SyncInstancerAndParents(renderIndex, instancerId);
351
352 HdPrmanInstancer *instancer = static_cast<HdPrmanInstancer*>(
353 renderIndex.GetInstancer(instancerId));
354 VtIntArray instanceIndices =
355 sceneDelegate->GetInstanceIndices(instancerId, id);
356
357 // Sample per-instance transforms.
358 HdTimeSampleArray<VtMatrix4dArray, HDPRMAN_MAX_TIME_SAMPLES> ixf;
359 instancer->SampleInstanceTransforms(id, instanceIndices, &ixf);
360
361 // Adjust _instanceIds array.
362 // Each Hydra instance produces a Riley instance for each
363 // geometry prototype. The number of geometry prototypes is
364 // based on the number of geometry subsets.
365 const size_t newNumHdInstances =
366 (ixf.count > 0) ? ixf.values[0].size() : 0;
367 const size_t oldCount = _instanceIds.size();
368 const size_t newCount = newNumHdInstances * _prototypeIds.size();
369 if (newCount != oldCount) {
370 for (const auto &oldInstanceId: _instanceIds) {
371 riley->DeleteGeometryInstance(
372 riley::GeometryPrototypeId::InvalidId(),
373 oldInstanceId);
374 }
375 _instanceIds.resize(newCount,
376 riley::GeometryInstanceId::InvalidId());
377 }
378
379 // Add "identifier:id" with the hydra prim id.
380 attrs.SetInteger(RixStr.k_identifier_id, primId);
381
382 // Retrieve instance categories.
383 std::vector<VtArray<TfToken>> instanceCategories =
384 sceneDelegate->GetInstanceCategories(instancerId);
385
386 // Process each Hydra instance.
387 for (size_t i=0; i < newNumHdInstances; ++i) {
388 // XXX: Add support for nested instancing instance primvars.
389 size_t instanceIndex = 0;
390 if (i < instanceIndices.size()) {
391 instanceIndex = instanceIndices[i];
392 }
393
394 // Create a copy of the instancer attrs.
395 RtParamList instanceAttrs = attrs;
396 instancer->GetInstancePrimvars(id, instanceIndex, instanceAttrs);
397 // Add "identifier:id2" with the instance number.
398 instanceAttrs.SetInteger(RixStr.k_identifier_id2, i);
399
400 // Convert categories.
401 if (instanceIndex < instanceCategories.size()) {
402 context->ConvertCategoriesToAttributes(
403 id, instanceCategories[instanceIndex], instanceAttrs);
404 }
405
406 // Convert transform.
407 // PRMan does not allow transforms on geometry prototypes,
408 // so we apply that transform (xf) to all the instances, here.
409 TfSmallVector<RtMatrix4x4, HDPRMAN_MAX_TIME_SAMPLES>
410 rt_xf(ixf.count);
411
412 if (xf.count == 0 ||
413 (xf.count == 1 && (xf.values[0] == GfMatrix4d(1)))) {
414 // Expected case: prototype xf is constant & exactly identity.
415 for (size_t j=0; j < ixf.count; ++j) {
416 rt_xf[j] = HdPrman_GfMatrixToRtMatrix(ixf.values[j][i]);
417 }
418 } else {
419 // Multiply resampled prototype xf against instance xforms.
420 for (size_t j=0; j < ixf.count; ++j) {
421 GfMatrix4d xf_j = xf.Resample(ixf.times[j]);
422 rt_xf[j] =
423 HdPrman_GfMatrixToRtMatrix(xf_j * ixf.values[j][i]);
424 }
425 }
426 const riley::Transform xform =
427 { unsigned(ixf.count), rt_xf.data(), ixf.times.data() };
428
429 // Create or modify Riley instances corresponding to this
430 // Hydra instance.
431 for (size_t j=0; j < _prototypeIds.size(); ++j) {
432 auto const& prototypeId = _prototypeIds[j];
433 auto& instanceId = _instanceIds[i*_prototypeIds.size() + j];
434 auto instanceMaterialId = materialId;
435 // If a valid subset material was bound, use it.
436 if (!subsetMaterialIds.empty()) {
437 TF_VERIFY(j < subsetMaterialIds.size());
438 instanceMaterialId = subsetMaterialIds[j];
439 }
440 if (instanceId == riley::GeometryInstanceId::InvalidId()) {
441 instanceId = riley->CreateGeometryInstance(
442 riley::UserId::DefaultId(),
443 riley::GeometryPrototypeId::InvalidId(),
444 prototypeId, instanceMaterialId, coordSysList,
445 xform, instanceAttrs);
446 } else if (*dirtyBits & prmanAttrBits) {
447 riley->ModifyGeometryInstance(
448 riley::GeometryPrototypeId::InvalidId(),
449 instanceId, &instanceMaterialId, &coordSysList,
450 &xform, &instanceAttrs);
451 }
452 }
453 }
454 }
455 *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
456 }
457
458 PXR_NAMESPACE_CLOSE_SCOPE
459
460 #endif // EXT_RMANPKG_24_0_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
461