17#include "moc_qgs3daxis.cpp" 
   19#include <Qt3DCore/QTransform> 
   20#include <Qt3DExtras/QCylinderMesh> 
   21#include <Qt3DExtras/QPhongMaterial> 
   22#include <Qt3DExtras/QConeMesh> 
   23#include <Qt3DRender/qcameralens.h> 
   24#include <Qt3DRender/QPointLight> 
   25#include <Qt3DRender/QSortPolicy> 
   28#include <QFontDatabase> 
   30#include <QApplication> 
   31#include <QActionGroup> 
   48  , mMapScene( mapScene )
 
   49  , mCameraController( cameraCtrl )
 
   54                                                           mCanvas, mCameraController, mMapSettings, 
 
   60  Q_ASSERT( mRenderView );
 
   61  constructAxisScene( parent3DScene );
 
   62  constructLabelsScene( parent3DScene );
 
   64  mTwoDLabelSceneEntity->addComponent( mRenderView->
labelLayer() );
 
   69  onAxisViewportSizeUpdate();
 
   71  init3DObjectPicking();
 
 
  103void Qgs3DAxis::init3DObjectPicking()
 
  111  mScreenRayCaster = 
new Qt3DRender::QScreenRayCaster( mAxisSceneEntity );
 
  112  mScreenRayCaster->addLayer( mRenderView->
objectLayer() ); 
 
  113  mScreenRayCaster->setFilterMode( Qt3DRender::QScreenRayCaster::AcceptAllMatchingLayers );
 
  114  mScreenRayCaster->setRunMode( Qt3DRender::QAbstractRayCaster::SingleShot );
 
  116  mAxisSceneEntity->addComponent( mScreenRayCaster );
 
  118  QObject::connect( mScreenRayCaster, &Qt3DRender::QScreenRayCaster::hitsChanged, 
this, &Qgs3DAxis::onTouchedByRay );
 
  124  if ( event->type() == QEvent::MouseButtonPress )
 
  128    QMouseEvent *mouseEvent = 
static_cast<QMouseEvent *
>( event );
 
  129    mLastClickedPos = mouseEvent->pos();
 
  133  else if ( event->type() == QEvent::MouseButtonRelease || 
event->type() == QEvent::MouseMove )
 
  135    QMouseEvent *mouseEvent = 
static_cast<QMouseEvent *
>( event );
 
  138    if ( event->type() == QEvent::MouseMove && ( ( mHasClicked && ( mouseEvent->pos() - mLastClickedPos ).manhattanLength() < QApplication::startDragDistance() ) || mIsDragging ) )
 
  144    else if ( mIsDragging && event->type() == QEvent::MouseButtonRelease )
 
  151    else if ( !mIsDragging )
 
  154      QPointF normalizedPos( 
static_cast<float>( mouseEvent->pos().x() ) / 
static_cast<float>( mCanvas->width() ), 
static_cast<float>( mouseEvent->pos().y() ) / 
static_cast<float>( mCanvas->height() ) );
 
  158        std::ostringstream os;
 
  159        os << 
"QGS3DAxis: normalized pos: " << normalizedPos << 
" / viewport: " << mRenderView->
viewport()->normalizedRect();
 
  163      if ( mRenderView->
viewport()->normalizedRect().contains( normalizedPos ) )
 
  165        mLastClickedButton = mouseEvent->button();
 
  166        mLastClickedPos = mouseEvent->pos();
 
  169        mScreenRayCaster->trigger( mLastClickedPos );
 
  175        if ( mPreviousCursor != Qt::ArrowCursor && mCanvas->cursor() == Qt::ArrowCursor )
 
  177          mCanvas->setCursor( mPreviousCursor );
 
  178          mPreviousCursor = Qt::ArrowCursor;
 
  182        if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() == Qt3DRender::QPickingSettings::TrianglePicking
 
  183             && mDefaultPickingMethod != Qt3DRender::QPickingSettings::TrianglePicking )
 
  185          mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( mDefaultPickingMethod );
 
 
  198void Qgs3DAxis::onTouchedByRay( 
const Qt3DRender::QAbstractRayCaster::Hits &hits )
 
  205      std::ostringstream os;
 
  206      os << 
"Qgs3DAxis::onTouchedByRay " << hits.length() << 
" hits at pos " << mLastClickedPos << 
" with QButton: " << mLastClickedButton;
 
  207      for ( 
int i = 0; i < hits.length(); ++i )
 
  210        os << 
"\tHit Type: " << hits.at( i ).type() << 
"\n";
 
  211        os << 
"\tHit triangle id: " << hits.at( i ).primitiveIndex() << 
"\n";
 
  212        os << 
"\tHit distance: " << hits.at( i ).distance() << 
"\n";
 
  213        os << 
"\tHit entity name: " << hits.at( i ).entity()->objectName().toStdString();
 
  218    for ( 
int i = 0; i < hits.length() && mHitsFound == -1; ++i )
 
  220      if ( hits.at( i ).distance() < 500.0f && hits.at( i ).entity() && ( hits.at( i ).entity() == mCubeRoot || hits.at( i ).entity() == mAxisRoot || hits.at( i ).entity()->parent() == mCubeRoot || hits.at( i ).entity()->parent() == mAxisRoot ) )
 
  227  if ( mLastClickedButton == Qt::NoButton ) 
 
  229    if ( mHitsFound != -1 )
 
  231      if ( mCanvas->cursor() != Qt::ArrowCursor )
 
  233        mPreviousCursor = mCanvas->cursor();
 
  234        mCanvas->setCursor( Qt::ArrowCursor );
 
  238        if ( mMapScene->
engine()->
renderSettings()->pickingSettings()->pickMethod() != Qt3DRender::QPickingSettings::TrianglePicking && mCubeRoot->isEnabled() )
 
  240          mMapScene->
engine()->
renderSettings()->pickingSettings()->setPickMethod( Qt3DRender::QPickingSettings::TrianglePicking );
 
  246  else if ( mLastClickedButton == Qt::MouseButton::RightButton && mHitsFound != -1 ) 
 
  248    displayMenuAt( mLastClickedPos );
 
  250  else if ( mLastClickedButton == Qt::MouseButton::LeftButton ) 
 
  254    if ( mHitsFound != -1 )
 
  256      if ( hits.at( mHitsFound ).entity() == mCubeRoot || hits.at( mHitsFound ).entity()->parent() == mCubeRoot )
 
  258        switch ( hits.at( mHitsFound ).primitiveIndex() / 2 )
 
  298void Qgs3DAxis::constructAxisScene( Qt3DCore::QEntity *parent3DScene )
 
  300  mAxisSceneEntity = 
new Qt3DCore::QEntity;
 
  301  mAxisSceneEntity->setParent( parent3DScene );
 
  302  mAxisSceneEntity->setObjectName( 
"3DAxis_SceneEntity" );
 
  305  mAxisCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
 
  306  mAxisCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
 
  310void Qgs3DAxis::constructLabelsScene( Qt3DCore::QEntity *parent3DScene )
 
  312  mTwoDLabelSceneEntity = 
new Qt3DCore::QEntity;
 
  313  mTwoDLabelSceneEntity->setParent( parent3DScene );
 
  314  mTwoDLabelSceneEntity->setEnabled( 
true );
 
  317  mTwoDLabelCamera->setUpVector( QVector3D( 0.0f, 0.0f, 1.0f ) );
 
  318  mTwoDLabelCamera->setViewCenter( QVector3D( 0.0f, 0.0f, 0.0f ) );
 
  319  mTwoDLabelCamera->setPosition( QVector3D( 0.0f, 0.0f, 100.0f ) );
 
  324  const int viewportWidth = 
static_cast<int>( std::round( mTwoDLabelCamera->lens()->right() - mTwoDLabelCamera->lens()->left() ) );
 
  325  const int viewportHeight = 
static_cast<int>( std::round( mTwoDLabelCamera->lens()->top() - mTwoDLabelCamera->lens()->bottom() ) );
 
  326  QRect viewportRect( 
static_cast<int>( std::round( mTwoDLabelCamera->lens()->left() ) ), 
static_cast<int>( std::round( mTwoDLabelCamera->lens()->bottom() ) ), viewportWidth, viewportHeight );
 
  328  QVector3D destPos = sourcePos.project( sourceCamera->viewMatrix(), destCamera->projectionMatrix(), viewportRect );
 
  329  destPos.setZ( 0.0f );
 
 
  333void Qgs3DAxis::setEnableCube( 
bool show )
 
  335  mCubeRoot->setEnabled( show );
 
  338    mCubeRoot->setParent( mAxisSceneEntity );
 
  342    mCubeRoot->setParent( 
static_cast<Qt3DCore::QEntity *
>( 
nullptr ) );
 
  346void Qgs3DAxis::setEnableAxis( 
bool show )
 
  348  mAxisRoot->setEnabled( show );
 
  351    mAxisRoot->setParent( mAxisSceneEntity );
 
  355    mAxisRoot->setParent( 
static_cast<Qt3DCore::QEntity *
>( 
nullptr ) );
 
  358  mTextX->setEnabled( show );
 
  359  mTextY->setEnabled( show );
 
  360  mTextZ->setEnabled( show );
 
  363void Qgs3DAxis::createAxisScene()
 
  365  if ( !mAxisRoot || !mCubeRoot )
 
  367    mAxisRoot = 
new Qt3DCore::QEntity;
 
  368    mAxisRoot->setParent( mAxisSceneEntity );
 
  369    mAxisRoot->setObjectName( 
"3DAxis_AxisRoot" );
 
  370    mAxisRoot->addComponent( mRenderView->
objectLayer() ); 
 
  372    createAxis( Qt::Axis::XAxis );
 
  373    createAxis( Qt::Axis::YAxis );
 
  374    createAxis( Qt::Axis::ZAxis );
 
  376    mCubeRoot = 
new Qt3DCore::QEntity;
 
  377    mCubeRoot->setParent( mAxisSceneEntity );
 
  378    mCubeRoot->setObjectName( 
"3DAxis_CubeRoot" );
 
  379    mCubeRoot->addComponent( mRenderView->
objectLayer() ); 
 
  388    mAxisSceneEntity->setEnabled( 
false );
 
  389    setEnableAxis( 
false );
 
  390    setEnableCube( 
false );
 
  396    mAxisSceneEntity->setEnabled( 
true );
 
  399      setEnableCube( 
false );
 
  400      setEnableAxis( 
true );
 
  402      const QList<Qgis::CrsAxisDirection> axisDirections = mCrs.
axisOrdering();
 
  404      if ( axisDirections.length() > 0 )
 
  407        mTextX->setText( 
"X?" );
 
  409      if ( axisDirections.length() > 1 )
 
  412        mTextY->setText( 
"Y?" );
 
  414      if ( axisDirections.length() > 2 )
 
  417        mTextZ->setText( QStringLiteral( 
"up" ) );
 
  421      setEnableCube( 
true );
 
  422      setEnableAxis( 
false );
 
  426      setEnableCube( 
false );
 
  427      setEnableAxis( 
true );
 
  428      mTextX->setText( 
"X?" );
 
  429      mTextY->setText( 
"Y?" );
 
  430      mTextZ->setText( 
"Z?" );
 
  433    updateAxisLabelPosition();
 
  437void Qgs3DAxis::createMenu()
 
  442  QAction *typeOffAct = 
new QAction( tr( 
"&Off" ), mMenu );
 
  443  typeOffAct->setCheckable( 
true );
 
  444  typeOffAct->setStatusTip( tr( 
"Disable 3D axis" ) );
 
  447      typeOffAct->setChecked( 
true );
 
  450  QAction *typeCrsAct = 
new QAction( tr( 
"Coordinate Reference &System" ), mMenu );
 
  451  typeCrsAct->setCheckable( 
true );
 
  452  typeCrsAct->setStatusTip( tr( 
"Coordinate Reference System 3D axis" ) );
 
  455      typeCrsAct->setChecked( 
true );
 
  458  QAction *typeCubeAct = 
new QAction( tr( 
"&Cube" ), mMenu );
 
  459  typeCubeAct->setCheckable( 
true );
 
  460  typeCubeAct->setStatusTip( tr( 
"Cube 3D axis" ) );
 
  463      typeCubeAct->setChecked( 
true );
 
  466  QActionGroup *typeGroup = 
new QActionGroup( mMenu );
 
  467  typeGroup->addAction( typeOffAct );
 
  468  typeGroup->addAction( typeCrsAct );
 
  469  typeGroup->addAction( typeCubeAct );
 
  475  QMenu *typeMenu = 
new QMenu( QStringLiteral( 
"Axis Type" ), mMenu );
 
  476  Q_ASSERT( typeMenu );
 
  477  typeMenu->addAction( typeOffAct );
 
  478  typeMenu->addAction( typeCrsAct );
 
  479  typeMenu->addAction( typeCubeAct );
 
  480  mMenu->addMenu( typeMenu );
 
  483  QAction *hPosLeftAct = 
new QAction( tr( 
"&Left" ), mMenu );
 
  484  hPosLeftAct->setCheckable( 
true );
 
  487      hPosLeftAct->setChecked( 
true );
 
  490  QAction *hPosMiddleAct = 
new QAction( tr( 
"&Center" ), mMenu );
 
  491  hPosMiddleAct->setCheckable( 
true );
 
  494      hPosMiddleAct->setChecked( 
true );
 
  497  QAction *hPosRightAct = 
new QAction( tr( 
"&Right" ), mMenu );
 
  498  hPosRightAct->setCheckable( 
true );
 
  501      hPosRightAct->setChecked( 
true );
 
  504  QActionGroup *hPosGroup = 
new QActionGroup( mMenu );
 
  505  hPosGroup->addAction( hPosLeftAct );
 
  506  hPosGroup->addAction( hPosMiddleAct );
 
  507  hPosGroup->addAction( hPosRightAct );
 
  509  connect( hPosLeftAct, &QAction::triggered, 
this, [
this]( 
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorLeft ); } );
 
  510  connect( hPosMiddleAct, &QAction::triggered, 
this, [
this]( 
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorHorizontalCenter ); } );
 
  511  connect( hPosRightAct, &QAction::triggered, 
this, [
this]( 
bool ) { mRenderView->
onHorizontalPositionChanged( Qt::AnchorPoint::AnchorRight ); } );
 
  513  QMenu *horizPosMenu = 
new QMenu( QStringLiteral( 
"Horizontal Position" ), mMenu );
 
  514  horizPosMenu->addAction( hPosLeftAct );
 
  515  horizPosMenu->addAction( hPosMiddleAct );
 
  516  horizPosMenu->addAction( hPosRightAct );
 
  517  mMenu->addMenu( horizPosMenu );
 
  520  QAction *vPosTopAct = 
new QAction( tr( 
"&Top" ), mMenu );
 
  521  vPosTopAct->setCheckable( 
true );
 
  524      vPosTopAct->setChecked( 
true );
 
  527  QAction *vPosMiddleAct = 
new QAction( tr( 
"&Middle" ), mMenu );
 
  528  vPosMiddleAct->setCheckable( 
true );
 
  531      vPosMiddleAct->setChecked( 
true );
 
  534  QAction *vPosBottomAct = 
new QAction( tr( 
"&Bottom" ), mMenu );
 
  535  vPosBottomAct->setCheckable( 
true );
 
  538      vPosBottomAct->setChecked( 
true );
 
  541  QActionGroup *vPosGroup = 
new QActionGroup( mMenu );
 
  542  vPosGroup->addAction( vPosTopAct );
 
  543  vPosGroup->addAction( vPosMiddleAct );
 
  544  vPosGroup->addAction( vPosBottomAct );
 
  546  connect( vPosTopAct, &QAction::triggered, 
this, [
this]( 
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorTop ); } );
 
  547  connect( vPosMiddleAct, &QAction::triggered, 
this, [
this]( 
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorVerticalCenter ); } );
 
  548  connect( vPosBottomAct, &QAction::triggered, 
this, [
this]( 
bool ) { mRenderView->
onVerticalPositionChanged( Qt::AnchorPoint::AnchorBottom ); } );
 
  550  QMenu *vertPosMenu = 
new QMenu( QStringLiteral( 
"Vertical Position" ), mMenu );
 
  551  vertPosMenu->addAction( vPosTopAct );
 
  552  vertPosMenu->addAction( vPosMiddleAct );
 
  553  vertPosMenu->addAction( vPosBottomAct );
 
  554  mMenu->addMenu( vertPosMenu );
 
  558  QAction *viewHomeAct = 
new QAction( tr( 
"&Home" ) + 
"\t Ctrl+5", mMenu );
 
  559  QAction *viewTopAct = 
new QAction( tr( 
"&Top" ) + 
"\t Ctrl+9", mMenu );
 
  560  QAction *viewNorthAct = 
new QAction( tr( 
"&North" ) + 
"\t Ctrl+8", mMenu );
 
  561  QAction *viewEastAct = 
new QAction( tr( 
"&East" ) + 
"\t Ctrl+6", mMenu );
 
  562  QAction *viewSouthAct = 
new QAction( tr( 
"&South" ) + 
"\t Ctrl+2", mMenu );
 
  563  QAction *viewWestAct = 
new QAction( tr( 
"&West" ) + 
"\t Ctrl+4", mMenu );
 
  564  QAction *viewBottomAct = 
new QAction( tr( 
"&Bottom" ) + 
"\t Ctrl+3", mMenu );
 
  574  QMenu *viewMenu = 
new QMenu( QStringLiteral( 
"Camera View" ), mMenu );
 
  575  viewMenu->addAction( viewHomeAct );
 
  576  viewMenu->addAction( viewTopAct );
 
  577  viewMenu->addAction( viewNorthAct );
 
  578  viewMenu->addAction( viewEastAct );
 
  579  viewMenu->addAction( viewSouthAct );
 
  580  viewMenu->addAction( viewWestAct );
 
  581  viewMenu->addAction( viewBottomAct );
 
  582  mMenu->addMenu( viewMenu );
 
  588void Qgs3DAxis::hideMenu()
 
  590  if ( mMenu && mMenu->isVisible() )
 
  594void Qgs3DAxis::displayMenuAt( 
const QPoint &sourcePos )
 
  601  mMenu->popup( mCanvas->mapToGlobal( sourcePos ) );
 
  611void Qgs3DAxis::createCube()
 
  613  QVector3D minPos = QVector3D( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f );
 
  616  Qt3DCore::QEntity *cubeLineEntity = 
new Qt3DCore::QEntity( mCubeRoot );
 
  617  cubeLineEntity->setObjectName( 
"3DAxis_cubeline" );
 
  618  Qgs3DWiredMesh *cubeLine = 
new Qgs3DWiredMesh;
 
  619  QgsAABB box = 
QgsAABB( -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, -mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f );
 
  621  cubeLineEntity->addComponent( cubeLine );
 
  623  Qt3DExtras::QPhongMaterial *cubeLineMaterial = 
new Qt3DExtras::QPhongMaterial;
 
  624  cubeLineMaterial->setAmbient( Qt::white );
 
  625  cubeLineEntity->addComponent( cubeLineMaterial );
 
  628  Qt3DExtras::QCuboidMesh *cubeMesh = 
new Qt3DExtras::QCuboidMesh;
 
  629  cubeMesh->setObjectName( 
"3DAxis_cubemesh" );
 
  630  cubeMesh->setXExtent( mCylinderLength );
 
  631  cubeMesh->setYExtent( mCylinderLength );
 
  632  cubeMesh->setZExtent( mCylinderLength );
 
  633  mCubeRoot->addComponent( cubeMesh );
 
  635  Qt3DExtras::QPhongMaterial *cubeMaterial = 
new Qt3DExtras::QPhongMaterial( mCubeRoot );
 
  636  cubeMaterial->setAmbient( QColor( 100, 100, 100, 50 ) );
 
  637  cubeMaterial->setShininess( 100 );
 
  638  mCubeRoot->addComponent( cubeMaterial );
 
  640  Qt3DCore::QTransform *cubeTransform = 
new Qt3DCore::QTransform;
 
  641  QMatrix4x4 transformMatrixcube;
 
  643  transformMatrixcube.translate( minPos + QVector3D( mCylinderLength * 0.5f, mCylinderLength * 0.5f, mCylinderLength * 0.5f ) );
 
  644  cubeTransform->setMatrix( transformMatrixcube );
 
  645  mCubeRoot->addComponent( cubeTransform );
 
  649  const int fontSize = 
static_cast<int>( std::round( 0.75f * 
static_cast<float>( mFontSize ) ) );
 
  650  const float textHeight = 
static_cast<float>( fontSize ) * 1.5f;
 
  652  const QFont font = createFont( fontSize );
 
  655    text = QStringLiteral( 
"top" );
 
  656    textWidth = 
static_cast<float>( text.length() * fontSize ) * 0.75f;
 
  657    QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f, mCylinderLength * 1.01f );
 
  659    mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
 
  663    text = QStringLiteral( 
"btm" );
 
  664    textWidth = 
static_cast<float>( text.length() * fontSize ) * 0.75f;
 
  665    QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f + textHeight / 2.0f, -mCylinderLength * 0.01f );
 
  667    rotation.rotate( 180.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
 
  668    mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
 
  672    text = QStringLiteral( 
"west" );
 
  673    textWidth = 
static_cast<float>( text.length() * fontSize ) * 0.75f;
 
  674    QVector3D translation = minPos + QVector3D( -mCylinderLength * 0.01f, mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
 
  676    rotation.rotate( 90.0f, QVector3D( 0.0f, -1.0f, 0.0f ).normalized() );
 
  677    rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, -1.0f ).normalized() );
 
  678    mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
 
  682    text = QStringLiteral( 
"east" );
 
  683    textWidth = 
static_cast<float>( text.length() * fontSize ) * 0.75f;
 
  684    QVector3D translation = minPos + QVector3D( mCylinderLength * 1.01f, mCylinderLength * 0.5f - textWidth / 2.0f, mCylinderLength * 0.5f - textHeight / 2.0f );
 
  686    rotation.rotate( 90.0f, QVector3D( 0.0f, 1.0f, 0.0f ).normalized() );
 
  687    rotation.rotate( 90.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
 
  688    mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
 
  692    text = QStringLiteral( 
"south" );
 
  693    textWidth = 
static_cast<float>( text.length() * fontSize ) * 0.75f;
 
  694    QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f - textWidth / 2.0f, -mCylinderLength * 0.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
 
  696    rotation.rotate( 90.0f, QVector3D( 1.0f, 0.0f, 0.0f ).normalized() );
 
  697    mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
 
  701    text = QStringLiteral( 
"north" );
 
  702    textWidth = 
static_cast<float>( text.length() * fontSize ) * 0.75f;
 
  703    QVector3D translation = minPos + QVector3D( mCylinderLength * 0.5f + textWidth / 2.0f, mCylinderLength * 1.01f, mCylinderLength * 0.5f - textHeight / 2.0f );
 
  705    rotation.rotate( 90.0f, QVector3D( -1.0f, 0.0f, 0.0f ).normalized() );
 
  706    rotation.rotate( 180.0f, QVector3D( 0.0f, 0.0f, 1.0f ).normalized() );
 
  707    mCubeLabels << addCubeText( text, textHeight, textWidth, font, rotation, translation );
 
  710  for ( Qt3DExtras::QText2DEntity *l : std::as_const( mCubeLabels ) )
 
  712    l->setParent( mCubeRoot );
 
  716Qt3DExtras::QText2DEntity *Qgs3DAxis::addCubeText( 
const QString &text, 
float textHeight, 
float textWidth, 
const QFont &font, 
const QMatrix4x4 &rotation, 
const QVector3D &translation )
 
  718  Qt3DExtras::QText2DEntity *textEntity = 
new Qt3DExtras::QText2DEntity;
 
  719  textEntity->setObjectName( 
"3DAxis_cube_label_" + text );
 
  720  textEntity->setFont( font );
 
  721  textEntity->setHeight( textHeight );
 
  722  textEntity->setWidth( textWidth );
 
  723  textEntity->setColor( QColor( 192, 192, 192 ) );
 
  724  textEntity->setText( text );
 
  726  Qt3DCore::QTransform *textFrontTransform = 
new Qt3DCore::QTransform();
 
  727  textFrontTransform->setMatrix( rotation );
 
  728  textFrontTransform->setTranslation( translation );
 
  729  textEntity->addComponent( textFrontTransform );
 
  734void Qgs3DAxis::createAxis( Qt::Axis axisType )
 
  736  float cylinderRadius = 0.05f * mCylinderLength;
 
  737  float coneLength = 0.3f * mCylinderLength;
 
  738  float coneBottomRadius = 0.1f * mCylinderLength;
 
  740  QQuaternion rotation;
 
  743  Qt3DExtras::QText2DEntity *text = 
nullptr;
 
  744  Qt3DCore::QTransform *textTransform = 
nullptr;
 
  749    case Qt::Axis::XAxis:
 
  750      mTextX = 
new Qt3DExtras::QText2DEntity();   
 
  751      mTextX->setParent( mTwoDLabelSceneEntity ); 
 
  752      connect( mTextX, &Qt3DExtras::QText2DEntity::textChanged, 
this, [
this]( 
const QString &text ) {
 
  753        updateAxisLabelText( mTextX, text );
 
  755      mTextTransformX = 
new Qt3DCore::QTransform();
 
  756      mTextCoordX = QVector3D( mCylinderLength + coneLength / 2.0f, 0.0f, 0.0f );
 
  758      rotation = QQuaternion::fromAxisAndAngle( QVector3D( 0.0f, 0.0f, 1.0f ), -90.0f );
 
  761      textTransform = mTextTransformX;
 
  762      name = 
"3DAxis_axisX";
 
  765    case Qt::Axis::YAxis:
 
  766      mTextY = 
new Qt3DExtras::QText2DEntity();   
 
  767      mTextY->setParent( mTwoDLabelSceneEntity ); 
 
  768      connect( mTextY, &Qt3DExtras::QText2DEntity::textChanged, 
this, [
this]( 
const QString &text ) {
 
  769        updateAxisLabelText( mTextY, text );
 
  771      mTextTransformY = 
new Qt3DCore::QTransform();
 
  772      mTextCoordY = QVector3D( 0.0f, mCylinderLength + coneLength / 2.0f, 0.0f );
 
  778      textTransform = mTextTransformY;
 
  779      name = 
"3DAxis_axisY";
 
  782    case Qt::Axis::ZAxis:
 
  783      mTextZ = 
new Qt3DExtras::QText2DEntity();   
 
  784      mTextZ->setParent( mTwoDLabelSceneEntity ); 
 
  785      connect( mTextZ, &Qt3DExtras::QText2DEntity::textChanged, 
this, [
this]( 
const QString &text ) {
 
  786        updateAxisLabelText( mTextZ, text );
 
  788      mTextTransformZ = 
new Qt3DCore::QTransform();
 
  789      mTextCoordZ = QVector3D( 0.0f, 0.0f, mCylinderLength + coneLength / 2.0f );
 
  791      rotation = QQuaternion::fromAxisAndAngle( QVector3D( 1.0f, 0.0f, 0.0f ), 90.0f );
 
  794      textTransform = mTextTransformZ;
 
  795      name = 
"3DAxis_axisZ";
 
  803  Qt3DCore::QEntity *cylinder = 
new Qt3DCore::QEntity( mAxisRoot );
 
  804  cylinder->setObjectName( name );
 
  806  Qt3DExtras::QCylinderMesh *cylinderMesh = 
new Qt3DExtras::QCylinderMesh;
 
  807  cylinderMesh->setRadius( cylinderRadius );
 
  808  cylinderMesh->setLength( mCylinderLength );
 
  809  cylinderMesh->setRings( 10 );
 
  810  cylinderMesh->setSlices( 4 );
 
  811  cylinder->addComponent( cylinderMesh );
 
  813  Qt3DExtras::QPhongMaterial *cylinderMaterial = 
new Qt3DExtras::QPhongMaterial( cylinder );
 
  814  cylinderMaterial->setAmbient( color );
 
  815  cylinderMaterial->setShininess( 0 );
 
  816  cylinder->addComponent( cylinderMaterial );
 
  818  Qt3DCore::QTransform *cylinderTransform = 
new Qt3DCore::QTransform;
 
  819  QMatrix4x4 transformMatrixCylinder;
 
  820  transformMatrixCylinder.rotate( rotation );
 
  821  transformMatrixCylinder.translate( QVector3D( 0.0f, mCylinderLength / 2.0f, 0.0f ) );
 
  822  cylinderTransform->setMatrix( transformMatrixCylinder );
 
  823  cylinder->addComponent( cylinderTransform );
 
  826  Qt3DCore::QEntity *coneEntity = 
new Qt3DCore::QEntity( mAxisRoot );
 
  827  coneEntity->setObjectName( name );
 
  828  Qt3DExtras::QConeMesh *coneMesh = 
new Qt3DExtras::QConeMesh;
 
  829  coneMesh->setLength( coneLength );
 
  830  coneMesh->setBottomRadius( coneBottomRadius );
 
  831  coneMesh->setTopRadius( 0.0f );
 
  832  coneMesh->setRings( 10 );
 
  833  coneMesh->setSlices( 4 );
 
  834  coneEntity->addComponent( coneMesh );
 
  836  Qt3DExtras::QPhongMaterial *coneMaterial = 
new Qt3DExtras::QPhongMaterial( coneEntity );
 
  837  coneMaterial->setAmbient( color );
 
  838  coneMaterial->setShininess( 0 );
 
  839  coneEntity->addComponent( coneMaterial );
 
  841  Qt3DCore::QTransform *coneTransform = 
new Qt3DCore::QTransform;
 
  842  QMatrix4x4 transformMatrixCone;
 
  843  transformMatrixCone.rotate( rotation );
 
  844  transformMatrixCone.translate( QVector3D( 0.0f, mCylinderLength, 0.0f ) );
 
  845  coneTransform->setMatrix( transformMatrixCone );
 
  846  coneEntity->addComponent( coneTransform );
 
  849  text->setColor( QColor( 192, 192, 192, 192 ) );
 
  850  text->addComponent( textTransform );
 
  856  onAxisViewportSizeUpdate();
 
 
  859void Qgs3DAxis::onAxisViewportSizeUpdate()
 
  868    updateAxisLabelPosition();
 
  875  if ( !mAxisRoot || !mCubeRoot )
 
  880  if ( scaleFactor > 0.0 )
 
  884      setEnableAxis( 
true );
 
  886      setEnableCube( 
true );
 
  888    mAxisScaleFactor = scaleFactor;
 
  889    QgsDebugMsgLevel( QString( 
"3DAxis viewport mAxisScaleFactor %1" ).arg( mAxisScaleFactor ), 2 );
 
  893    setEnableCube( 
false );
 
  894    setEnableAxis( 
false );
 
 
  898void Qgs3DAxis::onCameraUpdate()
 
  900  Qt3DRender::QCamera *parentCamera = mCameraController->
camera();
 
  902  if ( parentCamera->viewVector() != mPreviousVector
 
  903       && !std::isnan( parentCamera->viewVector().x() )
 
  904       && !std::isnan( parentCamera->viewVector().y() )
 
  905       && !std::isnan( parentCamera->viewVector().z() ) )
 
  907    mPreviousVector = parentCamera->viewVector();
 
  909    QQuaternion q = QQuaternion::fromDirection( -parentCamera->viewVector(), parentCamera->upVector() );
 
  910    mAxisCamera->setPosition( q.rotatedVector( QVector3D( 0, 0, mCylinderLength * 9.0f ) ) );
 
  911    mAxisCamera->setUpVector( q.rotatedVector( QVector3D( 0, 1, 0 ) ) );
 
  913    if ( mAxisRoot->isEnabled() )
 
  915      updateAxisLabelPosition();
 
  920void Qgs3DAxis::updateAxisLabelPosition()
 
  922  if ( mTextTransformX && mTextTransformY && mTextTransformZ )
 
  924    mTextTransformX->setTranslation( 
from3DTo2DLabelPosition( mTextCoordX * 
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
 
  925    updateAxisLabelText( mTextX, mTextX->text() );
 
  927    mTextTransformY->setTranslation( 
from3DTo2DLabelPosition( mTextCoordY * 
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
 
  928    updateAxisLabelText( mTextY, mTextY->text() );
 
  930    mTextTransformZ->setTranslation( 
from3DTo2DLabelPosition( mTextCoordZ * 
static_cast<float>( mAxisScaleFactor ), mAxisCamera, mTwoDLabelCamera ) );
 
  931    updateAxisLabelText( mTextZ, mTextZ->text() );
 
  935void Qgs3DAxis::updateAxisLabelText( Qt3DExtras::QText2DEntity *textEntity, 
const QString &text )
 
  937  const float scaledFontSize = 
static_cast<float>( mAxisScaleFactor ) * 
static_cast<float>( mFontSize );
 
  938  const QFont font = createFont( 
static_cast<int>( std::round( scaledFontSize ) ) );
 
  939  textEntity->setFont( font );
 
  940  textEntity->setWidth( scaledFontSize * 
static_cast<float>( text.length() ) );
 
  941  textEntity->setHeight( 1.5f * scaledFontSize );
 
  944QFont Qgs3DAxis::createFont( 
int pointSize )
 
  946  QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
 
  947  font.setPointSize( pointSize );
 
  948  font.setWeight( QFont::Weight::Black );
 
  949  font.setStyleStrategy( QFont::StyleStrategy::ForceOutline );
 
Qt3DRender::QLayer * labelLayer() const
Returns the layer to be used by entities to be included in the label renderpass.
 
void onVerticalPositionChanged(Qt::AnchorPoint position)
Updates viewport vertical position.
 
Qt3DRender::QCamera * labelCamera() const
Returns camera used for billboarded labels.
 
Qt3DRender::QLayer * objectLayer() const
Returns main object layer.
 
Qt3DRender::QViewport * viewport() const
Returns the viewport associated to this renderview.
 
void onViewportSizeUpdate(int width=-1, int height=-1)
Updates viewport size. Uses canvas size by default.
 
Qt3DRender::QCamera * objectCamera() const
Returns main object camera (used for axis or cube)
 
void onHorizontalPositionChanged(Qt::AnchorPoint position)
Updates viewport horizontal position.
 
Contains the configuration of a 3d axis.
 
void setMode(Qgs3DAxisSettings::Mode type)
Sets the type of the 3daxis.
 
Mode
Axis representation enum.
 
@ Crs
Respect CRS directions.
 
@ Cube
Abstract cube mode.
 
Qt::AnchorPoint verticalPosition() const
Returns the vertical position for the 3d axis.
 
Qgs3DAxisSettings::Mode mode() const
Returns the type of the 3daxis.
 
Qt::AnchorPoint horizontalPosition() const
Returns the horizontal position for the 3d axis.
 
bool handleEvent(QEvent *event)
Returns if the 3D axis controller will handle the specified event.
 
void onAxisSettingsChanged()
Force update of the axis and the viewport when a setting has changed.
 
void onViewportScaleFactorChanged(double scaleFactor)
Used as callback from renderview when viewport scale factor changes.
 
QVector3D from3DTo2DLabelPosition(const QVector3D &sourcePos, Qt3DRender::QCamera *sourceCamera, Qt3DRender::QCamera *destCamera)
Project a 3D position from sourceCamera to a 2D position for destCamera.
 
Qgs3DAxis(Qgs3DMapCanvas *canvas, Qt3DCore::QEntity *parent3DScene, Qgs3DMapScene *mapScene, QgsCameraController *camera, Qgs3DMapSettings *map)
Default Qgs3DAxis constructor.
 
Convenience wrapper to simplify the creation of a 3D window ready to be used with QGIS.
 
Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
 
QgsAbstract3DEngine * engine() const
Returns the abstract 3D engine.
 
Qgs3DAxisSettings get3DAxisSettings() const
Returns the current configuration of 3d axis.
 
void set3DAxisSettings(const Qgs3DAxisSettings &axisSettings, bool force=false)
Sets the current configuration of 3d axis.
 
void axisSettingsChanged()
Emitted when 3d axis rendering settings are changed.
 
Axis-aligned bounding box - in world coords.
 
QList< QVector3D > verticesForLines() const
Returns a list of pairs of vertices (useful for display of bounding boxes)
 
QgsFrameGraph * frameGraph()
Returns the shadow rendering frame graph object used to render the scene.
 
virtual Qt3DRender::QRenderSettings * renderSettings()=0
Returns access to the engine's render settings (the frame graph can be accessed from here)
 
virtual void setEnabled(bool enable)
Enable or disable via enable the render view sub tree.
 
Object that controls camera movement based on user input.
 
void rotateCameraToBottom()
Rotate to bottom-up view.
 
Qt3DRender::QCamera * camera() const
Returns camera that is being controlled.
 
void rotateCameraToTop()
Rotate to top-down view.
 
void rotateCameraToEast()
Rotate to view from the east.
 
void rotateCameraToHome()
Rotate to diagonal view.
 
void rotateCameraToNorth()
Rotate to view from the north.
 
void rotateCameraToWest()
Rotate to view from the west.
 
void cameraChanged()
Emitted when camera has been updated.
 
void rotateCameraToSouth()
Rotate to view from the south.
 
static QString axisDirectionToAbbreviatedString(Qgis::CrsAxisDirection axis)
Returns a translated abbreviation representing an axis direction.
 
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
 
bool registerRenderView(std::unique_ptr< QgsAbstractRenderView > renderView, const QString &name, Qt3DRender::QFrameGraphNode *topNode=nullptr)
Registers a new the render view renderView with name name.
 
QgsAbstractRenderView * renderView(const QString &name)
Returns the render view named name, if any.
 
static const QString AXIS3D_RENDERVIEW
 
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
 
#define QgsDebugMsgLevel(str, level)
 
const QgsCoordinateReferenceSystem & crs