1 //
2 // Copyright 2016 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/renderPassState.h"
34 #include "pxr/imaging/hd/task.h"
35 #include "pxr/imaging/hd/tokens.h"
36 
37 #include "pxr/imaging/hdSt/renderDelegate.h"
38 #include "pxr/imaging/hdSt/unitTestGLDrawing.h"
39 
40 #include "pxr/imaging/hdx/selectionTask.h"
41 #include "pxr/imaging/hdx/tokens.h"
42 #include "pxr/imaging/hdx/renderTask.h"
43 #include "pxr/imaging/hdx/unitTestDelegate.h"
44 #include "pxr/imaging/hdx/unitTestUtils.h"
45 
46 #include "pxr/imaging/hgi/hgi.h"
47 #include "pxr/imaging/hgi/tokens.h"
48 
49 #include "pxr/base/gf/frustum.h"
50 #include "pxr/base/gf/matrix4d.h"
51 #include "pxr/base/gf/vec2i.h"
52 #include "pxr/base/gf/vec4f.h"
53 #include "pxr/base/gf/vec4d.h"
54 #include "pxr/base/tf/errorMark.h"
55 
56 #include <iostream>
57 #include <unordered_set>
58 #include <memory>
59 
60 PXR_NAMESPACE_USING_DIRECTIVE
61 
62 TF_DEFINE_PRIVATE_TOKENS(
63     _tokens,
64 
65     (pickables)
66 );
67 
68 class My_TestGLDrawing : public HdSt_UnitTestGLDrawing {
69 public:
My_TestGLDrawing()70     My_TestGLDrawing()
71     {
72         SetCameraRotate(0, 0);
73         SetCameraTranslate(GfVec3f(0));
74     }
75     ~My_TestGLDrawing();
76 
77     void DrawScene();
78     void DrawMarquee();
79 
80     // HdSt_UnitTestGLDrawing overrides
81     void InitTest() override;
82     void UninitTest() override;
83     void DrawTest() override;
84     void OffscreenTest() override;
85 
86     void MousePress(int button, int x, int y, int modKeys) override;
87     void MouseRelease(int button, int x, int y, int modKeys) override;
88     void MouseMove(int x, int y, int modKeys) override;
89 
90 protected:
91     void _InitScene();
92     void _Clear();
93     HdSelectionSharedPtr _Pick(GfVec2i const& startPos, GfVec2i const& endPos);
94 
95 private:
96     // Hgi and HdDriver should be constructed before HdEngine to ensure they
97     // are destructed last. Hgi may be used during engine/delegate destruction.
98     HgiUniquePtr _hgi;
99     std::unique_ptr<HdDriver> _driver;
100     HdEngine _engine;
101     HdStRenderDelegate _renderDelegate;
102     HdRenderIndex *_renderIndex;
103     std::unique_ptr<Hdx_UnitTestDelegate> _delegate;
104 
105     HdRprimCollection _pickablesCol;
106     HdxUnitTestUtils::Marquee _marquee;
107     HdxSelectionTrackerSharedPtr _selTracker;
108 
109     GfVec2i _startPos, _endPos;
110 };
111 
112 ////////////////////////////////////////////////////////////
113 
114 GLuint vao;
115 
116 static GfMatrix4d
_GetTranslate(float tx,float ty,float tz)117 _GetTranslate(float tx, float ty, float tz)
118 {
119     GfMatrix4d m(1.0f);
120     m.SetRow(3, GfVec4f(tx, ty, tz, 1.0));
121     return m;
122 }
123 
~My_TestGLDrawing()124 My_TestGLDrawing::~My_TestGLDrawing()
125 {
126     delete _renderIndex;
127 }
128 
129 void
InitTest()130 My_TestGLDrawing::InitTest()
131 {
132     _hgi = Hgi::CreatePlatformDefaultHgi();
133     _driver.reset(new HdDriver{HgiTokens->renderDriver, VtValue(_hgi.get())});
134     _renderIndex = HdRenderIndex::New(&_renderDelegate, {_driver.get()});
135     TF_VERIFY(_renderIndex != nullptr);
136     _delegate.reset(new Hdx_UnitTestDelegate(_renderIndex));
137     _selTracker.reset(new HdxSelectionTracker);
138 
139     // prepare render task
140     SdfPath renderSetupTask("/renderSetupTask");
141     SdfPath renderTask("/renderTask");
142     SdfPath selectionTask("/selectionTask");
143     SdfPath pickTask("/pickTask");
144     _delegate->AddRenderSetupTask(renderSetupTask);
145     _delegate->AddRenderTask(renderTask);
146     _delegate->AddSelectionTask(selectionTask);
147     _delegate->AddPickTask(pickTask);
148 
149     // render task parameters.
150     VtValue vParam = _delegate->GetTaskParam(renderSetupTask, HdTokens->params);
151     HdxRenderTaskParams param = vParam.Get<HdxRenderTaskParams>();
152     param.enableLighting = true; // use default lighting
153     _delegate->SetTaskParam(renderSetupTask, HdTokens->params,
154                             VtValue(param));
155     _delegate->SetTaskParam(renderTask, HdTokens->collection,
156                             VtValue(HdRprimCollection(HdTokens->geometry,
157                                     HdReprSelector(HdReprTokens->hull))));
158     HdxSelectionTaskParams selParam;
159     selParam.enableSelection = true;
160     selParam.selectionColor = GfVec4f(1, 1, 0, 1);
161     selParam.locateColor = GfVec4f(1, 0, 1, 1);
162     _delegate->SetTaskParam(selectionTask, HdTokens->params,
163                             VtValue(selParam));
164 
165     // prepare scene
166     _InitScene();
167     SetCameraTranslate(GfVec3f(0, 0, -20));
168 
169     // picking related init
170     // The collection used for the ID render defaults to including the root path
171     // which essentially means that all scene graph prims are pickable.
172     //
173     // Worth noting that the collection's repr is set to refined (and not
174     // hull). When a prim has an authored repr, we'll use that instead, as
175     // the collection's forcedRepr defaults to false.
176     _pickablesCol = HdRprimCollection(_tokens->pickables,
177                         HdReprSelector(HdReprTokens->refined));
178     _marquee.InitGLResources();
179     // We have to unfortunately explictly add collections besides 'geometry'
180     // See HdRenderIndex constructor.
181     _delegate->GetRenderIndex().GetChangeTracker().AddCollection(_tokens->pickables);
182 
183     // XXX: Setup a VAO, the current drawing engine will not yet do this.
184     glGenVertexArrays(1, &vao);
185     glBindVertexArray(vao);
186     glBindVertexArray(0);
187 }
188 
189 void
UninitTest()190 My_TestGLDrawing::UninitTest()
191 {
192     _marquee.DestroyGLResources();
193 }
194 
195 void
_InitScene()196 My_TestGLDrawing::_InitScene()
197 {
198     _delegate->AddCube(SdfPath("/cube1"), _GetTranslate(-5, 0, 5));
199     _delegate->AddCube(SdfPath("/cube2"), _GetTranslate(-5, 0,-5));
200 }
201 
202 HdSelectionSharedPtr
_Pick(GfVec2i const & startPos,GfVec2i const & endPos)203 My_TestGLDrawing::_Pick(GfVec2i const& startPos, GfVec2i const& endPos)
204 {
205     HdxPickHitVector allHits;
206     HdxPickTaskContextParams p;
207     p.resolution = HdxUnitTestUtils::CalculatePickResolution(
208             startPos, endPos, GfVec2i(4,4));
209     p.resolveMode = HdxPickTokens->resolveUnique;
210     p.viewMatrix = GetViewMatrix();
211     p.projectionMatrix = HdxUnitTestUtils::ComputePickingProjectionMatrix(
212             startPos, endPos, GfVec2i(GetWidth(), GetHeight()), GetFrustum());
213     p.collection = _pickablesCol;
214     p.outHits = &allHits;
215 
216     HdTaskSharedPtrVector tasks;
217     tasks.push_back(_renderIndex->GetTask(SdfPath("/pickTask")));
218     VtValue pickParams(p);
219     _engine.SetTaskContextData(HdxPickTokens->pickParams, pickParams);
220     _engine.Execute(_renderIndex, &tasks);
221 
222     return HdxUnitTestUtils::TranslateHitsToSelection(
223             p.pickTarget, HdSelection::HighlightModeSelect, allHits);
224 }
225 
226 void
_Clear()227 My_TestGLDrawing::_Clear()
228 {
229     GLfloat clearColor[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
230     glClearBufferfv(GL_COLOR, 0, clearColor);
231 
232     GLfloat clearDepth[1] = { 1.0f };
233     glClearBufferfv(GL_DEPTH, 0, clearDepth);
234 }
235 
236 void
DrawTest()237 My_TestGLDrawing::DrawTest()
238 {
239     _Clear();
240 
241     DrawScene();
242 
243     DrawMarquee();
244 }
245 
246 void
OffscreenTest()247 My_TestGLDrawing::OffscreenTest()
248 {
249     DrawScene();
250     WriteToFile("color", "color1_unselected.png");
251 
252     // This test uses 2 collections:
253     // (i)  geometry
254     // (ii) pickables
255     // Picking in this test uses the 'refined' repr. See the collection
256     // created in Pick(..) for additional notes.
257     //
258     // We want to ensure that these collections' command buffers are updated
259     // correctly in the following scenarios:
260     // - changing a prim's refine level when using a different non-authored
261     // repr from that in the pickables collection
262     // - changing a prim's repr accounts for refineLevel dirtyness intercepted
263     // by the picking task.
264     //
265     // This test is run with the scene repr = 'hull'. We want to test several
266     // cases:
267     // (a) Change refine level on prim A with repr hull ==> Drawn image should
268     //  not change, since hull doesn't update topology on refinement. The
269     //  picking collection will however reflect this change (making this a
270     //  weird scenario)
271     //
272     // (b) Change repr on prim B ==> Drawn image should reflect the new repr
273     //
274     // (c) Change repr on prim A ==> Drawn image should reflect the refineLevel
275     //  update in (a) if its repr supports it (refined, refinedWire, refinedWireOnSurf)
276     //
277     // (d) Change refine level on prim B ==> Drawn image should reflect the refineLevel
278     //  if its repr supports it (refined, refinedWire, refinedWireOnSurf)
279 
280     HdSelection::HighlightMode mode = HdSelection::HighlightModeSelect;
281     HdSelectionSharedPtr selection;
282 
283     // (a)
284     {
285         std::cout << "Changing refine level of cube1" << std::endl;
286         _delegate->SetRefineLevel(SdfPath("/cube1"), 2);
287         // The repr corresponding to picking (refined) would be the one that
288         // handles the DirtyDisplayStyle bit, since we don't call DrawScene()
289         // before Pick(). We don't explicitly mark the collections dirty in this
290         // case, since refine level changes trigger change tracker garbage
291         // collection and the render delegate marks all collections dirty.
292         // See HdStRenderDelegate::CommitResources
293         // XXX: This is hacky.
294         //
295         // Since we're not overriding the scene repr, cube1 will still
296         // appear unrefined, since it defaults to the hull repr.
297         // However, the picking collection will render the refined version, and
298         // we won't be able to select cube1 by picking the unrefined version's
299         // left top corner.
300         selection = _Pick(GfVec2i(138, 60), GfVec2i(138, 60));
301         _selTracker->SetSelection(selection);
302         DrawScene();
303         WriteToFile("color", "color2_refine_wont_change_cube1.png");
304         TF_VERIFY(selection->GetSelectedPrimPaths(mode).size() == 0);
305     }
306 
307     // (b)
308     {
309         std::cout << "Changing repr for cube2" << std::endl;
310         _delegate->SetReprName(SdfPath("/cube2"),
311             HdReprTokens->refinedWireOnSurf);
312 
313         selection = _Pick(GfVec2i(152, 376), GfVec2i(152, 376));
314         _selTracker->SetSelection(selection);
315         DrawScene();
316         WriteToFile("color", "color3_repr_change_cube2.png");
317         TF_VERIFY(selection->GetSelectedPrimPaths(mode).size() == 1);
318         TF_VERIFY(selection->GetSelectedPrimPaths(mode)[0] == SdfPath("/cube2"));
319     }
320 
321     // (c)
322     {
323        std::cout << "Changing repr on cube1" << std::endl;
324 
325         _delegate->SetReprName(SdfPath("/cube1"), HdReprTokens->refinedWire);
326 
327         selection = _Pick(GfVec2i(176, 96), GfVec2i(179, 99));
328         _selTracker->SetSelection(selection);
329         DrawScene();
330         WriteToFile("color", "color4_repr_and_refine_change_cube1.png");
331         TF_VERIFY(selection->GetSelectedPrimPaths(mode).size() == 1);
332         TF_VERIFY(selection->GetSelectedPrimPaths(mode)[0] == SdfPath("/cube1"));
333     }
334 
335 
336     // (d)
337     {
338         std::cout << "## Changing refine level of cube2 ##" << std::endl;
339         _delegate->SetRefineLevel(SdfPath("/cube2"), 3);
340 
341         selection = _Pick(GfVec2i(152, 376), GfVec2i(152, 376));
342         _selTracker->SetSelection(selection);
343         DrawScene();
344         WriteToFile("color", "color5_refine_change_cube2.png");
345         TF_VERIFY(selection->GetSelectedPrimPaths(mode)[0] == SdfPath("/cube2"));
346     }
347 
348      // deselect
349     selection = _Pick(GfVec2i(0,0), GfVec2i(0,0));
350     _selTracker->SetSelection(selection);
351     DrawScene();
352     WriteToFile("color", "color6_unselected.png");
353 }
354 
355 void
DrawScene()356 My_TestGLDrawing::DrawScene()
357 {
358     _Clear();
359 
360     int width = GetWidth(), height = GetHeight();
361 
362     GfMatrix4d viewMatrix = GetViewMatrix();
363     GfFrustum frustum = GetFrustum();
364 
365     GfVec4d viewport(0, 0, width, height);
366 
367     GfMatrix4d projMatrix = frustum.ComputeProjectionMatrix();
368     _delegate->SetCamera(viewMatrix, projMatrix);
369 
370     glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
371 
372     SdfPath renderSetupTask("/renderSetupTask");
373     SdfPath renderTask("/renderTask");
374     SdfPath selectionTask("/selectionTask");
375 
376     // viewport
377     HdxRenderTaskParams param
378         = _delegate->GetTaskParam(
379             renderSetupTask, HdTokens->params).Get<HdxRenderTaskParams>();
380     param.viewport = viewport;
381     _delegate->SetTaskParam(renderSetupTask, HdTokens->params, VtValue(param));
382 
383     HdTaskSharedPtrVector tasks;
384     tasks.push_back(_renderIndex->GetTask(renderSetupTask));
385     tasks.push_back(_renderIndex->GetTask(renderTask));
386     tasks.push_back(_renderIndex->GetTask(selectionTask));
387 
388     glEnable(GL_DEPTH_TEST);
389     glBindVertexArray(vao);
390 
391     VtValue selTracker(_selTracker);
392     _engine.SetTaskContextData(HdxTokens->selectionState, selTracker);
393     _engine.Execute(_renderIndex, &tasks);
394 
395     glBindVertexArray(0);
396 }
397 
398 void
DrawMarquee()399 My_TestGLDrawing::DrawMarquee()
400 {
401     _marquee.Draw(GetWidth(), GetHeight(), _startPos, _endPos);
402 }
403 
404 void
MousePress(int button,int x,int y,int modKeys)405 My_TestGLDrawing::MousePress(int button, int x, int y, int modKeys)
406 {
407     HdSt_UnitTestGLDrawing::MousePress(button, x, y, modKeys);
408     _startPos = _endPos = GetMousePos();
409 }
410 
411 void
MouseRelease(int button,int x,int y,int modKeys)412 My_TestGLDrawing::MouseRelease(int button, int x, int y, int modKeys)
413 {
414     HdSt_UnitTestGLDrawing::MouseRelease(button, x, y, modKeys);
415 
416     if (!(modKeys & GarchGLDebugWindow::Alt)) {
417         HdSelectionSharedPtr selection = _Pick(_startPos, _endPos);
418         _selTracker->SetSelection(selection);
419     }
420     _startPos = _endPos = GfVec2i(0);
421 }
422 
423 void
MouseMove(int x,int y,int modKeys)424 My_TestGLDrawing::MouseMove(int x, int y, int modKeys)
425 {
426     HdSt_UnitTestGLDrawing::MouseMove(x, y, modKeys);
427 
428     if (!(modKeys & GarchGLDebugWindow::Alt)) {
429         _endPos = GetMousePos();
430     }
431 }
432 
433 void
BasicTest(int argc,char * argv[])434 BasicTest(int argc, char *argv[])
435 {
436     My_TestGLDrawing driver;
437 
438     driver.RunTest(argc, argv);
439 }
440 
main(int argc,char * argv[])441 int main(int argc, char *argv[])
442 {
443     TfErrorMark mark;
444 
445     BasicTest(argc, argv);
446 
447     if (mark.IsClean()) {
448         std::cout << "OK" << std::endl;
449         return EXIT_SUCCESS;
450     } else {
451         std::cout << "FAILED" << std::endl;
452         return EXIT_FAILURE;
453     }
454 }
455