Skip to content
Permalink
Browse files

[3d] Added Qgs3DRenderContext + QgsFeature3DHandler. Code refactoring.

Currently implementation of 3D symbols runs a feature iteration loop
inside each implementation, making it difficult to use elsewhere
(e.g. if source features are not coming from a vector layer, or just
for its subset). The new classes give better structure to the code.

QgsFeature3DHandler is somehow similar to the 2D renderer classes
where the object first has the ability to say what fields may be needed,
then there's a method that will process a given QgsFeature (this is
supposed to be called in a loop) and finally a method to finish the job
and create a 3D entity. Thanks to this separation of responsibilities
it should be easier to later move the heavy work to a worker thread.

Qgs3DRenderContext is similar to QgsRenderContext used in 2D rendering.
The purpose is to collect arbitrary objects useful during preparation
of 3D entities. Currently just keeping Qgs3DMapSettings and expression
context.

Updated implementation of 3D polygon symbols to use the new infrastructure.
A nice side effect is that instead of two feature iteration loops
(one for selected, the other for non-selected) there is just one.
  • Loading branch information
wonder-sk committed Jan 16, 2019
1 parent 0a7c8a7 commit 7f5d8ea5072c72870ab83dd66e316e4ce28c099e
@@ -9,6 +9,7 @@ SET(QGIS_3D_SRCS
qgs3dutils.cpp
qgscameracontroller.cpp
qgscamerapose.cpp
qgsfeature3dhandler_p.cpp
qgslayoutitem3dmap.cpp
qgsoffscreen3dengine.cpp
qgsphongmaterialsettings.cpp
@@ -94,6 +95,7 @@ SET(QGIS_3D_HDRS
qgs3dutils.h
qgscameracontroller.h
qgscamerapose.h
qgsfeature3dhandler_p.h
qgslayoutitem3dmap.h
qgsoffscreen3dengine.h
qgsphongmaterialsettings.h
@@ -0,0 +1,64 @@
/***************************************************************************
qgsfeature3dhandler_p.cpp
--------------------------------------
Date : January 2019
Copyright : (C) 2019 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/


#include "qgsfeature3dhandler_p.h"

#include "qgsfeaturerequest.h"
#include "qgsvectorlayer.h"

#include "qgs3dmapsettings.h"

/// @cond PRIVATE


namespace Qgs3DSymbolImpl
{

Qt3DCore::QEntity *entityFromHandler( QgsFeature3DHandler *handler, const Qgs3DMapSettings &map, QgsVectorLayer *layer )
{
Qgs3DRenderContext context( map );

QgsExpressionContext exprContext;
exprContext << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() );
exprContext.setFields( layer->fields() );
context.setExpressionContext( exprContext );

QSet<QString> attributeNames;
if ( !handler->prepare( context, attributeNames ) )
return nullptr;

// build the feature request
QgsFeatureRequest req;
req.setDestinationCrs( map.crs(), map.transformContext() );
req.setSubsetOfAttributes( attributeNames, layer->fields() );

QgsFeature f;
QgsFeatureIterator fi = layer->getFeatures( req );
while ( fi.nextFeature( f ) )
{
context.expressionContext().setFeature( f );
handler->processFeature( f, context );
}

Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
handler->finalize( entity, context );
return entity;
}

}

/// @endcond
@@ -0,0 +1,122 @@
/***************************************************************************
qgsfeature3dhandler_p.h
--------------------------------------
Date : January 2019
Copyright : (C) 2019 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSFEATURE3DHANDLER_P_H
#define QGSFEATURE3DHANDLER_P_H

/// @cond PRIVATE

//
// W A R N I N G
// -------------
//
// This file is not part of the QGIS API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//

#include <Qt3DCore/QEntity>

class QgsFeature;


#include "qgsexpressioncontext.h"

class Qgs3DMapSettings;


/**
* \ingroup 3d
* Rendering context for preparation of 3D entities.
*/
class Qgs3DRenderContext
{
public:
Qgs3DRenderContext( const Qgs3DMapSettings &map ) : mMap( map ) {}

const Qgs3DMapSettings &map() const { return mMap; }

/**
* Sets the expression context. This context is used for all expression evaluation
* associated with this render context.
* \see expressionContext()
*/
void setExpressionContext( const QgsExpressionContext &context ) { mExpressionContext = context; }

/**
* Gets the expression context. This context should be used for all expression evaluation
* associated with this render context.
* \see setExpressionContext()
*/
QgsExpressionContext &expressionContext() { return mExpressionContext; }

/**
* Gets the expression context (const version). This context should be used for all expression evaluation
* associated with this render context.
* \see setExpressionContext()
* \note not available in Python bindings
*/
const QgsExpressionContext &expressionContext() const { return mExpressionContext; } SIP_SKIP

private:
const Qgs3DMapSettings &mMap;
//! Expression context
QgsExpressionContext mExpressionContext;

};


/**
* \ingroup 3d
* Interface to be implemented by 3D symbol implementations in order to generate 3D entities.
*/
class QgsFeature3DHandler
{
public:
virtual ~QgsFeature3DHandler() = default;

/**
* Called before feature iteration starts to initialize, get required attributes.
* \returns true on success (on false the handler failed to initialize and processFeature() / finalize() should not be called
*/
virtual bool prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames ) = 0;

/**
* Called for every feature to extract information out of it into some
* temporary variables in the derived handler class.
*/
virtual void processFeature( QgsFeature &feature, const Qgs3DRenderContext &context ) = 0;

/**
* When feature iteration has finished, finalize() is called to turn the extracted data
* to a 3D entity object(s) attached to the given parent.
*/
virtual void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context ) = 0;
};


class Qgs3DMapSettings;
class QgsVectorLayer;

namespace Qgs3DSymbolImpl
{
//! generic method to iterate over a layer, handle features with handler and create an entity out of it
Qt3DCore::QEntity *entityFromHandler( QgsFeature3DHandler *handler, const Qgs3DMapSettings &map, QgsVectorLayer *layer );
}


/// @endcond

#endif // QGSFEATURE3DHANDLER_P_H
@@ -82,7 +82,7 @@ Qt3DCore::QEntity *QgsVectorLayer3DRenderer::createEntity( const Qgs3DMapSetting
return nullptr;

if ( mSymbol->type() == QLatin1String( "polygon" ) )
return new QgsPolygon3DSymbolEntity( map, vl, *static_cast<QgsPolygon3DSymbol *>( mSymbol.get() ) );
return Qgs3DSymbolImpl::entityForPolygon3DSymbol( map, vl, *static_cast<QgsPolygon3DSymbol *>( mSymbol.get() ) );
else if ( mSymbol->type() == QLatin1String( "point" ) )
return new QgsPoint3DSymbolEntity( map, vl, *static_cast<QgsPoint3DSymbol *>( mSymbol.get() ) );
else if ( mSymbol->type() == QLatin1String( "line" ) )

0 comments on commit 7f5d8ea

Please sign in to comment.
You can’t perform that action at this time.