QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgschunknode.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgschunknode.cpp
3 --------------------------------------
4 Date : July 2017
5 Copyright : (C) 2017 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgschunknode.h"
17
18#include "qgschunkedentity.h"
19#include "qgschunklist_p.h"
20#include "qgschunkloader.h"
21
22#include <Qt3DCore/QEntity>
23
25
26QgsChunkNode::QgsChunkNode( const QgsChunkNodeId &nodeId, const QgsBox3D &box3D, float error, QgsChunkNode *parent )
27 : mBox3D( box3D )
28 , mError( error )
29 , mNodeId( nodeId )
30 , mParent( parent )
31 , mState( Skeleton )
32 , mLoaderQueueEntry( nullptr )
33 , mReplacementQueueEntry( nullptr )
34 , mLoader( nullptr )
35 , mEntity( nullptr )
36 , mUpdaterFactory( nullptr )
37 , mUpdater( nullptr )
38{
39}
40
41QgsChunkNode::~QgsChunkNode()
42{
43 Q_ASSERT( mState == Skeleton );
44 Q_ASSERT( !mLoaderQueueEntry );
45 Q_ASSERT( !mReplacementQueueEntry );
46 Q_ASSERT( !mLoader ); // should be deleted when removed from loader queue
47 Q_ASSERT( !mEntity ); // should be deleted when removed from replacement queue
48 Q_ASSERT( !mUpdater );
49 Q_ASSERT( !mUpdaterFactory );
50
51 qDeleteAll( mChildren );
52}
53
54bool QgsChunkNode::allChildChunksResident( QTime currentTime ) const
55{
56 Q_ASSERT( mChildrenPopulated );
57 for ( int i = 0; i < childCount(); ++i )
58 {
59 if ( mChildren[i]->mHasData && !mChildren[i]->mEntity )
60 return false; // no there yet
61 Q_UNUSED( currentTime ) // seems we do not need this extra time (it just brings extra problems)
62 //if (children[i]->entityCreatedTime.msecsTo(currentTime) < 100)
63 // return false; // allow some time for upload of stuff within Qt3D (TODO: better way to check it is ready?)
64 }
65 return true;
66}
67
68void QgsChunkNode::populateChildren( const QVector<QgsChunkNode *> &children )
69{
70 Q_ASSERT( !mChildrenPopulated );
71 mChildrenPopulated = true;
72 mChildren = children;
73}
74
75int QgsChunkNode::level() const
76{
77 int lvl = 0;
78 QgsChunkNode *p = mParent;
79 while ( p )
80 {
81 ++lvl;
82 p = p->mParent;
83 }
84 return lvl;
85}
86
87QList<QgsChunkNode *> QgsChunkNode::descendants()
88{
89 QList<QgsChunkNode *> lst;
90 lst << this;
91
92 for ( int i = 0; i < childCount(); ++i )
93 {
94 lst << mChildren[i]->descendants();
95 }
96
97 return lst;
98}
99
100void QgsChunkNode::setQueuedForLoad( QgsChunkListEntry *entry )
101{
102 Q_ASSERT( mState == Skeleton );
103 Q_ASSERT( !mLoaderQueueEntry );
104 Q_ASSERT( !mLoader );
105
106 mState = QgsChunkNode::QueuedForLoad;
107 mLoaderQueueEntry = entry;
108}
109
110void QgsChunkNode::cancelQueuedForLoad()
111{
112 Q_ASSERT( mState == QueuedForLoad );
113 Q_ASSERT( mLoaderQueueEntry );
114
115 delete mLoaderQueueEntry;
116 mLoaderQueueEntry = nullptr;
117
118 mState = QgsChunkNode::Skeleton;
119}
120
121void QgsChunkNode::setLoading( QgsChunkLoader *chunkLoader )
122{
123 Q_ASSERT( mState == QueuedForLoad );
124 Q_ASSERT( !mLoader );
125 Q_ASSERT( mLoaderQueueEntry );
126
127 mState = Loading;
128 mLoader = chunkLoader;
129 mLoaderQueueEntry = nullptr;
130}
131
132void QgsChunkNode::cancelLoading()
133{
134 Q_ASSERT( mState == QgsChunkNode::Loading );
135 Q_ASSERT( mLoader );
136 Q_ASSERT( !mLoaderQueueEntry );
137 Q_ASSERT( !mEntity );
138 Q_ASSERT( !mReplacementQueueEntry );
139
140 mLoader = nullptr; // not owned by chunk node
141
142 mState = QgsChunkNode::Skeleton;
143}
144
145void QgsChunkNode::setLoaded( Qt3DCore::QEntity *newEntity )
146{
147 Q_ASSERT( mState == QgsChunkNode::Loading );
148 Q_ASSERT( mLoader );
149 Q_ASSERT( !mLoaderQueueEntry );
150 Q_ASSERT( !mReplacementQueueEntry );
151
152 mEntity = newEntity;
153 mEntityCreatedTime = QTime::currentTime();
154
155 mLoader = nullptr; // not owned by chunk node
156
157 mState = QgsChunkNode::Loaded;
158 mReplacementQueueEntry = new QgsChunkListEntry( this );
159}
160
161void QgsChunkNode::unloadChunk()
162{
163 Q_ASSERT( mState == QgsChunkNode::Loaded );
164 Q_ASSERT( mEntity );
165 Q_ASSERT( mReplacementQueueEntry );
166
167 delete mEntity;
168 mEntity = nullptr;
169
170 delete mReplacementQueueEntry;
171 mReplacementQueueEntry = nullptr;
172 mState = QgsChunkNode::Skeleton;
173}
174
175void QgsChunkNode::setQueuedForUpdate( QgsChunkListEntry *entry, QgsChunkQueueJobFactory *updateJobFactory )
176{
177 Q_ASSERT( mState == QgsChunkNode::Loaded );
178 Q_ASSERT( mEntity );
179 Q_ASSERT( mReplacementQueueEntry );
180 Q_ASSERT( !mLoaderQueueEntry );
181 Q_ASSERT( !mUpdater );
182 Q_ASSERT( !mUpdaterFactory );
183
184 mState = QueuedForUpdate;
185 mLoaderQueueEntry = entry;
186 mUpdaterFactory = updateJobFactory;
187}
188
189void QgsChunkNode::cancelQueuedForUpdate()
190{
191 Q_ASSERT( mState == QueuedForUpdate );
192 Q_ASSERT( mEntity );
193 Q_ASSERT( mLoaderQueueEntry );
194 Q_ASSERT( mUpdaterFactory );
195 Q_ASSERT( !mUpdater );
196
197 mState = Loaded;
198 mUpdaterFactory = nullptr; // not owned by the node
199
200 delete mLoaderQueueEntry;
201 mLoaderQueueEntry = nullptr;
202}
203
204void QgsChunkNode::setUpdating()
205{
206 Q_ASSERT( mState == QgsChunkNode::QueuedForUpdate );
207 Q_ASSERT( mEntity );
208 Q_ASSERT( mReplacementQueueEntry );
209 Q_ASSERT( mLoaderQueueEntry );
210 Q_ASSERT( !mUpdater );
211 Q_ASSERT( mUpdaterFactory );
212
213 mState = Updating;
214 mUpdater = mUpdaterFactory->createJob( this );
215 mUpdaterFactory = nullptr; // not owned by the node
216 mLoaderQueueEntry = nullptr;
217}
218
219void QgsChunkNode::cancelUpdating()
220{
221 Q_ASSERT( mState == QgsChunkNode::Updating );
222 Q_ASSERT( mUpdater );
223 Q_ASSERT( !mLoaderQueueEntry );
224
225 mUpdater = nullptr; // not owned by chunk node
226
227 mState = Loaded;
228}
229
230void QgsChunkNode::setUpdated()
231{
232 Q_ASSERT( mState == QgsChunkNode::Updating );
233 Q_ASSERT( mUpdater );
234 Q_ASSERT( !mLoaderQueueEntry );
235 Q_ASSERT( mReplacementQueueEntry );
236
237 mUpdater = nullptr; // not owned by chunk node
238
239 mState = QgsChunkNode::Loaded;
240}
241
242void QgsChunkNode::replaceEntity( Qt3DCore::QEntity *newEntity )
243{
244 Q_ASSERT( mState == QgsChunkNode::Updating );
245 Q_ASSERT( mUpdater );
246 Q_ASSERT( mEntity );
247 Q_ASSERT( newEntity );
248
249 mEntity->deleteLater();
250 mEntity = newEntity;
251}
252
253void QgsChunkNode::setExactBox3D( const QgsBox3D &box3D )
254{
255 mBox3D = box3D;
256
257 // TODO: propagate better estimate to children?
258}
259
260void QgsChunkNode::updateParentBoundingBoxesRecursively() const
261{
262 QgsChunkNode *currentNode = parent();
263 while ( currentNode )
264 {
265 QgsChunkNode *const *currentNodeChildren = currentNode->children();
266 double xMin = std::numeric_limits<double>::max();
267 double xMax = -std::numeric_limits<double>::max();
268 double yMin = std::numeric_limits<double>::max();
269 double yMax = -std::numeric_limits<double>::max();
270 double zMin = std::numeric_limits<double>::max();
271 double zMax = -std::numeric_limits<double>::max();
272
273 for ( int i = 0; i < currentNode->childCount(); ++i )
274 {
275 const QgsBox3D childBox3D = currentNodeChildren[i]->box3D();
276
277 // Nodes without data have an empty bbox and should be skipped
278 if ( childBox3D.isEmpty() )
279 continue;
280
281 if ( childBox3D.xMinimum() < xMin )
282 xMin = childBox3D.xMinimum();
283 if ( childBox3D.yMinimum() < yMin )
284 yMin = childBox3D.yMinimum();
285 if ( childBox3D.zMinimum() < zMin )
286 zMin = childBox3D.zMinimum();
287 if ( childBox3D.xMaximum() > xMax )
288 xMax = childBox3D.xMaximum();
289 if ( childBox3D.yMaximum() > yMax )
290 yMax = childBox3D.yMaximum();
291 if ( childBox3D.zMaximum() > zMax )
292 zMax = childBox3D.zMaximum();
293 }
294
295 // QgsBox3D is normalized in its constructor, so that min values are always smaller than max.
296 // If all child bboxes were empty, we can end up with min > max, so let's have an empty bbox instead.
297 const QgsBox3D currentNodeBox3D = xMin > xMax || yMin > yMax || zMin > zMax ? QgsBox3D() : QgsBox3D( xMin, yMin, zMin, xMax, yMax, zMax );
298
299 currentNode->setExactBox3D( currentNodeBox3D );
300 currentNode = currentNode->parent();
301 }
302}
303
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:42
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:230
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:195
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:258
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:202
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:251
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:223
bool isEmpty() const
Returns true if the box is empty.
Definition qgsbox3d.cpp:322