Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *engine ) : mMap( map ) , mEngine( engine ) { connect( &map, &Qgs3DMapSettings::backgroundColorChanged, this, &Qgs3DMapScene::onBackgroundColorChanged ); onBackgroundColorChanged(); // TODO: strange - setting OnDemand render policy still keeps QGIS busy (Qt 5.9.0) // actually it is more busy than with the default "Always" policy although there are no changes in the scene. //mRenderer->renderSettings()->setRenderPolicy( Qt3DRender::QRenderSettings::OnDemand ); #if QT_VERSION >= 0x050900 // we want precise picking of terrain (also bounding volume picking does not seem to work - not sure why) mEngine->renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking ); #endif QRect viewportRect( QPoint( 0, 0 ), mEngine->size() ); // Camera float aspectRatio = ( float )viewportRect.width() / viewportRect.height(); mEngine->camera()->lens()->setPerspectiveProjection( mMap.fieldOfView(), aspectRatio, 10.f, 10000.0f ); mFrameAction = new Qt3DLogic::QFrameAction(); connect( mFrameAction, &Qt3DLogic::QFrameAction::triggered, this, &Qgs3DMapScene::onFrameTriggered ); addComponent( mFrameAction ); // takes ownership // Camera controlling mCameraController = new QgsCameraController( this ); // attaches to the scene mCameraController->setViewport( viewportRect ); mCameraController->setCamera( mEngine->camera() ); mCameraController->resetView( 1000 ); addCameraViewCenterEntity( mEngine->camera() ); // create terrain entity createTerrainDeferred(); connect( &map, &Qgs3DMapSettings::terrainGeneratorChanged, this, &Qgs3DMapScene::createTerrain ); connect( &map, &Qgs3DMapSettings::terrainVerticalScaleChanged, this, &Qgs3DMapScene::createTerrain ); connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain ); connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain ); connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain ); connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain ); connect( &map, &Qgs3DMapSettings::pointLightsChanged, this, &Qgs3DMapScene::updateLights ); connect( &map, &Qgs3DMapSettings::fieldOfViewChanged, this, &Qgs3DMapScene::updateCameraLens ); // create entities of renderers Q_FOREACH ( const QgsAbstract3DRenderer *renderer, map.renderers() ) { Qt3DCore::QEntity *newEntity = renderer->createEntity( map ); newEntity->setParent( this ); } // listen to changes of layers in order to add/remove 3D renderer entities connect( &map, &Qgs3DMapSettings::layersChanged, this, &Qgs3DMapScene::onLayersChanged ); updateLights(); #if 0 ChunkedEntity *testChunkEntity = new ChunkedEntity( AABB( -500, 0, -500, 500, 100, 500 ), 2.f, 3.f, 7, new TestChunkLoaderFactory ); testChunkEntity->setEnabled( false ); testChunkEntity->setParent( this ); chunkEntities << testChunkEntity; #endif connect( mCameraController, &QgsCameraController::cameraChanged, this, &Qgs3DMapScene::onCameraChanged ); connect( mCameraController, &QgsCameraController::viewportChanged, this, &Qgs3DMapScene::onCameraChanged ); #if 0 // experiments with loading of existing 3D models. // scene loader only gets loaded only when added to a scene... // it loads everything: geometries, materials, transforms, lights, cameras (if any) Qt3DCore::QEntity *loaderEntity = new Qt3DCore::QEntity; Qt3DRender::QSceneLoader *loader = new Qt3DRender::QSceneLoader; loader->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.dae" ) ); loaderEntity->addComponent( loader ); loaderEntity->setParent( this ); // mesh loads just geometry as one geometry... // so if there are different materials (e.g. colors) used in the model, this information is lost Qt3DCore::QEntity *meshEntity = new Qt3DCore::QEntity; Qt3DRender::QMesh *mesh = new Qt3DRender::QMesh; mesh->setSource( QUrl( "file:///home/martin/Downloads/LowPolyModels/tree.obj" ) ); meshEntity->addComponent( mesh ); Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial; material->setAmbient( Qt::red ); meshEntity->addComponent( material ); Qt3DCore::QTransform *meshTransform = new Qt3DCore::QTransform; meshTransform->setScale( 1 ); meshEntity->addComponent( meshTransform ); meshEntity->setParent( this ); #endif if ( map.hasSkyboxEnabled() ) { Qt3DExtras::QSkyboxEntity *skybox = new Qt3DExtras::QSkyboxEntity; skybox->setBaseName( map.skyboxFileBase() ); skybox->setExtension( map.skyboxFileExtension() ); skybox->setParent( this ); // docs say frustum culling must be disabled for skybox. // it _somehow_ works even when frustum culling is enabled with some camera positions, // but then when zoomed in more it would disappear - so let's keep frustum culling disabled mEngine->setFrustumCullingEnabled( false ); } // force initial update of chunked entities onCameraChanged(); }
void QgsPolygon3DSymbolEntity::addEntityForSelectedPolygons( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsPolygon3DSymbol &symbol ) { // build the default material Qt3DExtras::QPhongMaterial *mat = material( symbol ); // update the material with selection colors mat->setDiffuse( map.selectionColor() ); mat->setAmbient( map.selectionColor().darker() ); // build a transform function Qt3DCore::QTransform *tform = new Qt3DCore::QTransform; tform->setTranslation( QVector3D( 0, 0, 0 ) ); // build the feature request to select features QgsFeatureRequest req; req.setDestinationCrs( map.crs() ); req.setSubsetOfAttributes( _requiredAttributes( symbol, layer ), layer->fields() ); req.setFilterFids( layer->selectedFeatureIds() ); // build the entity QgsPolygon3DSymbolEntityNode *entity = new QgsPolygon3DSymbolEntityNode( map, layer, symbol, req ); entity->addComponent( mat ); entity->addComponent( tform ); entity->setParent( this ); }
void Qgs3DUtils::clampAltitudes( QgsLineString *lineString, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, const QgsPoint ¢roid, float height, const Qgs3DMapSettings &map ) { for ( int i = 0; i < lineString->nCoordinates(); ++i ) { float terrainZ = 0; if ( altClamp == Qgs3DTypes::AltClampRelative || altClamp == Qgs3DTypes::AltClampTerrain ) { QgsPointXY pt; if ( altBind == Qgs3DTypes::AltBindVertex ) { pt.setX( lineString->xAt( i ) ); pt.setY( lineString->yAt( i ) ); } else { pt.set( centroid.x(), centroid.y() ); } terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map ); } float geomZ = 0; if ( altClamp == Qgs3DTypes::AltClampAbsolute || altClamp == Qgs3DTypes::AltClampRelative ) geomZ = lineString->zAt( i ); float z = ( terrainZ + geomZ ) * map.terrainVerticalScale() + height; lineString->setZAt( i, z ); } }
float QgsTerrainGenerator::rootChunkError( const Qgs3DMapSettings &map ) const { QgsRectangle te = extent(); QgsCoordinateTransform terrainToMapTransform( crs(), map.crs() ); te = terrainToMapTransform.transformBoundingBox( te ); // use texel size as the error return te.width() / map.mapTileResolution(); }
QgsAABB QgsTerrainGenerator::rootChunkBbox( const Qgs3DMapSettings &map ) const { QgsRectangle te = extent(); QgsCoordinateTransform terrainToMapTransform( crs(), map.crs() ); te = terrainToMapTransform.transformBoundingBox( te ); float hMin, hMax; rootChunkHeightRange( hMin, hMax ); return QgsAABB( te.xMinimum() - map.originX(), hMin * map.terrainVerticalScale(), -te.yMaximum() + map.originY(), te.xMaximum() - map.originX(), hMax * map.terrainVerticalScale(), -te.yMinimum() + map.originY() ); }
float Qgs3DUtils::clampAltitude( const QgsPoint &p, Qgs3DTypes::AltitudeClamping altClamp, Qgs3DTypes::AltitudeBinding altBind, float height, const QgsPoint ¢roid, const Qgs3DMapSettings &map ) { float terrainZ = 0; if ( altClamp == Qgs3DTypes::AltClampRelative || altClamp == Qgs3DTypes::AltClampTerrain ) { QgsPointXY pt = altBind == Qgs3DTypes::AltBindVertex ? p : centroid; terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map ); } float geomZ = altClamp == Qgs3DTypes::AltClampAbsolute || altClamp == Qgs3DTypes::AltClampRelative ? p.z() : 0; float z = ( terrainZ + geomZ ) * map.terrainVerticalScale() + height; return z; }
void QgsLine3DSymbolEntity::addEntityForNotSelectedLines( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol ) { // build the default material Qt3DExtras::QPhongMaterial *mat = material( symbol ); // build the feature request to select features QgsFeatureRequest req; req.setDestinationCrs( map.crs(), map.transformContext() ); QgsFeatureIds notSelected = layer->allFeatureIds(); notSelected.subtract( layer->selectedFeatureIds() ); req.setFilterFids( notSelected ); // build the entity QgsLine3DSymbolEntityNode *entity = new QgsLine3DSymbolEntityNode( map, layer, symbol, req ); entity->addComponent( mat ); entity->setParent( this ); }
void QgsLine3DSymbolEntity::addEntityForSelectedLines( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsLine3DSymbol &symbol ) { // build the default material Qt3DExtras::QPhongMaterial *mat = material( symbol ); // update the material with selection colors mat->setDiffuse( map.selectionColor() ); mat->setAmbient( map.selectionColor().darker() ); // build the feature request to select features QgsFeatureRequest req; req.setDestinationCrs( map.crs(), map.transformContext() ); req.setFilterFids( layer->selectedFeatureIds() ); // build the entity QgsLine3DSymbolEntityNode *entity = new QgsLine3DSymbolEntityNode( map, layer, symbol, req ); entity->addComponent( mat ); entity->setParent( this ); }
QList<QVector3D> Qgs3DUtils::positions( const Qgs3DMapSettings &map, QgsVectorLayer *layer, const QgsFeatureRequest &request, Qgs3DTypes::AltitudeClamping altClamp ) { QList<QVector3D> positions; QgsFeature f; QgsFeatureIterator fi = layer->getFeatures( request ); while ( fi.nextFeature( f ) ) { if ( f.geometry().isNull() ) continue; const QgsAbstractGeometry *g = f.geometry().constGet(); for ( auto it = g->vertices_begin(); it != g->vertices_end(); ++it ) { QgsPoint pt = *it; float geomZ = 0; if ( pt.is3D() ) { geomZ = pt.z(); } float terrainZ = map.terrainGenerator()->heightAt( pt.x(), pt.y(), map ) * map.terrainVerticalScale(); float h; switch ( altClamp ) { case Qgs3DTypes::AltClampAbsolute: default: h = geomZ; break; case Qgs3DTypes::AltClampTerrain: h = terrainZ; break; case Qgs3DTypes::AltClampRelative: h = terrainZ + geomZ; break; } positions.append( QVector3D( pt.x() - map.origin().x(), h, -( pt.y() - map.origin().y() ) ) ); //qDebug() << positions.last(); } } return positions; }