Skip to content
Permalink
Browse files

[FEATURE][3d] Enable embedded and remote 3D models for 3D point symbols

  • Loading branch information
nirvn committed Jul 18, 2020
1 parent 80627eb commit 3b249d12ff6c4641f239a8b3cce7dba41aca209c
@@ -194,10 +194,10 @@
%Include auto_generated/qgssimplifymethod.sip
%Include auto_generated/qgssnappingconfig.sip
%Include auto_generated/qgssnappingutils.sip
%Include auto_generated/qgssourcecache.sip
%Include auto_generated/qgsspatialindex.sip
%Include auto_generated/qgsspatialindexkdbush.sip
%Include auto_generated/qgsspatialindexkdbushdata.sip
%Include auto_generated/qgssourcecache.sip
%Include auto_generated/qgssqliteutils.sip
%Include auto_generated/qgssqlstatement.sip
%Include auto_generated/qgsstatisticalsummary.sip
@@ -35,6 +35,7 @@
#include <QOpenGLFunctions>
#include <QTimer>

#include "qgsapplication.h"
#include "qgsaabb.h"
#include "qgsabstract3dengine.h"
#include "qgs3dmapscenepickhandler.h"
@@ -47,7 +48,9 @@
#include "qgseventtracing.h"
#include "qgsmeshlayer.h"
#include "qgsmeshlayer3drenderer.h"
#include "qgspoint3dsymbol.h"
#include "qgsrulebased3drenderer.h"
#include "qgssourcecache.h"
#include "qgsterrainentity_p.h"
#include "qgsterraingenerator.h"
#include "qgstessellatedpolygongeometry.h"
@@ -110,6 +113,41 @@ Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *
connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens );
connect( &map, &Qgs3DMapSettings::renderersChanged, this, &Qgs3DMapScene::onRenderersChanged );

connect( QgsApplication::instance()->sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [ = ]( const QString & url )
{
const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
for ( QgsMapLayer *layer : modelVectorLayers )
{
QgsAbstract3DRenderer *renderer = layer->renderer3D();
if ( renderer )
{
if ( renderer->type() == QLatin1String( "vector" ) )
{
const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
if ( pointSymbol->shapeProperties()[QStringLiteral( "model" )].toString() == url )
{
removeLayerEntity( layer );
addLayerEntity( layer );
}
}
else if ( renderer->type() == QLatin1String( "rulebased" ) )
{
const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
for ( auto rule : rules )
{
const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
if ( pointSymbol->shapeProperties()[QStringLiteral( "model" )].toString() == url )
{
removeLayerEntity( layer );
addLayerEntity( layer );
break;
}
}
}
}
}
} );

// create entities of renderers

onRenderersChanged();
@@ -617,6 +655,31 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
( renderer->type() == QLatin1String( "vector" ) || renderer->type() == QLatin1String( "rulebased" ) ) )
{
static_cast<QgsAbstractVectorLayer3DRenderer *>( renderer )->setLayer( static_cast<QgsVectorLayer *>( layer ) );
if ( renderer->type() == QLatin1String( "vector" ) )
{
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
if ( vlayer->geometryType() == QgsWkbTypes::PointGeometry )
{
const QgsPoint3DSymbol *pointSymbol = static_cast< const QgsPoint3DSymbol * >( static_cast< QgsVectorLayer3DRenderer *>( renderer )->symbol() );
if ( pointSymbol->shape() == QgsPoint3DSymbol::Model )
{
mModelVectorLayers.append( layer );
}
}
}
else if ( renderer->type() == QLatin1String( "rulebased" ) )
{
const QgsRuleBased3DRenderer::RuleList rules = static_cast< QgsRuleBased3DRenderer *>( renderer )->rootRule()->descendants();
for ( auto rule : rules )
{
const QgsPoint3DSymbol *pointSymbol = dynamic_cast< const QgsPoint3DSymbol * >( rule->symbol() );
if ( pointSymbol && pointSymbol->shape() == QgsPoint3DSymbol::Model )
{
mModelVectorLayers.append( layer );
break;
}
}
}
}
else if ( layer->type() == QgsMapLayerType::MeshLayer && renderer->type() == QLatin1String( "mesh" ) )
{
@@ -692,6 +755,7 @@ void Qgs3DMapScene::removeLayerEntity( QgsMapLayer *layer )
{
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
disconnect( vlayer, &QgsVectorLayer::selectionChanged, this, &Qgs3DMapScene::onLayerRenderer3DChanged );
mModelVectorLayers.removeAll( layer );
}

if ( layer->type() == QgsMapLayerType::MeshLayer )
@@ -164,6 +164,7 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
QList<Qt3DCore::QEntity *> mLightEntities;
//! List of light origins in the scene
QList<Qt3DCore::QEntity *> mLightOriginEntities;
QList<QgsMapLayer *> mModelVectorLayers;
};

#endif // QGS3DMAPSCENE_H
@@ -44,12 +44,14 @@
#include "qgspoint3dsymbol.h"
#include "qgs3dmapsettings.h"

#include "qgsapplication.h"
#include "qgsvectorlayer.h"
#include "qgspoint.h"
#include "qgs3dutils.h"
#include "qgsbillboardgeometry.h"
#include "qgspoint3dbillboardmaterial.h"
#include "qgslogger.h"
#include "qgssourcecache.h"
#include "qgssymbol.h"
#include "qgssymbollayerutils.h"
#include "qgssymbollayer.h"
@@ -439,19 +441,24 @@ void QgsModelPoint3DSymbolHandler::addSceneEntities( const Qgs3DMapSettings &map
Q_UNUSED( map )
for ( const QVector3D &position : positions )
{
// build the entity
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
const QString source = QgsApplication::instance()->sourceCache()->localFilePath( symbol.shapeProperties()[QStringLiteral( "model" )].toString() );
// if the source is remote, the Qgs3DMapScene will take care of refreshing this 3D symbol when the source is fetched
if ( !source.isEmpty() )
{
// build the entity
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;

QUrl url = QUrl::fromLocalFile( symbol.shapeProperties()[QStringLiteral( "model" )].toString() );
Qt3DRender::QSceneLoader *modelLoader = new Qt3DRender::QSceneLoader;
modelLoader->setSource( url );
QUrl url = QUrl::fromLocalFile( source );
Qt3DRender::QSceneLoader *modelLoader = new Qt3DRender::QSceneLoader;
modelLoader->setSource( url );

entity->addComponent( modelLoader );
entity->addComponent( transform( position, symbol ) );
entity->setParent( parent );
entity->addComponent( modelLoader );
entity->addComponent( transform( position, symbol ) );
entity->setParent( parent );

// cppcheck wrongly believes entity will leak
// cppcheck-suppress memleak
}
}
}

@@ -469,20 +476,24 @@ void QgsModelPoint3DSymbolHandler::addMeshEntities( const Qgs3DMapSettings &map,
// get nodes
for ( const QVector3D &position : positions )
{
// build the entity
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity;
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( symbol.shapeProperties()[QStringLiteral( "model" )].toString() );
Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh;
mesh->setSource( url );
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 );
entity->addComponent( mesh );
entity->addComponent( mat );
entity->addComponent( transform( position, symbol ) );
entity->setParent( parent );

// cppcheck wrongly believes entity will leak
// cppcheck-suppress memleak
}
}
}

@@ -0,0 +1,59 @@
/***************************************************************************
qgs3dmodelsourcelineedit.cpp
-----------------------
begin : July 2020
copyright : (C) 2020 by Mathieu Pellerin
email : nirvn dot asia 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 "qgs3dmodelsourcelineedit.h"

//
// Qgs3DModelSourceLineEdit
//

///@cond PRIVATE

QString Qgs3DModelSourceLineEdit::fileFilter() const
{
return tr( "All files" ) + " (*.*)";
}

QString Qgs3DModelSourceLineEdit::selectFileTitle() const
{
return tr( "Select 3D Model File" );
}

QString Qgs3DModelSourceLineEdit::fileFromUrlTitle() const
{
return tr( "3D Model From URL" );
}

QString Qgs3DModelSourceLineEdit::fileFromUrlText() const
{
return tr( "Enter 3D Model URL" );
}

QString Qgs3DModelSourceLineEdit::embedFileTitle() const
{
return tr( "Embed 3D Model File" );
}

QString Qgs3DModelSourceLineEdit::extractFileTitle() const
{
return tr( "Extract 3D Model File" );
}

QString Qgs3DModelSourceLineEdit::defaultSettingsKey() const
{
return QStringLiteral( "/UI/last3DModelDir" );
}

///@endcond
@@ -0,0 +1,57 @@
/***************************************************************************
qgs3dmodelsourcelineedit.h
---------------------
begin : July 2020
copyright : (C) 2020 by Mathieu Pellerin
email : nirvn dot asia 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 QGS3DMODELSOURCELINEEDIT_H
#define QGS3DMODELSOURCELINEEDIT_H

#include "qgsfilecontentsourcelineedit.h"

#include <QString>

/**
* \class Qgs3DModelSourceLineEdit
* A line edit widget with toolbutton for setting a 3D model source path.
*
* Designed for use with QgsSourceCache.
*
* \since QGIS 3.16
*/
class Qgs3DModelSourceLineEdit : public QgsAbstractFileContentSourceLineEdit
{
Q_OBJECT
public:

/**
* Constructor for Qgs3DModelSourceLineEdit, with the specified \a parent widget.
*/
Qgs3DModelSourceLineEdit( QWidget *parent SIP_TRANSFERTHIS = nullptr )
: QgsAbstractFileContentSourceLineEdit( parent )
{}

private:
#ifndef SIP_RUN
///@cond PRIVATE
QString fileFilter() const override;
QString selectFileTitle() const override;
QString fileFromUrlTitle() const override;
QString fileFromUrlText() const override;
QString embedFileTitle() const override;
QString extractFileTitle() const override;
QString defaultSettingsKey() const override;
///@endcond
#endif
};

#endif // QGS3DMODELSOURCELINEEDIT_H
@@ -63,8 +63,7 @@ QgsPoint3DSymbolWidget::QgsPoint3DSymbolWidget( QWidget *parent )
const auto constSpinWidgets = spinWidgets;
for ( QDoubleSpinBox *spinBox : constSpinWidgets )
connect( spinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPoint3DSymbolWidget::changed );
connect( lineEditModel, static_cast<void ( QLineEdit::* )( const QString & )>( &QLineEdit::textChanged ), this, &QgsPoint3DSymbolWidget::changed );
connect( btnModel, static_cast<void ( QToolButton::* )( bool )>( &QToolButton::clicked ), this, &QgsPoint3DSymbolWidget::onChooseModelClicked );
connect( lineEditModel, &QgsAbstractFileContentSourceLineEdit::sourceChanged, this, &QgsPoint3DSymbolWidget::changed );
connect( cbOverwriteMaterial, static_cast<void ( QCheckBox::* )( int )>( &QCheckBox::stateChanged ), this, &QgsPoint3DSymbolWidget::onOverwriteMaterialChecked );
connect( widgetMaterial, &QgsPhongMaterialWidget::changed, this, &QgsPoint3DSymbolWidget::changed );
connect( btnChangeSymbol, static_cast<void ( QgsSymbolButton::* )( )>( &QgsSymbolButton::changed ), this, &QgsPoint3DSymbolWidget::changed );
@@ -74,29 +73,6 @@ QgsPoint3DSymbolWidget::QgsPoint3DSymbolWidget( QWidget *parent )
connect( spinTY, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), spinBillboardHeight, &QDoubleSpinBox::setValue );
}

void QgsPoint3DSymbolWidget::onChooseModelClicked( bool )
{
QgsSettings s;
QString lastDir = s.value( QStringLiteral( "/UI/lastModel3dDir" ), QDir::homePath() ).toString();

QString filePath = QFileDialog::getOpenFileName( this, tr( "Open 3d Model File" ), lastDir, QStringLiteral( "3D models (*.*)" ) );
if ( filePath.isEmpty() )
{
return;
}

//check if file exists
QFileInfo fileInfo( filePath );
if ( !fileInfo.exists() || !fileInfo.isReadable() )
{
QMessageBox::critical( nullptr, tr( "Invalid File" ), tr( "Error, file does not exist or is not readable." ) );
return;
}

s.setValue( QStringLiteral( "/UI/lastModel3dDir" ), fileInfo.absolutePath() );
lineEditModel->setText( filePath );
}

void QgsPoint3DSymbolWidget::onOverwriteMaterialChecked( int state )
{
if ( state == Qt::Checked )
@@ -144,7 +120,7 @@ void QgsPoint3DSymbolWidget::setSymbol( const QgsPoint3DSymbol &symbol )
break;
case 6: // 3d model
{
lineEditModel->setText( vm[QStringLiteral( "model" )].toString() );
lineEditModel->setSource( vm[QStringLiteral( "model" )].toString() );
bool overwriteMaterial = vm[QStringLiteral( "overwriteMaterial" )].toBool();
widgetMaterial->setEnabled( overwriteMaterial );
cbOverwriteMaterial->setChecked( overwriteMaterial );
@@ -219,7 +195,7 @@ QgsPoint3DSymbol QgsPoint3DSymbolWidget::symbol() const
vm[QStringLiteral( "minorRadius" )] = spinMinorRadius->value();
break;
case 6: // 3d model
vm[QStringLiteral( "model" )] = lineEditModel->text();
vm[QStringLiteral( "model" )] = lineEditModel->source();
vm[QStringLiteral( "overwriteMaterial" )] = cbOverwriteMaterial->isChecked();
break;
case 7: // billboard
@@ -253,7 +229,7 @@ void QgsPoint3DSymbolWidget::onShapeChanged()
<< labelTopRadius << spinTopRadius
<< labelBottomRadius << spinBottomRadius
<< labelLength << spinLength
<< labelModel << lineEditModel << btnModel << cbOverwriteMaterial
<< labelModel << lineEditModel << cbOverwriteMaterial
<< labelBillboardHeight << spinBillboardHeight << labelBillboardSymbol << btnChangeSymbol;

widgetMaterial->setEnabled( true );
@@ -281,7 +257,7 @@ void QgsPoint3DSymbolWidget::onShapeChanged()
activeWidgets << labelRadius << spinRadius << labelMinorRadius << spinMinorRadius;
break;
case 6: // 3d model
activeWidgets << labelModel << lineEditModel << btnModel << cbOverwriteMaterial;
activeWidgets << labelModel << lineEditModel << cbOverwriteMaterial;
widgetMaterial->setEnabled( cbOverwriteMaterial->isChecked() );
break;
case 7: // billboard

0 comments on commit 3b249d1

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