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