18 #include <Qt3DRender/QCamera>
19 #include <Qt3DRender/QMesh>
20 #include <Qt3DRender/QObjectPicker>
21 #include <Qt3DRender/QPickEvent>
22 #include <Qt3DRender/QPickingSettings>
23 #include <Qt3DRender/QPickTriangleEvent>
24 #include <Qt3DRender/QPointLight>
25 #include <Qt3DRender/QDirectionalLight>
26 #include <Qt3DRender/QRenderSettings>
27 #include <Qt3DRender/QSceneLoader>
28 #include <Qt3DExtras/QForwardRenderer>
29 #include <Qt3DExtras/QPhongMaterial>
30 #include <Qt3DExtras/QSphereMesh>
31 #include <Qt3DLogic/QFrameAction>
32 #include <Qt3DRender/QEffect>
33 #include <Qt3DRender/QTechnique>
34 #include <Qt3DRender/QRenderPass>
35 #include <Qt3DRender/QRenderState>
36 #include <Qt3DRender/QCullFace>
37 #include <Qt3DRender/QDepthTest>
40 #include <QOpenGLContext>
41 #include <QOpenGLFunctions>
85 onBackgroundColorChanged();
91 #if QT_VERSION >= 0x050900
93 mEngine->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
96 QRect viewportRect( QPoint( 0, 0 ), mEngine->
size() );
99 float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
100 mEngine->
camera()->lens()->setPerspectiveProjection( mMap.
fieldOfView(), aspectRatio, 10.f, 10000.0f );
102 mFrameAction =
new Qt3DLogic::QFrameAction();
103 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
104 this, &Qgs3DMapScene::onFrameTriggered );
105 addComponent( mFrameAction );
113 addCameraViewCenterEntity( mEngine->
camera() );
118 createTerrainDeferred();
135 const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
141 if ( renderer->
type() == QLatin1String(
"vector" ) )
144 if ( pointSymbol->
shapeProperties()[QStringLiteral(
"model" )].toString() == url )
146 removeLayerEntity( layer );
147 addLayerEntity( layer );
150 else if ( renderer->
type() == QLatin1String(
"rulebased" ) )
153 for (
auto rule : rules )
156 if ( pointSymbol->
shapeProperties()[QStringLiteral(
"model" )].toString() == url )
158 removeLayerEntity( layer );
159 addLayerEntity( layer );
170 onRenderersChanged();
177 ChunkedEntity *testChunkEntity =
new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7,
new TestChunkLoaderFactory );
178 testChunkEntity->setEnabled(
false );
179 testChunkEntity->setParent(
this );
180 chunkEntities << testChunkEntity;
191 Qt3DCore::QEntity *loaderEntity =
new Qt3DCore::QEntity;
192 Qt3DRender::QSceneLoader *loader =
new Qt3DRender::QSceneLoader;
193 loader->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
194 loaderEntity->addComponent( loader );
195 loaderEntity->setParent(
this );
199 Qt3DCore::QEntity *meshEntity =
new Qt3DCore::QEntity;
200 Qt3DRender::QMesh *mesh =
new Qt3DRender::QMesh;
201 mesh->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
202 meshEntity->addComponent( mesh );
203 Qt3DExtras::QPhongMaterial *material =
new Qt3DExtras::QPhongMaterial;
204 material->setAmbient( Qt::red );
205 meshEntity->addComponent( material );
206 Qt3DCore::QTransform *meshTransform =
new Qt3DCore::QTransform;
207 meshTransform->setScale( 1 );
208 meshEntity->addComponent( meshTransform );
209 meshEntity->setParent(
this );
211 onSkyboxSettingsChanged();
220 float side = std::max( extent.
width(), extent.
height() );
226 return mTerrain ? mTerrain->pendingJobsCount() : 0;
232 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
233 count += entity->pendingJobsCount();
239 if ( mPickHandlers.isEmpty() )
242 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
244 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
245 chunkedEntity->setPickingEnabled(
true );
249 mPickHandlers.append( pickHandler );
254 mPickHandlers.removeOne( pickHandler );
256 if ( mPickHandlers.isEmpty() )
259 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
261 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
262 chunkedEntity->setPickingEnabled(
false );
267 void Qgs3DMapScene::onLayerEntityPickedObject( Qt3DRender::QPickEvent *pickEvent,
QgsFeatureId fid )
269 QgsMapLayer *layer = mLayerEntities.key( qobject_cast<QgsChunkedEntity *>( sender() ) );
279 pickHandler->handlePickOnVectorLayer( vlayer, fid, pickEvent->worldIntersection(), pickEvent );
285 Qt3DRender::QCamera *camera = mCameraController->
camera();
286 float fov = camera->fieldOfView();
287 QRect rect = mCameraController->
viewport();
288 float screenSizePx = std::max( rect.width(), rect.height() );
292 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
293 float err = frustumWidthAtDistance * epsilon / screenSizePx;
299 Qt3DRender::QCamera *camera = cameraController->
camera();
300 QgsChunkedEntity::SceneState state;
301 state.cameraFov = camera->fieldOfView();
302 state.cameraPos = camera->position();
303 QRect rect = cameraController->
viewport();
304 state.screenSizePx = std::max( rect.width(), rect.height() );
305 state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
309 void Qgs3DMapScene::onCameraChanged()
312 bool changedCameraPlanes = updateCameraNearFarPlanes();
314 if ( changedCameraPlanes )
319 updateCameraNearFarPlanes();
322 onShadowSettingsChanged();
327 QVector<Qt3DCore::QComponent *> toBeRemovedComponents;
328 for ( Qt3DCore::QComponent *component : entity->components() )
330 Qt3DRender::QLayer *layer = qobject_cast<Qt3DRender::QLayer *>( component );
331 if ( layer !=
nullptr )
332 toBeRemovedComponents.push_back( layer );
334 for ( Qt3DCore::QComponent *component : toBeRemovedComponents )
335 entity->removeComponent( component );
336 for ( Qt3DCore::QEntity *obj : entity->findChildren<Qt3DCore::QEntity *>() )
338 if ( obj !=
nullptr )
345 for ( Qt3DRender::QLayer *layer : layers )
346 entity->addComponent( layer );
347 for ( Qt3DCore::QEntity *child : entity->findChildren<Qt3DCore::QEntity *>() )
349 if ( child !=
nullptr )
354 void Qgs3DMapScene::updateScene()
356 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral(
"3D" ), QStringLiteral(
"Update Scene" ) );
357 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
359 if ( entity->isEnabled() )
360 entity->update(
_sceneState( mCameraController ) );
362 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
364 if ( windowEngine !=
nullptr )
366 QVector<Qt3DRender::QLayer *> layers;
376 bool Qgs3DMapScene::updateCameraNearFarPlanes()
393 QMatrix4x4 viewMatrix = camera->viewMatrix();
397 QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
402 if ( activeNodes.isEmpty() )
403 activeNodes << mTerrain->rootNode();
405 Q_FOREACH ( QgsChunkNode *node, activeNodes )
410 for (
int i = 0; i < 8; ++i )
412 QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.
xMin : bbox.
xMax,
413 ( ( i >> 1 ) & 1 ) ? bbox.
yMin : bbox.
yMax,
414 ( ( i >> 2 ) & 1 ) ? bbox.
zMin : bbox.
zMax, 1 );
415 QVector4D pc = viewMatrix * p;
427 if ( fnear == 1e9 && ffar == 0 )
431 qDebug() <<
"oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
437 float newFar = ffar * 2;
438 float newNear = fnear / 2;
441 camera->setFarPlane( newFar );
442 camera->setNearPlane( newNear );
447 qDebug() <<
"no terrain - not setting near/far plane";
452 void Qgs3DMapScene::onFrameTriggered(
float dt )
456 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
458 if ( entity->isEnabled() && entity->needsUpdate() )
461 entity->update(
_sceneState( mCameraController ) );
468 void Qgs3DMapScene::createTerrain()
472 mChunkEntities.removeOne( mTerrain );
474 mTerrain->deleteLater();
480 if ( !mTerrainUpdateScheduled )
483 QTimer::singleShot( 0,
this, &Qgs3DMapScene::createTerrainDeferred );
484 mTerrainUpdateScheduled =
true;
489 void Qgs3DMapScene::createTerrainDeferred()
494 mTerrain =
new QgsTerrainEntity( maxZoomLevel, mMap );
496 mTerrain->setParent(
this );
499 mTerrain->setShowBoundingBoxes(
true );
503 mChunkEntities << mTerrain;
511 removeLayerEntity( layer );
514 addLayerEntity( layer );
517 mTerrainUpdateScheduled =
false;
525 void Qgs3DMapScene::onBackgroundColorChanged()
530 void Qgs3DMapScene::updateLights()
532 for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightEntities ) )
533 entity->deleteLater();
534 mLightEntities.clear();
535 for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightOriginEntities ) )
536 entity->deleteLater();
537 mLightOriginEntities.clear();
539 auto createLightOriginEntity = [ = ]( QVector3D translation,
const QColor & color )->Qt3DCore::QEntity *
541 Qt3DCore::QEntity *originEntity =
new Qt3DCore::QEntity;
543 Qt3DCore::QTransform *trLightOriginCenter =
new Qt3DCore::QTransform;
544 trLightOriginCenter->setTranslation( translation );
545 originEntity->addComponent( trLightOriginCenter );
547 Qt3DExtras::QPhongMaterial *materialLightOriginCenter =
new Qt3DExtras::QPhongMaterial;
548 materialLightOriginCenter->setAmbient( color );
549 originEntity->addComponent( materialLightOriginCenter );
551 Qt3DExtras::QSphereMesh *rendererLightOriginCenter =
new Qt3DExtras::QSphereMesh;
552 rendererLightOriginCenter->setRadius( 20 );
553 originEntity->addComponent( rendererLightOriginCenter );
555 originEntity->setEnabled(
true );
556 originEntity->setParent(
this );
564 Qt3DCore::QEntity *lightEntity =
new Qt3DCore::QEntity;
565 Qt3DCore::QTransform *lightTransform =
new Qt3DCore::QTransform;
566 lightTransform->setTranslation( QVector3D( pointLightSettings.position().x(),
567 pointLightSettings.position().y(),
568 pointLightSettings.position().z() ) );
570 Qt3DRender::QPointLight *light =
new Qt3DRender::QPointLight;
571 light->setColor( pointLightSettings.color() );
572 light->setIntensity( pointLightSettings.intensity() );
574 light->setConstantAttenuation( pointLightSettings.constantAttenuation() );
575 light->setLinearAttenuation( pointLightSettings.linearAttenuation() );
576 light->setQuadraticAttenuation( pointLightSettings.quadraticAttenuation() );
578 lightEntity->addComponent( light );
579 lightEntity->addComponent( lightTransform );
580 lightEntity->setParent(
this );
581 mLightEntities << lightEntity;
584 mLightOriginEntities << createLightOriginEntity( lightTransform->translation(), pointLightSettings.color() );
590 Qt3DCore::QEntity *lightEntity =
new Qt3DCore::QEntity;
591 Qt3DCore::QTransform *lightTransform =
new Qt3DCore::QTransform;
593 Qt3DRender::QDirectionalLight *light =
new Qt3DRender::QDirectionalLight;
594 light->setColor( directionalLightSettings.color() );
595 light->setIntensity( directionalLightSettings.intensity() );
596 QgsVector3D direction = directionalLightSettings.direction();
597 light->setWorldDirection( QVector3D( direction.
x(), direction.
y(), direction.
z() ) );
599 lightEntity->addComponent( light );
600 lightEntity->addComponent( lightTransform );
601 lightEntity->setParent(
this );
602 mLightEntities << lightEntity;
605 onShadowSettingsChanged();
608 void Qgs3DMapScene::updateCameraLens()
614 void Qgs3DMapScene::onRenderersChanged()
617 qDeleteAll( mRenderersEntities );
618 mRenderersEntities.clear();
621 const QList<QgsAbstract3DRenderer *> renderers = mMap.
renderers();
624 Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
627 newEntity->setParent(
this );
628 finalizeNewEntity( newEntity );
629 mRenderersEntities[renderer] = newEntity;
634 void Qgs3DMapScene::onLayerRenderer3DChanged()
636 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
640 removeLayerEntity( layer );
643 addLayerEntity( layer );
646 void Qgs3DMapScene::onLayersChanged()
648 QSet<QgsMapLayer *> layersBefore = qgis::listToSet( mLayerEntities.keys() );
649 QList<QgsMapLayer *> layersAdded;
652 if ( !layersBefore.contains( layer ) )
654 layersAdded << layer;
658 layersBefore.remove( layer );
665 removeLayerEntity( layer );
670 addLayerEntity( layer );
676 for (
auto layer : mLayerEntities.keys() )
680 removeLayerEntity( layer );
681 addLayerEntity( layer );
686 void Qgs3DMapScene::addLayerEntity(
QgsMapLayer *layer )
688 bool needsSceneUpdate =
false;
697 ( renderer->
type() == QLatin1String(
"vector" ) || renderer->
type() == QLatin1String(
"rulebased" ) ) )
700 if ( renderer->
type() == QLatin1String(
"vector" ) )
708 mModelVectorLayers.append( layer );
712 else if ( renderer->
type() == QLatin1String(
"rulebased" ) )
715 for (
auto rule : rules )
720 mModelVectorLayers.append( layer );
738 Qt3DCore::QEntity *newEntity = renderer->
createEntity( mMap );
741 newEntity->setParent(
this );
742 mLayerEntities.insert( layer, newEntity );
744 finalizeNewEntity( newEntity );
746 if ( QgsChunkedEntity *chunkedNewEntity = qobject_cast<QgsChunkedEntity *>( newEntity ) )
748 mChunkEntities.append( chunkedNewEntity );
749 needsSceneUpdate =
true;
751 chunkedNewEntity->setPickingEnabled( !mPickHandlers.isEmpty() );
752 connect( chunkedNewEntity, &QgsChunkedEntity::pickedObject,
this, &Qgs3DMapScene::onLayerEntityPickedObject );
754 connect( chunkedNewEntity, &QgsChunkedEntity::newEntityCreated,
this, [
this]( Qt3DCore::QEntity * entity )
756 finalizeNewEntity( entity );
764 if ( needsSceneUpdate )
782 void Qgs3DMapScene::removeLayerEntity(
QgsMapLayer *layer )
784 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
786 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
788 mChunkEntities.removeOne( chunkedEntity );
792 entity->deleteLater();
800 mModelVectorLayers.removeAll( layer );
809 void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
813 for ( QgsLineMaterial *lm : newEntity->findChildren<QgsLineMaterial *>() )
817 lm->setViewportSize( mCameraController->
viewport().size() );
827 bm->setViewportSize( mCameraController->
viewport().size() );
830 bm->setViewportSize( mCameraController->
viewport().size() );
834 int Qgs3DMapScene::maximumTextureSize()
const
836 QSurface *surface = mEngine->
surface();
837 QOpenGLContext context;
839 context.makeCurrent( surface );
840 QOpenGLFunctions openglFunctions( &context );
842 openglFunctions.glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
846 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
848 mEntityCameraViewCenter =
new Qt3DCore::QEntity;
850 Qt3DCore::QTransform *trCameraViewCenter =
new Qt3DCore::QTransform;
851 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
852 connect( camera, &Qt3DRender::QCamera::viewCenterChanged,
this, [trCameraViewCenter, camera]
854 trCameraViewCenter->setTranslation( camera->viewCenter() );
857 Qt3DExtras::QPhongMaterial *materialCameraViewCenter =
new Qt3DExtras::QPhongMaterial;
858 materialCameraViewCenter->setAmbient( Qt::red );
859 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
861 Qt3DExtras::QSphereMesh *rendererCameraViewCenter =
new Qt3DExtras::QSphereMesh;
862 rendererCameraViewCenter->setRadius( 10 );
863 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
866 mEntityCameraViewCenter->setParent(
this );
876 if ( mSceneState == state )
882 void Qgs3DMapScene::updateSceneState()
884 if ( mTerrainUpdateScheduled )
890 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
892 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
899 setSceneState(
Ready );
902 void Qgs3DMapScene::onSkyboxSettingsChanged()
905 if ( mSkybox !=
nullptr )
907 mSkybox->deleteLater();
915 QMap<QString, QString> faces;
921 faces[QStringLiteral(
"posX" )], faces[QStringLiteral(
"posY" )], faces[QStringLiteral(
"posZ" )],
922 faces[QStringLiteral(
"negX" )], faces[QStringLiteral(
"negY" )], faces[QStringLiteral(
"negZ" )],
933 void Qgs3DMapScene::onShadowSettingsChanged()
936 if ( windowEngine ==
nullptr )
943 if ( shadowSettings.
renderShadows() && selectedLight >= 0 && selectedLight < directionalLights.count() )
957 QVector<QString> notParsedLayers;
967 for (
auto it = mLayerEntities.constBegin(); it != mLayerEntities.constEnd(); ++it )
970 Qt3DCore::QEntity *rootEntity = it.value();
976 notParsedLayers.push_back( layer->
name() );
983 notParsedLayers.push_back( layer->
name() );
993 if ( !notParsedLayers.empty() )
995 QString message = tr(
"The following layers were not exported:" ) +
"\n";
996 for (
const QString &layerName : notParsedLayers )
997 message += layerName +
"\n";