18 #include <QElapsedTimer> 29 static float screenSpaceError(
float epsilon,
float distance,
float screenSize,
float fov )
51 float phi = epsilon * screenSize / ( 2 * distance * tan( fov * M_PI / ( 2 * 180 ) ) );
55 static float screenSpaceError( QgsChunkNode *node,
const QgsChunkedEntity::SceneState &state )
57 float dist = node->bbox().distanceFromPoint( state.cameraPos );
61 float sse = screenSpaceError( node->error(), dist, state.screenSizePx, state.cameraFov );
65 QgsChunkedEntity::QgsChunkedEntity(
const QgsAABB &rootBbox,
float rootError,
float tau,
int maxLevel, QgsChunkLoaderFactory *loaderFactory, Qt3DCore::QNode *parent )
67 , mNeedsUpdate( false )
69 , mMaxLevel( maxLevel )
70 , mChunkLoaderFactory( loaderFactory )
71 , mMaxLoadedChunks( 512 )
73 mRootNode =
new QgsChunkNode( 0, 0, 0, rootBbox, rootError );
74 mChunkLoaderQueue =
new QgsChunkList;
75 mReplacementQueue =
new QgsChunkList;
79 QgsChunkedEntity::~QgsChunkedEntity()
84 Q_ASSERT( !mActiveJob );
87 while ( !mChunkLoaderQueue->isEmpty() )
89 QgsChunkListEntry *entry = mChunkLoaderQueue->takeFirst();
90 QgsChunkNode *node = entry->chunk;
92 if ( node->state() == QgsChunkNode::QueuedForLoad )
93 node->cancelQueuedForLoad();
94 else if ( node->state() == QgsChunkNode::QueuedForUpdate )
95 node->cancelQueuedForUpdate();
100 delete mChunkLoaderQueue;
102 while ( !mReplacementQueue->isEmpty() )
104 QgsChunkListEntry *entry = mReplacementQueue->takeFirst();
107 entry->chunk->unloadChunk();
110 delete mReplacementQueue;
118 void QgsChunkedEntity::update(
const SceneState &state )
123 int oldJobsCount = pendingJobsCount();
125 QSet<QgsChunkNode *> activeBefore = QSet<QgsChunkNode *>::fromList( mActiveNodes );
126 mActiveNodes.clear();
128 mCurrentTime = QTime::currentTime();
130 update( mRootNode, state );
132 int enabled = 0, disabled = 0, unloaded = 0;
134 Q_FOREACH ( QgsChunkNode *node, mActiveNodes )
136 if ( activeBefore.contains( node ) )
137 activeBefore.remove( node );
140 node->entity()->setEnabled(
true );
146 Q_FOREACH ( QgsChunkNode *node, activeBefore )
148 node->entity()->setEnabled(
false );
154 while ( mReplacementQueue->count() > mMaxLoadedChunks )
156 QgsChunkListEntry *entry = mReplacementQueue->takeLast();
157 entry->chunk->unloadChunk();
163 QList<QgsAABB> bboxes;
164 Q_FOREACH ( QgsChunkNode *n, mActiveNodes )
166 mBboxesEntity->setBoxes( bboxes );
173 mNeedsUpdate =
false;
175 if ( pendingJobsCount() != oldJobsCount )
176 emit pendingJobsCountChanged();
178 qDebug() <<
"update: active " << mActiveNodes.count() <<
" enabled " << enabled <<
" disabled " << disabled <<
" | culled " << mFrustumCulled <<
" | loading " << mChunkLoaderQueue->count() <<
" loaded " << mReplacementQueue->count() <<
" | unloaded " << unloaded <<
" elapsed " << t.elapsed() <<
"ms";
181 void QgsChunkedEntity::setShowBoundingBoxes(
bool enabled )
183 if ( ( enabled && mBboxesEntity ) || ( !enabled && !mBboxesEntity ) )
188 mBboxesEntity =
new QgsChunkBoundsEntity(
this );
192 mBboxesEntity->deleteLater();
193 mBboxesEntity =
nullptr;
197 void QgsChunkedEntity::updateNodes(
const QList<QgsChunkNode *> &nodes, QgsChunkQueueJobFactory *updateJobFactory )
199 Q_FOREACH ( QgsChunkNode *node, nodes )
201 if ( node->state() == QgsChunkNode::QueuedForUpdate )
203 mChunkLoaderQueue->takeEntry( node->loaderQueueEntry() );
204 node->cancelQueuedForUpdate();
206 else if ( node->state() == QgsChunkNode::Updating )
211 Q_ASSERT( node->state() == QgsChunkNode::Loaded );
213 QgsChunkListEntry *entry =
new QgsChunkListEntry( node );
214 node->setQueuedForUpdate( entry, updateJobFactory );
215 mChunkLoaderQueue->insertLast( entry );
223 int QgsChunkedEntity::pendingJobsCount()
const 225 return mChunkLoaderQueue->count() + ( mActiveJob ? 1 : 0 );
229 void QgsChunkedEntity::update( QgsChunkNode *node,
const SceneState &state )
237 node->ensureAllChildrenExist();
241 requestResidency( node );
243 if ( !node->entity() )
251 if ( screenSpaceError( node, state ) <= mTau )
255 mActiveNodes << node;
257 else if ( node->allChildChunksResident( mCurrentTime ) )
261 QgsChunkNode *
const *children = node->children();
262 for (
int i = 0; i < 4; ++i )
263 update( children[i], state );
269 mActiveNodes << node;
271 if ( node->level() < mMaxLevel )
273 QgsChunkNode *
const *children = node->children();
274 for (
int i = 0; i < 4; ++i )
275 requestResidency( children[i] );
281 void QgsChunkedEntity::requestResidency( QgsChunkNode *node )
283 if ( node->state() == QgsChunkNode::Loaded || node->state() == QgsChunkNode::QueuedForUpdate || node->state() == QgsChunkNode::Updating )
285 Q_ASSERT( node->replacementQueueEntry() );
286 Q_ASSERT( node->entity() );
287 mReplacementQueue->takeEntry( node->replacementQueueEntry() );
288 mReplacementQueue->insertFirst( node->replacementQueueEntry() );
290 else if ( node->state() == QgsChunkNode::QueuedForLoad )
293 Q_ASSERT( node->loaderQueueEntry() );
294 Q_ASSERT( !node->loader() );
295 if ( node->loaderQueueEntry()->prev || node->loaderQueueEntry()->next )
297 mChunkLoaderQueue->takeEntry( node->loaderQueueEntry() );
298 mChunkLoaderQueue->insertFirst( node->loaderQueueEntry() );
301 else if ( node->state() == QgsChunkNode::Loading )
305 else if ( node->state() == QgsChunkNode::Skeleton )
307 if ( !node->hasData() )
311 QgsChunkListEntry *entry =
new QgsChunkListEntry( node );
312 node->setQueuedForLoad( entry );
313 mChunkLoaderQueue->insertFirst( entry );
316 Q_ASSERT(
false &&
"impossible!" );
320 void QgsChunkedEntity::onActiveJobFinished()
322 int oldJobsCount = pendingJobsCount();
324 QgsChunkQueueJob *job = qobject_cast<QgsChunkQueueJob *>( sender() );
326 Q_ASSERT( job == mActiveJob );
328 QgsChunkNode *node = job->chunk();
330 if ( QgsChunkLoader *loader = qobject_cast<QgsChunkLoader *>( job ) )
332 Q_ASSERT( node->state() == QgsChunkNode::Loading );
333 Q_ASSERT( node->loader() == loader );
336 Qt3DCore::QEntity *entity = node->loader()->createEntity(
this );
341 node->setLoaded( entity );
343 mReplacementQueue->insertFirst( node->replacementQueueEntry() );
347 node->setHasData(
false );
348 node->cancelLoading();
356 Q_ASSERT( node->state() == QgsChunkNode::Updating );
361 mActiveJob->deleteLater();
362 mActiveJob =
nullptr;
367 if ( pendingJobsCount() != oldJobsCount )
368 emit pendingJobsCountChanged();
371 void QgsChunkedEntity::startJob()
373 Q_ASSERT( !mActiveJob );
374 if ( mChunkLoaderQueue->isEmpty() )
377 QgsChunkListEntry *entry = mChunkLoaderQueue->takeFirst();
379 QgsChunkNode *node = entry->chunk;
382 if ( node->state() == QgsChunkNode::QueuedForLoad )
384 QgsChunkLoader *loader = mChunkLoaderFactory->createChunkLoader( node );
385 connect( loader, &QgsChunkQueueJob::finished,
this, &QgsChunkedEntity::onActiveJobFinished );
386 node->setLoading( loader );
389 else if ( node->state() == QgsChunkNode::QueuedForUpdate )
392 connect( node->updater(), &QgsChunkQueueJob::finished,
this, &QgsChunkedEntity::onActiveJobFinished );
393 mActiveJob = node->updater();
399 void QgsChunkedEntity::cancelActiveJob()
401 Q_ASSERT( mActiveJob );
403 QgsChunkNode *node = mActiveJob->chunk();
405 if ( qobject_cast<QgsChunkLoader *>( mActiveJob ) )
408 node->cancelLoading();
413 node->cancelUpdating();
416 mActiveJob->cancel();
417 mActiveJob->deleteLater();
418 mActiveJob =
nullptr;
3 Axis-aligned bounding box - in world coords.
static bool isCullable(const QgsAABB &bbox, const QMatrix4x4 &viewProjectionMatrix)
Returns true if bbox is completely outside the current viewing volume.