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> 63 onBackgroundColorChanged();
69 #if QT_VERSION >= 0x050900 71 mEngine->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
74 QRect viewportRect( QPoint( 0, 0 ), mEngine->
size() );
77 float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
78 mEngine->
camera()->lens()->setPerspectiveProjection( mMap.
fieldOfView(), aspectRatio, 10.f, 10000.0f );
80 mFrameAction =
new Qt3DLogic::QFrameAction();
81 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
82 this, &Qgs3DMapScene::onFrameTriggered );
83 addComponent( mFrameAction );
91 addCameraViewCenterEntity( mEngine->
camera() );
95 createTerrainDeferred();
108 onRenderersChanged();
116 ChunkedEntity *testChunkEntity =
new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7,
new TestChunkLoaderFactory );
117 testChunkEntity->setEnabled(
false );
118 testChunkEntity->setParent(
this );
119 chunkEntities << testChunkEntity;
130 Qt3DCore::QEntity *loaderEntity =
new Qt3DCore::QEntity;
131 Qt3DRender::QSceneLoader *loader =
new Qt3DRender::QSceneLoader;
132 loader->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
133 loaderEntity->addComponent( loader );
134 loaderEntity->setParent(
this );
138 Qt3DCore::QEntity *meshEntity =
new Qt3DCore::QEntity;
139 Qt3DRender::QMesh *mesh =
new Qt3DRender::QMesh;
140 mesh->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
141 meshEntity->addComponent( mesh );
142 Qt3DExtras::QPhongMaterial *material =
new Qt3DExtras::QPhongMaterial;
143 material->setAmbient( Qt::red );
144 meshEntity->addComponent( material );
145 Qt3DCore::QTransform *meshTransform =
new Qt3DCore::QTransform;
146 meshTransform->setScale( 1 );
147 meshEntity->addComponent( meshTransform );
148 meshEntity->setParent(
this );
153 Qt3DExtras::QSkyboxEntity *skybox =
new Qt3DExtras::QSkyboxEntity;
156 skybox->setParent(
this );
171 float side = std::max( extent.
width(), extent.
height() );
177 return mTerrain ? mTerrain->pendingJobsCount() : 0;
183 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
184 count += entity->pendingJobsCount();
190 if ( mPickHandlers.isEmpty() )
193 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
195 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
196 chunkedEntity->setPickingEnabled(
true );
200 mPickHandlers.append( pickHandler );
205 mPickHandlers.removeOne( pickHandler );
207 if ( mPickHandlers.isEmpty() )
210 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
212 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
213 chunkedEntity->setPickingEnabled(
false );
218 void Qgs3DMapScene::onLayerEntityPickedObject( Qt3DRender::QPickEvent *pickEvent,
QgsFeatureId fid )
220 QgsMapLayer *layer = mLayerEntities.key( qobject_cast<QgsChunkedEntity *>( sender() ) );
230 pickHandler->handlePickOnVectorLayer( vlayer, fid, pickEvent->worldIntersection(), pickEvent );
237 Qt3DRender::QCamera *camera = mCameraController->
camera();
238 float fov = camera->fieldOfView();
239 QRect rect = mCameraController->
viewport();
240 float screenSizePx = std::max( rect.width(), rect.height() );
244 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
245 float err = frustumWidthAtDistance * epsilon / screenSizePx;
251 Qt3DRender::QCamera *camera = cameraController->
camera();
252 QgsChunkedEntity::SceneState state;
253 state.cameraFov = camera->fieldOfView();
254 state.cameraPos = camera->position();
255 QRect rect = cameraController->
viewport();
256 state.screenSizePx = std::max( rect.width(), rect.height() );
257 state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
261 void Qgs3DMapScene::onCameraChanged()
264 bool changedCameraPlanes = updateCameraNearFarPlanes();
266 if ( changedCameraPlanes )
271 updateCameraNearFarPlanes();
275 void Qgs3DMapScene::updateScene()
277 QgsEventTracing::addEvent( QgsEventTracing::Instant, QStringLiteral(
"3D" ), QStringLiteral(
"Update Scene" ) );
279 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
281 if ( entity->isEnabled() )
282 entity->update(
_sceneState( mCameraController ) );
288 bool Qgs3DMapScene::updateCameraNearFarPlanes()
305 QMatrix4x4 viewMatrix = camera->viewMatrix();
309 QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
314 if ( activeNodes.isEmpty() )
315 activeNodes << mTerrain->rootNode();
317 Q_FOREACH ( QgsChunkNode *node, activeNodes )
322 for (
int i = 0; i < 8; ++i )
324 QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.
xMin : bbox.
xMax,
325 ( ( i >> 1 ) & 1 ) ? bbox.
yMin : bbox.
yMax,
326 ( ( i >> 2 ) & 1 ) ? bbox.
zMin : bbox.
zMax, 1 );
327 QVector4D pc = viewMatrix * p;
339 if ( fnear == 1e9 && ffar == 0 )
343 qDebug() <<
"oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
349 float newFar = ffar * 2;
350 float newNear = fnear / 2;
353 camera->setFarPlane( newFar );
354 camera->setNearPlane( newNear );
359 qDebug() <<
"no terrain - not setting near/far plane";
364 void Qgs3DMapScene::onFrameTriggered(
float dt )
368 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
370 if ( entity->isEnabled() && entity->needsUpdate() )
372 qDebug() <<
"need for update";
373 entity->update(
_sceneState( mCameraController ) );
380 void Qgs3DMapScene::createTerrain()
384 mChunkEntities.removeOne( mTerrain );
386 mTerrain->deleteLater();
392 if ( !mTerrainUpdateScheduled )
395 QTimer::singleShot( 0,
this, &Qgs3DMapScene::createTerrainDeferred );
396 mTerrainUpdateScheduled =
true;
401 void Qgs3DMapScene::createTerrainDeferred()
406 mTerrain =
new QgsTerrainEntity( maxZoomLevel, mMap );
408 mTerrain->setParent(
this );
411 mTerrain->setShowBoundingBoxes(
true );
415 mChunkEntities << mTerrain;
423 removeLayerEntity( layer );
426 addLayerEntity( layer );
429 mTerrainUpdateScheduled =
false;
437 void Qgs3DMapScene::onBackgroundColorChanged()
443 void Qgs3DMapScene::updateLights()
445 for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightEntities ) )
446 entity->deleteLater();
447 mLightEntities.clear();
452 Qt3DCore::QEntity *lightEntity =
new Qt3DCore::QEntity;
453 Qt3DCore::QTransform *lightTransform =
new Qt3DCore::QTransform;
454 lightTransform->setTranslation( QVector3D( pointLightSettings.position().x(),
455 pointLightSettings.position().y(),
456 pointLightSettings.position().z() ) );
458 Qt3DRender::QPointLight *light =
new Qt3DRender::QPointLight;
459 light->setColor( pointLightSettings.color() );
460 light->setIntensity( pointLightSettings.intensity() );
462 light->setConstantAttenuation( pointLightSettings.constantAttenuation() );
463 light->setLinearAttenuation( pointLightSettings.linearAttenuation() );
464 light->setQuadraticAttenuation( pointLightSettings.quadraticAttenuation() );
466 lightEntity->addComponent( light );
467 lightEntity->addComponent( lightTransform );
468 lightEntity->setParent(
this );
469 mLightEntities << lightEntity;
473 void Qgs3DMapScene::updateCameraLens()
479 void Qgs3DMapScene::onRenderersChanged()
482 qDeleteAll( mRenderersEntities );
483 mRenderersEntities.clear();
486 const QList<QgsAbstract3DRenderer *> renderers = mMap.
renderers();
489 Qt3DCore::QEntity *newEntity = renderer->createEntity( mMap );
492 newEntity->setParent(
this );
493 finalizeNewEntity( newEntity );
494 mRenderersEntities[renderer] = newEntity;
499 void Qgs3DMapScene::onLayerRenderer3DChanged()
505 removeLayerEntity( layer );
508 addLayerEntity( layer );
511 void Qgs3DMapScene::onLayersChanged()
513 QSet<QgsMapLayer *> layersBefore = QSet<QgsMapLayer *>::fromList( mLayerEntities.keys() );
514 QList<QgsMapLayer *> layersAdded;
517 if ( !layersBefore.contains( layer ) )
519 layersAdded << layer;
523 layersBefore.remove( layer );
530 removeLayerEntity( layer );
535 addLayerEntity( layer );
539 void Qgs3DMapScene::addLayerEntity(
QgsMapLayer *layer )
541 bool needsSceneUpdate =
false;
550 ( renderer->
type() == QLatin1String(
"vector" ) || renderer->
type() == QLatin1String(
"rulebased" ) ) )
559 Qt3DCore::QEntity *newEntity = renderer->
createEntity( mMap );
562 newEntity->setParent(
this );
563 mLayerEntities.insert( layer, newEntity );
565 finalizeNewEntity( newEntity );
567 if ( QgsChunkedEntity *chunkedNewEntity = qobject_cast<QgsChunkedEntity *>( newEntity ) )
569 mChunkEntities.append( chunkedNewEntity );
570 needsSceneUpdate =
true;
572 chunkedNewEntity->setPickingEnabled( !mPickHandlers.isEmpty() );
573 connect( chunkedNewEntity, &QgsChunkedEntity::pickedObject,
this, &Qgs3DMapScene::onLayerEntityPickedObject );
575 connect( chunkedNewEntity, &QgsChunkedEntity::newEntityCreated,
this, [
this]( Qt3DCore::QEntity * entity )
577 finalizeNewEntity( entity );
585 if ( needsSceneUpdate )
597 void Qgs3DMapScene::removeLayerEntity(
QgsMapLayer *layer )
599 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
601 if ( QgsChunkedEntity *chunkedEntity = qobject_cast<QgsChunkedEntity *>( entity ) )
603 mChunkEntities.removeOne( chunkedEntity );
607 entity->deleteLater();
618 void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
622 for ( QgsLineMaterial *lm : newEntity->findChildren<QgsLineMaterial *>() )
626 lm->setViewportSize( mCameraController->
viewport().size() );
636 bm->setViewportSize( mCameraController->
viewport().size() );
639 bm->setViewportSize( mCameraController->
viewport().size() );
643 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
645 mEntityCameraViewCenter =
new Qt3DCore::QEntity;
647 Qt3DCore::QTransform *trCameraViewCenter =
new Qt3DCore::QTransform;
648 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
649 connect( camera, &Qt3DRender::QCamera::viewCenterChanged,
this, [trCameraViewCenter, camera]
651 trCameraViewCenter->setTranslation( camera->viewCenter() );
654 Qt3DExtras::QPhongMaterial *materialCameraViewCenter =
new Qt3DExtras::QPhongMaterial;
655 materialCameraViewCenter->setAmbient( Qt::red );
656 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
658 Qt3DExtras::QSphereMesh *rendererCameraViewCenter =
new Qt3DExtras::QSphereMesh;
659 rendererCameraViewCenter->setRadius( 10 );
660 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
663 mEntityCameraViewCenter->setParent(
this );
673 if ( mSceneState == state )
679 void Qgs3DMapScene::updateSceneState()
681 if ( mTerrainUpdateScheduled )
687 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
689 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
696 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 totalPendingJobsCountChanged()
Emitted when the total number of pending jobs changes.
void terrainVerticalScaleChanged()
Emitted when the vertical scale of the terrain has changed.
3 Definition of the world
int totalPendingJobsCount() const
Returns number of pending jobs for all chunked entities.
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.
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.
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.
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())
3 Base class for 3D renderers that are based on vector layers.
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...