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