Navigation Menu

Skip to content

Commit

Permalink
Fixed identify tool to work with chunked entities
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Jan 8, 2020
1 parent e2863e3 commit 108e8c8
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 63 deletions.
76 changes: 76 additions & 0 deletions src/3d/chunks/qgschunkedentity_p.cpp
Expand Up @@ -17,12 +17,15 @@

#include <QElapsedTimer>
#include <QVector4D>
#include <Qt3DRender/QObjectPicker>
#include <Qt3DRender/QPickTriangleEvent>

#include "qgs3dutils.h"
#include "qgschunkboundsentity_p.h"
#include "qgschunklist_p.h"
#include "qgschunkloader_p.h"
#include "qgschunknode_p.h"
#include "qgstessellatedpolygongeometry.h"

#include "qgseventtracing.h"

Expand Down Expand Up @@ -344,6 +347,13 @@ void QgsChunkedEntity::onActiveJobFinished()

mReplacementQueue->insertFirst( node->replacementQueueEntry() );

if ( mPickingEnabled )
{
Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker( node->entity() );
node->entity()->addComponent( picker );
connect( picker, &Qt3DRender::QObjectPicker::clicked, this, &QgsChunkedEntity::onPickEvent );
}

emit newEntityCreated( entity );
}
else
Expand Down Expand Up @@ -452,4 +462,70 @@ void QgsChunkedEntity::cancelActiveJobs()
}
}


void QgsChunkedEntity::setPickingEnabled( bool enabled )
{
if ( mPickingEnabled == enabled )
return;

mPickingEnabled = enabled;

if ( enabled )
{
QgsChunkListEntry *entry = mReplacementQueue->first();
while ( entry )
{
QgsChunkNode *node = entry->chunk;
Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker( node->entity() );
node->entity()->addComponent( picker );
connect( picker, &Qt3DRender::QObjectPicker::clicked, this, &QgsChunkedEntity::onPickEvent );

entry = entry->next;
}
}
else
{
for ( Qt3DRender::QObjectPicker *picker : findChildren<Qt3DRender::QObjectPicker *>() )
picker->deleteLater();
}
}

void QgsChunkedEntity::onPickEvent( Qt3DRender::QPickEvent *event )
{
Qt3DRender::QPickTriangleEvent *triangleEvent = qobject_cast<Qt3DRender::QPickTriangleEvent *>( event );
if ( !triangleEvent )
return;

Qt3DRender::QObjectPicker *picker = qobject_cast<Qt3DRender::QObjectPicker *>( sender() );
if ( !picker )
return;

Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( picker->parent() );
if ( !entity )
return;

// go figure out feature ID from the triangle index
QgsFeatureId fid = FID_NULL;
for ( Qt3DRender::QGeometryRenderer *geomRenderer : entity->findChildren<Qt3DRender::QGeometryRenderer *>() )
{
// unfortunately we can't access which sub-entity triggered the pick event
// so as a temporary workaround let's just ignore the entity with selection
// and hope the event was the main entity (QTBUG-58206)
if ( geomRenderer->objectName() != QLatin1String( "main" ) )
continue;

if ( QgsTessellatedPolygonGeometry *g = qobject_cast<QgsTessellatedPolygonGeometry *>( geomRenderer->geometry() ) )
{
fid = g->triangleIndexToFeatureId( triangleEvent->triangleIndex() );
if ( !FID_IS_NULL( fid ) )
break;
}
}

if ( !FID_IS_NULL( fid ) )
{
emit pickedObject( event, fid );
}
}

/// @endcond
20 changes: 20 additions & 0 deletions src/3d/chunks/qgschunkedentity_p.h
Expand Up @@ -42,6 +42,13 @@ class QgsChunkQueueJobFactory;

#include <QTime>

#include "qgsfeatureid.h"

namespace Qt3DRender
{
class QPickEvent;
}

/**
* \ingroup 3d
* Implementation of entity that handles chunks of data organized in quadtree with loading data when necessary
Expand Down Expand Up @@ -85,6 +92,11 @@ class QgsChunkedEntity : public Qt3DCore::QEntity
//! Returns number of jobs pending for this entity until it is fully loaded/updated in the current view
int pendingJobsCount() const;

//! Enables or disables object picking on this entity. When enabled, pickedObject() signals will be emitted on mouse clicks
void setPickingEnabled( bool enabled );
//! Returns whether object picking is currently enabled
bool hasPickingEnabled() const { return mPickingEnabled; }

protected:
//! Cancels the background job that is currently in progress
void cancelActiveJob( QgsChunkQueueJob *job );
Expand All @@ -104,13 +116,18 @@ class QgsChunkedEntity : public Qt3DCore::QEntity
private slots:
void onActiveJobFinished();

void onPickEvent( Qt3DRender::QPickEvent *event );

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 );

//! Emitted on mouse clicks when picking is enabled and there is a feature under the cursor
void pickedObject( Qt3DRender::QPickEvent *pickEvent, QgsFeatureId fid );

protected:
//! root node of the quadtree hierarchy
QgsChunkNode *mRootNode = nullptr;
Expand Down Expand Up @@ -143,6 +160,9 @@ class QgsChunkedEntity : public Qt3DCore::QEntity

//! jobs that are currently being processed (asynchronously in worker threads)
QList<QgsChunkQueueJob *> mActiveJobs;

//! If picking is enabled, QObjectPicker objects will be assigned to chunks and pickedObject() signals fired on mouse click
bool mPickingEnabled = false;
};

/// @endcond
Expand Down
86 changes: 24 additions & 62 deletions src/3d/qgs3dmapscene.cpp
Expand Up @@ -184,9 +184,8 @@ void Qgs3DMapScene::registerPickHandler( Qgs3DMapScenePickHandler *pickHandler )
// we need to add object pickers
for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
{
Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker( entity );
entity->addComponent( picker );
connect( picker, &Qt3DRender::QObjectPicker::clicked, this, &Qgs3DMapScene::onLayerEntityPickEvent );
if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
chunkedEntity->setPickingEnabled( true );
}
}

Expand All @@ -202,12 +201,29 @@ void Qgs3DMapScene::unregisterPickHandler( Qgs3DMapScenePickHandler *pickHandler
// we need to remove pickers
for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
{
Qt3DRender::QObjectPicker *picker = entity->findChild<Qt3DRender::QObjectPicker *>();
picker->deleteLater();
if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
chunkedEntity->setPickingEnabled( false );
}
}
}

void Qgs3DMapScene::onLayerEntityPickedObject( Qt3DRender::QPickEvent *pickEvent, QgsFeatureId fid )
{
QgsMapLayer *layer = mLayerEntities.key( qobject_cast<QgsChunkedEntity *>( sender() ) );
if ( !layer )
return;

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
if ( !vlayer )
return;

for ( Qgs3DMapScenePickHandler *pickHandler : qgis::as_const( mPickHandlers ) )
{
pickHandler->handlePickOnVectorLayer( vlayer, fid, pickEvent->worldIntersection(), pickEvent );
}
}


float Qgs3DMapScene::worldSpaceError( float epsilon, float distance )
{
Qt3DRender::QCamera *camera = mCameraController->camera();
Expand Down Expand Up @@ -414,56 +430,6 @@ void Qgs3DMapScene::onBackgroundColorChanged()
mEngine->setClearColor( mMap.backgroundColor() );
}

void Qgs3DMapScene::onLayerEntityPickEvent( Qt3DRender::QPickEvent *event )
{
Qt3DRender::QPickTriangleEvent *triangleEvent = qobject_cast<Qt3DRender::QPickTriangleEvent *>( event );
if ( !triangleEvent )
return;

Qt3DRender::QObjectPicker *picker = qobject_cast<Qt3DRender::QObjectPicker *>( sender() );
if ( !picker )
return;

Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( picker->parent() );
if ( !entity )
return;

QgsMapLayer *layer = mLayerEntities.key( entity );
if ( !layer )
return;

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
if ( !vlayer )
return;

for ( Qgs3DMapScenePickHandler *pickHandler : qgis::as_const( mPickHandlers ) )
{
// go figure out feature ID from the triangle index
QgsFeatureId fid = FID_NULL;
for ( Qt3DRender::QGeometryRenderer *geomRenderer : entity->findChildren<Qt3DRender::QGeometryRenderer *>() )
{
// unfortunately we can't access which sub-entity triggered the pick event
// so as a temporary workaround let's just ignore the entity with selection
// and hope the event was the main entity (QTBUG-58206)
if ( geomRenderer->objectName() != QLatin1String( "main" ) )
continue;

if ( QgsTessellatedPolygonGeometry *g = qobject_cast<QgsTessellatedPolygonGeometry *>( geomRenderer->geometry() ) )
{
// because we have a single picker for all chunks of an entity and QPickEvent
// does not give better information than triangle index and world/local intersection point.
// TODO: the code below basically does not work with more than single sub-entity
// TODO: maybe have a separate picker for each chunk?
fid = g->triangleIndexToFeatureId( triangleEvent->triangleIndex() );
if ( !FID_IS_NULL( fid ) )
break;
}
}
if ( !FID_IS_NULL( fid ) )
pickHandler->handlePickOnVectorLayer( vlayer, fid, event->worldIntersection(), event );
}

}

void Qgs3DMapScene::updateLights()
{
Expand Down Expand Up @@ -591,20 +557,16 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
newEntity->setParent( this );
mLayerEntities.insert( layer, newEntity );

if ( !mPickHandlers.isEmpty() )
{
Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker( newEntity );
newEntity->addComponent( picker );
connect( picker, &Qt3DRender::QObjectPicker::pressed, this, &Qgs3DMapScene::onLayerEntityPickEvent );
}

finalizeNewEntity( newEntity );

if ( QgsChunkedEntity *chunkedNewEntity = qobject_cast<QgsChunkedEntity *>( newEntity ) )
{
mChunkEntities.append( chunkedNewEntity );
needsSceneUpdate = true;

chunkedNewEntity->setPickingEnabled( !mPickHandlers.isEmpty() );
connect( chunkedNewEntity, &QgsChunkedEntity::pickedObject, this, &Qgs3DMapScene::onLayerEntityPickedObject );

connect( chunkedNewEntity, &QgsChunkedEntity::newEntityCreated, this, [this]( Qt3DCore::QEntity * entity )
{
finalizeNewEntity( entity );
Expand Down
6 changes: 5 additions & 1 deletion src/3d/qgs3dmapscene.h
Expand Up @@ -25,6 +25,7 @@ namespace Qt3DRender
class QRenderSettings;
class QCamera;
class QPickEvent;
class QObjectPicker;
}

namespace Qt3DLogic
Expand All @@ -46,6 +47,9 @@ class Qgs3DMapSettings;
class QgsTerrainEntity;
class QgsChunkedEntity;

#include "qgsfeatureid.h"


/**
* \ingroup 3d
* Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
Expand Down Expand Up @@ -106,7 +110,7 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
void onLayersChanged();
void createTerrainDeferred();
void onBackgroundColorChanged();
void onLayerEntityPickEvent( Qt3DRender::QPickEvent *event );
void onLayerEntityPickedObject( Qt3DRender::QPickEvent *pickEvent, QgsFeatureId fid );
void updateLights();
void updateCameraLens();
void onRenderersChanged();
Expand Down

0 comments on commit 108e8c8

Please sign in to comment.