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 PXR_IMAGING_HDX_PICK_TASK_H
25 #define PXR_IMAGING_HDX_PICK_TASK_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/imaging/hdx/api.h"
29 
30 #include "pxr/imaging/hd/enums.h"
31 #include "pxr/imaging/hd/renderPass.h"
32 #include "pxr/imaging/hd/renderPassState.h"
33 #include "pxr/imaging/hd/rprimCollection.h"
34 #include "pxr/imaging/hd/task.h"
35 
36 #include "pxr/base/tf/declarePtrs.h"
37 #include "pxr/base/gf/matrix4d.h"
38 #include "pxr/base/gf/vec2i.h"
39 #include "pxr/base/gf/vec2f.h"
40 #include "pxr/base/gf/vec4i.h"
41 #include "pxr/base/gf/vec4d.h"
42 #include "pxr/usd/sdf/path.h"
43 
44 #include <vector>
45 #include <memory>
46 
47 PXR_NAMESPACE_OPEN_SCOPE
48 
49 #define HDX_PICK_TOKENS              \
50     /* Task context */               \
51     (pickParams)                     \
52                                      \
53     /* Pick target */                \
54     (pickPrimsAndInstances)          \
55     (pickFaces)                      \
56     (pickEdges)                      \
57     (pickPoints)                     \
58                                      \
59     /* Resolve mode */               \
60     (resolveNearestToCamera)         \
61     (resolveNearestToCenter)         \
62     (resolveUnique)                  \
63     (resolveAll)
64 
65 TF_DECLARE_PUBLIC_TOKENS(HdxPickTokens, HDX_API, HDX_PICK_TOKENS);
66 
67 TF_DECLARE_WEAK_AND_REF_PTRS(GlfDrawTarget);
68 
69 class HdStRenderPassState;
70 using HdStShaderCodeSharedPtr = std::shared_ptr<class HdStShaderCode>;
71 
72 /// Pick task params. This contains render-style state (for example), but is
73 /// augmented by HdxPickTaskContextParams, which is passed in on the task
74 /// context.
75 struct HdxPickTaskParams
76 {
HdxPickTaskParamsHdxPickTaskParams77     HdxPickTaskParams()
78         : cullStyle(HdCullStyleNothing)
79         , enableSceneMaterials(true)
80     {}
81 
82     HdCullStyle cullStyle;
83     bool enableSceneMaterials;
84 };
85 
86 /// Picking hit structure. This is output by the pick task as a record of
87 /// what objects the picking query found.
88 struct HdxPickHit {
89     SdfPath delegateId;
90     SdfPath objectId;
91     SdfPath instancerId;
92     int instanceIndex;
93     int elementIndex;
94     int edgeIndex;
95     int pointIndex;
96     GfVec3f worldSpaceHitPoint;
97     GfVec3f worldSpaceHitNormal;
98     // normalizedDepth is in the range [0,1].
99     float normalizedDepth;
100 
IsValidHdxPickHit101     inline bool IsValid() const {
102         return !objectId.IsEmpty();
103     }
104 
105     HDX_API
106     size_t GetHash() const;
107 };
108 
109 using HdxPickHitVector = std::vector<HdxPickHit>;
110 
111 /// Pick task context params.  This contains task params that can't come from
112 /// the scene delegate (like resolution mode and pick location, that might
113 /// be resolved late), as well as the picking collection and the output
114 /// hit vector.
115 /// 'pickTarget': The target of the pick operation, which may influence the
116 ///     data filled in the HdxPickHit(s).
117 ///     The available options are:
118 ///         HdxPickTokens->pickPrimsAndInstances
119 ///         HdxPickTokens->pickFaces
120 ///         HdxPickTokens->pickEdges
121 ///         HdxPickTokens->pickPoints
122 ///
123 /// 'resolveMode': Dictates the resolution of which hit(s) are returned in
124 ///     'outHits'.
125 ///     The available options are:
126 ///     1. HdxPickTokens->resolveNearestToCamera : Returns the hit whose
127 ///         position is nearest to the camera
128 ///     2. HdxPickTokens->resolveNearestToCenter : Returns the hit whose
129 ///         position is nearest to center of the pick location/region.
130 ///     3. HdxPickTokens->resolveUnique : Returns the unique hits, by hashing
131 ///         the relevant member fields of HdxPickHit. The 'pickTarget'
132 ///         influences this operation. For e.g., the subprim indices are ignored
133 ///         when the pickTarget is pickPrimsAndInstances.
134 ///     4. HdxPickTokens->resolveAll: Returns all the hits for the pick location
135 ///         or region. The number of hits returned depends on the resolution
136 ///         used and may have duplicates.
137 ///
138 struct HdxPickTaskContextParams
139 {
140     using DepthMaskCallback = std::function<void(void)>;
141 
HdxPickTaskContextParamsHdxPickTaskContextParams142     HdxPickTaskContextParams()
143         : resolution(128, 128)
144         , pickTarget(HdxPickTokens->pickPrimsAndInstances)
145         , resolveMode(HdxPickTokens->resolveNearestToCamera)
146         , doUnpickablesOcclude(false)
147         , viewMatrix(1)
148         , projectionMatrix(1)
149         , clipPlanes()
150         , depthMaskCallback(nullptr)
151         , collection()
152         , outHits(nullptr)
153     {}
154 
155     GfVec2i resolution;
156     TfToken pickTarget;
157     TfToken resolveMode;
158     bool doUnpickablesOcclude;
159     GfMatrix4d viewMatrix;
160     GfMatrix4d projectionMatrix;
161     std::vector<GfVec4d> clipPlanes;
162     DepthMaskCallback depthMaskCallback;
163     HdRprimCollection collection;
164     HdxPickHitVector *outHits;
165 };
166 
167 /// \class HdxPickTask
168 ///
169 /// A task for running picking queries against the current scene.
170 /// This task generates an id buffer for a "pick frustum" (normally the
171 /// camera frustum with the near plane narrowed to an (x,y) location and a
172 /// pick radius); then it resolves that id buffer into a series of prim paths.
173 /// The "Hit" output also contains subprim picking results (e.g. picked face,
174 /// edge, point, instance) and the intersection point in scene worldspace.
175 ///
176 /// HdxPickTask takes an HdxPickTaskParams through the scene delegate, and
177 /// HdxPickTaskContextParams through the task context as "pickParams".
178 /// It produces a hit vector, in the task context as "pickHits".
179 class HdxPickTask : public HdTask
180 {
181 public:
182     HDX_API
183     HdxPickTask(HdSceneDelegate* delegate, SdfPath const& id);
184 
185     HDX_API
186     ~HdxPickTask() override;
187 
188     /// Sync the render pass resources
189     HDX_API
190     void Sync(HdSceneDelegate* delegate,
191               HdTaskContext* ctx,
192               HdDirtyBits* dirtyBits) override;
193 
194     /// Prepare the pick task
195     HDX_API
196     void Prepare(HdTaskContext* ctx,
197                  HdRenderIndex* renderIndex) override;
198 
199     /// Execute the pick task
200     HDX_API
201     void Execute(HdTaskContext* ctx) override;
202 
203     HDX_API
204     const TfTokenVector &GetRenderTags() const override;
205 
206     /// Utility: Given a UNorm8Vec4 pixel, unpack it into an int32 ID.
DecodeIDRenderColor(unsigned char const idColor[4])207     static inline int DecodeIDRenderColor(unsigned char const idColor[4]) {
208         return (int32_t(idColor[0] & 0xff) << 0)  |
209                (int32_t(idColor[1] & 0xff) << 8)  |
210                (int32_t(idColor[2] & 0xff) << 16) |
211                (int32_t(idColor[3] & 0xff) << 24);
212     }
213 
214 private:
215     HdxPickTaskParams _params;
216     HdxPickTaskContextParams _contextParams;
217     TfTokenVector _renderTags;
218 
219     // We need to cache a pointer to the render index so Execute() can
220     // map prim ID to paths.
221     HdRenderIndex *_index;
222 
223     void _InitIfNeeded(GfVec2i const& widthHeight);
224     void _ConditionStencilWithGLCallback(
225             HdxPickTaskContextParams::DepthMaskCallback maskCallback);
226 
227     bool _UseOcclusionPass() const;
228 
229     // Create a shared render pass each for pickables and unpickables
230     HdRenderPassSharedPtr _pickableRenderPass;
231     HdRenderPassSharedPtr _occluderRenderPass;
232 
233     // Having separate render pass states allows us to use different
234     // shader mixins if we choose to (we don't currently).
235     HdRenderPassStateSharedPtr _pickableRenderPassState;
236     HdRenderPassStateSharedPtr _occluderRenderPassState;
237 
238     // A single draw target is shared for all contexts.  Since the FBO cannot
239     // be shared, we clone the attachments on each request.
240     GlfDrawTargetRefPtr _drawTarget;
241 
242     HdxPickTask() = delete;
243     HdxPickTask(const HdxPickTask &) = delete;
244     HdxPickTask &operator =(const HdxPickTask &) = delete;
245 };
246 
247 /// A utility class for resolving ID buffers into hits.
248 class HdxPickResult {
249 public:
250 
251     // Pick result takes a tuple of ID buffers:
252     // - (primId, instanceId, elementId, edgeId, pointId)
253     // along with some geometric buffers:
254     // - (depth, Neye)
255     // ... and resolves them into a series of hits, using one of the
256     // algorithms specified below.
257     //
258     // index is used to fill in the HdxPickHit structure;
259     // pickTarget is used to determine what a valid hit is;
260     // viewMatrix, projectionMatrix, depthRange are used for unprojection
261     // to calculate the worldSpaceHitPosition and worldSpaceHitNormal.
262     // bufferSize is the size of the ID buffers, and subRect is the sub-region
263     // of the id buffers to iterate over in the resolution algorithm.
264     //
265     // All buffers need to be the same size, if passed in.  It's legal for
266     // only the depth and primId buffers to be provided; everything else is
267     // optional but provides a richer picking result.
268     HDX_API
269     HdxPickResult(int const* primIds,
270                   int const* instanceIds,
271                   int const* elementIds,
272                   int const* edgeIds,
273                   int const* pointIds,
274                   int const* neyes,
275                   float const* depths,
276                   HdRenderIndex const *index,
277                   TfToken const& pickTarget,
278                   GfMatrix4d const& viewMatrix,
279                   GfMatrix4d const& projectionMatrix,
280                   GfVec2f const& depthRange,
281                   GfVec2i const& bufferSize,
282                   GfVec4i const& subRect);
283 
284     HDX_API
285     ~HdxPickResult();
286 
287     HDX_API
288     HdxPickResult(HdxPickResult &&);
289     HDX_API
290     HdxPickResult& operator=(HdxPickResult &&);
291 
292     /// Return whether the result was given well-formed parameters.
293     HDX_API
294     bool IsValid() const;
295 
296     /// Return the nearest single hit point. Note that this method may be
297     /// considerably more efficient, as it only needs to construct a single
298     /// Hit object.
299     HDX_API
300     void ResolveNearestToCamera(HdxPickHitVector* allHits) const;
301 
302     /// Return the nearest single hit point from the center of the viewport.
303     /// Note that this method may be considerably more efficient, as it only
304     /// needs to construct a single Hit object.
305     HDX_API
306     void ResolveNearestToCenter(HdxPickHitVector* allHits) const;
307 
308     /// Return all hit points. Note that this may contain redundant objects,
309     /// however it allows access to all depth values for a given object.
310     HDX_API
311     void ResolveAll(HdxPickHitVector* allHits) const;
312 
313     /// Return the set of unique hit points, keeping only the nearest depth
314     /// value.
315     HDX_API
316     void ResolveUnique(HdxPickHitVector* allHits) const;
317 
318 private:
319     bool _ResolveHit(int index, int x, int y, float z, HdxPickHit* hit) const;
320     size_t _GetHash(int index) const;
321     bool _IsValidHit(int index) const;
322 
323     // Provide accessors for all of the ID buffers.  Since all but _primIds
324     // are optional, if the buffer doesn't exist just return -1 (== no hit).
_GetPrimId(int index)325     int _GetPrimId(int index) const {
326         return _primIds ? _primIds[index] : -1;
327     }
_GetInstanceId(int index)328     int _GetInstanceId(int index) const {
329         return _instanceIds ? _instanceIds[index] : -1;
330     }
_GetElementId(int index)331     int _GetElementId(int index) const {
332         return _elementIds ? _elementIds[index] : -1;
333     }
_GetEdgeId(int index)334     int _GetEdgeId(int index) const {
335         return _edgeIds ? _edgeIds[index] : -1;
336     }
_GetPointId(int index)337     int _GetPointId(int index) const {
338         return _pointIds ? _pointIds[index] : -1;
339     }
340 
341     // Provide an accessor for the normal buffer.  If the normal buffer is
342     // provided, this function will unpack the normal.  The fallback is
343     // GfVec3f(0.0f).
344     GfVec3f _GetNormal(int index) const;
345 
346     int const* _primIds;
347     int const* _instanceIds;
348     int const* _elementIds;
349     int const* _edgeIds;
350     int const* _pointIds;
351     int const* _neyes;
352     float const* _depths;
353     HdRenderIndex const *_index;
354     TfToken  _pickTarget;
355     GfMatrix4d _ndcToWorld;
356     GfMatrix4d _eyeToWorld;
357     GfVec2f _depthRange;
358     GfVec2i _bufferSize;
359     GfVec4i _subRect;
360 };
361 
362 // For sorting, order hits by ndc depth.
363 HDX_API
364 bool operator<(HdxPickHit const& lhs, HdxPickHit const& rhs);
365 
366 // VtValue requirements
367 HDX_API
368 std::ostream& operator<<(std::ostream& out, const HdxPickHit& h);
369 HDX_API
370 bool operator==(const HdxPickHit& lhs,
371                 const HdxPickHit& rhs);
372 HDX_API
373 bool operator!=(const HdxPickHit& lhs,
374                 const HdxPickHit& rhs);
375 
376 HDX_API
377 std::ostream& operator<<(std::ostream& out, const HdxPickTaskParams& pv);
378 HDX_API
379 bool operator==(const HdxPickTaskParams& lhs,
380                 const HdxPickTaskParams& rhs);
381 HDX_API
382 bool operator!=(const HdxPickTaskParams& lhs,
383                 const HdxPickTaskParams& rhs);
384 
385 HDX_API
386 std::ostream& operator<<(std::ostream& out, const HdxPickTaskContextParams& pv);
387 HDX_API
388 bool operator==(const HdxPickTaskContextParams& lhs,
389                 const HdxPickTaskContextParams& rhs);
390 HDX_API
391 bool operator!=(const HdxPickTaskContextParams& lhs,
392                 const HdxPickTaskContextParams& rhs);
393 PXR_NAMESPACE_CLOSE_SCOPE
394 
395 #endif // PXR_IMAGING_HDX_PICK_TASK_H
396