Skip to content

Commit

Permalink
[FEATURE] Feature selection is also displayed in 3D map view for points
Browse files Browse the repository at this point in the history
  • Loading branch information
pblottiere authored and wonder-sk committed Sep 15, 2017
1 parent 56ed9b0 commit b0c98b7
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 44 deletions.
14 changes: 14 additions & 0 deletions src/3d/map3d.cpp
Expand Up @@ -223,6 +223,20 @@ QColor Map3D::backgroundColor() const
return mBackgroundColor;
}

void Map3D::setSelectionColor( const QColor &color )
{
if ( color == mSelectionColor )
return;

mSelectionColor = color;
emit selectionColorChanged();
}

QColor Map3D::selectionColor() const
{
return mSelectionColor;
}

void Map3D::setTerrainVerticalScale( double zScale )
{
if ( zScale == mTerrainVerticalScale )
Expand Down
5 changes: 5 additions & 0 deletions src/3d/map3d.h
Expand Up @@ -43,6 +43,9 @@ class _3D_EXPORT Map3D : public QObject
void setBackgroundColor( const QColor &color );
QColor backgroundColor() const;

void setSelectionColor( const QColor &color );
QColor selectionColor() const;

//
// terrain related config
//
Expand Down Expand Up @@ -83,6 +86,7 @@ class _3D_EXPORT Map3D : public QObject

signals:
void backgroundColorChanged();
void selectionColorChanged();
void layersChanged();
void terrainGeneratorChanged();
void terrainVerticalScaleChanged();
Expand All @@ -94,6 +98,7 @@ class _3D_EXPORT Map3D : public QObject

private:
QColor mBackgroundColor; //!< Background color of the scene
QColor mSelectionColor;
double mTerrainVerticalScale; //!< Multiplier of terrain heights to make the terrain shape more pronounced
std::unique_ptr<TerrainGenerator> mTerrainGenerator; //!< Implementation of the terrain generation
int mMapTileResolution; //!< Size of map textures of tiles in pixels (width/height)
Expand Down
101 changes: 66 additions & 35 deletions src/3d/pointentity.cpp
Expand Up @@ -3,9 +3,7 @@
#include <Qt3DRender/QAttribute>
#include <Qt3DRender/QBuffer>
#include <Qt3DRender/QEffect>
#include <Qt3DRender/QGeometryRenderer>
#include <Qt3DRender/QGraphicsApiFilter>
#include <Qt3DRender/QMaterial>
#include <Qt3DRender/QParameter>
#include <Qt3DRender/QTechnique>

Expand Down Expand Up @@ -34,33 +32,37 @@
PointEntity::PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3DSymbol &symbol, Qt3DCore::QNode *parent )
: Qt3DCore::QEntity( parent )
{
//
// load features
//
QgsFeatureRequest request;
request.setDestinationCrs( map.crs );
QList<QVector3D> pos = positions( map, layer, request );

QList<QVector3D> positions;
QgsFeature f;
addComponent( renderer( symbol, pos ) );
addComponent( material( map, symbol ) );
}

PointEntity::PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3DSymbol &symbol, bool sel, Qt3DCore::QNode *parent )
: Qt3DCore::QEntity( parent )
{
QgsFeatureRequest request;
request.setDestinationCrs( map.crs );
QgsFeatureIterator fi = layer->getFeatures( request );
while ( fi.nextFeature( f ) )
{
if ( f.geometry().isNull() )
continue;

QgsAbstractGeometry *g = f.geometry().geometry();
if ( QgsWkbTypes::flatType( g->wkbType() ) == QgsWkbTypes::Point )
{
QgsPoint *pt = static_cast<QgsPoint *>( g );
// TODO: use Z coordinates if the point is 3D
float h = map.terrainGenerator()->heightAt( pt->x(), pt->y(), map ) * map.terrainVerticalScale();
positions.append( QVector3D( pt->x() - map.originX, h, -( pt->y() - map.originY ) ) );
//qDebug() << positions.last();
}
else
qDebug() << "not a point";
if ( sel )
request.setFilterFids( layer->selectedFeatureIds() );
else
{
QgsFeatureIds notSelected = layer->allFeatureIds();
notSelected.subtract( layer->selectedFeatureIds() );
request.setFilterFids( notSelected );
}

QList<QVector3D> pos = positions( map, layer, request );

addComponent( renderer( symbol, pos ) );
addComponent( material( map, symbol, sel ) );
}

Qt3DRender::QGeometryRenderer *PointEntity::renderer( const Point3DSymbol &symbol, const QList<QVector3D> &positions ) const
{
int count = positions.count();

QByteArray ba;
Expand All @@ -72,10 +74,6 @@ PointEntity::PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3D
++posData;
}

//
// geometry renderer
//

Qt3DRender::QBuffer *instanceBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer );
instanceBuffer->setData( ba );

Expand All @@ -86,8 +84,8 @@ PointEntity::PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3D
instanceDataAttribute->setVertexSize( 3 );
instanceDataAttribute->setDivisor( 1 );
instanceDataAttribute->setBuffer( instanceBuffer );
instanceDataAttribute->setCount(count);
instanceDataAttribute->setByteStride(3 * sizeof(float));
instanceDataAttribute->setCount( count );
instanceDataAttribute->setByteStride( 3 * sizeof( float ) );

Qt3DRender::QGeometry *geometry = nullptr;
QString shape = symbol.shapeProperties["shape"].toString();
Expand Down Expand Up @@ -161,17 +159,17 @@ PointEntity::PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3D
}

geometry->addAttribute( instanceDataAttribute );
geometry->setBoundingVolumePositionAttribute(instanceDataAttribute);
geometry->setBoundingVolumePositionAttribute( instanceDataAttribute );

Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
renderer->setGeometry( geometry );
renderer->setInstanceCount( count );
addComponent( renderer );

//
// material
//
return renderer;
}

Qt3DRender::QMaterial *PointEntity::material( const Map3D &map, const Point3DSymbol &symbol, bool sel ) const
{
Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
filterKey->setName( "renderingStyle" );
filterKey->setValue( "forward" );
Expand Down Expand Up @@ -204,6 +202,12 @@ PointEntity::PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3D
specularParameter->setValue( symbol.material.specular() );
shininessParameter->setValue( symbol.material.shininess() );

if ( sel )
{
diffuseParameter->setValue( map.selectionColor() );
ambientParameter->setValue( map.selectionColor().darker() );
}

QMatrix4x4 transformMatrix = symbol.transform;
QMatrix3x3 normalMatrix = transformMatrix.normalMatrix(); // transponed inverse of 3x3 sub-matrix

Expand Down Expand Up @@ -235,5 +239,32 @@ PointEntity::PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3D

Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial;
material->setEffect( effect );
addComponent( material );

return material;
}

QList<QVector3D> PointEntity::positions( const Map3D &map, const QgsVectorLayer *layer, const QgsFeatureRequest &request ) const
{
QList<QVector3D> positions;
QgsFeature f;
QgsFeatureIterator fi = layer->getFeatures( request );
while ( fi.nextFeature( f ) )
{
if ( f.geometry().isNull() )
continue;

QgsAbstractGeometry *g = f.geometry().geometry();
if ( QgsWkbTypes::flatType( g->wkbType() ) == QgsWkbTypes::Point )
{
QgsPoint *pt = static_cast<QgsPoint *>( g );
// TODO: use Z coordinates if the point is 3D
float h = map.terrainGenerator()->heightAt( pt->x(), pt->y(), map ) * map.terrainVerticalScale();
positions.append( QVector3D( pt->x() - map.originX, h, -( pt->y() - map.originY ) ) );
//qDebug() << positions.last();
}
else
qDebug() << "not a point";
}

return positions;
}
10 changes: 10 additions & 0 deletions src/3d/pointentity.h
Expand Up @@ -2,18 +2,28 @@
#define POINTENTITY_H

#include <Qt3DCore/QEntity>
#include <Qt3DRender/QMaterial>
#include <Qt3DRender/QGeometryRenderer>

class Map3D;
class Point3DSymbol;

class QgsVectorLayer;
class QgsFeatureRequest;


//! Entity that handles rendering of points as 3D objects
class PointEntity : public Qt3DCore::QEntity
{
public:
PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3DSymbol &symbol, Qt3DCore::QNode *parent = nullptr );

PointEntity( const Map3D &map, QgsVectorLayer *layer, const Point3DSymbol &symbol, bool sel, Qt3DCore::QNode *parent = nullptr );

private:
Qt3DRender::QGeometryRenderer *renderer( const Point3DSymbol &symbol, const QList<QVector3D> &positions ) const;
Qt3DRender::QMaterial *material( const Map3D &map, const Point3DSymbol &symbol, bool sel = false ) const;
QList<QVector3D> positions( const Map3D &map, const QgsVectorLayer *layer, const QgsFeatureRequest &req ) const;
};

#endif // POINTENTITY_H
35 changes: 27 additions & 8 deletions src/3d/scene.cpp
Expand Up @@ -12,6 +12,7 @@
#include "aabb.h"
#include "qgsabstract3drenderer.h"
#include "cameracontroller.h"
#include "qgsvectorlayer.h"
#include "map3d.h"
#include "terrain.h"
#include "terraingenerator.h"
Expand Down Expand Up @@ -71,8 +72,10 @@ Scene::Scene( const Map3D &map, Qt3DExtras::QForwardRenderer *defaultFrameGraph,

Q_FOREACH ( const QgsAbstract3DRenderer *renderer, map.renderers )
{
Qt3DCore::QEntity *p = renderer->createEntity( map );
p->setParent( this );
QList<Qt3DCore::QEntity *> entities = renderer->createEntities( map );

Q_FOREACH ( Qt3DCore::QEntity *entity, entities )
entity->setParent( this );
}

// listen to changes of layers in order to add/remove 3D renderer entities
Expand Down Expand Up @@ -192,7 +195,7 @@ void Scene::createTerrain()

if ( !mTerrainUpdateScheduled )
{
// defer re-creation of terrain: there may be multiple invokations of this slot, so create the new entity just once
// defer re-creation of terrain: there may be multiple invocations of this slot, so create the new entity just once
QTimer::singleShot( 0, this, &Scene::createTerrainDeferred );
mTerrainUpdateScheduled = true;
}
Expand Down Expand Up @@ -279,19 +282,35 @@ void Scene::addLayerEntity( QgsMapLayer *layer )
QgsAbstract3DRenderer *renderer = layer->renderer3D();
if ( renderer )
{
Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
newEntity->setParent( this );
mLayerEntities.insert( layer, newEntity );
QList<Qt3DCore::QEntity *> entities = renderer->createEntities( mMap );

Q_FOREACH ( Qt3DCore::QEntity *entity, entities )
{
entity->setParent( this );
mLayerEntities.insert( layer, entity );
}
}

connect( layer, &QgsMapLayer::renderer3DChanged, this, &Scene::onLayerRenderer3DChanged );

if ( layer->type() == QgsMapLayer::VectorLayer )
{
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
connect( vlayer, &QgsVectorLayer::selectionChanged, this, &Scene::onLayerRenderer3DChanged );
}
}

void Scene::removeLayerEntity( QgsMapLayer *layer )
{
Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
if ( entity )
Qt3DCore::QEntity *entity;
while ( ( entity = mLayerEntities.take( layer ) ) )
entity->deleteLater();

disconnect( layer, &QgsMapLayer::renderer3DChanged, this, &Scene::onLayerRenderer3DChanged );

if ( layer->type() == QgsMapLayer::VectorLayer )
{
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Scene::onLayerRenderer3DChanged );
}
}
2 changes: 1 addition & 1 deletion src/3d/scene.h
Expand Up @@ -62,7 +62,7 @@ class _3D_EXPORT Scene : public Qt3DCore::QEntity
Qt3DExtras::QForwardRenderer *mForwardRenderer;
QList<ChunkedEntity *> chunkEntities;
//! Keeps track of entities that belong to a particular layer
QMap<QgsMapLayer *, Qt3DCore::QEntity *> mLayerEntities;
QMultiMap<QgsMapLayer *, Qt3DCore::QEntity *> mLayerEntities;
bool mTerrainUpdateScheduled = false;
};

Expand Down
22 changes: 22 additions & 0 deletions src/3d/vectorlayer3drenderer.cpp
Expand Up @@ -78,6 +78,28 @@ Qt3DCore::QEntity *VectorLayer3DRenderer::createEntity( const Map3D &map ) const
return nullptr;
}

QList<Qt3DCore::QEntity *> VectorLayer3DRenderer::createEntities( const Map3D &map ) const
{
QList<Qt3DCore::QEntity *> entities;

QgsVectorLayer *vl = layer();

if ( !mSymbol || !vl )
return entities;

if ( mSymbol->type() == "polygon" )
entities.append( new PolygonEntity( map, vl, *static_cast<Polygon3DSymbol *>( mSymbol.get() ) ) );
else if ( mSymbol->type() == "point" )
{
entities.append( new PointEntity( map, vl, *static_cast<Point3DSymbol *>( mSymbol.get() ), false ) );
entities.append( new PointEntity( map, vl, *static_cast<Point3DSymbol *>( mSymbol.get() ), true ) );
}
else if ( mSymbol->type() == "line" )
entities.append( new LineEntity( map, vl, *static_cast<Line3DSymbol *>( mSymbol.get() ) ) );

return entities;
}

void VectorLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
{
QDomDocument doc = elem.ownerDocument();
Expand Down
1 change: 1 addition & 0 deletions src/3d/vectorlayer3drenderer.h
Expand Up @@ -48,6 +48,7 @@ class _3D_EXPORT VectorLayer3DRenderer : public QgsAbstract3DRenderer
QString type() const override { return "vector"; }
VectorLayer3DRenderer *clone() const override;
Qt3DCore::QEntity *createEntity( const Map3D &map ) const override;
QList<Qt3DCore::QEntity *> createEntities( const Map3D &map ) const override;

void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
Expand Down
7 changes: 7 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -9902,14 +9902,21 @@ void QgisApp::init3D()
void QgisApp::new3DMapCanvas()
{
#ifdef HAVE_3D

// initialize from project
QgsProject *prj = QgsProject::instance();
QgsRectangle fullExtent = mMapCanvas->fullExtent();

int r = prj->readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 );
int g = prj->readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 );
int b = prj->readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 );
int a = prj->readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 );

Map3D *map = new Map3D;
map->crs = prj->crs();
map->originX = fullExtent.center().x();
map->originY = fullExtent.center().y();
map->setSelectionColor( QColor( r, g, b, a ) );
map->setBackgroundColor( mMapCanvas->canvasColor() );
map->setLayers( mMapCanvas->layers() );

Expand Down
1 change: 1 addition & 0 deletions src/core/3d/qgsabstract3drenderer.h
Expand Up @@ -25,6 +25,7 @@ class CORE_EXPORT QgsAbstract3DRenderer //: public QObject
virtual QString type() const = 0;
virtual QgsAbstract3DRenderer *clone() const = 0;
virtual Qt3DCore::QEntity *createEntity( const Map3D &map ) const = 0;
virtual QList<Qt3DCore::QEntity *> createEntities( const Map3D &map ) const = 0;

virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const = 0;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) = 0;
Expand Down

0 comments on commit b0c98b7

Please sign in to comment.