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