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