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 #include "hdPrman/instancer.h"
25 #include "pxr/imaging/hd/sceneDelegate.h"
26 #include "pxr/imaging/hd/tokens.h"
27 
28 #include "pxr/base/gf/vec2f.h"
29 #include "pxr/base/gf/vec3f.h"
30 #include "pxr/base/gf/vec4f.h"
31 #include "pxr/base/gf/matrix4d.h"
32 #include "pxr/base/gf/rotation.h"
33 #include "pxr/base/gf/quatf.h"
34 #include "pxr/base/gf/quath.h"
35 #include "pxr/base/gf/quaternion.h"
36 #include "pxr/base/tf/staticTokens.h"
37 
38 #include "RiTypesHelper.h"
39 
40 PXR_NAMESPACE_OPEN_SCOPE
41 
42 
HdPrmanInstancer(HdSceneDelegate * delegate,SdfPath const & id)43 HdPrmanInstancer::HdPrmanInstancer(HdSceneDelegate* delegate,
44                                      SdfPath const& id)
45     : HdInstancer(delegate, id)
46 {
47 }
48 
~HdPrmanInstancer()49 HdPrmanInstancer::~HdPrmanInstancer()
50 {
51 }
52 
53 void
Sync(HdSceneDelegate * delegate,HdRenderParam * renderParam,HdDirtyBits * dirtyBits)54 HdPrmanInstancer::Sync(HdSceneDelegate* delegate,
55                        HdRenderParam* renderParam,
56                        HdDirtyBits* dirtyBits)
57 {
58     _UpdateInstancer(delegate, dirtyBits);
59 
60     if (HdChangeTracker::IsAnyPrimvarDirty(*dirtyBits, GetId())) {
61         _SyncPrimvars(delegate, *dirtyBits);
62     }
63 }
64 
65 void
_SyncPrimvars(HdSceneDelegate * delegate,HdDirtyBits dirtyBits)66 HdPrmanInstancer::_SyncPrimvars(HdSceneDelegate *delegate,
67                                 HdDirtyBits dirtyBits)
68 {
69     HD_TRACE_FUNCTION();
70     HF_MALLOC_TAG_FUNCTION();
71 
72     SdfPath const& id = GetId();
73 
74     // Get the list of primvar names and then cache each one.
75     for (HdPrimvarDescriptor const& primvar:
76             delegate->GetPrimvarDescriptors(id, HdInterpolationInstance)) {
77         // Skip primvars that have special handling elsewhere.
78         // The transform primvars are all handled in
79         // SampleInstanceTransform.
80         if (primvar.name == HdInstancerTokens->instanceTransform ||
81                 primvar.name == HdInstancerTokens->rotate ||
82                 primvar.name == HdInstancerTokens->scale ||
83                 primvar.name == HdInstancerTokens->translate) {
84             continue;
85         }
86         if (HdChangeTracker::IsPrimvarDirty(dirtyBits, id, primvar.name)) {
87             VtValue value = delegate->Get(id, primvar.name);
88             if (!value.IsEmpty()) {
89                 _PrimvarValue &entry = _primvarMap[primvar.name];
90                 entry.desc = primvar;
91                 std::swap(entry.value, value);
92             }
93         }
94     }
95 }
96 
97 // Helper to accumulate sample times from the largest set of
98 // samples seen, up to maxNumSamples.
99 template <typename T1, typename T2, unsigned int C>
100 static void
_AccumulateSampleTimes(HdTimeSampleArray<T1,C> const & in,HdTimeSampleArray<T2,C> * out)101 _AccumulateSampleTimes(HdTimeSampleArray<T1,C> const& in,
102                        HdTimeSampleArray<T2,C> *out)
103 {
104     if (in.count > out->count) {
105         out->Resize(in.count);
106         out->times = in.times;
107     }
108 }
109 
110 void
SampleInstanceTransforms(SdfPath const & prototypeId,VtIntArray const & instanceIndices,HdTimeSampleArray<VtMatrix4dArray,HDPRMAN_MAX_TIME_SAMPLES> * sa)111 HdPrmanInstancer::SampleInstanceTransforms(
112     SdfPath const& prototypeId,
113     VtIntArray const& instanceIndices,
114     HdTimeSampleArray<VtMatrix4dArray, HDPRMAN_MAX_TIME_SAMPLES> *sa)
115 {
116     HD_TRACE_FUNCTION();
117     HF_MALLOC_TAG_FUNCTION();
118 
119     HdSceneDelegate *delegate = GetDelegate();
120     const SdfPath &instancerId = GetId();
121 
122     // Sample the inputs
123     HdTimeSampleArray<GfMatrix4d, HDPRMAN_MAX_TIME_SAMPLES> instancerXform;
124     HdTimeSampleArray<VtValue, HDPRMAN_MAX_TIME_SAMPLES> boxedInstanceXforms;
125     HdTimeSampleArray<VtValue, HDPRMAN_MAX_TIME_SAMPLES> boxedTranslates;
126     HdTimeSampleArray<VtValue, HDPRMAN_MAX_TIME_SAMPLES> boxedRotates;
127     HdTimeSampleArray<VtValue, HDPRMAN_MAX_TIME_SAMPLES> boxedScales;
128     delegate->SampleInstancerTransform(instancerId, &instancerXform);
129     delegate->SamplePrimvar(instancerId, HdInstancerTokens->instanceTransform,
130                             &boxedInstanceXforms);
131     delegate->SamplePrimvar(instancerId, HdInstancerTokens->translate,
132                             &boxedTranslates);
133     delegate->SamplePrimvar(instancerId, HdInstancerTokens->scale,
134                             &boxedScales);
135     delegate->SamplePrimvar(instancerId, HdInstancerTokens->rotate,
136                             &boxedRotates);
137 
138     // Unbox samples held as VtValues
139     HdTimeSampleArray<VtMatrix4dArray, HDPRMAN_MAX_TIME_SAMPLES> instanceXforms;
140     HdTimeSampleArray<VtVec3fArray, HDPRMAN_MAX_TIME_SAMPLES> translates;
141     HdTimeSampleArray<VtQuathArray, HDPRMAN_MAX_TIME_SAMPLES> rotates;
142     HdTimeSampleArray<VtVec3fArray, HDPRMAN_MAX_TIME_SAMPLES> scales;
143     instanceXforms.UnboxFrom(boxedInstanceXforms);
144     translates.UnboxFrom(boxedTranslates);
145     rotates.UnboxFrom(boxedRotates);
146     scales.UnboxFrom(boxedScales);
147 
148     // As a simple resampling strategy, find the input with the max #
149     // of samples and use its sample placement.  In practice we expect
150     // them to all be the same, i.e. to not require resampling.
151     sa->Resize(0);
152     _AccumulateSampleTimes(instancerXform, sa);
153     _AccumulateSampleTimes(instanceXforms, sa);
154     _AccumulateSampleTimes(translates, sa);
155     _AccumulateSampleTimes(scales, sa);
156     _AccumulateSampleTimes(rotates, sa);
157 
158     // Resample inputs and concatenate transformations.
159     //
160     // XXX:PERFORMANCE: This currently samples the transform arrays for
161     // all indices.  We should only do this work for the instances
162     // indicated in the instanceIndices array.
163     //
164     for (size_t i=0; i < sa->count; ++i) {
165         const float t = sa->times[i];
166         GfMatrix4d xf(1);
167         if (instancerXform.count > 0) {
168             xf = instancerXform.Resample(t);
169         }
170         VtMatrix4dArray ixf;
171         if (instanceXforms.count > 0) {
172             ixf = instanceXforms.Resample(t);
173         }
174         VtVec3fArray trans;
175         if (translates.count > 0) {
176             trans = translates.Resample(t);
177         }
178         VtQuathArray rot;
179         if (rotates.count > 0) {
180             rot = rotates.Resample(t);
181         }
182         VtVec3fArray scale;
183         if (scales.count > 0) {
184             scale = scales.Resample(t);
185         }
186 
187         // Concatenate transformations and filter to just the instanceIndices.
188         VtMatrix4dArray &ma = sa->values[i];
189         ma.resize(instanceIndices.size());
190         for (size_t j=0; j < instanceIndices.size(); ++j) {
191             ma[j] = xf;
192             size_t instanceIndex = instanceIndices[j];
193             if (trans.size() > instanceIndex) {
194                 GfMatrix4d t(1);
195                 t.SetTranslate(GfVec3d(trans[instanceIndex]));
196                 ma[j] = t * ma[j];
197             }
198             if (rot.size() > instanceIndex) {
199                 GfMatrix4d r(1);
200                 r.SetRotate(GfQuatd(rot[instanceIndex]));
201                 ma[j] = r * ma[j];
202             }
203             if (scale.size() > instanceIndex) {
204                 GfMatrix4d s(1);
205                 s.SetScale(GfVec3d(scale[instanceIndex]));
206                 ma[j] = s * ma[j];
207             }
208             if (ixf.size() > instanceIndex) {
209                 ma[j] = ixf[instanceIndex] * ma[j];
210             }
211         }
212     }
213 
214     // If there is a parent instancer, continue to unroll
215     // the child instances across the parent; otherwise we're done.
216     if (GetParentId().IsEmpty()) {
217         return;
218     }
219 
220     HdInstancer *parentInstancer =
221         GetDelegate()->GetRenderIndex().GetInstancer(GetParentId());
222     if (!TF_VERIFY(parentInstancer)) {
223         return;
224     }
225     HdPrmanInstancer *hdPrmanParentInstancer =
226         static_cast<HdPrmanInstancer*>(parentInstancer);
227 
228     // Multiply the instance samples against the parent instancer samples.
229     // The transforms taking nesting into account are computed by:
230     // parentTransforms = parentInstancer->ComputeInstanceTransforms(GetId())
231     // foreach (parentXf : parentTransforms, xf : transforms) {
232     //     parentXf * xf
233     // }
234     HdTimeSampleArray<VtMatrix4dArray, HDPRMAN_MAX_TIME_SAMPLES> parentXf;
235     VtIntArray instanceIndicesParent =
236         GetDelegate()->GetInstanceIndices(GetParentId(), GetId());
237     hdPrmanParentInstancer->
238         SampleInstanceTransforms(GetId(), instanceIndicesParent, &parentXf);
239     if (parentXf.count == 0 || parentXf.values[0].empty()) {
240         // No samples for parent instancer.
241         return;
242     }
243     // Move aside previously computed child xform samples to childXf.
244     HdTimeSampleArray<VtMatrix4dArray, HDPRMAN_MAX_TIME_SAMPLES> childXf(*sa);
245     // Merge sample times, taking the densest sampling.
246     _AccumulateSampleTimes(parentXf, sa);
247     // Apply parent xforms to the children.
248     for (size_t i=0; i < sa->count; ++i) {
249         const float t = sa->times[i];
250         // Resample transforms at the same time.
251         VtMatrix4dArray curParentXf = parentXf.Resample(t);
252         VtMatrix4dArray curChildXf = childXf.Resample(t);
253         // Multiply out each combination.
254         VtMatrix4dArray &result = sa->values[i];
255         result.resize(curParentXf.size() * curChildXf.size());
256         for (size_t j = 0; j < curParentXf.size(); ++j) {
257             for (size_t k = 0; k < curChildXf.size(); ++k) {
258                 result[j * curChildXf.size() + k] =
259                     curChildXf[k] * curParentXf[j];
260             }
261         }
262     }
263 }
264 
265 void
GetInstancePrimvars(SdfPath const & prototypeId,size_t instanceIndex,RtParamList & attrs)266 HdPrmanInstancer::GetInstancePrimvars(
267     SdfPath const& prototypeId,
268     size_t instanceIndex,
269     RtParamList& attrs)
270 {
271     for (auto entry: _primvarMap) {
272         HdPrimvarDescriptor const& primvar = entry.second.desc;
273         // Skip non-instance-rate primvars.
274         if (primvar.interpolation != HdInterpolationInstance) {
275             continue;
276         }
277         // Confirm that instance-rate primvars are array-valued
278         // and have sufficient dimensions.
279         VtValue const& val = entry.second.value;
280         if (instanceIndex >= val.GetArraySize()) {
281             TF_WARN("HdPrman: Instance-rate primvar has array size %zu; "
282                     "cannot provide a value for instance index %zu\n",
283                     val.GetArraySize(), instanceIndex);
284             continue;
285         }
286 
287         // Instance primvars with the "ri:attributes:" prefix correspond to
288         // renderman-namespace attributes and have that prefix stripped.
289         // All other primvars are in the "user:" namespace, so if they don't
290         // have that prefix we need to add it.
291         RtUString name;
292         static const char *userPrefix = "user:";
293         static const char *riAttrPrefix = "ri:attributes:";
294         if (!strncmp(entry.first.GetText(), userPrefix, strlen(userPrefix))) {
295             name = RtUString(entry.first.GetText());
296         } else if (!strncmp(entry.first.GetText(), riAttrPrefix,
297                             strlen(riAttrPrefix))) {
298             const char *strippedName = entry.first.GetText();
299             strippedName += strlen(riAttrPrefix);
300             name = RtUString(strippedName);
301         } else {
302             std::string mangled =
303                 TfStringPrintf("user:%s", entry.first.GetText());
304             name = RtUString(mangled.c_str());
305         }
306 
307         if (val.IsHolding<VtArray<float>>()) {
308             const VtArray<float>& v = val.UncheckedGet<VtArray<float>>();
309             attrs.SetFloat(name, v[instanceIndex]);
310         } else if (val.IsHolding<VtArray<int>>()) {
311             const VtArray<int>& v = val.UncheckedGet<VtArray<int>>();
312             attrs.SetInteger(name, v[instanceIndex]);
313         } else if (val.IsHolding<VtArray<GfVec2f>>()) {
314             const VtArray<GfVec2f>& v = val.UncheckedGet<VtArray<GfVec2f>>();
315             attrs.SetFloatArray(
316                 name, reinterpret_cast<const float*>(v.cdata()), 2);
317         } else if (val.IsHolding<VtArray<GfVec3f>>()) {
318             const GfVec3f& v =
319                 val.UncheckedGet<VtArray<GfVec3f>>()[instanceIndex];
320             if (primvar.role == HdPrimvarRoleTokens->color) {
321                 attrs.SetColor(name, RtColorRGB(v[0], v[1], v[2]));
322             } else if (primvar.role == HdPrimvarRoleTokens->point) {
323                 attrs.SetPoint(name, RtPoint3(v[0], v[1], v[2]));
324             } else if (primvar.role == HdPrimvarRoleTokens->normal) {
325                 attrs.SetPoint(name, RtNormal3(v[0], v[1], v[2]));
326             } else {
327                 attrs.SetVector(name, RtVector3(v[0], v[1], v[2]));
328             }
329         } else if (val.IsHolding<VtArray<GfVec4f>>()) {
330             const VtArray<GfVec4f>& v = val.UncheckedGet<VtArray<GfVec4f>>();
331             attrs.SetFloatArray(
332                 name, reinterpret_cast<const float*>(v.cdata()), 4);
333         } else if (val.IsHolding<VtArray<GfMatrix4d>>()) {
334             const VtArray<GfMatrix4d>& v =
335                 val.UncheckedGet<VtArray<GfMatrix4d>>();
336             attrs.SetMatrix(name,
337                 HdPrman_GfMatrixToRtMatrix(v[instanceIndex]));
338         } else if (val.IsHolding<VtArray<std::string>>()) {
339             const VtArray<std::string>& v =
340                 val.UncheckedGet<VtArray<std::string>>();
341             attrs.SetString(name, RtUString(v[instanceIndex].c_str()));
342         } else if (val.IsHolding<VtArray<TfToken>>()) {
343             const VtArray<TfToken>& v = val.UncheckedGet<VtArray<TfToken>>();
344             attrs.SetString(name, RtUString(v[instanceIndex].GetText()));
345         }
346     }
347 }
348 
349 PXR_NAMESPACE_CLOSE_SCOPE
350 
351