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/QRenderSettings> 26 #include <Qt3DRender/QSceneLoader> 27 #include <Qt3DExtras/QForwardRenderer> 28 #include <Qt3DExtras/QPhongMaterial> 29 #include <Qt3DExtras/QSkyboxEntity> 30 #include <Qt3DExtras/QSphereMesh> 31 #include <Qt3DLogic/QFrameAction> 62 onBackgroundColorChanged();
68 #if QT_VERSION >= 0x050900 70 mEngine->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
73 QRect viewportRect( QPoint( 0, 0 ), mEngine->
size() );
76 float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
77 mEngine->
camera()->lens()->setPerspectiveProjection( mMap.
fieldOfView(), aspectRatio, 10.f, 10000.0f );
79 mFrameAction =
new Qt3DLogic::QFrameAction();
80 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
81 this, &Qgs3DMapScene::onFrameTriggered );
82 addComponent( mFrameAction );
90 addCameraViewCenterEntity( mEngine->
camera() );
94 createTerrainDeferred();
107 onRenderersChanged();
115 ChunkedEntity *testChunkEntity =
new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7,
new TestChunkLoaderFactory );
116 testChunkEntity->setEnabled(
false );
117 testChunkEntity->setParent(
this );
118 chunkEntities << testChunkEntity;
129 Qt3DCore::QEntity *loaderEntity =
new Qt3DCore::QEntity;
130 Qt3DRender::QSceneLoader *loader =
new Qt3DRender::QSceneLoader;
131 loader->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
132 loaderEntity->addComponent( loader );
133 loaderEntity->setParent(
this );
137 Qt3DCore::QEntity *meshEntity =
new Qt3DCore::QEntity;
138 Qt3DRender::QMesh *mesh =
new Qt3DRender::QMesh;
139 mesh->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
140 meshEntity->addComponent( mesh );
141 Qt3DExtras::QPhongMaterial *material =
new Qt3DExtras::QPhongMaterial;
142 material->setAmbient( Qt::red );
143 meshEntity->addComponent( material );
144 Qt3DCore::QTransform *meshTransform =
new Qt3DCore::QTransform;
145 meshTransform->setScale( 1 );
146 meshEntity->addComponent( meshTransform );
147 meshEntity->setParent(
this );
152 Qt3DExtras::QSkyboxEntity *skybox =
new Qt3DExtras::QSkyboxEntity;
155 skybox->setParent(
this );
170 float side = std::max( extent.
width(), extent.
height() );
176 return mTerrain ? mTerrain->pendingJobsCount() : 0;
181 if ( mPickHandlers.isEmpty() )
184 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
186 Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker( entity );
187 entity->addComponent( picker );
188 connect( picker, &Qt3DRender::QObjectPicker::clicked,
this, &Qgs3DMapScene::onLayerEntityPickEvent );
192 mPickHandlers.append( pickHandler );
197 mPickHandlers.removeOne( pickHandler );
199 if ( mPickHandlers.isEmpty() )
202 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
204 Qt3DRender::QObjectPicker *picker = entity->findChild<Qt3DRender::QObjectPicker *>();
205 picker->deleteLater();
212 Qt3DRender::QCamera *camera = mCameraController->
camera();
213 float fov = camera->fieldOfView();
214 QRect rect = mCameraController->
viewport();
215 float screenSizePx = std::max( rect.width(), rect.height() );
219 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
220 float err = frustumWidthAtDistance * epsilon / screenSizePx;
226 Qt3DRender::QCamera *camera = cameraController->
camera();
227 QgsChunkedEntity::SceneState state;
228 state.cameraFov = camera->fieldOfView();
229 state.cameraPos = camera->position();
230 QRect rect = cameraController->
viewport();
231 state.screenSizePx = std::max( rect.width(), rect.height() );
232 state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
236 void Qgs3DMapScene::onCameraChanged()
239 bool changedCameraPlanes = updateCameraNearFarPlanes();
241 if ( changedCameraPlanes )
246 updateCameraNearFarPlanes();
250 void Qgs3DMapScene::updateScene()
252 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
254 if ( entity->isEnabled() )
255 entity->update(
_sceneState( mCameraController ) );
261 bool Qgs3DMapScene::updateCameraNearFarPlanes()
278 QMatrix4x4 viewMatrix = camera->viewMatrix();
282 QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
287 if ( activeNodes.isEmpty() )
288 activeNodes << mTerrain->rootNode();
290 Q_FOREACH ( QgsChunkNode *node, activeNodes )
295 for (
int i = 0; i < 8; ++i )
297 QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.
xMin : bbox.
xMax,
298 ( ( i >> 1 ) & 1 ) ? bbox.
yMin : bbox.
yMax,
299 ( ( i >> 2 ) & 1 ) ? bbox.
zMin : bbox.
zMax, 1 );
300 QVector4D pc = viewMatrix * p;
312 if ( fnear == 1e9 && ffar == 0 )
316 qDebug() <<
"oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
322 float newFar = ffar * 2;
323 float newNear = fnear / 2;
326 camera->setFarPlane( newFar );
327 camera->setNearPlane( newNear );
332 qDebug() <<
"no terrain - not setting near/far plane";
337 void Qgs3DMapScene::onFrameTriggered(
float dt )
341 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
343 if ( entity->isEnabled() && entity->needsUpdate() )
345 qDebug() <<
"need for update";
346 entity->update(
_sceneState( mCameraController ) );
353 void Qgs3DMapScene::createTerrain()
357 mChunkEntities.removeOne( mTerrain );
359 mTerrain->deleteLater();
365 if ( !mTerrainUpdateScheduled )
368 QTimer::singleShot( 0,
this, &Qgs3DMapScene::createTerrainDeferred );
369 mTerrainUpdateScheduled =
true;
374 void Qgs3DMapScene::createTerrainDeferred()
379 mTerrain =
new QgsTerrainEntity( maxZoomLevel, mMap );
381 mTerrain->setParent(
this );
384 mTerrain->setShowBoundingBoxes(
true );
388 mChunkEntities << mTerrain;
396 removeLayerEntity( layer );
399 addLayerEntity( layer );
402 mTerrainUpdateScheduled =
false;
409 void Qgs3DMapScene::onBackgroundColorChanged()
414 void Qgs3DMapScene::onLayerEntityPickEvent( Qt3DRender::QPickEvent *event )
416 Qt3DRender::QPickTriangleEvent *triangleEvent = qobject_cast<Qt3DRender::QPickTriangleEvent *>( event );
417 if ( !triangleEvent )
420 Qt3DRender::QObjectPicker *picker = qobject_cast<Qt3DRender::QObjectPicker *>( sender() );
424 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( picker->parent() );
440 for ( Qt3DRender::QGeometryRenderer *geomRenderer : entity->findChildren<Qt3DRender::QGeometryRenderer *>() )
445 if ( geomRenderer->objectName() != QLatin1String(
"main" ) )
450 fid = g->triangleIndexToFeatureId( triangleEvent->triangleIndex() );
454 pickHandler->handlePickOnVectorLayer( vlayer, fid, event->worldIntersection(), event );
459 void Qgs3DMapScene::updateLights()
461 for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightEntities ) )
462 entity->deleteLater();
463 mLightEntities.clear();
468 Qt3DCore::QEntity *lightEntity =
new Qt3DCore::QEntity;
469 Qt3DCore::QTransform *lightTransform =
new Qt3DCore::QTransform;
470 lightTransform->setTranslation( QVector3D( pointLightSettings.position().x(),
471 pointLightSettings.position().y(),
472 pointLightSettings.position().z() ) );
474 Qt3DRender::QPointLight *light =
new Qt3DRender::QPointLight;
475 light->setColor( pointLightSettings.color() );
476 light->setIntensity( pointLightSettings.intensity() );
478 light->setConstantAttenuation( pointLightSettings.constantAttenuation() );
479 light->setLinearAttenuation( pointLightSettings.linearAttenuation() );
480 light->setQuadraticAttenuation( pointLightSettings.quadraticAttenuation() );
482 lightEntity->addComponent( light );
483 lightEntity->addComponent( lightTransform );
484 lightEntity->setParent(
this );
485 mLightEntities << lightEntity;
489 void Qgs3DMapScene::updateCameraLens()
495 void Qgs3DMapScene::onRenderersChanged()
498 qDeleteAll( mRenderersEntities.values() );
499 mRenderersEntities.clear();
502 const QList<QgsAbstract3DRenderer *> renderers = mMap.
renderers();
505 Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
508 newEntity->setParent(
this );
509 finalizeNewEntity( newEntity );
510 mRenderersEntities[renderer] = newEntity;
515 void Qgs3DMapScene::onLayerRenderer3DChanged()
521 removeLayerEntity( layer );
524 addLayerEntity( layer );
527 void Qgs3DMapScene::onLayersChanged()
529 QSet<QgsMapLayer *> layersBefore = QSet<QgsMapLayer *>::fromList( mLayerEntities.keys() );
530 QList<QgsMapLayer *> layersAdded;
533 if ( !layersBefore.contains( layer ) )
535 layersAdded << layer;
539 layersBefore.remove( layer );
546 removeLayerEntity( layer );
551 addLayerEntity( layer );
555 void Qgs3DMapScene::addLayerEntity(
QgsMapLayer *layer )
578 Qt3DCore::QEntity *newEntity = renderer->
createEntity( mMap );
581 newEntity->setParent(
this );
582 mLayerEntities.insert( layer, newEntity );
584 if ( !mPickHandlers.isEmpty() )
586 Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker( newEntity );
587 newEntity->addComponent( picker );
588 connect( picker, &Qt3DRender::QObjectPicker::pressed,
this, &Qgs3DMapScene::onLayerEntityPickEvent );
591 finalizeNewEntity( newEntity );
604 void Qgs3DMapScene::removeLayerEntity(
QgsMapLayer *layer )
606 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
608 entity->deleteLater();
619 void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
623 for ( QgsLineMaterial *lm : newEntity->findChildren<QgsLineMaterial *>() )
627 lm->setViewportSize( mCameraController->
viewport().size() );
637 bm->setViewportSize( mCameraController->
viewport().size() );
640 bm->setViewportSize( mCameraController->
viewport().size() );
644 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
646 mEntityCameraViewCenter =
new Qt3DCore::QEntity;
648 Qt3DCore::QTransform *trCameraViewCenter =
new Qt3DCore::QTransform;
649 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
650 connect( camera, &Qt3DRender::QCamera::viewCenterChanged,
this, [trCameraViewCenter, camera]
652 trCameraViewCenter->setTranslation( camera->viewCenter() );
655 Qt3DExtras::QPhongMaterial *materialCameraViewCenter =
new Qt3DExtras::QPhongMaterial;
656 materialCameraViewCenter->setAmbient( Qt::red );
657 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
659 Qt3DExtras::QSphereMesh *rendererCameraViewCenter =
new Qt3DExtras::QSphereMesh;
660 rendererCameraViewCenter->setRadius( 10 );
661 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
664 mEntityCameraViewCenter->setParent(
this );
674 if ( mSceneState == state )
680 void Qgs3DMapScene::updateSceneState()
682 if ( mTerrainUpdateScheduled )
688 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
690 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
697 setSceneState(
Ready );
3 Abstract base class for handlers that process pick events from a 3D map scene.
3 Material of the billboard rendering for points in 3D map view.
QList< QgsMapLayer * > layers() const
Returns the list of map layers to be rendered as a texture of the terrain.
void cameraChanged()
Emitted when camera has been updated.
3 Axis-aligned bounding box - in world coords.
void renderersChanged()
Emitted when the list of map's extra renderers have been modified.
A rectangle specified with double values.
Base class for all map layer types.
void maxTerrainGroundErrorChanged()
Emitted when the maximum terrain ground error has changed.
bool showTerrainBoundingBoxes() const
Returns whether to display bounding boxes of terrain tiles (for debugging)
void frameTriggered(float dt)
Called internally from 3D scene when a new frame is generated. Updates camera according to keyboard/m...
float worldSpaceError(float epsilon, float distance)
Given screen error (in pixels) and distance from camera (in 3D world coordinates), this function estimates the error in world space.
Base class for all renderers that may to participate in 3D view.
QgsMapLayerType type() const
Returns the type of the layer.
void layersChanged()
Emitted when the list of map layers for terrain texture has changed.
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
bool showCameraViewCenter() const
Returns whether to show camera's view center as a sphere (for debugging)
QString skyboxFileBase() const
Returns base part of filenames of skybox (see setSkybox())
virtual Qt3DCore::QEntity * createEntity(const Qgs3DMapSettings &map) const =0
Returns a 3D entity that will be used to show renderer's data in 3D scene.
3D renderer that renders all mesh triangles of a mesh layer.
void setViewport(QRect viewport)
Sets viewport rectangle. Called internally from 3D canvas. Allows conversion of mouse coordinates...
SceneState
Enumeration of possible states of the 3D scene.
void terrainPendingJobsCountChanged()
Emitted when the number of terrain's pending jobs changes.
void resetView(float distance)
Move camera back to the initial position (looking down towards origin of world's coordinates) ...
QList< QgsPointLightSettings > pointLights() const
Returns list of point lights defined in the scene.
void terrainVerticalScaleChanged()
Emitted when the vertical scale of the terrain has changed.
3 Definition of the world
void registerPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Registers an object that will get results of pick events on 3D entities. Does not take ownership of t...
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here) ...
3 Definition of a point light in a 3D map scene
void pointLightsChanged()
Emitted when the list of point lights changes.
float maxTerrainGroundError() const
Returns maximum ground error of terrain tiles in world units.
3 Rule-based 3D renderer.
void viewportChanged()
Emitted when viewport rectangle has been updated.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
bool hasSkyboxEnabled() const
Returns whether skybox is enabled.
double width() const
Returns the width of the rectangle.
void fieldOfViewChanged()
Emitted when the camera lens field of view changes.
3 Class derived from Qt3DRender::QGeometry that represents polygons tessellated into 3D geometry...
void showCameraViewCenterChanged()
Emitted when the flag whether camera's view center is shown has changed.
int mapTileResolution() const
Returns resolution (in pixels) of the texture of a terrain tile.
void terrainGeneratorChanged()
Emitted when the terrain generator has changed.
3 Base class for 3D engine implementation.
QColor backgroundColor() const
Returns background color of the 3D map view.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
3 Object that controls camera movement based on user input
The scene is fully loaded/updated.
virtual Qt3DRender::QCamera * camera()=0
Returns pointer to the engine's camera entity.
static int maxZoomLevel(double tile0width, double tileResolution, double maxError)
Calculates the highest needed zoom level for tiles in quad-tree given width of the base tile (zoom le...
void mapTileResolutionChanged()
Emitted when the map tile resoulution has changed.
virtual void setClearColor(const QColor &color)=0
Sets background color of the scene.
Qt3DRender::QCamera camera
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
3D renderer that renders all features of a vector layer with the same 3D symbol.
void unregisterPickHandler(Qgs3DMapScenePickHandler *pickHandler)
Unregisters previously registered pick handler. Pick handler is not deleted. Also removes object pick...
void setCamera(Qt3DRender::QCamera *camera)
Assigns camera that should be controlled by this class. Called internally from 3D scene...
void maxTerrainScreenErrorChanged()
Emitted when the maximum terrain screen error has changed.
QList< QgsAbstract3DRenderer * > renderers() const
Returns list of extra 3D renderers.
void sceneStateChanged()
Emitted when the scene's state has changed.
virtual QSize size() const =0
Returns size of the engine's rendering area in pixels.
void viewZoomFull()
Resets camera view to show the whole scene (top view)
int terrainPendingJobsCount() const
Returns number of pending jobs of the terrain entity.
virtual QgsRectangle extent() const =0
extent of the terrain in terrain's CRS
QString skyboxFileExtension() const
Returns extension part of filenames of skybox (see setSkybox())
float fieldOfView() const
Returns the camera lens' field of view.
void terrainEntityChanged()
Emitted when the current terrain entity is replaced by a new one.
void backgroundColorChanged()
Emitted when the background color has changed.
QgsChunkedEntity::SceneState _sceneState(QgsCameraController *cameraController)
Represents a vector layer which manages a vector based data sets.
Qgs3DMapScene(const Qgs3DMapSettings &map, QgsAbstract3DEngine *engine)
Constructs a 3D scene based on map settings and Qt 3D renderer configuration.
The scene is still being loaded/updated.
void setTerrainEntity(QgsTerrainEntity *te)
Connects to object picker attached to terrain entity.
void terrainShadingChanged()
Emitted when terrain shading enabled flag or terrain shading material has changed.
QgsCameraController * cameraController()
Returns camera controller.
virtual void setFrustumCullingEnabled(bool enabled)=0
Sets whether frustum culling is enabled (this should make rendering faster by not rendering entities ...
double height() const
Returns the height of the rectangle.
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data...