1/****************************************************************************
2**
3** Copyright (C) 2016 Paul Lemire
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#include <QtTest/QTest>
30#include <Qt3DCore/qentity.h>
31#include <Qt3DCore/qtransform.h>
32#include <Qt3DRender/qgeometry.h>
33#include <Qt3DRender/qgeometryrenderer.h>
34#include <Qt3DRender/qattribute.h>
35#include <Qt3DRender/qbuffer.h>
36#include <Qt3DRender/private/nodemanagers_p.h>
37#include <Qt3DRender/private/managers_p.h>
38#include <Qt3DRender/private/entity_p.h>
39#include <Qt3DRender/private/filterproximitydistancejob_p.h>
40#include <Qt3DRender/private/updatetreeenabledjob_p.h>
41#include <Qt3DRender/private/updateworldtransformjob_p.h>
42#include <Qt3DRender/private/updateworldboundingvolumejob_p.h>
43#include <Qt3DRender/private/calcboundingvolumejob_p.h>
44#include <Qt3DRender/private/expandboundingvolumejob_p.h>
45#include <Qt3DRender/qproximityfilter.h>
46
47#include "testaspect.h"
48
49namespace {
50
51Qt3DCore::QEntity *buildEntityAtDistance(float distance, Qt3DCore::QEntity *parent)
52{
53 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity(parent);
54
55 // create geometry with a valid bounding volume - a single point is sufficient
56 auto geometry = new Qt3DRender::QGeometry;
57 auto vertexBuffer = new Qt3DRender::QBuffer(geometry);
58
59 auto positionAttribute = new Qt3DRender::QAttribute;
60 positionAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
61 positionAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute);
62 positionAttribute->setVertexBaseType(Qt3DRender::QAttribute::Float);
63 positionAttribute->setVertexSize(3);
64 positionAttribute->setByteStride(3 * sizeof(float));
65 positionAttribute->setBuffer(vertexBuffer);
66
67 QByteArray vertexBufferData;
68 vertexBufferData.resize(size: static_cast<int>(3 * sizeof(float)));
69
70 auto vertexArray = reinterpret_cast<float*>(vertexBufferData.data());
71
72 int i = 0;
73 vertexArray[i++] = 0.0f;
74 vertexArray[i++] = 0.0f;
75 vertexArray[i++] = 0.0f;
76
77 vertexBuffer->setData(vertexBufferData);
78 positionAttribute->setCount(1);
79
80 geometry->addAttribute(attribute: positionAttribute);
81
82 auto geometryRenderer = new Qt3DRender::QGeometryRenderer;
83 geometryRenderer->setPrimitiveType(Qt3DRender::QGeometryRenderer::Points);
84 geometryRenderer->setGeometry(geometry);
85
86 entity->addComponent(comp: geometryRenderer);
87
88 Qt3DCore::QTransform *transform = new Qt3DCore::QTransform(parent);
89 const QVector3D t = QVector3D(1.0f, 0.0f, 0.0f) * distance;
90
91 transform->setTranslation(t);
92 entity->addComponent(comp: transform);
93
94 return entity;
95}
96
97} // anonymous
98
99class tst_ProximityFiltering : public QObject
100{
101 Q_OBJECT
102private Q_SLOTS:
103
104 void checkInitialState()
105 {
106 // GIVEN
107 Qt3DRender::Render::FilterProximityDistanceJob filterJob;
108
109 // THEN
110 QCOMPARE(filterJob.hasProximityFilter(), false);
111 QCOMPARE(filterJob.filteredEntities().size(), 0);
112 QCOMPARE(filterJob.proximityFilterIds().size(), 0);
113 QVERIFY(filterJob.manager() == nullptr);
114 }
115
116 void filterEntities_data()
117 {
118 QTest::addColumn<Qt3DCore::QEntity *>(name: "entitySubtree");
119 QTest::addColumn<Qt3DCore::QNodeIdVector>(name: "proximityFilterIds");
120 QTest::addColumn<Qt3DCore::QNodeIdVector>(name: "expectedSelectedEntities");
121
122 {
123 Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
124 Qt3DCore::QEntity *targetEntity = new Qt3DCore::QEntity(rootEntity);
125 Qt3DCore::QEntity *childEntity1 = buildEntityAtDistance(distance: 50.0f, parent: rootEntity);
126 Qt3DCore::QEntity *childEntity2 = buildEntityAtDistance(distance: 25.0f, parent: rootEntity);
127 Qt3DCore::QEntity *childEntity3 = buildEntityAtDistance(distance: 75.0f, parent: rootEntity);
128
129 Qt3DRender::QProximityFilter *proximityFilter = new Qt3DRender::QProximityFilter(rootEntity);
130 proximityFilter->setDistanceThreshold(200.0f);
131 proximityFilter->setEntity(targetEntity);
132
133 QTest::newRow(dataTag: "ShouldSelectAll") << rootEntity
134 << (Qt3DCore::QNodeIdVector() << proximityFilter->id())
135 << (Qt3DCore::QNodeIdVector()
136 << rootEntity->id()
137 << targetEntity->id()
138 << childEntity1->id()
139 << childEntity2->id()
140 << childEntity3->id()
141 );
142 }
143
144 {
145 Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
146 Qt3DCore::QEntity *childEntity1 = new Qt3DCore::QEntity(rootEntity);
147 Qt3DCore::QEntity *childEntity2 = new Qt3DCore::QEntity(rootEntity);
148 Qt3DCore::QEntity *childEntity3 = new Qt3DCore::QEntity(rootEntity);
149 Q_UNUSED(childEntity1);
150 Q_UNUSED(childEntity2);
151 Q_UNUSED(childEntity3);
152
153 Qt3DRender::QProximityFilter *proximityFilter = new Qt3DRender::QProximityFilter(rootEntity);
154
155 QTest::newRow(dataTag: "ShouldSelectNone") << rootEntity
156 << (Qt3DCore::QNodeIdVector() << proximityFilter->id())
157 << (Qt3DCore::QNodeIdVector());
158 }
159
160 {
161 Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
162 Qt3DCore::QEntity *targetEntity = new Qt3DCore::QEntity(rootEntity);
163 Qt3DCore::QEntity *childEntity1 = buildEntityAtDistance(distance: 50.0f, parent: rootEntity);
164 Qt3DCore::QEntity *childEntity2 = buildEntityAtDistance(distance: 25.0f, parent: rootEntity);
165 Qt3DCore::QEntity *childEntity3 = buildEntityAtDistance(distance: 75.0f, parent: rootEntity);
166 Qt3DRender::QProximityFilter *proximityFilter = new Qt3DRender::QProximityFilter(rootEntity);
167 proximityFilter->setDistanceThreshold(30.0f);
168
169 // Note: rootEntity BoundingSphere will be centered in vec3(75.0f / 2.0, 0.0f 0.0f);
170
171 // Note: we cannot set rootEntity here as that would mean
172 // that the parent of the root would then be the proximity filter
173 // (since rootEntity has no parent) but this isn't valid in the way
174 // we have build the test
175 // Also rootEntity is centered based on the size of the child it contains
176 proximityFilter->setEntity(targetEntity);
177
178 Q_UNUSED(childEntity1);
179 Q_UNUSED(childEntity3);
180
181 QTest::newRow(dataTag: "ShouldSelectChild2") << rootEntity
182 << (Qt3DCore::QNodeIdVector() << proximityFilter->id())
183 << (Qt3DCore::QNodeIdVector()
184 << targetEntity->id()
185 << childEntity2->id()
186 );
187 }
188
189 {
190 Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
191 Qt3DCore::QEntity *targetEntity = new Qt3DCore::QEntity(rootEntity);
192 Qt3DCore::QEntity *childEntity1 = buildEntityAtDistance(distance: 50.0f, parent: rootEntity);
193 Qt3DCore::QEntity *childEntity2 = buildEntityAtDistance(distance: 25.0f, parent: rootEntity);
194 Qt3DCore::QEntity *childEntity3 = buildEntityAtDistance(distance: 49.9f, parent: rootEntity);
195 Qt3DRender::QProximityFilter *proximityFilter = new Qt3DRender::QProximityFilter(rootEntity);
196 proximityFilter->setDistanceThreshold(50.0f);
197
198 // Note: rootEntity BoundingSphere will be centered in vec3(50.0f / 2.0, 0.0f 0.0f);
199
200 // Note: we cannot set rootEntity here as that would mean
201 // that the parent of the root would then be the proximity filter
202 // (since rootEntity has no parent) but this isn't valid in the way
203 // we have build the test
204 // Also rootEntity is centered based on the size of the child it contains
205 proximityFilter->setEntity(targetEntity);
206
207 QTest::newRow(dataTag: "ShouldSelectRootChild123") << rootEntity
208 << (Qt3DCore::QNodeIdVector() << proximityFilter->id())
209 << (Qt3DCore::QNodeIdVector()
210 << rootEntity->id()
211 << targetEntity->id()
212 << childEntity1->id()
213 << childEntity2->id()
214 << childEntity3->id()
215 );
216 }
217
218 {
219 Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
220 Qt3DCore::QEntity *targetEntity = new Qt3DCore::QEntity(rootEntity);
221 Qt3DCore::QEntity *childEntity1 = buildEntityAtDistance(distance: 51.0f, parent: rootEntity);
222 Qt3DCore::QEntity *childEntity2 = buildEntityAtDistance(distance: 75.0f, parent: rootEntity);
223 Qt3DCore::QEntity *childEntity3 = buildEntityAtDistance(distance: 883.0f, parent: rootEntity);
224 Qt3DRender::QProximityFilter *proximityFilter = new Qt3DRender::QProximityFilter(rootEntity);
225 proximityFilter->setDistanceThreshold(50.0f);
226
227 // Note: rootEntity BoundingSphere will be centered in vec3(883.0f / 2.0, 0.0f 0.0f);
228
229 // Note: we cannot set rootEntity here as that would mean
230 // that the parent of the root would then be the proximity filter
231 // (since rootEntity has no parent) but this isn't valid in the way
232 // we have build the test
233 // Also rootEntity is centered based on the size of the child it contains
234 proximityFilter->setEntity(targetEntity);
235
236 Q_UNUSED(childEntity1);
237 Q_UNUSED(childEntity2);
238 Q_UNUSED(childEntity3);
239
240 QTest::newRow(dataTag: "ShouldSelectNoneButTarget") << rootEntity
241 << (Qt3DCore::QNodeIdVector() << proximityFilter->id())
242 << (Qt3DCore::QNodeIdVector() << targetEntity->id());
243 }
244
245 {
246 Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
247 Qt3DCore::QEntity *targetEntity = new Qt3DCore::QEntity(rootEntity);
248 Qt3DCore::QEntity *childEntity1 = buildEntityAtDistance(distance: 50.0f, parent: rootEntity);
249 Qt3DCore::QEntity *childEntity2 = buildEntityAtDistance(distance: 150.0f, parent: rootEntity);
250 Qt3DCore::QEntity *childEntity3 = buildEntityAtDistance(distance: 25.0f, parent: rootEntity);
251
252 Qt3DRender::QProximityFilter *proximityFilter = new Qt3DRender::QProximityFilter(rootEntity);
253 proximityFilter->setDistanceThreshold(50.0f);
254
255 Qt3DRender::QProximityFilter *proximityFilter2 = new Qt3DRender::QProximityFilter(rootEntity);
256 proximityFilter2->setDistanceThreshold(30.0f);
257
258 // Note: rootEntity BoundingSphere will be centered in vec3(150.0f / 2.0, 0.0f 0.0f);
259
260 // Note: we cannot set rootEntity here as that would mean
261 // that the parent of the root would then be the proximity filter
262 // (since rootEntity has no parent) but this isn't valid in the way
263 // we have build the test
264 // Also rootEntity is centered based on the size of the child it contains
265 proximityFilter->setEntity(targetEntity);
266 proximityFilter2->setEntity(targetEntity);
267
268 Q_UNUSED(childEntity2);
269
270 QTest::newRow(dataTag: "Nested-Step1") << rootEntity
271 << (Qt3DCore::QNodeIdVector() << proximityFilter->id())
272 << (Qt3DCore::QNodeIdVector()
273 << targetEntity->id()
274 << childEntity1->id()
275 << childEntity3->id()
276 );
277 QTest::newRow(dataTag: "Nested-Step2") << rootEntity
278 << (Qt3DCore::QNodeIdVector() << proximityFilter->id() << proximityFilter2->id())
279 << (Qt3DCore::QNodeIdVector()
280 << targetEntity->id()
281 << childEntity3->id()
282 );
283 }
284 }
285
286 void filterEntities()
287 {
288 QFETCH(Qt3DCore::QEntity *, entitySubtree);
289 QFETCH(Qt3DCore::QNodeIdVector, proximityFilterIds);
290 QFETCH(Qt3DCore::QNodeIdVector, expectedSelectedEntities);
291
292 // GIVEN
293 QScopedPointer<Qt3DRender::TestAspect> aspect(new Qt3DRender::TestAspect(entitySubtree));
294
295 // WHEN
296 Qt3DRender::Render::Entity *backendRoot = aspect->nodeManagers()->renderNodesManager()->getOrCreateResource(id: entitySubtree->id());
297
298 Qt3DRender::Render::UpdateTreeEnabledJob updateTreeEnabledJob;
299 updateTreeEnabledJob.setRoot(backendRoot);
300 updateTreeEnabledJob.setManagers(aspect->nodeManagers());
301 updateTreeEnabledJob.run();
302
303 Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform;
304 updateWorldTransform.setRoot(backendRoot);
305 updateWorldTransform.setManagers(aspect->nodeManagers());
306 updateWorldTransform.run();
307
308 Qt3DRender::Render::CalculateBoundingVolumeJob calcBVolume;
309 calcBVolume.setManagers(aspect->nodeManagers());
310 calcBVolume.setRoot(backendRoot);
311 calcBVolume.run();
312
313 Qt3DRender::Render::UpdateWorldBoundingVolumeJob updateWorldBVolume;
314 updateWorldBVolume.setManager(aspect->nodeManagers()->renderNodesManager());
315 updateWorldBVolume.run();
316
317 Qt3DRender::Render::ExpandBoundingVolumeJob expandBVolume;
318 expandBVolume.setRoot(backendRoot);
319 expandBVolume.setManagers(aspect->nodeManagers());
320 expandBVolume.run();
321
322 // WHEN
323 Qt3DRender::Render::FilterProximityDistanceJob filterJob;
324 filterJob.setProximityFilterIds(proximityFilterIds);
325 filterJob.setManager(aspect->nodeManagers());
326 filterJob.run();
327
328 // THEN
329 const QVector<Qt3DRender::Render::Entity *> filterEntities = filterJob.filteredEntities();
330 QCOMPARE(filterEntities.size(), expectedSelectedEntities.size());
331
332 for (auto i = 0, m = expectedSelectedEntities.size(); i < m; ++i)
333 QCOMPARE(filterEntities.at(i)->peerId(), expectedSelectedEntities.at(i));
334 }
335};
336
337QTEST_MAIN(tst_ProximityFiltering)
338
339#include "tst_proximityfiltering.moc"
340

source code of qt3d/tests/auto/render/proximityfiltering/tst_proximityfiltering.cpp