1 //
2 // Copyright 2020 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 "pxr/pxr.h"
25 
26 #include "pxr/imaging/garch/glApi.h"
27 
28 #include "pxr/imaging/garch/glDebugWindow.h"
29 #include "pxr/imaging/glf/drawTarget.h"
30 
31 #include "pxr/imaging/hd/driver.h"
32 #include "pxr/imaging/hd/engine.h"
33 #include "pxr/imaging/hd/mesh.h"
34 #include "pxr/imaging/hd/renderPassState.h"
35 #include "pxr/imaging/hd/selection.h"
36 #include "pxr/imaging/hd/task.h"
37 #include "pxr/imaging/hd/tokens.h"
38 
39 #include "pxr/imaging/hdSt/renderDelegate.h"
40 #include "pxr/imaging/hdSt/unitTestGLDrawing.h"
41 
42 #include "pxr/imaging/hdx/pickTask.h"
43 #include "pxr/imaging/hdx/selectionTask.h"
44 #include "pxr/imaging/hdx/selectionTracker.h"
45 #include "pxr/imaging/hdx/tokens.h"
46 #include "pxr/imaging/hdx/renderTask.h"
47 #include "pxr/imaging/hdx/unitTestDelegate.h"
48 #include "pxr/imaging/hdx/unitTestUtils.h"
49 
50 #include "pxr/imaging/hgi/hgi.h"
51 #include "pxr/imaging/hgi/tokens.h"
52 
53 #include "pxr/base/gf/frustum.h"
54 #include "pxr/base/gf/matrix4d.h"
55 #include "pxr/base/gf/vec2i.h"
56 #include "pxr/base/gf/vec4f.h"
57 #include "pxr/base/gf/vec4d.h"
58 #include "pxr/base/tf/errorMark.h"
59 
60 #include <iostream>
61 #include <memory>
62 
63 PXR_NAMESPACE_USING_DIRECTIVE
64 
65 TF_DEFINE_PRIVATE_TOKENS(
66     _tokens,
67 
68     (meshPoints)
69     (pickables)
70 );
71 
72 class My_TestGLDrawing : public HdSt_UnitTestGLDrawing {
73 public:
My_TestGLDrawing()74     My_TestGLDrawing()
75     {
76         SetCameraRotate(0, 0);
77         SetCameraTranslate(GfVec3f(0));
78         _reprName = HdReprTokens->wireOnSurf;
79         _refineLevel = 0;
80     }
81     ~My_TestGLDrawing();
82 
83     void DrawScene();
84     void DrawMarquee();
85 
86     // HdSt_UnitTestGLDrawing overrides
87     void InitTest() override;
88     void UninitTest() override;
89     void DrawTest() override;
90     void OffscreenTest() override;
91 
92     void MousePress(int button, int x, int y, int modKeys) override;
93     void MouseRelease(int button, int x, int y, int modKeys) override;
94     void MouseMove(int x, int y, int modKeys) override;
95 
96 protected:
97     void ParseArgs(int argc, char *argv[]) override;
98     void _InitScene();
99     void _Clear();
100     HdSelectionSharedPtr _Pick(
101         GfVec2i const& startPos, GfVec2i const& endPos,
102         TfToken const& pickTarget,
103         TfToken const& resolveMode,
104         HdxPickHitVector *allHits);
105 
106 private:
107     // Hgi and HdDriver should be constructed before HdEngine to ensure they
108     // are destructed last. Hgi may be used during engine/delegate destruction.
109     HgiUniquePtr _hgi;
110     std::unique_ptr<HdDriver> _driver;
111     HdEngine _engine;
112     HdStRenderDelegate _renderDelegate;
113     HdRenderIndex *_renderIndex;
114     std::unique_ptr<Hdx_UnitTestDelegate> _delegate;
115 
116     HdRprimCollection _pickablesCol;
117     HdxUnitTestUtils::Marquee _marquee;
118     HdxSelectionTrackerSharedPtr _selTracker;
119 
120     TfToken _reprName;
121     int _refineLevel;
122     GfVec2i _startPos, _endPos;
123 };
124 
125 ////////////////////////////////////////////////////////////
126 
127 GLuint vao;
128 
129 static GfMatrix4d
_GetTranslate(float tx,float ty,float tz)130 _GetTranslate(float tx, float ty, float tz)
131 {
132     GfMatrix4d m(1.0f);
133     m.SetRow(3, GfVec4f(tx, ty, tz, 1.0));
134     return m;
135 }
136 
~My_TestGLDrawing()137 My_TestGLDrawing::~My_TestGLDrawing()
138 {
139     delete _renderIndex;
140 }
141 
142 void
InitTest()143 My_TestGLDrawing::InitTest()
144 {
145     _hgi = Hgi::CreatePlatformDefaultHgi();
146     _driver.reset(new HdDriver{HgiTokens->renderDriver, VtValue(_hgi.get())});
147 
148     _renderIndex = HdRenderIndex::New(&_renderDelegate, {_driver.get()});
149     TF_VERIFY(_renderIndex != nullptr);
150     _delegate.reset(new Hdx_UnitTestDelegate(_renderIndex));
151     _delegate->SetRefineLevel(_refineLevel);
152     _selTracker.reset(new HdxSelectionTracker);
153 
154     // Add a meshPoints repr since it isn't populated in
155     // HdRenderIndex::_ConfigureReprs
156     HdMesh::ConfigureRepr(_tokens->meshPoints,
157                           HdMeshReprDesc(HdMeshGeomStylePoints,
158                                          HdCullStyleNothing,
159                                          HdMeshReprDescTokens->pointColor,
160                                          /*flatShadingEnabled=*/true,
161                                          /*blendWireframeColor=*/false));
162 
163     // prepare render task
164     SdfPath renderSetupTask("/renderSetupTask");
165     SdfPath renderTask("/renderTask");
166     SdfPath selectionTask("/selectionTask");
167     SdfPath pickTask("/pickTask");
168     _delegate->AddRenderSetupTask(renderSetupTask);
169     _delegate->AddRenderTask(renderTask);
170     _delegate->AddSelectionTask(selectionTask);
171     _delegate->AddPickTask(pickTask);
172 
173     // render task parameters.
174     VtValue vParam = _delegate->GetTaskParam(renderSetupTask, HdTokens->params);
175     HdxRenderTaskParams param = vParam.Get<HdxRenderTaskParams>();
176     param.enableLighting = true; // use default lighting
177     _delegate->SetTaskParam(renderSetupTask, HdTokens->params,
178                             VtValue(param));
179     // Use wireframe and enable points for edge and point picking.
180     const auto sceneReprSel = HdReprSelector(HdReprTokens->wireOnSurf,
181                                              HdReprTokens->disabled,
182                                              _tokens->meshPoints);
183     _delegate->SetTaskParam(renderTask, HdTokens->collection,
184                             VtValue(HdRprimCollection(HdTokens->geometry,
185                                                       sceneReprSel)));
186     HdxSelectionTaskParams selParam;
187     selParam.enableSelection = true;
188     selParam.selectionColor = GfVec4f(1, 1, 0, 1);
189     selParam.locateColor = GfVec4f(1, 0, 1, 1);
190     _delegate->SetTaskParam(selectionTask, HdTokens->params,
191                             VtValue(selParam));
192 
193     // prepare scene
194     _InitScene();
195     SetCameraTranslate(GfVec3f(0, 0, -20));
196 
197     // picking related init
198     _pickablesCol = HdRprimCollection(_tokens->pickables,
199                                       sceneReprSel);
200     _marquee.InitGLResources();
201     _delegate->GetRenderIndex().GetChangeTracker().AddCollection(
202         _tokens->pickables);
203 
204     // XXX: Setup a VAO, the current drawing engine will not yet do this.
205     glGenVertexArrays(1, &vao);
206     glBindVertexArray(vao);
207     glBindVertexArray(0);
208 }
209 
210 void
UninitTest()211 My_TestGLDrawing::UninitTest()
212 {
213     _marquee.DestroyGLResources();
214 }
215 
216 void
_InitScene()217 My_TestGLDrawing::_InitScene()
218 {
219     _delegate->AddCube(SdfPath("/cube0"), _GetTranslate( 5, 0, 5));
220     _delegate->AddCube(SdfPath("/cube1"), _GetTranslate(-5, 0, 5));
221     _delegate->AddCube(SdfPath("/cube2"), _GetTranslate(-5, 0,-5));
222     _delegate->AddCube(SdfPath("/cube3"), _GetTranslate( 5, 0,-5));
223 
224     {
225         _delegate->AddInstancer(SdfPath("/instancerTop"));
226         _delegate->AddCube(SdfPath("/protoTop"),
227                          GfMatrix4d(1), false, SdfPath("/instancerTop"));
228 
229         std::vector<SdfPath> prototypes;
230         prototypes.push_back(SdfPath("/protoTop"));
231 
232         VtVec3fArray scale(3);
233         VtVec4fArray rotate(3);
234         VtVec3fArray translate(3);
235         VtIntArray prototypeIndex(3);
236 
237         scale[0] = GfVec3f(1);
238         rotate[0] = GfVec4f(0);
239         translate[0] = GfVec3f(3, 0, 2);
240         prototypeIndex[0] = 0;
241 
242         scale[1] = GfVec3f(1);
243         rotate[1] = GfVec4f(0);
244         translate[1] = GfVec3f(0, 0, 2);
245         prototypeIndex[1] = 0;
246 
247         scale[2] = GfVec3f(1);
248         rotate[2] = GfVec4f(0);
249         translate[2] = GfVec3f(-3, 0, 2);
250         prototypeIndex[2] = 0;
251 
252         _delegate->SetInstancerProperties(SdfPath("/instancerTop"),
253                                         prototypeIndex,
254                                         scale, rotate, translate);
255     }
256 
257     {
258         _delegate->AddInstancer(SdfPath("/instancerBottom"));
259         _delegate->AddTet(SdfPath("/protoBottom"),
260                          GfMatrix4d(1), false, SdfPath("/instancerBottom"));
261         _delegate->SetRefineLevel(SdfPath("/protoBottom"), 2);
262 
263         std::vector<SdfPath> prototypes;
264         prototypes.push_back(SdfPath("/protoBottom"));
265 
266         VtVec3fArray scale(3);
267         VtVec4fArray rotate(3);
268         VtVec3fArray translate(3);
269         VtIntArray prototypeIndex(3);
270 
271         scale[0] = GfVec3f(1);
272         rotate[0] = GfVec4f(0);
273         translate[0] = GfVec3f(3, 0, -2);
274         prototypeIndex[0] = 0;
275 
276         scale[1] = GfVec3f(1);
277         rotate[1] = GfVec4f(0);
278         translate[1] = GfVec3f(0, 0, -2);
279         prototypeIndex[1] = 0;
280 
281         scale[2] = GfVec3f(1);
282         rotate[2] = GfVec4f(0);
283         translate[2] = GfVec3f(-3, 0, -2);
284         prototypeIndex[2] = 0;
285 
286         _delegate->SetInstancerProperties(SdfPath("/instancerBottom"),
287                                         prototypeIndex,
288                                         scale, rotate, translate);
289     }
290 }
291 
292 HdSelectionSharedPtr
_Pick(GfVec2i const & startPos,GfVec2i const & endPos,TfToken const & pickTarget,TfToken const & resolveMode,HdxPickHitVector * allHits)293 My_TestGLDrawing::_Pick(GfVec2i const& startPos, GfVec2i const& endPos,
294                         TfToken const& pickTarget,
295                         TfToken const& resolveMode,
296                         HdxPickHitVector *allHits)
297 {
298     if (!allHits) {
299         return HdSelectionSharedPtr();
300     }
301     HdxPickTaskContextParams p;
302     p.resolution = HdxUnitTestUtils::CalculatePickResolution(
303         startPos, endPos, GfVec2i(4,4));
304     p.pickTarget = pickTarget;
305     p.resolveMode = resolveMode;
306     p.viewMatrix = GetViewMatrix();
307     p.projectionMatrix = HdxUnitTestUtils::ComputePickingProjectionMatrix(
308         startPos, endPos, GfVec2i(GetWidth(), GetHeight()), GetFrustum());
309     p.collection = _pickablesCol;
310     p.outHits = allHits;
311 
312     HdTaskSharedPtrVector tasks;
313     tasks.push_back(_renderIndex->GetTask(SdfPath("/pickTask")));
314     VtValue pickParams(p);
315     _engine.SetTaskContextData(HdxPickTokens->pickParams, pickParams);
316     _engine.Execute(_renderIndex, &tasks);
317 
318     return HdxUnitTestUtils::TranslateHitsToSelection(
319         p.pickTarget, HdSelection::HighlightModeSelect, *allHits);
320 }
321 
322 void
_Clear()323 My_TestGLDrawing::_Clear()
324 {
325     GLfloat clearColor[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
326     glClearBufferfv(GL_COLOR, 0, clearColor);
327 
328     GLfloat clearDepth[1] = { 1.0f };
329     glClearBufferfv(GL_DEPTH, 0, clearDepth);
330 }
331 
332 void
DrawTest()333 My_TestGLDrawing::DrawTest()
334 {
335     _Clear();
336 
337     DrawScene();
338 
339     DrawMarquee();
340 }
341 
342 void
OffscreenTest()343 My_TestGLDrawing::OffscreenTest()
344 {
345     GLfloat clearColor[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
346     glClearBufferfv(GL_COLOR, 0, clearColor);
347 
348     GLfloat clearDepth[1] = { 1.0f };
349     glClearBufferfv(GL_DEPTH, 0, clearDepth);
350 
351     DrawScene();
352 
353     HdxPickHitVector allHits;
354     const HdSelection::HighlightMode mode = HdSelection::HighlightModeSelect;
355     // Use the same "marquee" style area pick with different resolve modes
356     // This picks:
357     //      instances 0 and 1 of /protoTop and /protoBottom
358     //      cube0 and cube3
359     GfVec2i pickStartPos(270, 80);
360     GfVec2i pickEndPos(500, 400);
361 
362     // 1. Nearest to camera
363     {
364         HdSelectionSharedPtr selection = _Pick(pickStartPos, pickEndPos,
365             HdxPickTokens->pickPrimsAndInstances,
366             HdxPickTokens->resolveNearestToCamera,
367             &allHits);
368         TF_VERIFY(allHits.size() == 1);
369         TF_VERIFY(selection->GetSelectedPrimPaths(mode).size() == 1);
370         TF_VERIFY(selection->GetSelectedPrimPaths(mode)[0] ==
371                  SdfPath("/protoTop"));
372     }
373 
374     // 2. Nearest to center (of pick region)
375     {
376         allHits.clear();
377         HdSelectionSharedPtr selection = _Pick(pickStartPos, pickEndPos,
378             HdxPickTokens->pickPrimsAndInstances,
379             HdxPickTokens->resolveNearestToCenter,
380             &allHits);
381         TF_VERIFY(allHits.size() == 1);
382         TF_VERIFY(selection->GetSelectedPrimPaths(mode).size() == 1);
383         TF_VERIFY(selection->GetSelectedPrimPaths(mode)[0]
384                   == SdfPath("/protoBottom"));
385     }
386 
387     // 3. Unique
388     {
389         // The pick target influences what a "unique" hit is, so cycle through
390         // all the supported pickTarget, and verify that a different number of
391         // hits is returned each time.
392         TfTokenVector pickTargets = {
393             HdxPickTokens->pickPrimsAndInstances,
394             HdxPickTokens->pickFaces,
395             HdxPickTokens->pickEdges,
396             HdxPickTokens->pickPoints
397         };
398         size_t expectedHitCount[] = {
399             6  /*primsAndInstances*/,
400             69 /*faces*/,
401            135 /*edges*/,
402             41 /*points*/};
403 
404         for (size_t i = 0; i < pickTargets.size(); i++) {
405             allHits.clear();
406             HdSelectionSharedPtr selection = _Pick(pickStartPos, pickEndPos,
407                 pickTargets[i],
408                 HdxPickTokens->resolveUnique,
409                 &allHits);
410             std::cout << "allHits: " << allHits.size()
411                       << " expectedHitCount:  " << expectedHitCount[i]
412                       << std::endl;
413             TF_VERIFY(allHits.size() == expectedHitCount[i]);
414         }
415     }
416 
417     // 4. All
418     {
419         allHits.clear();
420         HdSelectionSharedPtr selection = _Pick(pickStartPos, pickEndPos,
421             HdxPickTokens->pickPrimsAndInstances,
422             HdxPickTokens->resolveAll,
423             &allHits);
424         TF_VERIFY(allHits.size() == 22515);
425     }
426 }
427 
428 void
DrawScene()429 My_TestGLDrawing::DrawScene()
430 {
431     _Clear();
432 
433     int width = GetWidth(), height = GetHeight();
434 
435     GfMatrix4d viewMatrix = GetViewMatrix();
436     GfFrustum frustum = GetFrustum();
437 
438     GfVec4d viewport(0, 0, width, height);
439 
440     GfMatrix4d projMatrix = frustum.ComputeProjectionMatrix();
441     _delegate->SetCamera(viewMatrix, projMatrix);
442 
443     glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
444 
445     SdfPath renderSetupTask("/renderSetupTask");
446     SdfPath renderTask("/renderTask");
447     SdfPath selectionTask("/selectionTask");
448 
449     // viewport
450     HdxRenderTaskParams param
451         = _delegate->GetTaskParam(
452             renderSetupTask, HdTokens->params).Get<HdxRenderTaskParams>();
453     param.viewport = viewport;
454     _delegate->SetTaskParam(renderSetupTask, HdTokens->params, VtValue(param));
455 
456     HdTaskSharedPtrVector tasks;
457     tasks.push_back(_renderIndex->GetTask(renderSetupTask));
458     tasks.push_back(_renderIndex->GetTask(renderTask));
459     tasks.push_back(_renderIndex->GetTask(selectionTask));
460 
461     glEnable(GL_DEPTH_TEST);
462     glBindVertexArray(vao);
463 
464     VtValue selTracker(_selTracker);
465     _engine.SetTaskContextData(HdxTokens->selectionState, selTracker);
466     _engine.Execute(&_delegate->GetRenderIndex(), &tasks);
467 
468     glBindVertexArray(0);
469 }
470 
471 void
DrawMarquee()472 My_TestGLDrawing::DrawMarquee()
473 {
474     _marquee.Draw(GetWidth(), GetHeight(), _startPos, _endPos);
475 }
476 
477 void
MousePress(int button,int x,int y,int modKeys)478 My_TestGLDrawing::MousePress(int button, int x, int y, int modKeys)
479 {
480     HdSt_UnitTestGLDrawing::MousePress(button, x, y, modKeys);
481     _startPos = _endPos = GetMousePos();
482 }
483 
484 void
MouseRelease(int button,int x,int y,int modKeys)485 My_TestGLDrawing::MouseRelease(int button, int x, int y, int modKeys)
486 {
487     HdSt_UnitTestGLDrawing::MouseRelease(button, x, y, modKeys);
488 
489     if (!(modKeys & GarchGLDebugWindow::Alt)) {
490         std::cout << "Pick region: (" << _startPos << ") to (" << _endPos
491                   << ")" << std::endl;
492         HdxPickHitVector allHits;
493         HdSelectionSharedPtr selection = _Pick(_startPos, _endPos,
494             HdxPickTokens->pickPrimsAndInstances,
495             HdxPickTokens->resolveNearestToCenter,
496             &allHits);
497         _selTracker->SetSelection(selection);
498     }
499     _startPos = _endPos = GfVec2i(0);
500 }
501 
502 void
MouseMove(int x,int y,int modKeys)503 My_TestGLDrawing::MouseMove(int x, int y, int modKeys)
504 {
505     HdSt_UnitTestGLDrawing::MouseMove(x, y, modKeys);
506 
507     if (!(modKeys & GarchGLDebugWindow::Alt)) {
508         _endPos = GetMousePos();
509     }
510 }
511 
512 void
ParseArgs(int argc,char * argv[])513 My_TestGLDrawing::ParseArgs(int argc, char *argv[])
514 {
515     for (int i=0; i<argc; ++i) {
516         std::string arg(argv[i]);
517         if (arg == "--repr") {
518             _reprName = TfToken(argv[++i]);
519         } else if (arg == "--refineLevel") {
520             _refineLevel = atoi(argv[++i]);
521         }
522     }
523 }
524 
525 void
BasicTest(int argc,char * argv[])526 BasicTest(int argc, char *argv[])
527 {
528     My_TestGLDrawing driver;
529 
530     driver.RunTest(argc, argv);
531 }
532 
main(int argc,char * argv[])533 int main(int argc, char *argv[])
534 {
535     TfErrorMark mark;
536 
537     BasicTest(argc, argv);
538 
539     if (mark.IsClean()) {
540         std::cout << "OK" << std::endl;
541         return EXIT_SUCCESS;
542     } else {
543         std::cout << "FAILED" << std::endl;
544         return EXIT_FAILURE;
545     }
546 }
547 
548