Skip to content
Permalink
Browse files

port academic code from Martin's prototype. ugly me

  • Loading branch information
PeterPetrik authored and nyalldawson committed Oct 26, 2020
1 parent acccc63 commit 62faf3f9223e86184f4ea58b53eb07ec89586019
@@ -20,18 +20,73 @@
#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 ).z();
rawVertexArray[idx++] = data.positions.at( i ).y();
rawVertexArray[idx++] = data.classes.at( i );
}

mVertexCount = data.positions.size();
mVertexBuffer->setData( vertexBufferData );
}


QgsPointCloud3DSymbolHandler::QgsPointCloud3DSymbolHandler()
{

@@ -42,11 +97,35 @@ bool QgsPointCloud3DSymbolHandler::prepare( const Qgs3DRenderContext &context )
return true;
}

void QgsPointCloud3DSymbolHandler::processFeature( const QVector3D &point, const Qgs3DRenderContext &context )
void QgsPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const Qgs3DRenderContext &context )
{
mZMin = std::min( mZMin, point.z() );
mZMax = std::max( mZMax, point.z() );
outNormal.positions.push_back( point );
const QVector<qint32> data = pc->nodePositionDataAsInt32( n );
const QVector<char> classes = pc->nodeClassesDataAsChar( n );
const QgsVector3D scale = pc->scale();
const QgsVector3D offset = pc->offset();

// 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(), p.z() ) );

// }
}
outNormal.classes.append(classes);
}

void QgsPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context )
@@ -68,36 +147,53 @@ void QgsPointCloud3DSymbolHandler::makeEntity( Qt3DCore::QEntity *parent, const
if ( out.positions.empty() )
return;

// build the default material
QgsMaterialContext materialContext;
materialContext.setIsSelected( selected );
materialContext.setSelectionColor( context.map().selectionColor() );
/*
Qt3DRender::QMaterial *mat = symbol->material()->toMaterial( QgsMaterialSettingsRenderingTechnique::Triangles, materialContext );
// get nodes
for ( const QVector3D &position : positions )
{
const QString source = QgsApplication::instance()->sourceCache()->localFilePath( symbol->shapeProperties()[QStringLiteral( "model" )].toString() );
if ( !source.isEmpty() )
{
// build the entity
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
QUrl url = QUrl::fromLocalFile( source );
Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
mesh->setSource( url );
entity->addComponent( mesh );
entity->addComponent( mat );
entity->addComponent( transform( position, symbol ) );
entity->setParent( parent );
// 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 );

// 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
}
}
*/
}


@@ -119,6 +215,8 @@ QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointClou
QgsPointCloud3DSymbolHandler *handler = new QgsPointCloud3DSymbolHandler;
mHandler.reset( handler );

QgsPointCloudIndex *pc = layer->pointCloudIndex();

//QgsExpressionContext exprContext( Qgs3DUtils::globalProjectLayerExpressionContext( layer ) );
//exprContext.setFields( layer->fields() );
//mContext.setExpressionContext( exprContext );
@@ -131,38 +229,41 @@ QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointClou
//}

// build the feature request
//QgsFeatureRequest req;
// 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() );
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( [req, this]
{
QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "VL chunk load" ) );
QFuture<void> future = QtConcurrent::run( [pc, nodes, this]
{
QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "PC chunk load" ) );

QgsFeature f;
QgsFeatureIterator fi = mSource->getFeatures( req );
while ( fi.nextFeature( f ) )
for ( const IndexedPointCloudNode &n : nodes )
{
if ( mCanceled )
{
if ( mCanceled )
break;
mContext.expressionContext().setFeature( f );
// mHandler->processFeature( f, mContext );
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 );

// 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()
@@ -187,7 +288,7 @@ Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntit
}

Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
// mHandler->finalize( entity, mContext );
mHandler->finalize( entity, mContext );
return entity;
}

@@ -35,21 +35,34 @@

class Qgs3DMapSettings;
class QgsPointCloudLayer;
class IndexedPointCloudNode;
class QgsPointCloudIndex;

#include <QFutureWatcher>
#include <Qt3DRender/QGeometry>
#include <Qt3DRender/QBuffer>
#include <QVector3D>


class QgsPointCloud3DSymbolHandler // : public QgsFeature3DHandler
{
public:
QgsPointCloud3DSymbolHandler( );

bool prepare( const Qgs3DRenderContext &context );// override;
void processFeature( const QVector3D &point, const Qgs3DRenderContext &context );// override;
void processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const Qgs3DRenderContext &context ); // override;
void finalize( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context );// override;

float zMinimum() const { return mZMin; }
float zMaximum() const { return mZMax; }

//! temporary data we will pass to the tessellator
struct PointData
{
QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
QVector<char> classes;
};

protected:
float mZMin = std::numeric_limits<float>::max();
float mZMax = std::numeric_limits<float>::lowest();
@@ -60,11 +73,7 @@ class QgsPointCloud3DSymbolHandler // : public QgsFeature3DHandler
//static void addMeshEntities( const Qgs3DMapSettings &map, const QVector<QVector3D> &positions, const QgsPoint3DSymbol *symbol, Qt3DCore::QEntity *parent, bool are_selected );
//static Qt3DCore::QTransform *transform( QVector3D position, const QgsPoint3DSymbol *symbol );

//! temporary data we will pass to the tessellator
struct PointData
{
QVector<QVector3D> positions; // contains triplets of float x,y,z for each point
};


void makeEntity( Qt3DCore::QEntity *parent, const Qgs3DRenderContext &context, PointData &out, bool selected );

@@ -78,6 +87,20 @@ class QgsPointCloud3DSymbolHandler // : public QgsFeature3DHandler
// PointData outSelected; //!< Features that are selected
};

class QgsPointCloud3DGeometry: public Qt3DRender::QGeometry
{
public:
QgsPointCloud3DGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data );

private:
void makeVertexBuffer( const QgsPointCloud3DSymbolHandler::PointData &data );

Qt3DRender::QAttribute *mPositionAttribute = nullptr;
Qt3DRender::QAttribute *mClassAttribute = nullptr;
Qt3DRender::QBuffer *mVertexBuffer = nullptr;
int mVertexCount = 0;
};

/**
* \ingroup 3d
* This loader factory is responsible for creation of loaders for individual tiles
@@ -12,6 +12,8 @@
<file>shaders/mesh/mesh.vert</file>
<file>shaders/mesh/mesh.geom</file>
<file>shaders/mesh/mesh.frag</file>
<file>shaders/pointcloud.frag</file>
<file>shaders/pointcloud.vert</file>
<file>shaders/skybox.frag</file>
<file>shaders/skybox.vert</file>
<file>shaders/hdr_skybox.frag</file>
@@ -0,0 +1,27 @@
#version 150

in float clsid;

out vec4 color;

void main(void)
{
if ( abs(clsid-2) < 0.1 ) // ground
color = vec4(1,1,0,1);
else if ( abs( clsid - 3 ) < 0.1 ) // low vegetation
color = vec4(0,0.4,0,1);
else if ( abs( clsid - 4 ) < 0.1 ) // medium vegetation
color = vec4(0,0.6,0,1);
else if ( abs( clsid - 5 ) < 0.1 ) // high vegetation
color = vec4(0,1,0,1);
else if ( abs( clsid - 12 ) < 0.1 ) // overlaps
{
color = vec4(1,0,0,1);
//discard;
}
else
{
color = vec4(0,1,1,1);
//discard;
}
}

0 comments on commit 62faf3f

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