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