mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-09 00:08:52 -04:00
338 lines
12 KiB
C++
338 lines
12 KiB
C++
/***************************************************************************
|
|
qgspointcloudlayerchunkloader_p.cpp
|
|
--------------------------------------
|
|
Date : October 2020
|
|
Copyright : (C) 2020 by Peter Petrik
|
|
Email : zilolv 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 "qgspointcloudlayerchunkloader_p.h"
|
|
|
|
#include "qgs3dutils.h"
|
|
#include "qgspointcloudlayer3drenderer.h"
|
|
#include "qgschunknode_p.h"
|
|
#include "qgslogger.h"
|
|
#include "qgspointcloudlayer.h"
|
|
#include "qgspointcloudindex.h"
|
|
#include "qgseventtracing.h"
|
|
#include "qgsabstractvectorlayer3drenderer.h" // for QgsVectorLayer3DTilingSettings
|
|
|
|
#include "qgspoint3dsymbol.h"
|
|
#include "qgsphongmaterialsettings.h"
|
|
|
|
#include "qgsapplication.h"
|
|
#include "qgs3dsymbolregistry.h"
|
|
|
|
#include <QtConcurrent>
|
|
#include <Qt3DRender/QAttribute>
|
|
#include <Qt3DRender/QTechnique>
|
|
#include <Qt3DRender/QShaderProgram>
|
|
#include <Qt3DRender/QGraphicsApiFilter>
|
|
|
|
///@cond PRIVATE
|
|
|
|
QgsPointCloud3DGeometry::QgsPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data )
|
|
: Qt3DRender::QGeometry( parent )
|
|
, mPositionAttribute( new Qt3DRender::QAttribute( this ) )
|
|
, mClassAttribute( new Qt3DRender::QAttribute( this ) )
|
|
, mVertexBuffer( new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this ) )
|
|
{
|
|
mPositionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
|
|
mPositionAttribute->setBuffer( mVertexBuffer );
|
|
mPositionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
|
|
mPositionAttribute->setVertexSize( 3 );
|
|
mPositionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() );
|
|
mPositionAttribute->setByteOffset( 0 );
|
|
mPositionAttribute->setByteStride( 16 );
|
|
|
|
mClassAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute );
|
|
mClassAttribute->setBuffer( mVertexBuffer );
|
|
mClassAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float );
|
|
mClassAttribute->setVertexSize( 1 );
|
|
mClassAttribute->setName( "cls" );
|
|
mClassAttribute->setByteOffset( 12 );
|
|
mClassAttribute->setByteStride( 16 );
|
|
|
|
addAttribute( mPositionAttribute );
|
|
addAttribute( mClassAttribute );
|
|
|
|
|
|
makeVertexBuffer( data );
|
|
}
|
|
|
|
void QgsPointCloud3DGeometry::makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data )
|
|
{
|
|
QByteArray vertexBufferData;
|
|
vertexBufferData.resize( data.positions.size() * 4 * sizeof( float ) );
|
|
float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() );
|
|
int idx = 0;
|
|
int i = 0;
|
|
for ( int i = 0; i < data.positions.size(); ++i )
|
|
{
|
|
rawVertexArray[idx++] = data.positions.at( i ).x();
|
|
rawVertexArray[idx++] = data.positions.at( i ).y();
|
|
rawVertexArray[idx++] = data.positions.at( i ).z();
|
|
rawVertexArray[idx++] = data.classes.at( i );
|
|
}
|
|
|
|
mVertexCount = data.positions.size();
|
|
mVertexBuffer->setData( vertexBufferData );
|
|
}
|
|
|
|
|
|
QgsPointCloud3DSymbolHandler::QgsPointCloud3DSymbolHandler()
|
|
{
|
|
|
|
}
|
|
|
|
bool QgsPointCloud3DSymbolHandler::prepare( const Qgs3DRenderContext &context )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void QgsPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const Qgs3DRenderContext &context )
|
|
{
|
|
const QVector<qint32> data = pc->nodePositionDataAsInt32( n );
|
|
const QVector<char> classes = pc->nodeClassesDataAsChar( n );
|
|
const QgsVector3D scale = pc->scale();
|
|
const QgsVector3D offset = pc->offset();
|
|
|
|
//qDebug() << " node " << data.count()/3 << " points";
|
|
|
|
// QgsRectangle mapExtent = context.map().extent()???
|
|
|
|
const qint32 *ptr = data.constData();
|
|
int count = data.count() / 3;
|
|
for ( int i = 0; i < count; ++i )
|
|
{
|
|
qint32 ix = ptr[i * 3 + 0];
|
|
qint32 iy = ptr[i * 3 + 1];
|
|
qint32 iz = ptr[i * 3 + 2];
|
|
|
|
double x = offset.x() + scale.x() * ix;
|
|
double y = offset.y() + scale.y() * iy;
|
|
// if ( mapExtent.contains( QgsPointXY( x, y ) ) )
|
|
// {
|
|
double z = offset.z() + scale.z() * iz;
|
|
QVector3D point( x, y, z );
|
|
QgsVector3D p = context.map().mapToWorldCoordinates( point );
|
|
outNormal.positions.push_back( QVector3D( p.x(), p.y() - 400, p.z() ) );
|
|
|
|
// }
|
|
}
|
|
outNormal.classes.append( classes );
|
|
}
|
|
|
|
void QgsPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
|
|
{
|
|
makeEntity( parent, context, outNormal, false );
|
|
// makeEntity( parent, context, outSelected, true );
|
|
|
|
// updateZRangeFromPositions( outNormal.positions );
|
|
// updateZRangeFromPositions( outSelected.positions );
|
|
|
|
// the elevation offset is applied separately in QTransform added to sub-entities
|
|
//float symbolHeight = mSymbol->transform().data()[13];
|
|
//mZMin += symbolHeight;
|
|
//mZMax += symbolHeight;
|
|
}
|
|
#include <QPointSize>
|
|
void QgsPointCloud3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, QgsPointCloud3DSymbolHandler::PointData &out, bool selected )
|
|
{
|
|
if ( out.positions.empty() )
|
|
return;
|
|
|
|
// Geometry
|
|
QgsPointCloud3DGeometry *geom = new QgsPointCloud3DGeometry( parent, out );
|
|
Qt3DRender::QGeometryRenderer *gr = new Qt3DRender::QGeometryRenderer;
|
|
gr->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
|
|
gr->setVertexCount( out.positions.count() );
|
|
gr->setGeometry( geom );
|
|
|
|
// Transform
|
|
Qt3DCore::QTransform *tr = new Qt3DCore::QTransform;
|
|
// tr->setMatrix( symbol->transform() );
|
|
// tr->setTranslation( position + tr->translation() );
|
|
|
|
// Material
|
|
Qt3DRender::QMaterial *mat = new Qt3DRender::QMaterial;
|
|
|
|
Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram( mat );
|
|
shaderProgram->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/pointcloud.vert" ) ) ) );
|
|
shaderProgram->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( QStringLiteral( "qrc:/shaders/pointcloud.frag" ) ) ) );
|
|
|
|
Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass( mat );
|
|
renderPass->setShaderProgram( shaderProgram );
|
|
|
|
Qt3DRender::QPointSize *pointSize = new Qt3DRender::QPointSize( renderPass );
|
|
pointSize->setSizeMode( Qt3DRender::QPointSize::Programmable ); // supported since OpenGL 3.2
|
|
renderPass->addRenderState( pointSize );
|
|
|
|
// without this filter the default forward renderer would not render this
|
|
Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey;
|
|
filterKey->setName( QStringLiteral( "renderingStyle" ) );
|
|
filterKey->setValue( "forward" );
|
|
|
|
Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
|
|
technique->addRenderPass( renderPass );
|
|
technique->addFilterKey( filterKey );
|
|
technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
|
|
technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
|
|
technique->graphicsApiFilter()->setMajorVersion( 3 );
|
|
technique->graphicsApiFilter()->setMinorVersion( 1 );
|
|
|
|
Qt3DRender::QEffect *eff = new Qt3DRender::QEffect;
|
|
eff->addTechnique( technique );
|
|
mat->setEffect( eff );
|
|
|
|
// All together
|
|
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
|
|
entity->addComponent( gr );
|
|
entity->addComponent( tr );
|
|
entity->addComponent( mat );
|
|
entity->setParent( parent );
|
|
// cppcheck wrongly believes entity will leak
|
|
// cppcheck-suppress memleak
|
|
}
|
|
|
|
|
|
QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node )
|
|
: QgsChunkLoader( node )
|
|
, mFactory( factory )
|
|
, mContext( factory->mMap )
|
|
// , mSource( new QgsPointCloudLayerFeatureSource( factory->mLayer ) )
|
|
{
|
|
if ( node->level() < mFactory->mLeafLevel )
|
|
{
|
|
QTimer::singleShot( 0, this, &QgsPointCloudLayerChunkLoader::finished );
|
|
return;
|
|
}
|
|
qDebug() << "creating entity!";
|
|
|
|
QgsPointCloudLayer *layer = mFactory->mLayer;
|
|
const Qgs3DMapSettings &map = mFactory->mMap;
|
|
|
|
QgsPointCloud3DSymbolHandler *handler = new QgsPointCloud3DSymbolHandler;
|
|
mHandler.reset( handler );
|
|
|
|
QgsPointCloudIndex *pc = layer->dataProvider()->index();
|
|
|
|
//QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( layer ) );
|
|
//exprContext.setFields( layer->fields() );
|
|
//mContext.setExpressionContext( exprContext );
|
|
|
|
//QSet<QString> attributeNames;
|
|
//if ( !mHandler->prepare( mContext, attributeNames ) )
|
|
// {
|
|
// QgsDebugMsg( QStringLiteral( "Failed to prepare 3D feature handler!" ) );
|
|
// return;
|
|
//}
|
|
|
|
// build the feature request
|
|
// QgsFeatureRequest req;
|
|
//req.setDestinationCrs( map.crs(), map.transformContext() );
|
|
//req.setSubsetOfAttributes( attributeNames, layer->fields() );
|
|
|
|
// only a subset of data to be queried
|
|
QgsRectangle rect = Qgs3DUtils::worldToMapExtent( node->bbox(), map.origin() );
|
|
//req.setFilterRect( rect );
|
|
|
|
// TODO: set depth based on map units per pixel
|
|
int depth = 3;
|
|
QList<IndexedPointCloudNode> nodes = pc->traverseTree( rect, pc->root(), depth );
|
|
|
|
//
|
|
// this will be run in a background thread
|
|
//
|
|
QFuture<void> future = QtConcurrent::run( [pc, nodes, this]
|
|
{
|
|
QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "PC chunk load" ) );
|
|
|
|
for ( const IndexedPointCloudNode &n : nodes )
|
|
{
|
|
if ( mCanceled )
|
|
{
|
|
qDebug() << "canceled";
|
|
break;
|
|
}
|
|
mHandler->processNode( pc, n, mContext );
|
|
}
|
|
} );
|
|
|
|
// emit finished() as soon as the handler is populated with features
|
|
mFutureWatcher = new QFutureWatcher<void>( this );
|
|
mFutureWatcher->setFuture( future );
|
|
connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
|
|
|
|
}
|
|
|
|
QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
|
|
{
|
|
if ( mFutureWatcher && !mFutureWatcher->isFinished() )
|
|
{
|
|
disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
|
|
mFutureWatcher->waitForFinished();
|
|
}
|
|
}
|
|
|
|
void QgsPointCloudLayerChunkLoader::cancel()
|
|
{
|
|
mCanceled = true;
|
|
}
|
|
|
|
Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
|
|
{
|
|
if ( mNode->level() < mFactory->mLeafLevel )
|
|
{
|
|
return new Qt3DCore::QEntity( parent ); // dummy entity
|
|
}
|
|
|
|
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
|
|
mHandler->finalize( entity, mContext );
|
|
return entity;
|
|
}
|
|
|
|
|
|
///////////////
|
|
|
|
|
|
QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudLayer *vl, int leafLevel )
|
|
: mMap( map )
|
|
, mLayer( vl )
|
|
, mLeafLevel( leafLevel )
|
|
{
|
|
}
|
|
|
|
QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
|
|
{
|
|
return new QgsPointCloudLayerChunkLoader( this, node );
|
|
}
|
|
|
|
|
|
///////////////
|
|
|
|
QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudLayer *vl, double zMin, double zMax, const Qgs3DMapSettings &map )
|
|
: QgsChunkedEntity( Qgs3DUtils::layerToWorldExtent( vl->extent(), zMin, zMax, vl->crs(), map.origin(), map.crs(), map.transformContext() ),
|
|
-1, // rootError (negative error means that the node does not contain anything)
|
|
-1, // max. allowed screen error (negative tau means that we need to go until leaves are reached)
|
|
0, //QgsVectorLayer3DTilingSettings().zoomLevelsCount() - 1,
|
|
new QgsPointCloudLayerChunkLoaderFactory( map, vl, 0 /*QgsVectorLayer3DTilingSettings().zoomLevelsCount() - 1*/ ), true )
|
|
{
|
|
setShowBoundingBoxes( QgsVectorLayer3DTilingSettings().showBoundingBoxes() );
|
|
}
|
|
|
|
QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
|
|
{
|
|
// cancel / wait for jobs
|
|
cancelActiveJobs();
|
|
}
|
|
|
|
/// @endcond
|