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>
31QT_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
56typedef Qt3DCore::QNodeId (*UuidMethod)(Qt3DRender::Render::Entity *);
57typedef QVector<Qt3DCore::QNodeId> (*UuidListMethod)(Qt3DRender::Render::Entity *);
58
59using namespace Qt3DCore;
60using namespace Qt3DRender;
61using namespace Qt3DRender::Render;
62
63QNodeId transformUuid(Entity *entity) { return entity->componentUuid<Transform>(); }
64QNodeId cameraLensUuid(Entity *entity) { return entity->componentUuid<CameraLens>(); }
65QNodeId materialUuid(Entity *entity) { return entity->componentUuid<Material>(); }
66QNodeId geometryRendererUuid(Entity *entity) { return entity->componentUuid<GeometryRenderer>(); }
67QNodeId objectPickerUuid(Entity *entity) { return entity->componentUuid<ObjectPicker>(); }
68QNodeId computeJobUuid(Entity *entity) { return entity->componentUuid<ComputeCommand>(); }
69QNodeId armatureUuid(Entity *entity) { return entity->componentUuid<Armature>(); }
70
71QVector<QNodeId> layersUuid(Entity *entity) { return entity->componentsUuid<Layer>(); }
72QVector<QNodeId> shadersUuid(Entity *entity) { return entity->componentsUuid<ShaderData>(); }
73QVector<QNodeId> environmentLightsUuid(Entity *entity) { return entity->componentsUuid<EnvironmentLight>(); }
74
75class CompleteVisitor : public EntityVisitor
76{
77public:
78 CompleteVisitor(NodeManagers *manager) : EntityVisitor(manager) { }
79
80 int count = 0;
81 Operation visit(Entity *) { count++; return Continue; }
82};
83
84class EnabledVisitor : public EntityVisitor
85{
86public:
87 EnabledVisitor(NodeManagers *manager) : EntityVisitor(manager) { }
88
89 int count = 0;
90 Operation visit(Entity *e) { count++; return e->isEnabled() ? Continue : Prune; }
91};
92
93class tst_RenderEntity : public QObject
94{
95 Q_OBJECT
96public:
97 tst_RenderEntity() {}
98 ~tst_RenderEntity() {}
99
100private 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
594private:
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
607QTEST_APPLESS_MAIN(tst_RenderEntity)
608
609#include "tst_entity.moc"
610

source code of qt3d/tests/auto/render/entity/tst_entity.cpp