1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB). |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | // TODO Remove in Qt6 |
30 | #include <QtCore/qcompilerdetection.h> |
31 | QT_WARNING_DISABLE_DEPRECATED |
32 | |
33 | #include <QtTest/QtTest> |
34 | #include <Qt3DRender/private/entity_p.h> |
35 | #include <Qt3DRender/private/entity_p_p.h> |
36 | #include <Qt3DRender/private/nodemanagers_p.h> |
37 | #include <Qt3DRender/private/managers_p.h> |
38 | #include <Qt3DRender/private/entityvisitor_p.h> |
39 | #include <Qt3DRender/private/entityaccumulator_p.h> |
40 | |
41 | #include <Qt3DRender/QCameraLens> |
42 | #include <Qt3DCore/QTransform> |
43 | |
44 | #include <Qt3DRender/QEnvironmentLight> |
45 | #include <Qt3DRender/QMesh> |
46 | #include <Qt3DRender/QMaterial> |
47 | #include <Qt3DRender/QLayer> |
48 | #include <Qt3DRender/QShaderData> |
49 | #include <Qt3DRender/QGeometryRenderer> |
50 | #include <Qt3DRender/QObjectPicker> |
51 | #include <Qt3DRender/QComputeCommand> |
52 | #include <Qt3DCore/QArmature> |
53 | |
54 | #include "testrenderer.h" |
55 | |
56 | typedef Qt3DCore::QNodeId (*UuidMethod)(Qt3DRender::Render::Entity *); |
57 | typedef QVector<Qt3DCore::QNodeId> (*UuidListMethod)(Qt3DRender::Render::Entity *); |
58 | |
59 | using namespace Qt3DCore; |
60 | using namespace Qt3DRender; |
61 | using namespace Qt3DRender::Render; |
62 | |
63 | QNodeId transformUuid(Entity *entity) { return entity->componentUuid<Transform>(); } |
64 | QNodeId cameraLensUuid(Entity *entity) { return entity->componentUuid<CameraLens>(); } |
65 | QNodeId materialUuid(Entity *entity) { return entity->componentUuid<Material>(); } |
66 | QNodeId geometryRendererUuid(Entity *entity) { return entity->componentUuid<GeometryRenderer>(); } |
67 | QNodeId objectPickerUuid(Entity *entity) { return entity->componentUuid<ObjectPicker>(); } |
68 | QNodeId computeJobUuid(Entity *entity) { return entity->componentUuid<ComputeCommand>(); } |
69 | QNodeId armatureUuid(Entity *entity) { return entity->componentUuid<Armature>(); } |
70 | |
71 | QVector<QNodeId> layersUuid(Entity *entity) { return entity->componentsUuid<Layer>(); } |
72 | QVector<QNodeId> shadersUuid(Entity *entity) { return entity->componentsUuid<ShaderData>(); } |
73 | QVector<QNodeId> environmentLightsUuid(Entity *entity) { return entity->componentsUuid<EnvironmentLight>(); } |
74 | |
75 | class CompleteVisitor : public EntityVisitor |
76 | { |
77 | public: |
78 | CompleteVisitor(NodeManagers *manager) : EntityVisitor(manager) { } |
79 | |
80 | int count = 0; |
81 | Operation visit(Entity *) { count++; return Continue; } |
82 | }; |
83 | |
84 | class EnabledVisitor : public EntityVisitor |
85 | { |
86 | public: |
87 | EnabledVisitor(NodeManagers *manager) : EntityVisitor(manager) { } |
88 | |
89 | int count = 0; |
90 | Operation visit(Entity *e) { count++; return e->isEnabled() ? Continue : Prune; } |
91 | }; |
92 | |
93 | class tst_RenderEntity : public QObject |
94 | { |
95 | Q_OBJECT |
96 | public: |
97 | tst_RenderEntity() {} |
98 | ~tst_RenderEntity() {} |
99 | |
100 | private slots: |
101 | |
102 | void checkInitialAndCleanUpState_data() |
103 | { |
104 | QTest::addColumn< QList<QComponent*> >(name: "components" ); |
105 | |
106 | QList<QComponent *> components = QList<QComponent *>() |
107 | << new Qt3DCore::QTransform |
108 | << new QGeometryRenderer |
109 | << new QCameraLens |
110 | << new QMaterial |
111 | << new QObjectPicker |
112 | << new QLayer |
113 | << new QShaderData |
114 | << new QComputeCommand |
115 | << new QEnvironmentLight |
116 | << new QArmature; |
117 | |
118 | QTest::newRow(dataTag: "all components" ) << components; |
119 | } |
120 | |
121 | void checkInitialAndCleanUpState() |
122 | { |
123 | // GIVEN |
124 | QFETCH(const QList<QComponent*>, components); |
125 | |
126 | TestRenderer renderer; |
127 | NodeManagers nodeManagers; |
128 | Qt3DRender::Render::Entity entity; |
129 | entity.setRenderer(&renderer); |
130 | entity.setNodeManagers(&nodeManagers); |
131 | |
132 | // THEN |
133 | QCOMPARE(renderer.dirtyBits(), 0); |
134 | |
135 | // THEN |
136 | QVERIFY(entity.componentUuid<Transform>().isNull()); |
137 | QVERIFY(entity.componentUuid<CameraLens>().isNull()); |
138 | QVERIFY(entity.componentUuid<Material>().isNull()); |
139 | QVERIFY(entity.componentUuid<GeometryRenderer>().isNull()); |
140 | QVERIFY(entity.componentUuid<ObjectPicker>().isNull()); |
141 | QVERIFY(entity.componentUuid<ComputeCommand>().isNull()); |
142 | QVERIFY(entity.componentsUuid<Layer>().isEmpty()); |
143 | QVERIFY(entity.componentsUuid<ShaderData>().isEmpty()); |
144 | QVERIFY(entity.componentsUuid<EnvironmentLight>().isEmpty()); |
145 | QVERIFY(entity.componentUuid<Armature>().isNull()); |
146 | QVERIFY(!entity.isBoundingVolumeDirty()); |
147 | QVERIFY(entity.childrenHandles().isEmpty()); |
148 | QVERIFY(entity.layerIds().isEmpty()); |
149 | QCOMPARE(entity.renderer(), &renderer); |
150 | |
151 | QCOMPARE(renderer.dirtyBits(), 0); |
152 | |
153 | // WHEN |
154 | for (QComponent *component : components) |
155 | EntityPrivate::get(node: &entity)->componentAdded(frontend: component); |
156 | |
157 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
158 | renderer.resetDirty(); |
159 | |
160 | // THEN |
161 | QVERIFY(!entity.componentUuid<Transform>().isNull()); |
162 | QVERIFY(!entity.componentUuid<CameraLens>().isNull()); |
163 | QVERIFY(!entity.componentUuid<Material>().isNull()); |
164 | QVERIFY(!entity.componentUuid<GeometryRenderer>().isNull()); |
165 | QVERIFY(!entity.componentUuid<ObjectPicker>().isNull()); |
166 | QVERIFY(!entity.componentUuid<ComputeCommand>().isNull()); |
167 | QVERIFY(!entity.componentsUuid<Layer>().isEmpty()); |
168 | QVERIFY(!entity.componentsUuid<ShaderData>().isEmpty()); |
169 | QVERIFY(!entity.componentsUuid<EnvironmentLight>().isEmpty()); |
170 | QVERIFY(!entity.componentUuid<Armature>().isNull()); |
171 | QVERIFY(entity.isBoundingVolumeDirty()); |
172 | QVERIFY(entity.childrenHandles().isEmpty()); |
173 | QVERIFY(!entity.layerIds().isEmpty()); |
174 | QCOMPARE(renderer.dirtyBits(), 0); |
175 | bool containsAll = entity.containsComponentsOfType<Transform, |
176 | CameraLens, Material, GeometryRenderer, ObjectPicker, ComputeCommand, Armature>(); |
177 | QVERIFY(containsAll); |
178 | |
179 | // WHEN |
180 | entity.cleanup(); |
181 | |
182 | // THEN |
183 | QVERIFY(entity.componentUuid<Transform>().isNull()); |
184 | QVERIFY(entity.componentUuid<CameraLens>().isNull()); |
185 | QVERIFY(entity.componentUuid<Material>().isNull()); |
186 | QVERIFY(entity.componentUuid<GeometryRenderer>().isNull()); |
187 | QVERIFY(entity.componentUuid<ObjectPicker>().isNull()); |
188 | QVERIFY(entity.componentUuid<QComputeCommand>().isNull()); |
189 | QVERIFY(entity.componentsUuid<Layer>().isEmpty()); |
190 | QVERIFY(entity.componentsUuid<ShaderData>().isEmpty()); |
191 | QVERIFY(entity.componentsUuid<EnvironmentLight>().isEmpty()); |
192 | QVERIFY(entity.componentUuid<Armature>().isNull()); |
193 | QVERIFY(!entity.isBoundingVolumeDirty()); |
194 | QVERIFY(entity.childrenHandles().isEmpty()); |
195 | QVERIFY(entity.layerIds().isEmpty()); |
196 | containsAll = entity.containsComponentsOfType<Transform, |
197 | CameraLens, Material, GeometryRenderer, ObjectPicker, ComputeCommand, Armature>(); |
198 | QVERIFY(!containsAll); |
199 | |
200 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
201 | renderer.resetDirty(); |
202 | } |
203 | |
204 | void checkEntityReparenting() |
205 | { |
206 | // GIVEN |
207 | TestRenderer renderer; |
208 | NodeManagers nodeManagers; |
209 | Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC; |
210 | |
211 | auto backendA = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityA); |
212 | auto backendB = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityB); |
213 | auto backendC = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityC); |
214 | |
215 | // THEN |
216 | QVERIFY(backendA->parent() == nullptr); |
217 | QVERIFY(backendB->parent() == nullptr); |
218 | QVERIFY(backendC->parent() == nullptr); |
219 | |
220 | QVERIFY(backendA->childrenHandles().isEmpty()); |
221 | QVERIFY(backendB->childrenHandles().isEmpty()); |
222 | QVERIFY(backendC->childrenHandles().isEmpty()); |
223 | |
224 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
225 | renderer.resetDirty(); |
226 | |
227 | // WHEN |
228 | auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) { |
229 | Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(id: entity.id()); |
230 | backendEntity->syncFromFrontEnd(frontEnd: &entity, firstTime: false); |
231 | }; |
232 | |
233 | // reparent B to A and C to B. |
234 | frontendEntityB.setParent(&frontendEntityA); |
235 | sendParentChange(frontendEntityB); |
236 | frontendEntityC.setParent(&frontendEntityB); |
237 | sendParentChange(frontendEntityC); |
238 | |
239 | // THEN |
240 | QVERIFY(backendA->parent() == nullptr); |
241 | QVERIFY(backendB->parent() == backendA); |
242 | QVERIFY(backendC->parent() == backendB); |
243 | |
244 | QCOMPARE(backendA->childrenHandles().count(), 1); |
245 | QCOMPARE(backendB->childrenHandles().count(), 1); |
246 | QVERIFY(backendC->childrenHandles().isEmpty()); |
247 | |
248 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
249 | renderer.resetDirty(); |
250 | |
251 | // WHEN - reparent C to A |
252 | frontendEntityC.setParent(&frontendEntityA); |
253 | sendParentChange(frontendEntityC); |
254 | |
255 | // THEN |
256 | QVERIFY(backendA->parent() == nullptr); |
257 | QVERIFY(backendB->parent() == backendA); |
258 | QVERIFY(backendC->parent() == backendA); |
259 | |
260 | QCOMPARE(backendA->childrenHandles().size(), 2); |
261 | QVERIFY(backendB->childrenHandles().isEmpty()); |
262 | QVERIFY(backendC->childrenHandles().isEmpty()); |
263 | |
264 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
265 | renderer.resetDirty(); |
266 | |
267 | // WHEN - reparent B to null. |
268 | frontendEntityB.setParent(static_cast<Qt3DCore::QNode *>(nullptr)); |
269 | sendParentChange(frontendEntityB); |
270 | |
271 | // THEN |
272 | QVERIFY(backendA->parent() == nullptr); |
273 | QVERIFY(backendB->parent() == nullptr); |
274 | QVERIFY(backendC->parent() == backendA); |
275 | |
276 | QCOMPARE(backendA->childrenHandles().count(), 1); |
277 | QVERIFY(!backendA->childrenHandles().contains(backendB->handle())); |
278 | QVERIFY(backendB->childrenHandles().isEmpty()); |
279 | QVERIFY(backendC->childrenHandles().isEmpty()); |
280 | |
281 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
282 | renderer.resetDirty(); |
283 | } |
284 | |
285 | void checkEntityCleanup() |
286 | { |
287 | // GIVEN |
288 | TestRenderer renderer; |
289 | NodeManagers nodeManagers; |
290 | Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC; |
291 | |
292 | auto backendA = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityA); |
293 | auto backendB = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityB); |
294 | auto backendC = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityC); |
295 | |
296 | // WHEN |
297 | auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) { |
298 | Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(id: entity.id()); |
299 | backendEntity->syncFromFrontEnd(frontEnd: &entity, firstTime: false); |
300 | }; |
301 | |
302 | // reparent B and C to A. |
303 | frontendEntityB.setParent(&frontendEntityA); |
304 | sendParentChange(frontendEntityB); |
305 | frontendEntityC.setParent(&frontendEntityA); |
306 | sendParentChange(frontendEntityC); |
307 | |
308 | // THEN |
309 | QVERIFY(backendA->parent() == nullptr); |
310 | QVERIFY(backendB->parent() == backendA); |
311 | QVERIFY(backendC->parent() == backendA); |
312 | |
313 | QCOMPARE(backendA->childrenHandles().count(), 2); |
314 | QVERIFY(backendB->childrenHandles().isEmpty()); |
315 | QVERIFY(backendC->childrenHandles().isEmpty()); |
316 | |
317 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
318 | renderer.resetDirty(); |
319 | |
320 | // WHEN - cleaning up a child |
321 | backendC->cleanup(); |
322 | |
323 | // THEN - the child's parent should be null and it |
324 | // should be removed from its parent's list of children |
325 | QVERIFY(backendA->parent() == nullptr); |
326 | QVERIFY(backendB->parent() == backendA); |
327 | QVERIFY(backendC->parent() == nullptr); |
328 | |
329 | QCOMPARE(backendA->childrenHandles().count(), 1); |
330 | QVERIFY(!backendA->childrenHandles().contains(backendC->handle())); |
331 | QVERIFY(backendB->childrenHandles().isEmpty()); |
332 | QVERIFY(backendC->childrenHandles().isEmpty()); |
333 | |
334 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
335 | renderer.resetDirty(); |
336 | |
337 | // WHEN (cleaning up parent) |
338 | backendA->cleanup(); |
339 | |
340 | // THEN (it's children's parent should be set to null) |
341 | QVERIFY(backendA->parent() == nullptr); |
342 | QVERIFY(backendB->parent() == nullptr); |
343 | QVERIFY(backendC->parent() == nullptr); |
344 | |
345 | QVERIFY(backendA->childrenHandles().isEmpty()); |
346 | QVERIFY(backendB->childrenHandles().isEmpty()); |
347 | QVERIFY(backendC->childrenHandles().isEmpty()); |
348 | |
349 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
350 | renderer.resetDirty(); |
351 | |
352 | // WHEN |
353 | backendB->cleanup(); |
354 | |
355 | // THEN nothing should change |
356 | QVERIFY(backendA->childrenHandles().isEmpty()); |
357 | QVERIFY(backendB->childrenHandles().isEmpty()); |
358 | QVERIFY(backendC->childrenHandles().isEmpty()); |
359 | |
360 | QVERIFY(backendA->parent() == nullptr); |
361 | QVERIFY(backendB->parent() == nullptr); |
362 | QVERIFY(backendC->parent() == nullptr); |
363 | |
364 | QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty); |
365 | renderer.resetDirty(); |
366 | } |
367 | |
368 | void shouldHandleSingleComponentEvents_data() |
369 | { |
370 | QTest::addColumn<QComponent*>(name: "component" ); |
371 | QTest::addColumn<void*>(name: "functionPtr" ); |
372 | |
373 | QComponent *component = new Qt3DCore::QTransform; |
374 | QTest::newRow(dataTag: "transform" ) << component << reinterpret_cast<void*>(transformUuid); |
375 | |
376 | component = new QGeometryRenderer; |
377 | QTest::newRow(dataTag: "mesh" ) << component << reinterpret_cast<void*>(geometryRendererUuid); |
378 | |
379 | component = new QCameraLens; |
380 | QTest::newRow(dataTag: "camera lens" ) << component << reinterpret_cast<void*>(cameraLensUuid); |
381 | |
382 | component = new QMaterial; |
383 | QTest::newRow(dataTag: "material" ) << component << reinterpret_cast<void*>(materialUuid); |
384 | |
385 | component = new QObjectPicker; |
386 | QTest::newRow(dataTag: "objectPicker" ) << component << reinterpret_cast<void*>(objectPickerUuid); |
387 | |
388 | component = new QComputeCommand; |
389 | QTest::newRow(dataTag: "computeJob" ) << component << reinterpret_cast<void*>(computeJobUuid); |
390 | |
391 | component = new QArmature; |
392 | QTest::newRow(dataTag: "armature" ) << component << reinterpret_cast<void*>(armatureUuid); |
393 | } |
394 | |
395 | void shouldHandleSingleComponentEvents() |
396 | { |
397 | // GIVEN |
398 | QFETCH(QComponent*, component); |
399 | QFETCH(void*, functionPtr); |
400 | UuidMethod method = reinterpret_cast<UuidMethod>(functionPtr); |
401 | |
402 | TestRenderer renderer; |
403 | Qt3DRender::Render::Entity entity; |
404 | Qt3DCore::QEntity dummyFrontendEntity; |
405 | entity.setRenderer(&renderer); |
406 | |
407 | // THEN |
408 | QVERIFY(method(&entity).isNull()); |
409 | |
410 | // WHEN |
411 | EntityPrivate::get(node: &entity)->componentAdded(frontend: component); |
412 | |
413 | // THEN |
414 | QCOMPARE(method(&entity), component->id()); |
415 | QVERIFY(renderer.dirtyBits() != 0); |
416 | |
417 | // WHEN |
418 | renderer.resetDirty(); |
419 | EntityPrivate::get(node: &entity)->componentRemoved(frontend: component); |
420 | |
421 | // THEN |
422 | QVERIFY(method(&entity).isNull()); |
423 | QVERIFY(renderer.dirtyBits() != 0); |
424 | |
425 | delete component; |
426 | } |
427 | |
428 | void shouldHandleComponentsEvents_data() |
429 | { |
430 | QTest::addColumn< QList<QComponent*> >(name: "components" ); |
431 | QTest::addColumn<void*>(name: "functionPtr" ); |
432 | |
433 | QList<QComponent*> components; |
434 | |
435 | components.clear(); |
436 | components << new QLayer << new QLayer << new QLayer; |
437 | QTest::newRow(dataTag: "layers" ) << components << reinterpret_cast<void*>(layersUuid); |
438 | |
439 | components.clear(); |
440 | components << new QShaderData << new QShaderData << new QShaderData; |
441 | QTest::newRow(dataTag: "shader data" ) << components << reinterpret_cast<void*>(shadersUuid); |
442 | |
443 | components.clear(); |
444 | components << new QEnvironmentLight << new QEnvironmentLight << new QEnvironmentLight; |
445 | QTest::newRow(dataTag: "environmentLights" ) << components << reinterpret_cast<void*>(environmentLightsUuid); |
446 | } |
447 | |
448 | void shouldHandleComponentsEvents() |
449 | { |
450 | // GIVEN |
451 | QFETCH(const QList<QComponent*>, components); |
452 | QFETCH(void*, functionPtr); |
453 | UuidListMethod method = reinterpret_cast<UuidListMethod>(functionPtr); |
454 | |
455 | TestRenderer renderer; |
456 | Qt3DRender::Render::Entity entity; |
457 | Qt3DCore::QEntity dummyFrontendEntity; |
458 | entity.setRenderer(&renderer); |
459 | |
460 | // THEN |
461 | QVERIFY(method(&entity).isEmpty()); |
462 | |
463 | // WHEN |
464 | for (QComponent *component : components) |
465 | EntityPrivate::get(node: &entity)->componentAdded(frontend: component); |
466 | |
467 | // THEN |
468 | QCOMPARE(method(&entity).size(), components.size()); |
469 | for (QComponent *component : components) { |
470 | QVERIFY(method(&entity).contains(component->id())); |
471 | } |
472 | QVERIFY(renderer.dirtyBits() != 0); |
473 | |
474 | // WHEN |
475 | renderer.resetDirty(); |
476 | EntityPrivate::get(node: &entity)->componentRemoved(frontend: components.first()); |
477 | |
478 | // THEN |
479 | QCOMPARE(method(&entity).size(), components.size() - 1); |
480 | QVERIFY(!method(&entity).contains(components.first()->id())); |
481 | QVERIFY(renderer.dirtyBits() != 0); |
482 | |
483 | qDeleteAll(c: components); |
484 | } |
485 | |
486 | void traversal() { |
487 | // GIVEN |
488 | TestRenderer renderer; |
489 | NodeManagers nodeManagers; |
490 | Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC; |
491 | |
492 | auto backendA = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityA); |
493 | createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityB); |
494 | createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityC); |
495 | |
496 | auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) { |
497 | Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(id: entity.id()); |
498 | backendEntity->syncFromFrontEnd(frontEnd: &entity, firstTime: false); |
499 | }; |
500 | |
501 | // reparent B to A and C to B. |
502 | frontendEntityB.setParent(&frontendEntityA); |
503 | sendParentChange(frontendEntityB); |
504 | frontendEntityC.setParent(&frontendEntityB); |
505 | sendParentChange(frontendEntityC); |
506 | |
507 | // WHEN |
508 | int visitCount = 0; |
509 | auto counter = [&visitCount](const Entity *) { ++visitCount; }; |
510 | backendA->traverse(operation: counter); |
511 | |
512 | // THEN |
513 | QCOMPARE(visitCount, 3); |
514 | } |
515 | |
516 | void visitor() |
517 | { |
518 | // GIVEN |
519 | TestRenderer renderer; |
520 | NodeManagers nodeManagers; |
521 | Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC; |
522 | |
523 | frontendEntityA.setEnabled(false); |
524 | frontendEntityB.setEnabled(false); |
525 | frontendEntityC.setEnabled(false); |
526 | |
527 | auto backendA = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityA); |
528 | createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityB); |
529 | createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityC); |
530 | |
531 | auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) { |
532 | Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(id: entity.id()); |
533 | backendEntity->syncFromFrontEnd(frontEnd: &entity, firstTime: false); |
534 | }; |
535 | |
536 | // reparent B to A and C to B. |
537 | frontendEntityB.setParent(&frontendEntityA); |
538 | sendParentChange(frontendEntityB); |
539 | frontendEntityC.setParent(&frontendEntityB); |
540 | sendParentChange(frontendEntityC); |
541 | |
542 | // WHEN |
543 | CompleteVisitor v1(&nodeManagers); |
544 | EnabledVisitor v2(&nodeManagers); |
545 | CompleteVisitor v3(&nodeManagers); |
546 | v3.setPruneDisabled(true); |
547 | v1.apply(root: backendA); |
548 | v2.apply(root: backendA); |
549 | v3.apply(root: backendA); |
550 | |
551 | // THEN |
552 | QCOMPARE(v1.count, 3); |
553 | QCOMPARE(v2.count, 1); // nodes disabled but the first one is still visited before visitation finds out it's disabled |
554 | QCOMPARE(v3.count, 0); // nodes disabled |
555 | } |
556 | |
557 | void accumulator() |
558 | { |
559 | // GIVEN |
560 | TestRenderer renderer; |
561 | NodeManagers nodeManagers; |
562 | Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC; |
563 | |
564 | frontendEntityA.setEnabled(false); |
565 | frontendEntityB.setEnabled(false); |
566 | frontendEntityC.setEnabled(false); |
567 | |
568 | auto backendA = createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityA); |
569 | createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityB); |
570 | createEntity(renderer, nodeManagers, frontEndEntity: frontendEntityC); |
571 | |
572 | auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) { |
573 | Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(id: entity.id()); |
574 | backendEntity->syncFromFrontEnd(frontEnd: &entity, firstTime: false); |
575 | }; |
576 | |
577 | // reparent B to A and C to B. |
578 | frontendEntityB.setParent(&frontendEntityA); |
579 | sendParentChange(frontendEntityB); |
580 | frontendEntityC.setParent(&frontendEntityB); |
581 | sendParentChange(frontendEntityC); |
582 | |
583 | // WHEN |
584 | EntityAccumulator v1(&nodeManagers); |
585 | EntityAccumulator v2([](Entity *e) { return e->isEnabled(); }, &nodeManagers); |
586 | const auto r1 = v1.apply(root: backendA); |
587 | const auto r2 = v2.apply(root: backendA); |
588 | |
589 | // THEN |
590 | QCOMPARE(r1.count(), 3); |
591 | QCOMPARE(r2.count(), 0); |
592 | } |
593 | |
594 | private: |
595 | Entity *createEntity(TestRenderer &renderer, NodeManagers &nodeManagers, const Qt3DCore::QEntity &frontEndEntity) { |
596 | HEntity renderNodeHandle = nodeManagers.renderNodesManager()->getOrAcquireHandle(id: frontEndEntity.id()); |
597 | Entity *entity = nodeManagers.renderNodesManager()->data(handle: renderNodeHandle); |
598 | entity->setNodeManagers(&nodeManagers); |
599 | entity->setHandle(renderNodeHandle); |
600 | entity->setRenderer(&renderer); |
601 | entity->syncFromFrontEnd(frontEnd: &frontEndEntity, firstTime: true); |
602 | return entity; |
603 | } |
604 | |
605 | }; |
606 | |
607 | QTEST_APPLESS_MAIN(tst_RenderEntity) |
608 | |
609 | #include "tst_entity.moc" |
610 | |