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> 59 onBackgroundColorChanged();
65 #if QT_VERSION >= 0x050900 67 mEngine->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
70 QRect viewportRect( QPoint( 0, 0 ), mEngine->
size() );
73 float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
74 mEngine->
camera()->lens()->setPerspectiveProjection( 45.0f, aspectRatio, 10.f, 10000.0f );
76 mFrameAction =
new Qt3DLogic::QFrameAction();
77 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
78 this, &Qgs3DMapScene::onFrameTriggered );
79 addComponent( mFrameAction );
87 addCameraViewCenterEntity( mEngine->
camera() );
91 createTerrainDeferred();
104 Qt3DCore::QEntity *newEntity = renderer->
createEntity( map );
105 newEntity->setParent(
this );
114 ChunkedEntity *testChunkEntity =
new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7,
new TestChunkLoaderFactory );
115 testChunkEntity->setEnabled(
false );
116 testChunkEntity->setParent(
this );
117 chunkEntities << testChunkEntity;
128 Qt3DCore::QEntity *loaderEntity =
new Qt3DCore::QEntity;
129 Qt3DRender::QSceneLoader *loader =
new Qt3DRender::QSceneLoader;
130 loader->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
131 loaderEntity->addComponent( loader );
132 loaderEntity->setParent(
this );
136 Qt3DCore::QEntity *meshEntity =
new Qt3DCore::QEntity;
137 Qt3DRender::QMesh *mesh =
new Qt3DRender::QMesh;
138 mesh->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
139 meshEntity->addComponent( mesh );
140 Qt3DExtras::QPhongMaterial *material =
new Qt3DExtras::QPhongMaterial;
141 material->setAmbient( Qt::red );
142 meshEntity->addComponent( material );
143 Qt3DCore::QTransform *meshTransform =
new Qt3DCore::QTransform;
144 meshTransform->setScale( 1 );
145 meshEntity->addComponent( meshTransform );
146 meshEntity->setParent(
this );
151 Qt3DExtras::QSkyboxEntity *skybox =
new Qt3DExtras::QSkyboxEntity;
154 skybox->setParent(
this );
169 float side = std::max( extent.
width(), extent.
height() );
175 return mTerrain ? mTerrain->pendingJobsCount() : 0;
180 if ( mPickHandlers.isEmpty() )
183 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
185 Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker( entity );
186 entity->addComponent( picker );
187 connect( picker, &Qt3DRender::QObjectPicker::clicked,
this, &Qgs3DMapScene::onLayerEntityPickEvent );
191 mPickHandlers.append( pickHandler );
196 mPickHandlers.removeOne( pickHandler );
198 if ( mPickHandlers.isEmpty() )
201 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
203 Qt3DRender::QObjectPicker *picker = entity->findChild<Qt3DRender::QObjectPicker *>();
204 picker->deleteLater();
211 Qt3DRender::QCamera *camera = mCameraController->
camera();
212 float fov = camera->fieldOfView();
213 QRect rect = mCameraController->
viewport();
214 float screenSizePx = std::max( rect.width(), rect.height() );
218 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
219 float err = frustumWidthAtDistance * epsilon / screenSizePx;
225 Qt3DRender::QCamera *camera = cameraController->
camera();
226 QgsChunkedEntity::SceneState state;
227 state.cameraFov = camera->fieldOfView();
228 state.cameraPos = camera->position();
229 QRect rect = cameraController->
viewport();
230 state.screenSizePx = std::max( rect.width(), rect.height() );
231 state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
235 void Qgs3DMapScene::onCameraChanged()
238 bool changedCameraPlanes = updateCameraNearFarPlanes();
240 if ( changedCameraPlanes )
245 updateCameraNearFarPlanes();
249 void Qgs3DMapScene::updateScene()
251 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
253 if ( entity->isEnabled() )
254 entity->update(
_sceneState( mCameraController ) );
260 bool Qgs3DMapScene::updateCameraNearFarPlanes()
277 QMatrix4x4 viewMatrix = camera->viewMatrix();
281 QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
286 if ( activeNodes.isEmpty() )
287 activeNodes << mTerrain->rootNode();
289 Q_FOREACH ( QgsChunkNode *node, activeNodes )
294 for (
int i = 0; i < 8; ++i )
296 QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.
xMin : bbox.
xMax,
297 ( ( i >> 1 ) & 1 ) ? bbox.
yMin : bbox.
yMax,
298 ( ( i >> 2 ) & 1 ) ? bbox.
zMin : bbox.
zMax, 1 );
299 QVector4D pc = viewMatrix * p;
311 if ( fnear == 1e9 && ffar == 0 )
315 qDebug() <<
"oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
321 float newFar = ffar * 2;
322 float newNear = fnear / 2;
325 camera->setFarPlane( newFar );
326 camera->setNearPlane( newNear );
331 qDebug() <<
"no terrain - not setting near/far plane";
336 void Qgs3DMapScene::onFrameTriggered(
float dt )
340 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
342 if ( entity->isEnabled() && entity->needsUpdate() )
344 qDebug() <<
"need for update";
345 entity->update(
_sceneState( mCameraController ) );
352 void Qgs3DMapScene::createTerrain()
354 if ( !mTerrainUpdateScheduled )
357 QTimer::singleShot( 0,
this, &Qgs3DMapScene::createTerrainDeferred );
358 mTerrainUpdateScheduled =
true;
363 void Qgs3DMapScene::createTerrainDeferred()
367 mChunkEntities.removeOne( mTerrain );
369 mTerrain->deleteLater();
376 mTerrain =
new QgsTerrainEntity( maxZoomLevel, mMap );
378 mTerrain->setParent(
this );
381 mTerrain->setShowBoundingBoxes(
true );
385 mChunkEntities << mTerrain;
393 removeLayerEntity( layer );
396 addLayerEntity( layer );
399 mTerrainUpdateScheduled =
false;
406 void Qgs3DMapScene::onBackgroundColorChanged()
411 void Qgs3DMapScene::onLayerEntityPickEvent( Qt3DRender::QPickEvent *event )
413 if ( event->button() != Qt3DRender::QPickEvent::LeftButton )
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() );
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::onLayerRenderer3DChanged()
495 removeLayerEntity( layer );
498 addLayerEntity( layer );
501 void Qgs3DMapScene::onLayersChanged()
503 QSet<QgsMapLayer *> layersBefore = QSet<QgsMapLayer *>::fromList( mLayerEntities.keys() );
504 QList<QgsMapLayer *> layersAdded;
507 if ( !layersBefore.contains( layer ) )
509 layersAdded << layer;
513 layersBefore.remove( layer );
520 removeLayerEntity( layer );
525 addLayerEntity( layer );
529 void Qgs3DMapScene::addLayerEntity(
QgsMapLayer *layer )
552 Qt3DCore::QEntity *newEntity = renderer->
createEntity( mMap );
555 newEntity->setParent(
this );
556 mLayerEntities.insert( layer, newEntity );
558 if ( !mPickHandlers.isEmpty() )
560 Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker( newEntity );
561 newEntity->addComponent( picker );
562 connect( picker, &Qt3DRender::QObjectPicker::pressed,
this, &Qgs3DMapScene::onLayerEntityPickEvent );
576 void Qgs3DMapScene::removeLayerEntity(
QgsMapLayer *layer )
578 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
580 entity->deleteLater();
591 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
593 mEntityCameraViewCenter =
new Qt3DCore::QEntity;
595 Qt3DCore::QTransform *trCameraViewCenter =
new Qt3DCore::QTransform;
596 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
597 connect( camera, &Qt3DRender::QCamera::viewCenterChanged,
this, [trCameraViewCenter, camera]
599 trCameraViewCenter->setTranslation( camera->viewCenter() );
602 Qt3DExtras::QPhongMaterial *materialCameraViewCenter =
new Qt3DExtras::QPhongMaterial;
603 materialCameraViewCenter->setAmbient( Qt::red );
604 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
606 Qt3DExtras::QSphereMesh *rendererCameraViewCenter =
new Qt3DExtras::QSphereMesh;
607 rendererCameraViewCenter->setRadius( 10 );
608 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
611 mEntityCameraViewCenter->setParent(
this );
621 if ( mSceneState == state )
627 void Qgs3DMapScene::updateSceneState()
629 if ( mTerrainUpdateScheduled )
635 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
637 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
644 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.
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
QgsMapLayer::LayerType type() const
Returns the type of the layer.
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.
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)
This signal is 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())
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...