Skip to content

Commit

Permalink
initial 3d classification rendering implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
NEDJIMAbelgacem authored and wonder-sk committed Dec 7, 2020
1 parent b45eb5c commit 69f4476
Show file tree
Hide file tree
Showing 12 changed files with 415 additions and 2 deletions.
50 changes: 50 additions & 0 deletions python/3d/auto_generated/symbols/qgspointcloud3dsymbol.sip.in
Expand Up @@ -392,6 +392,56 @@ Ownership of ``enhancement`` is transferred.
QgsRgbPointCloud3DSymbol( const QgsRgbPointCloud3DSymbol &other );
};

class QgsClassificationPointCloud3DSymbol : QgsPointCloud3DSymbol
{

%TypeHeaderCode
#include "qgspointcloud3dsymbol.h"
%End
public:
QgsClassificationPointCloud3DSymbol();
%Docstring
Constructor for QgsClassificationPointCloud3DSymbol
%End

virtual QgsAbstract3DSymbol *clone() const /Factory/;

virtual QString symbolType() const;


virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const;

virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );


QString renderingParameter() const;
%Docstring
Returns the parameter used to select the color of the point cloud

.. seealso:: :py:func:`setRenderingParameter`
%End

void setRenderingParameter( const QString &parameter );
%Docstring
Sets the parameter used to select the color of the point cloud

.. seealso:: :py:func:`renderingParameter`
%End

QgsPointCloudCategoryList categoriesList() const;
void setCategoriesList( QgsPointCloudCategoryList categories );

QgsColorRampShader colorRampShader() const;
%Docstring
Returns the color ramp shader used to render the color

.. seealso:: :py:func:`setColorRampShader`
%End

virtual unsigned int byteStride();

};

/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
2 changes: 2 additions & 0 deletions src/3d/qgspointcloudlayer3drenderer.cpp
Expand Up @@ -140,6 +140,8 @@ void QgsPointCloudLayer3DRenderer::readXml( const QDomElement &elem, const QgsRe
mSymbol.reset( new QgsColorRampPointCloud3DSymbol );
else if ( symbolType == QLatin1String( "rgb" ) )
mSymbol.reset( new QgsRgbPointCloud3DSymbol );
else if ( symbolType == QLatin1String( "classification" ) )
mSymbol.reset( new QgsClassificationPointCloud3DSymbol );
else
mSymbol.reset();

Expand Down
2 changes: 2 additions & 0 deletions src/3d/qgspointcloudlayerchunkloader_p.cpp
Expand Up @@ -67,6 +67,8 @@ QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointClou
mHandler.reset( new QgsColorRampPointCloud3DSymbolHandler() );
else if ( symbol->symbolType() == QLatin1String( "rgb" ) )
mHandler.reset( new QgsRGBPointCloud3DSymbolHandler() );
else if ( symbol->symbolType() == QLatin1String( "classification" ) )
mHandler.reset( new QgsClassificationPointCloud3DSymbolHandler() );

//
// this will be run in a background thread
Expand Down
131 changes: 131 additions & 0 deletions src/3d/symbols/qgspointcloud3dsymbol.cpp
Expand Up @@ -367,3 +367,134 @@ void QgsRgbPointCloud3DSymbol::setBlueContrastEnhancement( QgsContrastEnhancemen
mBlueContrastEnhancement.reset( enhancement );
}

// QgsClassificationPointCloud3DSymbol


QgsClassificationPointCloud3DSymbol::QgsClassificationPointCloud3DSymbol()
: QgsPointCloud3DSymbol()
{

}

QgsAbstract3DSymbol *QgsClassificationPointCloud3DSymbol::clone() const
{
QgsClassificationPointCloud3DSymbol *result = new QgsClassificationPointCloud3DSymbol;
result->mPointSize = mPointSize;
result->mRenderingParameter = mRenderingParameter;
result->mCategoriesList = mCategoriesList;
copyBaseSettings( result );
return result;
}

QString QgsClassificationPointCloud3DSymbol::symbolType() const
{
return QStringLiteral( "classification" );
}

void QgsClassificationPointCloud3DSymbol::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
{
Q_UNUSED( context )
QDomDocument doc = elem.ownerDocument();

elem.setAttribute( QStringLiteral( "point-size" ), mPointSize );
elem.setAttribute( QStringLiteral( "rendering-parameter" ), mRenderingParameter );

// categories
QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
for ( const QgsPointCloudCategory &category : mCategoriesList )
{
QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
catElem.setAttribute( QStringLiteral( "value" ), QString::number( category.value() ) );
catElem.setAttribute( QStringLiteral( "label" ), category.label() );
catElem.setAttribute( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( category.color() ) );
catElem.setAttribute( QStringLiteral( "render" ), category.renderState() ? "true" : "false" );
catsElem.appendChild( catElem );
}
elem.appendChild( catsElem );
}

void QgsClassificationPointCloud3DSymbol::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
{
Q_UNUSED( context )

mPointSize = elem.attribute( "point-size", QStringLiteral( "2.0" ) ).toFloat();
mRenderingParameter = elem.attribute( "rendering-parameter", QString() );

const QDomElement catsElem = elem.firstChildElement( QStringLiteral( "categories" ) );
if ( !catsElem.isNull() )
{
mCategoriesList.clear();
QDomElement catElem = catsElem.firstChildElement();
while ( !catElem.isNull() )
{
if ( catElem.tagName() == QLatin1String( "category" ) )
{
const int value = catElem.attribute( QStringLiteral( "value" ) ).toInt();
const QString label = catElem.attribute( QStringLiteral( "label" ) );
const bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
const QColor color = QgsSymbolLayerUtils::decodeColor( catElem.attribute( QStringLiteral( "color" ) ) );
mCategoriesList.append( QgsPointCloudCategory( value, color, label, render ) );
}
catElem = catElem.nextSiblingElement();
}
}
}

QString QgsClassificationPointCloud3DSymbol::renderingParameter() const
{
return mRenderingParameter;
}

void QgsClassificationPointCloud3DSymbol::setRenderingParameter( const QString &parameter )
{
mRenderingParameter = parameter;
}

void QgsClassificationPointCloud3DSymbol::setCategoriesList( QgsPointCloudCategoryList categories )
{
mCategoriesList = categories;
}

QgsColorRampShader QgsClassificationPointCloud3DSymbol::colorRampShader() const
{
QgsColorRampShader colorRampShader;
colorRampShader.setColorRampType( QgsColorRampShader::Type::Exact );
colorRampShader.setClassificationMode( QgsColorRampShader::ClassificationMode::Continuous );
QList<QgsColorRampShader::ColorRampItem> colorRampItemList;
for ( const QgsPointCloudCategory &category : mCategoriesList )
{
QgsColorRampShader::ColorRampItem item( category.value(), category.color(), category.label() );
colorRampItemList.push_back( item );
}
colorRampShader.setColorRampItemList( colorRampItemList );
return colorRampShader;
}


void QgsClassificationPointCloud3DSymbol::fillMaterial( Qt3DRender::QMaterial *mat )
{
QgsColorRampShader mColorRampShader = colorRampShader();
Qt3DRender::QParameter *renderingStyle = new Qt3DRender::QParameter( "u_renderingStyle", QgsPointCloud3DSymbol::ColorRamp );
mat->addParameter( renderingStyle );
Qt3DRender::QParameter *pointSizeParameter = new Qt3DRender::QParameter( "u_pointSize", QVariant::fromValue( mPointSize ) );
mat->addParameter( pointSizeParameter );
// Create the texture to pass the color ramp
Qt3DRender::QTexture1D *colorRampTexture = nullptr;
if ( mColorRampShader.colorRampItemList().count() > 0 )
{
colorRampTexture = new Qt3DRender::QTexture1D( mat );
colorRampTexture->addTextureImage( new QgsColorRampTexture( mColorRampShader, 1 ) );
colorRampTexture->setMinificationFilter( Qt3DRender::QTexture1D::Linear );
colorRampTexture->setMagnificationFilter( Qt3DRender::QTexture1D::Linear );
}

// Parameters
Qt3DRender::QParameter *colorRampTextureParameter = new Qt3DRender::QParameter( "u_colorRampTexture", colorRampTexture );
mat->addParameter( colorRampTextureParameter );
Qt3DRender::QParameter *colorRampCountParameter = new Qt3DRender::QParameter( "u_colorRampCount", mColorRampShader.colorRampItemList().count() );
mat->addParameter( colorRampCountParameter );
int colorRampType = mColorRampShader.colorRampType();
Qt3DRender::QParameter *colorRampTypeParameter = new Qt3DRender::QParameter( "u_colorRampType", colorRampType );
mat->addParameter( colorRampTypeParameter );
}

42 changes: 42 additions & 0 deletions src/3d/symbols/qgspointcloud3dsymbol.h
Expand Up @@ -24,6 +24,7 @@
#include "qgscolorrampshader.h"
#include "qgspointcloudlayer.h"
#include "qgscontrastenhancement.h"
#include "qgspointcloudclassifiedrenderer.h"

/**
* \ingroup 3d
Expand Down Expand Up @@ -360,4 +361,45 @@ class _3D_EXPORT QgsRgbPointCloud3DSymbol : public QgsPointCloud3DSymbol

};

class _3D_EXPORT QgsClassificationPointCloud3DSymbol : public QgsPointCloud3DSymbol
{
public:
//! Constructor for QgsClassificationPointCloud3DSymbol
QgsClassificationPointCloud3DSymbol();

QgsAbstract3DSymbol *clone() const override SIP_FACTORY;
QString symbolType() const override;

void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;

/**
* Returns the parameter used to select the color of the point cloud
* \see setRenderingParameter( const QString &parameter )
*/
QString renderingParameter() const;

/**
* Sets the parameter used to select the color of the point cloud
* \see renderingParameter()
*/
void setRenderingParameter( const QString &parameter );

QgsPointCloudCategoryList categoriesList() const { return mCategoriesList; }
void setCategoriesList( QgsPointCloudCategoryList categories );

/**
* Returns the color ramp shader used to render the color
* \see setColorRampShader( const QgsColorRampShader &colorRampShader )
*/
QgsColorRampShader colorRampShader() const;

unsigned int byteStride() override { return 4 * sizeof( float ); }
void fillMaterial( Qt3DRender::QMaterial *material ) override SIP_SKIP;

private:
QString mRenderingParameter;
QgsPointCloudCategoryList mCategoriesList;
};

#endif // QGSPOINTCLOUD3DSYMBOL_H
114 changes: 114 additions & 0 deletions src/3d/symbols/qgspointcloud3dsymbol_p.cpp
Expand Up @@ -521,4 +521,118 @@ Qt3DRender::QGeometry *QgsRGBPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::
return new QgsRGBPointCloud3DGeometry( parent, data, byteStride );
}

QgsClassificationPointCloud3DSymbolHandler::QgsClassificationPointCloud3DSymbolHandler()
: QgsPointCloud3DSymbolHandler()
{

}

bool QgsClassificationPointCloud3DSymbolHandler::prepare( const QgsPointCloud3DRenderContext &context )
{
Q_UNUSED( context )
return true;
}

void QgsClassificationPointCloud3DSymbolHandler::processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context )
{
QgsPointCloudAttributeCollection attributes;
constexpr int xOffset = 0;
attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
const int yOffset = attributes.pointRecordSize();
attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
const int zOffset = attributes.pointRecordSize();
attributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );

QString attributeName;
bool attrIsX = false;
bool attrIsY = false;
bool attrIsZ = false;
QgsPointCloudAttribute::DataType attributeType = QgsPointCloudAttribute::Float;
int attributeOffset = 0;
QgsClassificationPointCloud3DSymbol *symbol = dynamic_cast<QgsClassificationPointCloud3DSymbol *>( context.symbol() );
if ( symbol )
{
int offset = 0;
QgsPointCloudAttributeCollection collection = context.attributes();

if ( symbol->renderingParameter() == QLatin1String( "X" ) )
{
attrIsX = true;
}
else if ( symbol->renderingParameter() == QLatin1String( "Y" ) )
{
attrIsY = true;
}
else if ( symbol->renderingParameter() == QLatin1String( "Z" ) )
{
attrIsZ = true;
}
else
{
const QgsPointCloudAttribute *attr = collection.find( symbol->renderingParameter(), offset );
if ( attr )
{
attributeType = attr->type();
attributeName = attr->name();
attributeOffset = attributes.pointRecordSize();
attributes.push_back( *attr );
}
}
}

if ( attributeName.isEmpty() && !attrIsX && !attrIsY && !attrIsZ )
return;

QgsPointCloudRequest request;
request.setAttributes( attributes );
std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
if ( !block )
return;

const char *ptr = block->data();
int count = block->pointCount();
const std::size_t recordSize = attributes.pointRecordSize();

const QgsVector3D scale = pc->scale();
const QgsVector3D offset = pc->offset();

for ( int i = 0; i < count; ++i )
{
qint32 ix = *( qint32 * )( ptr + i * recordSize + xOffset );
qint32 iy = *( qint32 * )( ptr + i * recordSize + yOffset );
qint32 iz = *( qint32 * )( ptr + i * recordSize + zOffset );

double x = offset.x() + scale.x() * ix;
double y = offset.y() + scale.y() * iy;
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() ) );

if ( attrIsX )
outNormal.parameter.push_back( x );
else if ( attrIsY )
outNormal.parameter.push_back( y );
else if ( attrIsZ )
outNormal.parameter.push_back( z );
else
{
float iParam = 0.0f;
context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, iParam );
outNormal.parameter.push_back( iParam );
}
}
}

void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context )
{
makeEntity( parent, context, outNormal, false );
}

Qt3DRender::QGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
{
return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
}

/// @endcond

0 comments on commit 69f4476

Please sign in to comment.