Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Introduce Qgs3DMapSceneEntity that Chunked entities and virtual point…
… cloud entities inherit from.

Move logic from Qgs3DMapScene to Qgs3DMapSceneEntity virtual methods.
  • Loading branch information
uclaros authored and wonder-sk committed May 11, 2023
1 parent 8623512 commit 6452004
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 150 deletions.
1 change: 1 addition & 0 deletions src/3d/CMakeLists.txt
Expand Up @@ -176,6 +176,7 @@ set(QGIS_3D_PRIVATE_HDRS
qgsvectorlayerchunkloader_p.h
qgspointcloudlayerchunkloader_p.h
qgsvirtualpointcloudentity_p.h
qgs3dmapsceneentity_p.h
chunks/qgschunkboundsentity_p.h
chunks/qgschunkedentity_p.h
chunks/qgschunklist_p.h
Expand Down
39 changes: 36 additions & 3 deletions src/3d/chunks/qgschunkedentity_p.cpp
Expand Up @@ -53,7 +53,7 @@ static float screenSpaceError( QgsChunkNode *node, const QgsChunkedEntity::Scene
}

QgsChunkedEntity::QgsChunkedEntity( float tau, QgsChunkLoaderFactory *loaderFactory, bool ownsFactory, int primitiveBudget, Qt3DCore::QNode *parent )
: Qt3DCore::QEntity( parent )
: Qgs3DMapSceneEntity( parent )
, mTau( tau )
, mChunkLoaderFactory( loaderFactory )
, mOwnsFactory( ownsFactory )
Expand Down Expand Up @@ -105,8 +105,7 @@ QgsChunkedEntity::~QgsChunkedEntity()
}
}


void QgsChunkedEntity::update( const SceneState &state )
void QgsChunkedEntity::handleSceneUpdate( const SceneState &state )
{
if ( !mIsValid )
return;
Expand Down Expand Up @@ -208,6 +207,40 @@ void QgsChunkedEntity::update( const SceneState &state )
.arg( t.elapsed() ), 2 );
}

QgsRange<float> QgsChunkedEntity::getNearFarPlaneRange( const QMatrix4x4 &viewMatrix ) const
{
QList<QgsChunkNode *> activeEntityNodes = activeNodes();

// it could be that there are no active nodes - they could be all culled or because root node
// is not yet loaded - we still need at least something to understand bounds of our scene
// so lets use the root node
if ( activeEntityNodes.empty() )
activeEntityNodes << rootNode();

float fnear = 1e9;
float ffar = 0;

for ( QgsChunkNode *node : std::as_const( activeEntityNodes ) )
{
// project each corner of bbox to camera coordinates
// and determine closest and farthest point.
QgsAABB bbox = node->bbox();
for ( int i = 0; i < 8; ++i )
{
const QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );

const QVector4D pc = viewMatrix * p;

const float dst = -pc.z(); // in camera coordinates, x grows right, y grows down, z grows to the back
fnear = std::min( fnear, dst );
ffar = std::max( ffar, dst );
}
}
return QgsRange<float>( fnear, ffar );
}

void QgsChunkedEntity::setShowBoundingBoxes( bool enabled )
{
if ( ( enabled && mBboxesEntity ) || ( !enabled && !mBboxesEntity ) )
Expand Down
32 changes: 9 additions & 23 deletions src/3d/chunks/qgschunkedentity_p.h
Expand Up @@ -27,7 +27,7 @@
// version without notice, or even be removed.
//

#include <Qt3DCore/QEntity>
#include "qgs3dmapsceneentity_p.h"
#include <numeric>

#define SIP_NO_FILE
Expand Down Expand Up @@ -61,7 +61,7 @@ namespace QgsRayCastingUtils
* based on data error and unloading of data when data are not necessary anymore
* \since QGIS 3.0
*/
class QgsChunkedEntity : public Qt3DCore::QEntity
class QgsChunkedEntity : public Qgs3DMapSceneEntity
{
Q_OBJECT
public:
Expand All @@ -71,20 +71,16 @@ class QgsChunkedEntity : public Qt3DCore::QEntity
Qt3DCore::QNode *parent = nullptr );
~QgsChunkedEntity() override;

//! Records some bits about the scene (context for update() method)
struct SceneState
{
QVector3D cameraPos; //!< Camera position
float cameraFov; //!< Field of view (in degrees)
int screenSizePx; //!< Size of the viewport in pixels
QMatrix4x4 viewProjectionMatrix; //!< For frustum culling
};

//! Called when e.g. camera changes and entity may need updated
void update( const SceneState &state );
void handleSceneUpdate( const SceneState &state ) override;

//! Returns number of jobs pending for this entity until it is fully loaded/updated in the current view
int pendingJobsCount() const override;

//! Returns whether the entity needs update of active nodes
bool needsUpdate() const { return mNeedsUpdate; }
bool needsUpdate() const override { return mNeedsUpdate; }

QgsRange<float> getNearFarPlaneRange( const QMatrix4x4 &viewMatrix ) const override;

//! Determines whether bounding boxes of tiles should be shown (for debugging)
void setShowBoundingBoxes( bool enabled );
Expand All @@ -97,9 +93,6 @@ class QgsChunkedEntity : public Qt3DCore::QEntity
//! Returns the root node of the whole quadtree hierarchy of nodes
QgsChunkNode *rootNode() const { return mRootNode; }

//! Returns number of jobs pending for this entity until it is fully loaded/updated in the current view
int pendingJobsCount() const;

//! Sets whether additive strategy is enabled - see usingAditiveStrategy()
void setUsingAdditiveStrategy( bool additive ) { mAdditiveStrategy = additive; }

Expand Down Expand Up @@ -157,13 +150,6 @@ class QgsChunkedEntity : public Qt3DCore::QEntity
private slots:
void onActiveJobFinished();

signals:
//! Emitted when the number of pending jobs changes (some jobs have finished or some jobs have been just created)
void pendingJobsCountChanged();

//! Emitted when a new 3D entity has been created. Other components can use that to do extra work
void newEntityCreated( Qt3DCore::QEntity *entity );

protected:
//! root node of the quadtree hierarchy
QgsChunkNode *mRootNode = nullptr;
Expand Down
126 changes: 24 additions & 102 deletions src/3d/qgs3dmapscene.cpp
Expand Up @@ -74,8 +74,6 @@
#include "qgsskyboxentity.h"
#include "qgsskyboxsettings.h"

#include "qgsvirtualpointcloudentity_p.h"
#include "qgsvirtualpointcloudprovider.h"
#include "qgswindow3dengine.h"
#include "qgspointcloudlayer.h"

Expand Down Expand Up @@ -273,7 +271,7 @@ int Qgs3DMapScene::terrainPendingJobsCount() const
int Qgs3DMapScene::totalPendingJobsCount() const
{
int count = 0;
for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
count += entity->pendingJobsCount();
return count;
}
Expand All @@ -285,17 +283,17 @@ float Qgs3DMapScene::worldSpaceError( float epsilon, float distance )
const QSize size = mEngine->size();
float screenSizePx = std::max( size.width(), size.height() ); // TODO: is this correct?

// in qgschunkedentity_p.cpp there is inverse calculation (world space error to screen space error)
// see Qgs3DUtils::screenSpaceError() for the inverse calculation (world space error to screen space error)
// with explanation of the math.
float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
float err = frustumWidthAtDistance * epsilon / screenSizePx;
return err;
}

QgsChunkedEntity::SceneState sceneState_( QgsAbstract3DEngine *engine )
Qgs3DMapSceneEntity::SceneState sceneState_( QgsAbstract3DEngine *engine )
{
Qt3DRender::QCamera *camera = engine->camera();
QgsChunkedEntity::SceneState state;
Qgs3DMapSceneEntity::SceneState state;
state.cameraFov = camera->fieldOfView();
state.cameraPos = camera->position();
const QSize size = engine->size();
Expand Down Expand Up @@ -364,42 +362,14 @@ void Qgs3DMapScene::updateScene()
{
QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral( "3D" ), QStringLiteral( "Update Scene" ) );

for ( QgsVirtualPointCloudEntity *entity : std::as_const( mVirtualPointCloudEntities ) )
for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
{
entity->handleSceneUpdate( sceneState_( mEngine ) );
}

for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
{
if ( entity->isEnabled() )
entity->update( sceneState_( mEngine ) );
}
updateSceneState();
}

static void _updateNearFarPlane( const QList<QgsChunkNode *> &activeNodes, const QMatrix4x4 &viewMatrix, float &fnear, float &ffar )
{
for ( QgsChunkNode *node : activeNodes )
{
// project each corner of bbox to camera coordinates
// and determine closest and farthest point.
QgsAABB bbox = node->bbox();
for ( int i = 0; i < 8; ++i )
{
QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.xMin : bbox.xMax,
( ( i >> 1 ) & 1 ) ? bbox.yMin : bbox.yMax,
( ( i >> 2 ) & 1 ) ? bbox.zMin : bbox.zMax, 1 );

QVector4D pc = viewMatrix * p;


float dst = -pc.z(); // in camera coordinates, x grows right, y grows down, z grows to the back
fnear = std::min( fnear, dst );
ffar = std::max( ffar, dst );
}
}
}

bool Qgs3DMapScene::updateCameraNearFarPlanes()
{
// Update near and far plane from the terrain.
Expand All @@ -418,29 +388,15 @@ bool Qgs3DMapScene::updateCameraNearFarPlanes()
QMatrix4x4 viewMatrix = camera->viewMatrix();
float fnear = 1e9;
float ffar = 0;
QList<QgsChunkNode *> activeNodes;
if ( mTerrain )
activeNodes = mTerrain->activeNodes();

// it could be that there are no active nodes - they could be all culled or because root node
// is not yet loaded - we still need at least something to understand bounds of our scene
// so lets use the root node
if ( mTerrain && activeNodes.isEmpty() )
activeNodes << mTerrain->rootNode();

_updateNearFarPlane( activeNodes, viewMatrix, fnear, ffar );

// Also involve all the other chunked entities to make sure that they will not get
// Iterate all scene entities to make sure that they will not get
// clipped by the near or far plane
for ( QgsChunkedEntity *e : std::as_const( mChunkEntities ) )
for ( Qgs3DMapSceneEntity *se : std::as_const( mSceneEntities ) )
{
if ( e != mTerrain )
{
QList<QgsChunkNode *> activeEntityNodes = e->activeNodes();
if ( activeEntityNodes.empty() )
activeEntityNodes << e->rootNode();
_updateNearFarPlane( activeEntityNodes, viewMatrix, fnear, ffar );
}
const QgsRange<float> depthRange = se->getNearFarPlaneRange( viewMatrix );

fnear = std::min( fnear, depthRange.lower() );
ffar = std::max( ffar, depthRange.upper() );
}

if ( fnear < 1 )
Expand Down Expand Up @@ -476,12 +432,12 @@ void Qgs3DMapScene::onFrameTriggered( float dt )
{
mCameraController->frameTriggered( dt );

for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
{
if ( entity->isEnabled() && entity->needsUpdate() )
{
QgsDebugMsgLevel( QStringLiteral( "need for update" ), 2 );
entity->update( sceneState_( mEngine ) );
entity->handleSceneUpdate( sceneState_( mEngine ) );
}
}

Expand Down Expand Up @@ -513,7 +469,7 @@ void Qgs3DMapScene::createTerrain()
{
if ( mTerrain )
{
mChunkEntities.removeOne( mTerrain );
mSceneEntities.removeOne( mTerrain );

mTerrain->deleteLater();
mTerrain = nullptr;
Expand Down Expand Up @@ -547,7 +503,7 @@ void Qgs3DMapScene::createTerrainDeferred()
mTerrain->setParent( this );
mTerrain->setShowBoundingBoxes( mMap.showTerrainBoundingBoxes() );

mChunkEntities << mTerrain;
mSceneEntities << mTerrain;

connect( mTerrain, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
connect( mTerrain, &QgsTerrainEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::terrainPendingJobsCountChanged );
Expand Down Expand Up @@ -719,28 +675,17 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )

finalizeNewEntity( newEntity );

if ( QgsChunkedEntity *chunkedNewEntity = qobject_cast<QgsChunkedEntity *>( newEntity ) )
if ( Qgs3DMapSceneEntity *sceneNewEntity = qobject_cast<Qgs3DMapSceneEntity *>( newEntity ) )
{
needsSceneUpdate = true;
addNewChunkedEntity( chunkedNewEntity );
}
mSceneEntities.append( sceneNewEntity );

if ( QgsVirtualPointCloudEntity *virtualPointCloudEntity = qobject_cast<QgsVirtualPointCloudEntity *>( newEntity ) )
{
mVirtualPointCloudEntities.append( virtualPointCloudEntity );

const QList<QgsChunkedEntity *> chunkedEntities = virtualPointCloudEntity->chunkedEntities();
for ( QgsChunkedEntity *ce : chunkedEntities )
{
needsSceneUpdate = true;
addNewChunkedEntity( ce );
}

connect( virtualPointCloudEntity, &QgsVirtualPointCloudEntity::newEntityCreated, this, [ = ]( QgsChunkedEntity * newChildChunkedEntity )
connect( sceneNewEntity, &Qgs3DMapSceneEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity * entity )
{
addNewChunkedEntity( newChildChunkedEntity );
onCameraChanged();
finalizeNewEntity( entity );
} );

connect( sceneNewEntity, &Qgs3DMapSceneEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
}
}
}
Expand Down Expand Up @@ -774,20 +719,9 @@ void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
{
Qt3DCore::QEntity *entity = mLayerEntities.take( layer );

if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
{
mChunkEntities.removeOne( chunkedEntity );
}

if ( QgsVirtualPointCloudEntity *vpcNewEntity = qobject_cast<QgsVirtualPointCloudEntity *>( entity ) )
if ( Qgs3DMapSceneEntity *sceneEntity = qobject_cast<Qgs3DMapSceneEntity *>( entity ) )
{
const QList<QgsChunkedEntity *> chunkedEntities = vpcNewEntity->chunkedEntities();
for ( QgsChunkedEntity *chunkedEntity : chunkedEntities )
{
mChunkEntities.removeOne( chunkedEntity );
chunkedEntity->deleteLater();
}
mVirtualPointCloudEntities.removeOne( vpcNewEntity );
mSceneEntities.removeOne( sceneEntity );
}

if ( entity )
Expand Down Expand Up @@ -880,18 +814,6 @@ void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
}
}

void Qgs3DMapScene::addNewChunkedEntity( QgsChunkedEntity *newEntity )
{
mChunkEntities.append( newEntity );

connect( newEntity, &QgsChunkedEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity * entity )
{
finalizeNewEntity( entity );
} );

connect( newEntity, &QgsChunkedEntity::pendingJobsCountChanged, this, &Qgs3DMapScene::totalPendingJobsCountChanged );
}

int Qgs3DMapScene::maximumTextureSize() const
{
QSurface *surface = mEngine->surface();
Expand Down Expand Up @@ -959,7 +881,7 @@ void Qgs3DMapScene::updateSceneState()
return;
}

for ( QgsChunkedEntity *entity : std::as_const( mChunkEntities ) )
for ( Qgs3DMapSceneEntity *entity : std::as_const( mSceneEntities ) )
{
if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
{
Expand Down

0 comments on commit 6452004

Please sign in to comment.