QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgschunknode_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgschunknode_p.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_p.h"
17 
18 #include "qgschunkedentity_p.h" // for ChunkLoader destructor
19 #include "qgschunklist_p.h"
20 #include "qgschunkloader_p.h"
21 #include <Qt3DCore/QEntity>
22 
24 
25 QgsChunkNode::QgsChunkNode( const QgsChunkNodeId &nodeId, const QgsAABB &bbox, float error, QgsChunkNode *parent )
26  : mBbox( bbox )
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  // TODO: still using a fixed size array. Use QVector instead?
39  for ( int i = 0; i < 8; ++i )
40  mChildren[i] = nullptr;
41 }
42 
43 QgsChunkNode::~QgsChunkNode()
44 {
45  Q_ASSERT( mState == Skeleton );
46  Q_ASSERT( !mLoaderQueueEntry );
47  Q_ASSERT( !mReplacementQueueEntry );
48  Q_ASSERT( !mLoader ); // should be deleted when removed from loader queue
49  Q_ASSERT( !mEntity ); // should be deleted when removed from replacement queue
50  Q_ASSERT( !mUpdater );
51  Q_ASSERT( !mUpdaterFactory );
52 
53  for ( int i = 0; i < childCount(); ++i )
54  delete mChildren[i];
55 }
56 
57 bool QgsChunkNode::allChildChunksResident( QTime currentTime ) const
58 {
59  Q_ASSERT( mChildCount != -1 );
60  for ( int i = 0; i < childCount(); ++i )
61  {
62  if ( !mChildren[i] )
63  return false; // not even a skeleton
64  if ( mChildren[i]->mHasData && !mChildren[i]->mEntity )
65  return false; // no there yet
66  Q_UNUSED( currentTime ) // seems we do not need this extra time (it just brings extra problems)
67  //if (children[i]->entityCreatedTime.msecsTo(currentTime) < 100)
68  // return false; // allow some time for upload of stuff within Qt3D (TODO: better way to check it is ready?)
69  }
70  return true;
71 }
72 
73 void QgsChunkNode::populateChildren( const QVector<QgsChunkNode *> &children )
74 {
75  Q_ASSERT( mChildCount == -1 );
76  mChildCount = children.count();
77  for ( int i = 0; i < mChildCount; ++i )
78  mChildren[i] = children[i];
79 }
80 
81 int QgsChunkNode::level() const
82 {
83  int lvl = 0;
84  QgsChunkNode *p = mParent;
85  while ( p )
86  {
87  ++lvl;
88  p = p->mParent;
89  }
90  return lvl;
91 }
92 
93 QList<QgsChunkNode *> QgsChunkNode::descendants()
94 {
95  QList<QgsChunkNode *> lst;
96  lst << this;
97 
98  for ( int i = 0; i < childCount(); ++i )
99  {
100  if ( mChildren[i] )
101  lst << mChildren[i]->descendants();
102  }
103 
104  return lst;
105 }
106 
107 void QgsChunkNode::setQueuedForLoad( QgsChunkListEntry *entry )
108 {
109  Q_ASSERT( mState == Skeleton );
110  Q_ASSERT( !mLoaderQueueEntry );
111  Q_ASSERT( !mLoader );
112 
113  mState = QgsChunkNode::QueuedForLoad;
114  mLoaderQueueEntry = entry;
115 }
116 
117 void QgsChunkNode::cancelQueuedForLoad()
118 {
119  Q_ASSERT( mState == QueuedForLoad );
120  Q_ASSERT( mLoaderQueueEntry );
121 
122  delete mLoaderQueueEntry;
123  mLoaderQueueEntry = nullptr;
124 
125  mState = QgsChunkNode::Skeleton;
126 }
127 
128 void QgsChunkNode::setLoading( QgsChunkLoader *chunkLoader )
129 {
130  Q_ASSERT( mState == QueuedForLoad );
131  Q_ASSERT( !mLoader );
132  Q_ASSERT( mLoaderQueueEntry );
133 
134  mState = Loading;
135  mLoader = chunkLoader;
136  mLoaderQueueEntry = nullptr;
137 }
138 
139 void QgsChunkNode::cancelLoading()
140 {
141  Q_ASSERT( mState == QgsChunkNode::Loading );
142  Q_ASSERT( mLoader );
143  Q_ASSERT( !mLoaderQueueEntry );
144  Q_ASSERT( !mEntity );
145  Q_ASSERT( !mReplacementQueueEntry );
146 
147  mLoader = nullptr; // not owned by chunk node
148 
149  mState = QgsChunkNode::Skeleton;
150 }
151 
152 void QgsChunkNode::setLoaded( Qt3DCore::QEntity *newEntity )
153 {
154  Q_ASSERT( mState == QgsChunkNode::Loading );
155  Q_ASSERT( mLoader );
156  Q_ASSERT( !mLoaderQueueEntry );
157  Q_ASSERT( !mReplacementQueueEntry );
158 
159  mEntity = newEntity;
160  mEntityCreatedTime = QTime::currentTime();
161 
162  mLoader = nullptr; // not owned by chunk node
163 
164  mState = QgsChunkNode::Loaded;
165  mReplacementQueueEntry = new QgsChunkListEntry( this );
166 }
167 
168 void QgsChunkNode::unloadChunk()
169 {
170  Q_ASSERT( mState == QgsChunkNode::Loaded );
171  Q_ASSERT( mEntity );
172  Q_ASSERT( mReplacementQueueEntry );
173 
174  delete mEntity;
175  mEntity = nullptr;
176 
177  delete mReplacementQueueEntry;
178  mReplacementQueueEntry = nullptr;
179  mState = QgsChunkNode::Skeleton;
180 }
181 
182 void QgsChunkNode::setQueuedForUpdate( QgsChunkListEntry *entry, QgsChunkQueueJobFactory *updateJobFactory )
183 {
184  Q_ASSERT( mState == QgsChunkNode::Loaded );
185  Q_ASSERT( mEntity );
186  Q_ASSERT( mReplacementQueueEntry );
187  Q_ASSERT( !mLoaderQueueEntry );
188  Q_ASSERT( !mUpdater );
189  Q_ASSERT( !mUpdaterFactory );
190 
191  mState = QueuedForUpdate;
192  mLoaderQueueEntry = entry;
193  mUpdaterFactory = updateJobFactory;
194 }
195 
196 void QgsChunkNode::cancelQueuedForUpdate()
197 {
198  Q_ASSERT( mState == QueuedForUpdate );
199  Q_ASSERT( mEntity );
200  Q_ASSERT( mLoaderQueueEntry );
201  Q_ASSERT( mUpdaterFactory );
202  Q_ASSERT( !mUpdater );
203 
204  mState = Loaded;
205  mUpdaterFactory = nullptr; // not owned by the node
206 
207  delete mLoaderQueueEntry;
208  mLoaderQueueEntry = nullptr;
209 }
210 
211 void QgsChunkNode::setUpdating()
212 {
213  Q_ASSERT( mState == QgsChunkNode::QueuedForUpdate );
214  Q_ASSERT( mEntity );
215  Q_ASSERT( mReplacementQueueEntry );
216  Q_ASSERT( mLoaderQueueEntry );
217  Q_ASSERT( !mUpdater );
218  Q_ASSERT( mUpdaterFactory );
219 
220  mState = Updating;
221  mUpdater = mUpdaterFactory->createJob( this );
222  mUpdaterFactory = nullptr; // not owned by the node
223  mLoaderQueueEntry = nullptr;
224 }
225 
226 void QgsChunkNode::cancelUpdating()
227 {
228  Q_ASSERT( mState == QgsChunkNode::Updating );
229  Q_ASSERT( mUpdater );
230  Q_ASSERT( !mLoaderQueueEntry );
231 
232  mUpdater = nullptr; // not owned by chunk node
233 
234  mState = Loaded;
235 }
236 
237 void QgsChunkNode::setUpdated()
238 {
239  Q_ASSERT( mState == QgsChunkNode::Updating );
240  Q_ASSERT( mUpdater );
241  Q_ASSERT( !mLoaderQueueEntry );
242  Q_ASSERT( mReplacementQueueEntry );
243 
244  mUpdater = nullptr; // not owned by chunk node
245 
246  mState = QgsChunkNode::Loaded;
247 }
248 
249 void QgsChunkNode::setExactBbox( const QgsAABB &box )
250 {
251  mBbox = box;
252 
253  // TODO: propagate better estimate to children?
254 }
255 
256 void QgsChunkNode::updateParentBoundingBoxesRecursively() const
257 {
258  QgsChunkNode *currentNode = parent();
259  while ( currentNode )
260  {
261  QgsChunkNode *const *currentNodeChildren = currentNode->children();
262  float xMin = std::numeric_limits< float >::max();
263  float xMax = -std::numeric_limits< float >::max();
264  float yMin = std::numeric_limits< float >::max();
265  float yMax = -std::numeric_limits< float >::max();
266  float zMin = std::numeric_limits< float >::max();
267  float zMax = -std::numeric_limits< float >::max();
268  for ( int i = 0; i < currentNode->childCount(); ++i )
269  {
270  const QgsAABB childBBox = currentNodeChildren[i]->bbox();
271  if ( childBBox.xMin < xMin )
272  xMin = childBBox.xMin;
273  if ( childBBox.yMin < yMin )
274  yMin = childBBox.yMin;
275  if ( childBBox.zMin < zMin )
276  zMin = childBBox.zMin;
277  if ( childBBox.xMax > xMax )
278  xMax = childBBox.xMax;
279  if ( childBBox.yMax > yMax )
280  yMax = childBBox.yMax;
281  if ( childBBox.zMax > zMax )
282  zMax = childBBox.zMax;
283  }
284  currentNode->setExactBbox( QgsAABB( xMin, yMin, zMin, xMax, yMax, zMax ) );
285  currentNode = currentNode->parent();
286  }
287 }
288 
qgschunknode_p.h
QgsAABB::zMax
float zMax
Definition: qgsaabb.h:86
QgsAABB::yMin
float yMin
Definition: qgsaabb.h:82
qgschunkloader_p.h
qgschunkedentity_p.h
QgsAABB::xMin
float xMin
Definition: qgsaabb.h:81
QgsAABB
Axis-aligned bounding box - in world coords.
Definition: qgsaabb.h:33
QgsAABB::yMax
float yMax
Definition: qgsaabb.h:85
qgschunklist_p.h
QgsAABB::zMin
float zMin
Definition: qgsaabb.h:83
QgsAABB::xMax
float xMax
Definition: qgsaabb.h:84