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