1/****************************************************************************
2**
3** Copyright (C) 2016 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#include "qmlscenereader.h"
30#include "testpostmanarbiter.h"
31
32#include <QUrl>
33
34#include <QtTest/QTest>
35#include <Qt3DCore/qentity.h>
36#include <Qt3DCore/qtransform.h>
37#include <Qt3DCore/private/qaspectjobmanager_p.h>
38#include <Qt3DCore/private/qnodevisitor_p.h>
39#include <QtQuick/qquickwindow.h>
40
41#include <Qt3DRender/QCamera>
42#include <Qt3DRender/QObjectPicker>
43#include <Qt3DRender/QPickEvent>
44#include <Qt3DRender/QPickTriangleEvent>
45#include <Qt3DRender/private/nodemanagers_p.h>
46#include <Qt3DRender/private/managers_p.h>
47#include <Qt3DRender/private/entity_p.h>
48#include <Qt3DRender/qrenderaspect.h>
49#include <Qt3DRender/private/qrenderaspect_p.h>
50#include <Qt3DRender/private/pickboundingvolumejob_p.h>
51#include <Qt3DRender/private/updatemeshtrianglelistjob_p.h>
52#include <Qt3DRender/private/updateworldboundingvolumejob_p.h>
53#include <Qt3DRender/private/updateworldtransformjob_p.h>
54#include <Qt3DRender/private/expandboundingvolumejob_p.h>
55#include <Qt3DRender/private/calcboundingvolumejob_p.h>
56#include <Qt3DRender/private/calcgeometrytrianglevolumes_p.h>
57#include <Qt3DRender/private/loadbufferjob_p.h>
58#include <Qt3DRender/private/buffermanager_p.h>
59#include <Qt3DRender/private/geometryrenderermanager_p.h>
60#include <Qt3DRender/private/sphere_p.h>
61
62#include <Qt3DExtras/qspheremesh.h>
63#include <Qt3DExtras/qcylindermesh.h>
64#include <Qt3DExtras/qtorusmesh.h>
65#include <Qt3DExtras/qcuboidmesh.h>
66#include <Qt3DExtras/qplanemesh.h>
67
68#include <qbackendnodetester.h>
69
70QT_BEGIN_NAMESPACE
71
72namespace Qt3DRender {
73
74QVector<Qt3DCore::QNode *> getNodesForCreation(Qt3DCore::QNode *root)
75{
76 using namespace Qt3DCore;
77
78 QVector<QNode *> nodes;
79 Qt3DCore::QNodeVisitor visitor;
80 visitor.traverse(rootNode_: root, fN: [&nodes](QNode *node) {
81 nodes.append(t: node);
82
83 // Store the metaobject of the node in the QNode so that we have it available
84 // to us during destruction in the QNode destructor. This allows us to send
85 // the QNodeId and the metaobject as typeinfo to the backend aspects so they
86 // in turn can find the correct QBackendNodeMapper object to handle the destruction
87 // of the corresponding backend nodes.
88 QNodePrivate *d = QNodePrivate::get(q: node);
89 d->m_typeInfo = const_cast<QMetaObject*>(QNodePrivate::findStaticMetaObject(metaObject: node->metaObject()));
90
91 // Mark this node as having been handled for creation so that it is picked up
92 d->m_hasBackendNode = true;
93 });
94
95 return nodes;
96}
97
98QVector<Qt3DCore::NodeTreeChange> nodeTreeChangesForNodes(const QVector<Qt3DCore::QNode *> nodes)
99{
100 QVector<Qt3DCore::NodeTreeChange> nodeTreeChanges;
101 nodeTreeChanges.reserve(size: nodes.size());
102
103 for (Qt3DCore::QNode *n : nodes) {
104 nodeTreeChanges.push_back(t: {
105 .id: n->id(),
106 .metaObj: Qt3DCore::QNodePrivate::get(q: n)->m_typeInfo,
107 .type: Qt3DCore::NodeTreeChange::Added,
108 .node: n
109 });
110 }
111
112 return nodeTreeChanges;
113}
114
115class TestAspect : public Qt3DRender::QRenderAspect
116{
117public:
118 TestAspect(Qt3DCore::QNode *root)
119 : Qt3DRender::QRenderAspect(Qt3DRender::QRenderAspect::Synchronous)
120 , m_sceneRoot(nullptr)
121 {
122 QRenderAspect::onRegistered();
123
124 const QVector<Qt3DCore::QNode *> nodes = getNodesForCreation(root);
125 const QVector<Qt3DCore::NodeTreeChange> nodeTreeChanges = nodeTreeChangesForNodes(nodes);
126 d_func()->setRootAndCreateNodes(rootObject: qobject_cast<Qt3DCore::QEntity *>(object: root), nodesTreeChanges: nodeTreeChanges);
127
128 Render::Entity *rootEntity = nodeManagers()->lookupResource<Render::Entity, Render::EntityManager>(id: rootEntityId());
129 Q_ASSERT(rootEntity);
130 m_sceneRoot = rootEntity;
131 }
132
133 ~TestAspect()
134 {
135 QRenderAspect::onUnregistered();
136 }
137
138 void onRegistered() { QRenderAspect::onRegistered(); }
139 void onUnregistered() { QRenderAspect::onUnregistered(); }
140
141 Qt3DRender::Render::NodeManagers *nodeManagers() const { return d_func()->m_renderer->nodeManagers(); }
142 Qt3DRender::Render::FrameGraphNode *frameGraphRoot() const { return d_func()->m_renderer->frameGraphRoot(); }
143 Qt3DRender::Render::RenderSettings *renderSettings() const { return d_func()->m_renderer->settings(); }
144 Qt3DRender::Render::Entity *sceneRoot() const { return m_sceneRoot; }
145 Qt3DRender::Render::AbstractRenderer *renderer() const { return d_func()->m_renderer; }
146
147private:
148 Render::Entity *m_sceneRoot;
149};
150
151} // namespace Qt3DRender
152
153QT_END_NAMESPACE
154
155namespace {
156
157void runRequiredJobs(Qt3DRender::TestAspect *test)
158{
159 Qt3DRender::Render::UpdateWorldTransformJob updateWorldTransform;
160 updateWorldTransform.setRoot(test->sceneRoot());
161 updateWorldTransform.setManagers(test->nodeManagers());
162 updateWorldTransform.run();
163
164 // For each buffer
165 const std::vector<Qt3DRender::Render::HBuffer> &bufferHandles = test->nodeManagers()->bufferManager()->activeHandles();
166 for (auto bufferHandle : bufferHandles) {
167 Qt3DRender::Render::LoadBufferJob loadBuffer(bufferHandle);
168 loadBuffer.setNodeManager(test->nodeManagers());
169 loadBuffer.run();
170 }
171
172 Qt3DRender::Render::CalculateBoundingVolumeJob calcBVolume;
173 calcBVolume.setManagers(test->nodeManagers());
174 calcBVolume.setRoot(test->sceneRoot());
175 calcBVolume.run();
176
177 Qt3DRender::Render::UpdateWorldBoundingVolumeJob updateWorldBVolume;
178 updateWorldBVolume.setManager(test->nodeManagers()->renderNodesManager());
179 updateWorldBVolume.run();
180
181 Qt3DRender::Render::ExpandBoundingVolumeJob expandBVolume;
182 expandBVolume.setRoot(test->sceneRoot());
183 expandBVolume.setManagers(test->nodeManagers());
184 expandBVolume.run();
185
186 Qt3DRender::Render::UpdateMeshTriangleListJob updateTriangleList;
187 updateTriangleList.setManagers(test->nodeManagers());
188 updateTriangleList.run();
189
190 // For each geometry id
191 const std::vector<Qt3DRender::Render::HGeometryRenderer> &geometryRenderHandles = test->nodeManagers()->geometryRendererManager()->activeHandles();
192 for (auto geometryRenderHandle : geometryRenderHandles) {
193 Qt3DCore::QNodeId geometryRendererId = test->nodeManagers()->geometryRendererManager()->data(handle: geometryRenderHandle)->peerId();
194 Qt3DRender::Render::CalcGeometryTriangleVolumes calcGeometryTriangles(geometryRendererId, test->nodeManagers());
195 calcGeometryTriangles.run();
196 }
197}
198
199} // anonymous
200
201class tst_BoundingSphere : public Qt3DCore::QBackendNodeTester
202{
203 Q_OBJECT
204private:
205
206private Q_SLOTS:
207 void checkIsNull() {
208 auto defaultSphere = Qt3DRender::Render::Sphere();
209 QVERIFY(defaultSphere.isNull());
210 }
211
212 void remainsNotNullAfterTransform() {
213 QMatrix4x4 mat;
214 mat.translate(x: -5,y: -5,z: -5);
215 auto mMat = Matrix4x4(mat);
216 auto pointSphere = Qt3DRender::Render::Sphere(Vector3D(5.f,5.f,5.f),0.f);
217 pointSphere.transform(mat: mMat);
218 QVERIFY(!pointSphere.isNull());
219 QVERIFY(pointSphere.center() == Vector3D(0.,0.,0));
220 QVERIFY(pointSphere.radius() == 0.f);
221 }
222
223 void remainsNullAfterTransform() {
224 QMatrix4x4 mat;
225 mat.translate(x: -5,y: -5,z: -5);
226 auto mMat = Matrix4x4(mat);
227 auto defaultSphere = Qt3DRender::Render::Sphere();
228 defaultSphere.transform(mat: mMat);
229 QVERIFY(defaultSphere.isNull());
230 }
231
232 void expandToContainSphere() {
233 auto firstValidSphere = Qt3DRender::Render::Sphere(Vector3D(-10.f,-10.f,-10.f),1.f);
234 auto secondValidSphere = Qt3DRender::Render::Sphere(Vector3D(10.f,10.f,10.f),1.f);
235 firstValidSphere.expandToContain(sphere: secondValidSphere);
236 QVERIFY(firstValidSphere.center()==Vector3D(0.f,0.f,0.f));
237 float dist = static_cast<float>((2 + sqrt(x: 3.*(20)*(20)))/2.);
238 QVERIFY(qFuzzyCompare(firstValidSphere.radius(), dist));
239 }
240
241 void expandToContainSphereOneInvalid() {
242 auto firstValidSphere = Qt3DRender::Render::Sphere(Vector3D(-10.f,-10.f,-10.f),1.f);
243 auto defaultSphere = Qt3DRender::Render::Sphere();
244 auto copiedSphere = firstValidSphere;
245 firstValidSphere.expandToContain(sphere: defaultSphere);
246 QVERIFY(firstValidSphere.center() == copiedSphere.center());
247 QVERIFY(firstValidSphere.radius() == copiedSphere.radius());
248 QVERIFY(!firstValidSphere.isNull());
249 }
250
251 void expandToContainOtherSphereInvalid() {
252 auto firstValidSphere = Qt3DRender::Render::Sphere(Vector3D(-10.f,-10.f,-10.f),1.f);
253 auto defaultSphere = Qt3DRender::Render::Sphere();
254 defaultSphere.expandToContain(sphere: firstValidSphere);
255 QVERIFY(defaultSphere.center() == firstValidSphere.center());
256 QVERIFY(defaultSphere.radius() == firstValidSphere.radius());
257 QVERIFY(!defaultSphere.isNull());
258 }
259
260 void expandNullSphereWithNullSphere() {
261 auto defaultSphere = Qt3DRender::Render::Sphere();
262 auto otherDefaultSphere = Qt3DRender::Render::Sphere();
263 defaultSphere.expandToContain(sphere: otherDefaultSphere);
264 QVERIFY(defaultSphere.isNull());
265 }
266
267 void expandToContainPoint() {
268 auto firstValidSphere = Qt3DRender::Render::Sphere(Vector3D(-10.f,-10.f,-10.f),1.f);
269 firstValidSphere.expandToContain(point: Vector3D(0,0,0));
270 QVERIFY(!firstValidSphere.isNull());
271 float expectedRadius = static_cast<float>((1 + qSqrt(v: 3.*(10)*(10)))/2.);
272 QVERIFY(qFuzzyCompare(firstValidSphere.radius(), expectedRadius));
273 }
274
275 void nullSphereExpandToContainPoint() {
276 auto defaultSphere = Qt3DRender::Render::Sphere();
277 defaultSphere.expandToContain(point: Vector3D(5,5,5));
278 QVERIFY(!defaultSphere.isNull());
279 QVERIFY(defaultSphere.center() == Vector3D(5,5,5));
280 QVERIFY(qFuzzyIsNull(defaultSphere.radius()));
281 }
282
283 void nullSphereExpandToOrigin() {
284 auto defaultSphere = Qt3DRender::Render::Sphere();
285 defaultSphere.expandToContain(point: Vector3D(0,0,0));
286 QVERIFY(!defaultSphere.isNull());
287 QVERIFY(defaultSphere.center() == Vector3D(0,0,0));
288 QVERIFY(qFuzzyIsNull(defaultSphere.radius()));
289 }
290
291 void ritterSphereCubePoints() {
292 QVector<Vector3D> cubePts={
293 Vector3D(-0.5, -0.5, 0.5),
294 Vector3D( 0.5, -0.5, -0.5),
295 Vector3D(-0.5, 0.5, -0.5),
296 Vector3D( 0.5, 0.5, -0.5),
297 Vector3D(-0.5, -0.5, -0.5),
298 Vector3D( 0.5, -0.5, 0.5),
299 Vector3D(-0.5, 0.5, 0.5),
300 Vector3D( 0.5, 0.5, 0.5)
301 };
302 auto ritterSphere=Qt3DRender::Render::Sphere::fromPoints(points: cubePts);
303 QVERIFY(!ritterSphere.isNull());
304 QVERIFY(qFuzzyIsNull(ritterSphere.center().x()));
305 QVERIFY(qFuzzyIsNull(ritterSphere.center().y()));
306 QVERIFY(qFuzzyIsNull(ritterSphere.center().z()));
307 QVERIFY(qFuzzyCompare(ritterSphere.radius(), static_cast<float>(qSqrt(3)/2)));
308 }
309
310 void ritterSphereRandomPoints() {
311 QVector<Vector3D> randomPts={
312 Vector3D(-81, 55, 46),
313 Vector3D(-91, -73, -42),
314 Vector3D(-50, -76, -77),
315 Vector3D(-40, 63, 58),
316 Vector3D(-28, -2, -57),
317 Vector3D(84, 17, 33),
318 Vector3D(53, 11, -49),
319 Vector3D(-7, -24, -86),
320 Vector3D(-89, 6, 76),
321 Vector3D(46, -18, -27)
322 };
323
324 auto ritterSphere = Qt3DRender::Render::Sphere::fromPoints(points: randomPts);
325 QVERIFY(!ritterSphere.isNull());
326 QVERIFY(qFuzzyCompare(ritterSphere.center().x(), 17.f));
327 QVERIFY(qFuzzyCompare(ritterSphere.center().y(), -29.5f));
328 QVERIFY(qFuzzyCompare(ritterSphere.center().z(), -22.0f));
329 QVERIFY(qFuzzyCompare(ritterSphere.radius(), 148.66152831179963f));
330 }
331
332 void ritterSphereOnePoint() {
333 QVector<Vector3D> singlePt={
334 Vector3D(-0.5, -0.5, -0.5),
335 };
336 auto ritterSphere = Qt3DRender::Render::Sphere::fromPoints(points: singlePt);
337 QVERIFY(!ritterSphere.isNull());
338 QVERIFY(qFuzzyCompare(ritterSphere.center().x(), -0.5f));
339 QVERIFY(qFuzzyCompare(ritterSphere.center().y(), -0.5f));
340 QVERIFY(qFuzzyCompare(ritterSphere.center().z(), -0.5f));
341 QVERIFY(qFuzzyIsNull(ritterSphere.radius()));
342 }
343
344 void checkExtraGeometries_data()
345 {
346 QTest::addColumn<QString>(name: "qmlFile");
347 QTest::addColumn<QVector3D>(name: "sphereCenter");
348 QTest::addColumn<float>(name: "sphereRadius");
349 QTest::newRow(dataTag: "SphereMesh") << "qrc:/sphere.qml" << QVector3D(0.f, 0.f, 0.f) << 1.f;
350 QTest::newRow(dataTag: "CubeMesh") << "qrc:/cube.qml" << QVector3D(0.f, 0.f, 0.f) << static_cast<float>(qSqrt(v: 3.)/2.); // not weird at all
351 }
352
353 void checkExtraGeometries()
354 {
355 // GIVEN
356 QFETCH(QString, qmlFile);
357 QFETCH(QVector3D, sphereCenter);
358 QFETCH(float, sphereRadius);
359
360 QUrl qmlUrl(qmlFile);
361 QmlSceneReader sceneReader(qmlUrl);
362 QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(object: sceneReader.root()));
363 QVERIFY(root);
364
365 QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data()));
366
367 // Runs Required jobs
368 runRequiredJobs(test: test.data());
369
370 // THEN
371 QVERIFY(test->sceneRoot()->worldBoundingVolumeWithChildren());
372 const auto boundingSphere = test->sceneRoot()->worldBoundingVolumeWithChildren();
373 qDebug() << qmlFile << boundingSphere->radius() << boundingSphere->center();
374 QCOMPARE(boundingSphere->radius(), sphereRadius);
375
376 QVERIFY(qFuzzyIsNull(boundingSphere->center().x() - sphereCenter.x()));
377 QVERIFY(qFuzzyIsNull(boundingSphere->center().y() - sphereCenter.y()));
378 QVERIFY(qFuzzyIsNull(boundingSphere->center().z() - sphereCenter.z()));
379 }
380
381 void checkCustomGeometry_data()
382 {
383 QTest::addColumn<int>(name: "drawVertexCount");
384 QTest::addColumn<int>(name: "indexByteOffset");
385 QTest::addColumn<QVector3D>(name: "expectedCenter");
386 QTest::addColumn<float>(name: "expectedRadius");
387 QTest::addColumn<bool>(name: "withPrimitiveRestart");
388 QTest::newRow(dataTag: "all") << 0 << 0 << QVector3D(0.0f, 0.0f, -75.0f) << 25.03997f << false;
389 QTest::newRow(dataTag: "first only") << 3 << 0 << QVector3D(0, 1, -100) << 1.0f << false;
390 QTest::newRow(dataTag: "second only") << 3 << int(3 * sizeof(ushort)) << QVector3D(0, -1, -50) << 1.0f << false;
391 QTest::newRow(dataTag: "all with primitive restart") << 0 << 0 << QVector3D(0.0f, 0.0f, -75.0f) << 25.03997f << true;
392 QTest::newRow(dataTag: "first only with primitive restart") << 4 << 0 << QVector3D(0, 1, -100) << 1.0f << true;
393 QTest::newRow(dataTag: "second only with primitive restart") << 4 << int(3 * sizeof(ushort)) << QVector3D(0, -1, -50) << 1.0f << true;
394 }
395
396 void checkCustomGeometry()
397 {
398 QFETCH(int, drawVertexCount);
399 QFETCH(int, indexByteOffset);
400 QFETCH(QVector3D, expectedCenter);
401 QFETCH(float, expectedRadius);
402 QFETCH(bool, withPrimitiveRestart);
403
404 // two triangles with different Z, and an index buffer
405 QByteArray vdata;
406 vdata.resize(size: 6 * 3 * sizeof(float));
407 float *vp = reinterpret_cast<float *>(vdata.data());
408 *vp++ = -1.0f;
409 *vp++ = 1.0f;
410 *vp++ = -100.0f;
411 *vp++ = 0.0f;
412 *vp++ = 0.0f;
413 *vp++ = -100.0f;
414 *vp++ = 1.0f;
415 *vp++ = 1.0f;
416 *vp++ = -100.0f;
417
418 *vp++ = -1.0f;
419 *vp++ = -1.0f;
420 *vp++ = -50.0f;
421 *vp++ = 0.0f;
422 *vp++ = 0.0f;
423 *vp++ = -50.0f;
424 *vp++ = 1.0f;
425 *vp++ = -1.0f;
426 *vp++ = -50.0f;
427
428 QByteArray idata;
429 const int indexCount = withPrimitiveRestart ? 7 : 6;
430 idata.resize(size: indexCount * sizeof(ushort));
431 ushort *ip = reinterpret_cast<ushort *>(idata.data());
432 *ip++ = 0;
433 *ip++ = 1;
434 *ip++ = 2;
435 if (withPrimitiveRestart)
436 *ip++ = 65535;
437 *ip++ = 3;
438 *ip++ = 4;
439 *ip++ = 5;
440
441 QScopedPointer<Qt3DCore::QEntity> entity(new Qt3DCore::QEntity);
442 QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(entity.data()));
443 Qt3DRender::QBuffer *vbuffer = new Qt3DRender::QBuffer;
444 Qt3DRender::QBuffer *ibuffer = new Qt3DRender::QBuffer;
445
446 vbuffer->setData(vdata);
447 Qt3DRender::Render::Buffer *vbufferBackend = test->nodeManagers()->bufferManager()->getOrCreateResource(id: vbuffer->id());
448 vbufferBackend->setRenderer(test->renderer());
449 vbufferBackend->setManager(test->nodeManagers()->bufferManager());
450 simulateInitializationSync(frontend: vbuffer, backend: vbufferBackend);
451
452 ibuffer->setData(idata);
453 Qt3DRender::Render::Buffer *ibufferBackend = test->nodeManagers()->bufferManager()->getOrCreateResource(id: ibuffer->id());
454 ibufferBackend->setRenderer(test->renderer());
455 ibufferBackend->setManager(test->nodeManagers()->bufferManager());
456 simulateInitializationSync(frontend: ibuffer, backend: ibufferBackend);
457
458 Qt3DRender::QGeometry *g = new Qt3DRender::QGeometry;
459 for (int i = 0; i < 2; ++i)
460 g->addAttribute(attribute: new Qt3DRender::QAttribute);
461
462 const QVector<Qt3DRender::QAttribute *> attrs = g->attributes();
463 Qt3DRender::QAttribute *attr = attrs[0];
464 attr->setBuffer(vbuffer);
465 attr->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
466 attr->setVertexBaseType(Qt3DRender::QAttribute::Float);
467 attr->setVertexSize(3);
468 attr->setCount(6);
469 attr->setByteOffset(0);
470 attr->setByteStride(3 * sizeof(float));
471
472 attr = attrs[1];
473 attr->setBuffer(ibuffer);
474 attr->setAttributeType(Qt3DRender::QAttribute::IndexAttribute);
475 attr->setVertexBaseType(Qt3DRender::QAttribute::UnsignedShort);
476 attr->setVertexSize(1);
477 attr->setCount(indexCount);
478 attr->setByteOffset(indexByteOffset);
479
480 Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer;
481 gr->setVertexCount(drawVertexCount); // when 0, indexAttribute->count() is used instead
482 gr->setPrimitiveRestartEnabled(withPrimitiveRestart);
483 if (withPrimitiveRestart)
484 gr->setRestartIndexValue(65535);
485 gr->setGeometry(g);
486 entity->addComponent(comp: gr);
487
488 Qt3DRender::Render::Attribute *attr0Backend = test->nodeManagers()->attributeManager()->getOrCreateResource(id: attrs[0]->id());
489 attr0Backend->setRenderer(test->renderer());
490 simulateInitializationSync(frontend: attrs[0], backend: attr0Backend);
491 Qt3DRender::Render::Attribute *attr1Backend = test->nodeManagers()->attributeManager()->getOrCreateResource(id: attrs[1]->id());
492 attr1Backend->setRenderer(test->renderer());
493 simulateInitializationSync(frontend: attrs[1], backend: attr1Backend);
494
495 Qt3DRender::Render::Geometry *gBackend = test->nodeManagers()->geometryManager()->getOrCreateResource(id: g->id());
496 gBackend->setRenderer(test->renderer());
497 simulateInitializationSync(frontend: g, backend: gBackend);
498
499 Qt3DRender::Render::GeometryRenderer *grBackend = test->nodeManagers()->geometryRendererManager()->getOrCreateResource(id: gr->id());
500 grBackend->setRenderer(test->renderer());
501 grBackend->setManager(test->nodeManagers()->geometryRendererManager());
502 simulateInitializationSync(frontend: gr, backend: grBackend);
503
504 Qt3DRender::Render::Entity *entityBackend = test->nodeManagers()->renderNodesManager()->getOrCreateResource(id: entity->id());
505 entityBackend->setRenderer(test->renderer());
506 simulateInitializationSync(frontend: entity.data(), backend: entityBackend);
507
508 Qt3DRender::Render::CalculateBoundingVolumeJob calcBVolume;
509 calcBVolume.setManagers(test->nodeManagers());
510 calcBVolume.setRoot(test->sceneRoot());
511 calcBVolume.run();
512
513 Vector3D center = entityBackend->localBoundingVolume()->center();
514 float radius = entityBackend->localBoundingVolume()->radius();
515 qDebug() << radius << center;
516
517 QCOMPARE(radius, expectedRadius);
518 QCOMPARE(center.x(), expectedCenter.x());
519 QCOMPARE(center.y(), expectedCenter.y());
520 QCOMPARE(center.z(), expectedCenter.z());
521 }
522
523 void checkCustomPackedGeometry()
524 {
525 int drawVertexCount = 6;
526 QVector3D expectedCenter(0.0f, 0.0f, -75.0f);
527 float expectedRadius = 25.03997f;
528
529 // two triangles with different Z
530 QByteArray vdata;
531 vdata.resize(size: 6 * 3 * sizeof(float));
532 float *vp = reinterpret_cast<float *>(vdata.data());
533 *vp++ = -1.0f;
534 *vp++ = 1.0f;
535 *vp++ = -100.0f;
536 *vp++ = 0.0f;
537 *vp++ = 0.0f;
538 *vp++ = -100.0f;
539 *vp++ = 1.0f;
540 *vp++ = 1.0f;
541 *vp++ = -100.0f;
542
543 *vp++ = -1.0f;
544 *vp++ = -1.0f;
545 *vp++ = -50.0f;
546 *vp++ = 0.0f;
547 *vp++ = 0.0f;
548 *vp++ = -50.0f;
549 *vp++ = 1.0f;
550 *vp++ = -1.0f;
551 *vp++ = -50.0f;
552
553 QScopedPointer<Qt3DCore::QEntity> entity(new Qt3DCore::QEntity);
554 QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(entity.data()));
555 Qt3DRender::QBuffer *vbuffer = new Qt3DRender::QBuffer;
556
557 vbuffer->setData(vdata);
558 Qt3DRender::Render::Buffer *vbufferBackend = test->nodeManagers()->bufferManager()->getOrCreateResource(id: vbuffer->id());
559 vbufferBackend->setRenderer(test->renderer());
560 vbufferBackend->setManager(test->nodeManagers()->bufferManager());
561 simulateInitializationSync(frontend: vbuffer, backend: vbufferBackend);
562
563 Qt3DRender::QGeometry *g = new Qt3DRender::QGeometry;
564 g->addAttribute(attribute: new Qt3DRender::QAttribute);
565
566 const QVector<Qt3DRender::QAttribute *> attrs = g->attributes();
567 Qt3DRender::QAttribute *attr = attrs[0];
568 attr->setBuffer(vbuffer);
569 attr->setName(Qt3DRender::QAttribute::defaultPositionAttributeName());
570 attr->setVertexBaseType(Qt3DRender::QAttribute::Float);
571 attr->setVertexSize(3);
572 attr->setCount(6);
573 attr->setByteOffset(0);
574 attr->setByteStride(0);
575
576 Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer;
577 gr->setVertexCount(drawVertexCount);
578 gr->setGeometry(g);
579 entity->addComponent(comp: gr);
580
581 Qt3DRender::Render::Attribute *attr0Backend = test->nodeManagers()->attributeManager()->getOrCreateResource(id: attrs[0]->id());
582 attr0Backend->setRenderer(test->renderer());
583 simulateInitializationSync(frontend: attrs[0], backend: attr0Backend);
584
585 Qt3DRender::Render::Geometry *gBackend = test->nodeManagers()->geometryManager()->getOrCreateResource(id: g->id());
586 gBackend->setRenderer(test->renderer());
587 simulateInitializationSync(frontend: g, backend: gBackend);
588
589 Qt3DRender::Render::GeometryRenderer *grBackend = test->nodeManagers()->geometryRendererManager()->getOrCreateResource(id: gr->id());
590 grBackend->setRenderer(test->renderer());
591 grBackend->setManager(test->nodeManagers()->geometryRendererManager());
592 simulateInitializationSync(frontend: gr, backend: grBackend);
593
594 Qt3DRender::Render::Entity *entityBackend = test->nodeManagers()->renderNodesManager()->getOrCreateResource(id: entity->id());
595 entityBackend->setRenderer(test->renderer());
596 simulateInitializationSync(frontend: entity.data(), backend: entityBackend);
597
598 Qt3DRender::Render::CalculateBoundingVolumeJob calcBVolume;
599 calcBVolume.setManagers(test->nodeManagers());
600 calcBVolume.setRoot(test->sceneRoot());
601 calcBVolume.run();
602
603 Vector3D center = entityBackend->localBoundingVolume()->center();
604 float radius = entityBackend->localBoundingVolume()->radius();
605 qDebug() << radius << center;
606
607 QCOMPARE(radius, expectedRadius);
608 QCOMPARE(center.x(), expectedCenter.x());
609 QCOMPARE(center.y(), expectedCenter.y());
610 QCOMPARE(center.z(), expectedCenter.z());
611 }
612};
613
614QTEST_MAIN(tst_BoundingSphere)
615
616#include "tst_boundingsphere.moc"
617

source code of qt3d/tests/auto/render/boundingsphere/tst_boundingsphere.cpp