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> 57 onBackgroundColorChanged();
63 #if QT_VERSION >= 0x050900 65 mEngine->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
68 QRect viewportRect( QPoint( 0, 0 ), mEngine->
size() );
71 float aspectRatio = ( float )viewportRect.width() / viewportRect.height();
72 mEngine->
camera()->lens()->setPerspectiveProjection( 45.0f, aspectRatio, 10.f, 10000.0f );
74 mFrameAction =
new Qt3DLogic::QFrameAction();
75 connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered,
76 this, &Qgs3DMapScene::onFrameTriggered );
77 addComponent( mFrameAction );
85 addCameraViewCenterEntity( mEngine->
camera() );
100 Qt3DCore::QEntity *newEntity = renderer->
createEntity( map );
101 newEntity->setParent(
this );
107 Qt3DCore::QEntity *lightEntity =
new Qt3DCore::QEntity;
108 Qt3DCore::QTransform *lightTransform =
new Qt3DCore::QTransform;
109 lightTransform->setTranslation( QVector3D( 0, 1000, 0 ) );
112 Qt3DRender::QPointLight *light =
new Qt3DRender::QPointLight;
113 light->setConstantAttenuation( 0 );
116 lightEntity->addComponent( light );
117 lightEntity->addComponent( lightTransform );
118 lightEntity->setParent(
this );
122 ChunkedEntity *testChunkEntity =
new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7,
new TestChunkLoaderFactory );
123 testChunkEntity->setEnabled(
false );
124 testChunkEntity->setParent(
this );
125 chunkEntities << testChunkEntity;
136 Qt3DCore::QEntity *loaderEntity =
new Qt3DCore::QEntity;
137 Qt3DRender::QSceneLoader *loader =
new Qt3DRender::QSceneLoader;
138 loader->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.dae" ) );
139 loaderEntity->addComponent( loader );
140 loaderEntity->setParent(
this );
144 Qt3DCore::QEntity *meshEntity =
new Qt3DCore::QEntity;
145 Qt3DRender::QMesh *mesh =
new Qt3DRender::QMesh;
146 mesh->setSource( QUrl(
"file:///home/martin/Downloads/LowPolyModels/tree.obj" ) );
147 meshEntity->addComponent( mesh );
148 Qt3DExtras::QPhongMaterial *material =
new Qt3DExtras::QPhongMaterial;
149 material->setAmbient( Qt::red );
150 meshEntity->addComponent( material );
151 Qt3DCore::QTransform *meshTransform =
new Qt3DCore::QTransform;
152 meshTransform->setScale( 1 );
153 meshEntity->addComponent( meshTransform );
154 meshEntity->setParent(
this );
159 Qt3DExtras::QSkyboxEntity *skybox =
new Qt3DExtras::QSkyboxEntity;
162 skybox->setParent(
this );
177 float side = std::max( extent.
width(), extent.
height() );
183 return mTerrain ? mTerrain->pendingJobsCount() : 0;
188 if ( mPickHandlers.isEmpty() )
191 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
193 Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker( entity );
194 entity->addComponent( picker );
195 connect( picker, &Qt3DRender::QObjectPicker::clicked,
this, &Qgs3DMapScene::onLayerEntityPickEvent );
199 mPickHandlers.append( pickHandler );
204 mPickHandlers.removeOne( pickHandler );
206 if ( mPickHandlers.isEmpty() )
209 for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
211 Qt3DRender::QObjectPicker *picker = entity->findChild<Qt3DRender::QObjectPicker *>();
212 picker->deleteLater();
219 Qt3DRender::QCamera *camera = mCameraController->
camera();
220 float fov = camera->fieldOfView();
221 QRect rect = mCameraController->
viewport();
222 float screenSizePx = std::max( rect.width(), rect.height() );
226 float frustumWidthAtDistance = 2 * distance * tan( fov / 2 );
227 float err = frustumWidthAtDistance * epsilon / screenSizePx;
233 Qt3DRender::QCamera *camera = cameraController->
camera();
234 QgsChunkedEntity::SceneState state;
235 state.cameraFov = camera->fieldOfView();
236 state.cameraPos = camera->position();
237 QRect rect = cameraController->
viewport();
238 state.screenSizePx = std::max( rect.width(), rect.height() );
239 state.viewProjectionMatrix = camera->projectionMatrix() * camera->viewMatrix();
243 void Qgs3DMapScene::onCameraChanged()
246 bool changedCameraPlanes = updateCameraNearFarPlanes();
248 if ( changedCameraPlanes )
253 updateCameraNearFarPlanes();
257 void Qgs3DMapScene::updateScene()
259 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
261 if ( entity->isEnabled() )
262 entity->update(
_sceneState( mCameraController ) );
268 bool Qgs3DMapScene::updateCameraNearFarPlanes()
285 QMatrix4x4 viewMatrix = camera->viewMatrix();
289 QList<QgsChunkNode *> activeNodes = mTerrain->activeNodes();
294 if ( activeNodes.isEmpty() )
295 activeNodes << mTerrain->rootNode();
297 Q_FOREACH ( QgsChunkNode *node, activeNodes )
302 for (
int i = 0; i < 8; ++i )
304 QVector4D p( ( ( i >> 0 ) & 1 ) ? bbox.
xMin : bbox.
xMax,
305 ( ( i >> 1 ) & 1 ) ? bbox.
yMin : bbox.
yMax,
306 ( ( i >> 2 ) & 1 ) ? bbox.
zMin : bbox.
zMax, 1 );
307 QVector4D pc = viewMatrix * p;
319 if ( fnear == 1e9 && ffar == 0 )
323 qDebug() <<
"oops... this should not happen! couldn't determine near/far plane. defaulting to 1...1e9";
329 float newFar = ffar * 2;
330 float newNear = fnear / 2;
333 camera->setFarPlane( newFar );
334 camera->setNearPlane( newNear );
339 qDebug() <<
"no terrain - not setting near/far plane";
344 void Qgs3DMapScene::onFrameTriggered(
float dt )
348 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
350 if ( entity->isEnabled() && entity->needsUpdate() )
352 qDebug() <<
"need for update";
353 entity->update(
_sceneState( mCameraController ) );
360 void Qgs3DMapScene::createTerrain()
364 mChunkEntities.removeOne( mTerrain );
366 mTerrain->deleteLater();
372 if ( !mTerrainUpdateScheduled )
375 QTimer::singleShot( 0,
this, &Qgs3DMapScene::createTerrainDeferred );
376 mTerrainUpdateScheduled =
true;
381 void Qgs3DMapScene::createTerrainDeferred()
386 mTerrain =
new QgsTerrainEntity( maxZoomLevel, mMap );
388 mTerrain->setParent(
this );
391 mTerrain->setShowBoundingBoxes(
true );
395 mChunkEntities << mTerrain;
403 removeLayerEntity( layer );
406 addLayerEntity( layer );
409 mTerrainUpdateScheduled =
false;
416 void Qgs3DMapScene::onBackgroundColorChanged()
421 void Qgs3DMapScene::onLayerEntityPickEvent( Qt3DRender::QPickEvent *event )
423 if ( event->button() != Qt3DRender::QPickEvent::LeftButton )
426 Qt3DRender::QPickTriangleEvent *triangleEvent = qobject_cast<Qt3DRender::QPickTriangleEvent *>( event );
427 if ( !triangleEvent )
430 Qt3DRender::QObjectPicker *picker = qobject_cast<Qt3DRender::QObjectPicker *>( sender() );
434 Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( picker->parent() );
450 for ( Qt3DRender::QGeometryRenderer *geomRenderer : entity->findChildren<Qt3DRender::QGeometryRenderer *>() )
455 if ( geomRenderer->objectName() != QLatin1String(
"main" ) )
460 fid = g->triangleIndexToFeatureId( triangleEvent->triangleIndex() );
464 pickHandler->handlePickOnVectorLayer( vlayer, fid, event->worldIntersection() );
469 void Qgs3DMapScene::onLayerRenderer3DChanged()
475 removeLayerEntity( layer );
478 addLayerEntity( layer );
481 void Qgs3DMapScene::onLayersChanged()
483 QSet<QgsMapLayer *> layersBefore = QSet<QgsMapLayer *>::fromList( mLayerEntities.keys() );
484 QList<QgsMapLayer *> layersAdded;
487 if ( !layersBefore.contains( layer ) )
489 layersAdded << layer;
493 layersBefore.remove( layer );
500 removeLayerEntity( layer );
505 addLayerEntity( layer );
509 void Qgs3DMapScene::addLayerEntity(
QgsMapLayer *layer )
524 Qt3DCore::QEntity *newEntity = renderer->
createEntity( mMap );
527 newEntity->setParent(
this );
528 mLayerEntities.insert( layer, newEntity );
530 if ( !mPickHandlers.isEmpty() )
532 Qt3DRender::QObjectPicker *picker =
new Qt3DRender::QObjectPicker( newEntity );
533 newEntity->addComponent( picker );
534 connect( picker, &Qt3DRender::QObjectPicker::pressed,
this, &Qgs3DMapScene::onLayerEntityPickEvent );
548 void Qgs3DMapScene::removeLayerEntity(
QgsMapLayer *layer )
550 Qt3DCore::QEntity *entity = mLayerEntities.take( layer );
552 entity->deleteLater();
563 void Qgs3DMapScene::addCameraViewCenterEntity( Qt3DRender::QCamera *camera )
565 mEntityCameraViewCenter =
new Qt3DCore::QEntity;
567 Qt3DCore::QTransform *trCameraViewCenter =
new Qt3DCore::QTransform;
568 mEntityCameraViewCenter->addComponent( trCameraViewCenter );
569 connect( camera, &Qt3DRender::QCamera::viewCenterChanged,
this, [trCameraViewCenter, camera]
571 trCameraViewCenter->setTranslation( camera->viewCenter() );
574 Qt3DExtras::QPhongMaterial *materialCameraViewCenter =
new Qt3DExtras::QPhongMaterial;
575 materialCameraViewCenter->setAmbient( Qt::red );
576 mEntityCameraViewCenter->addComponent( materialCameraViewCenter );
578 Qt3DExtras::QSphereMesh *rendererCameraViewCenter =
new Qt3DExtras::QSphereMesh;
579 rendererCameraViewCenter->setRadius( 10 );
580 mEntityCameraViewCenter->addComponent( rendererCameraViewCenter );
583 mEntityCameraViewCenter->setParent(
this );
593 if ( mSceneState == state )
599 void Qgs3DMapScene::updateSceneState()
601 if ( mTerrainUpdateScheduled )
607 for ( QgsChunkedEntity *entity : qgis::as_const( mChunkEntities ) )
609 if ( entity->isEnabled() && entity->pendingJobsCount() > 0 )
616 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.
int terrainPendingJobsCount() const
Returns number of pending jobs of the terrain entity.
QgsMapLayer::LayerType type() const
Returns the type of the layer.
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)
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.
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) ...
void terrainVerticalScaleChanged()
Emitted when the vertical scale of the terrain has changed.
QString skyboxFileBase() const
Returns base part of filenames of skybox (see setSkybox())
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) ...
void viewportChanged()
Emitted when viewport rectangle has been updated.
QColor backgroundColor() const
Returns background color of the 3D map view.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
bool hasSkyboxEnabled() const
Returns whether skybox is enabled.
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.
float maxTerrainGroundError() const
Returns maximum ground error of terrain tiles in world units.
void terrainGeneratorChanged()
Emitted when the terrain generator has changed.
3 Base class for 3D engine implementation.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
This signal is emitted when selection was changed.
int mapTileResolution() const
Returns resolution (in pixels) of the texture of a terrain tile.
3 Object that controls camera movement based on user input
The scene is fully loaded/updated.
bool showCameraViewCenter() const
Returns whether to show camera's view center as a sphere (for debugging)
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
3D renderer that renders all features of a vector layer with the same 3D symbol.
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)
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.
double width() const
Returns the width of the rectangle.
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.
QgsTerrainGenerator * terrainGenerator() const
Returns terrain generator. It takes care of producing terrain tiles from the input data...
QgsCameraController * cameraController()
Returns camera controller.
bool showTerrainBoundingBoxes() const
Returns whether to display bounding boxes of terrain tiles (for debugging)
double height() const
Returns the height of the rectangle.
virtual void setFrustumCullingEnabled(bool enabled)=0
Sets whether frustum culling is enabled (this should make rendering faster by not rendering entities ...
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.