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> 61 onBackgroundColorChanged();
67 #if QT_VERSION >= 0x050900 69 mEngine->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
72 QRect viewportRect( QPoint( 0, 0 ), mEngine->
size() );
75 float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
76 mEngine->
camera()->lens()->setPerspectiveProjection( mMap.
fieldOfView(), aspectRatio, 10.f, 10000.0f );
78 mFrameAction =
new Qt3DLogic::QFrameAction();
79 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
80 this, &Qgs3DMapScene::onFrameTriggered );
81 addComponent( mFrameAction );
89 addCameraViewCenterEntity( mEngine->
camera() );
93 createTerrainDeferred();
107 Qt3DCore::QEntity *newEntity = renderer->
createEntity( map );
108 newEntity->setParent(
this );
117 ChunkedEntity *testChunkEntity =
new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7,
new TestChunkLoaderFactory );
118 testChunkEntity->setEnabled(
false );
119 testChunkEntity->setParent(
this );
120 chunkEntities << testChunkEntity;
131 Qt3DCore::QEntity *loaderEntity =
new Qt3DCore::QEntity;
132 Qt3DRender::QSceneLoader *loader =
new Qt3DRender::QSceneLoader;
133 loader->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
134 loaderEntity->addComponent( loader );
135 loaderEntity->setParent(
this );
139 Qt3DCore::QEntity *meshEntity =
new Qt3DCore::QEntity;
140 Qt3DRender::QMesh *mesh =
new Qt3DRender::QMesh;
141 mesh->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
142 meshEntity->addComponent( mesh );
143 Qt3DExtras::QPhongMaterial *material =
new Qt3DExtras::QPhongMaterial;
144 material->setAmbient( Qt::red );
145 meshEntity->addComponent( material );
146 Qt3DCore::QTransform *meshTransform =
new Qt3DCore::QTransform;
147 meshTransform->setScale( 1 );
148 meshEntity->addComponent( meshTransform );
149 meshEntity->setParent(
this );
154 Qt3DExtras::QSkyboxEntity *skybox =
new Qt3DExtras::QSkyboxEntity;
157 skybox->setParent(
this );
172 float side = std::max( extent.
width(), extent.
height() );
178 return mTerrain ? mTerrain->pendingJobsCount() : 0;
183 if ( mPickHandlers.isEmpty() )
186 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
188 Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker( entity );
189 entity->addComponent( picker );
190 connect( picker, &Qt3DRender::QObjectPicker::clicked,
this, &Qgs3DMapScene::onLayerEntityPickEvent );
194 mPickHandlers.append( pickHandler );
199 mPickHandlers.removeOne( pickHandler );
201 if ( mPickHandlers.isEmpty() )
204 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
206 Qt3DRender::QObjectPicker *picker = entity->findChild<Qt3DRender::QObjectPicker *>();
207 picker->deleteLater();
214 Qt3DRender::QCamera *camera = mCameraController->
camera();
215 float fov = camera->fieldOfView();
216 QRect rect = mCameraController->
viewport();
217 float screenSizePx = std::max( rect.width(), rect.height() );
221 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
222 float err = frustumWidthAtDistance * epsilon / screenSizePx;
228 Qt3DRender::QCamera *camera = cameraController->
camera();
229 QgsChunkedEntity::SceneState state;
230 state.cameraFov = camera->fieldOfView();
231 state.cameraPos = camera->position();
232 QRect rect = cameraController->
viewport();
233 state.screenSizePx = std::max( rect.width(), rect.height() );
234 state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
238 void Qgs3DMapScene::onCameraChanged()
241 bool changedCameraPlanes = updateCameraNearFarPlanes();
243 if ( changedCameraPlanes )
248 updateCameraNearFarPlanes();
252 void Qgs3DMapScene::updateScene()
254 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
256 if ( entity->isEnabled() )
257 entity->update(
_sceneState( mCameraController ) );
263 bool Qgs3DMapScene::updateCameraNearFarPlanes()
280 QMatrix4x4 viewMatrix = camera->viewMatrix();
284 QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
289 if ( activeNodes.isEmpty() )
290 activeNodes << mTerrain->rootNode();
292 Q_FOREACH ( QgsChunkNode *node, activeNodes )
297 for (
int i = 0; i < 8; ++i )
299 QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.
xMin : bbox.
xMax,
300 ( ( i >> 1 ) & 1 ) ? bbox.
yMin : bbox.
yMax,
301 ( ( i >> 2 ) & 1 ) ? bbox.
zMin : bbox.
zMax, 1 );
302 QVector4D pc = viewMatrix * p;
314 if ( fnear == 1e9 && ffar == 0 )
318 qDebug() <<
"oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
324 float newFar = ffar * 2;
325 float newNear = fnear / 2;
328 camera->setFarPlane( newFar );
329 camera->setNearPlane( newNear );
334 qDebug() <<
"no terrain - not setting near/far plane";
339 void Qgs3DMapScene::onFrameTriggered(
float dt )
343 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
345 if ( entity->isEnabled() && entity->needsUpdate() )
347 qDebug() <<
"need for update";
348 entity->update(
_sceneState( mCameraController ) );
355 void Qgs3DMapScene::createTerrain()
359 mChunkEntities.removeOne( mTerrain );
361 mTerrain->deleteLater();
367 if ( !mTerrainUpdateScheduled )
370 QTimer::singleShot( 0,
this, &Qgs3DMapScene::createTerrainDeferred );
371 mTerrainUpdateScheduled =
true;
376 void Qgs3DMapScene::createTerrainDeferred()
381 mTerrain =
new QgsTerrainEntity( maxZoomLevel, mMap );
383 mTerrain->setParent(
this );
386 mTerrain->setShowBoundingBoxes(
true );
390 mChunkEntities << mTerrain;
398 removeLayerEntity( layer );
401 addLayerEntity( layer );
404 mTerrainUpdateScheduled =
false;
411 void Qgs3DMapScene::onBackgroundColorChanged()
416 void Qgs3DMapScene::onLayerEntityPickEvent( Qt3DRender::QPickEvent *event )
418 if ( event->button() != Qt3DRender::QPickEvent::LeftButton )
421 Qt3DRender::QPickTriangleEvent *triangleEvent = qobject_cast<Qt3DRender::QPickTriangleEvent *>( event );
422 if ( !triangleEvent )
425 Qt3DRender::QObjectPicker *picker = qobject_cast<Qt3DRender::QObjectPicker *>( sender() );
429 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( picker->parent() );
445 for ( Qt3DRender::QGeometryRenderer *geomRenderer : entity->findChildren<Qt3DRender::QGeometryRenderer *>() )
450 if ( geomRenderer->objectName() != QLatin1String(
"main" ) )
455 fid = g->triangleIndexToFeatureId( triangleEvent->triangleIndex() );
459 pickHandler->handlePickOnVectorLayer( vlayer, fid, event->worldIntersection() );
464 void Qgs3DMapScene::updateLights()
466 for ( Qt3DCore::QEntity *entity : qgis::as_const( mLightEntities ) )
467 entity->deleteLater();
468 mLightEntities.clear();
473 Qt3DCore::QEntity *lightEntity =
new Qt3DCore::QEntity;
474 Qt3DCore::QTransform *lightTransform =
new Qt3DCore::QTransform;
475 lightTransform->setTranslation( QVector3D( pointLightSettings.position().x(),
476 pointLightSettings.position().y(),
477 pointLightSettings.position().z() ) );
479 Qt3DRender::QPointLight *light =
new Qt3DRender::QPointLight;
480 light->setColor( pointLightSettings.color() );
481 light->setIntensity( pointLightSettings.intensity() );
483 light->setConstantAttenuation( pointLightSettings.constantAttenuation() );
484 light->setLinearAttenuation( pointLightSettings.linearAttenuation() );
485 light->setQuadraticAttenuation( pointLightSettings.quadraticAttenuation() );
487 lightEntity->addComponent( light );
488 lightEntity->addComponent( lightTransform );
489 lightEntity->setParent(
this );
490 mLightEntities << lightEntity;
494 void Qgs3DMapScene::updateCameraLens()
500 void Qgs3DMapScene::onLayerRenderer3DChanged()
506 removeLayerEntity( layer );
509 addLayerEntity( layer );
512 void Qgs3DMapScene::onLayersChanged()
514 QSet<QgsMapLayer *> layersBefore = QSet<QgsMapLayer *>::fromList( mLayerEntities.keys() );
515 QList<QgsMapLayer *> layersAdded;
518 if ( !layersBefore.contains( layer ) )
520 layersAdded << layer;
524 layersBefore.remove( layer );
531 removeLayerEntity( layer );
536 addLayerEntity( layer );
540 void Qgs3DMapScene::addLayerEntity(
QgsMapLayer *layer )
563 Qt3DCore::QEntity *newEntity = renderer->
createEntity( mMap );
566 newEntity->setParent(
this );
567 mLayerEntities.insert( layer, newEntity );
569 if ( !mPickHandlers.isEmpty() )
571 Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker( newEntity );
572 newEntity->addComponent( picker );
573 connect( picker, &Qt3DRender::QObjectPicker::pressed,
this, &Qgs3DMapScene::onLayerEntityPickEvent );
578 for ( QgsLineMaterial *lm : newEntity->findChildren<QgsLineMaterial *>() )
582 lm->setViewportSize( mCameraController->
viewport().size() );
599 void Qgs3DMapScene::removeLayerEntity(
QgsMapLayer *layer )
601 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
603 entity->deleteLater();
614 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
616 mEntityCameraViewCenter =
new Qt3DCore::QEntity;
618 Qt3DCore::QTransform *trCameraViewCenter =
new Qt3DCore::QTransform;
619 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
620 connect( camera, &Qt3DRender::QCamera::viewCenterChanged,
this, [trCameraViewCenter, camera]
622 trCameraViewCenter->setTranslation( camera->viewCenter() );
625 Qt3DExtras::QPhongMaterial *materialCameraViewCenter =
new Qt3DExtras::QPhongMaterial;
626 materialCameraViewCenter->setAmbient( Qt::red );
627 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
629 Qt3DExtras::QSphereMesh *rendererCameraViewCenter =
new Qt3DExtras::QSphereMesh;
630 rendererCameraViewCenter->setRadius( 10 );
631 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
634 mEntityCameraViewCenter->setParent(
this );
644 if ( mSceneState == state )
650 void Qgs3DMapScene::updateSceneState()
652 if ( mTerrainUpdateScheduled )
658 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
660 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
667 setSceneState(
Ready );
3 Abstract base class for handlers that process pick events from a 3D map scene.
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.
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 camer 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...