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