Browse Source

Include QT

Johannes Kreutz 3 years ago
parent
commit
8a4bdb387b

+ 20 - 7
trackpoint-app/CMakeLists.txt

@@ -1,16 +1,18 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 3.19)
-PROJECT(TrackpointApp)
+PROJECT(TrackpointApp VERSION 0.0.1 LANGUAGES CXX)
 
 # Include modules
 INCLUDE(FetchContent)
 
 # Build TrackpointApp
 # Set C++ mode
-set(CMAKE_CXX_STANDARD 20)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
+SET(CMAKE_CXX_STANDARD 20)
+SET(CMAKE_CXX_STANDARD_REQUIRED True)
 
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
+SET(CMAKE_CXX_FLAGS_DEBUG "-O0")
+SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
+
+SET(CMAKE_AUTOMOC ON)
 
 # OpenSceneGraph
 INCLUDE(thirdparty/openscenegraph.cmake)
@@ -22,21 +24,32 @@ INCLUDE(thirdparty/lib3mf.cmake)
 INCLUDE(thirdparty/json.cmake)
 
 # Qt
-INCLUDE(thirdparty/qt.cmake)
+#INCLUDE(thirdparty/qt.cmake)
+find_package(Qt6 COMPONENTS Widgets OpenGLWidgets REQUIRED)
 
 # The executable we want to build
-ADD_EXECUTABLE(TrackpointApp src/main.cpp)
+ADD_EXECUTABLE(TrackpointApp
+  include/MainWindow.hpp
+  src/MainWindow.cpp
+  include/OSGWidget.hpp
+  src/OSGWidget.cpp
+  src/PickHandler.cpp
+  src/main.cpp
+)
 
 INCLUDE_DIRECTORIES(
   ${${OPENSCENEGRAPH_PREFIX}_SOURCE_DIR}/include
   ${${LIB3MF_PREFIX}_BINARY_DIR}/Autogenerated/Bindings/Cpp
   ${${JSON_PREFIX}_SOURCE_DIR}/include
+  include
 )
 
 TARGET_LINK_LIBRARIES(TrackpointApp
   osg osgViewer osgDB osgGA osgText osgUtil
   lib3mf
   nlohmann_json::nlohmann_json
+  Qt6::Widgets
+  Qt6::OpenGLWidgets
 )
 
 # Header to have version number available in the code

+ 22 - 0
trackpoint-app/include/MainWindow.hpp

@@ -0,0 +1,22 @@
+#ifndef MainWindow_h__
+#define MainWindow_h__
+
+#include <QMainWindow>
+#include <QMdiArea>
+
+class MainWindow : public QMainWindow
+{
+  Q_OBJECT
+
+public:
+  MainWindow( QWidget* parent = 0, Qt::WindowFlags flags = {} );
+  ~MainWindow();
+
+private slots:
+  void onCreateView();
+
+private:
+  QMdiArea* mdiArea_;
+};
+
+#endif

+ 72 - 0
trackpoint-app/include/OSGWidget.hpp

@@ -0,0 +1,72 @@
+#ifndef OSGWidget_h__
+#define OSGWidget_h__
+
+#include <QPoint>
+#include <QOpenGLWidget>
+
+#include <osg/ref_ptr>
+
+#include <osgViewer/GraphicsWindow>
+#include <osgViewer/CompositeViewer>
+
+namespace osgWidget
+{
+  //! The subclass of osgViewer::CompositeViewer we use
+  /*!
+   * This subclassing allows us to remove the annoying automatic
+   * setting of the CPU affinity to core 0 by osgViewer::ViewerBase,
+   * osgViewer::CompositeViewer's base class.
+   */
+  class Viewer : public osgViewer::CompositeViewer
+  {
+    public:
+	    virtual void setupThreading();
+  };
+}
+
+class OSGWidget : public QOpenGLWidget
+{
+  Q_OBJECT
+
+public:
+  OSGWidget( QWidget* parent = 0,
+             Qt::WindowFlags f = {} );
+
+  virtual ~OSGWidget();
+
+protected:
+
+  virtual void paintEvent( QPaintEvent* paintEvent );
+  virtual void paintGL();
+  virtual void resizeGL( int width, int height );
+
+  virtual void keyPressEvent( QKeyEvent* event );
+  virtual void keyReleaseEvent( QKeyEvent* event );
+
+  virtual void mouseMoveEvent( QMouseEvent* event );
+  virtual void mousePressEvent( QMouseEvent* event );
+  virtual void mouseReleaseEvent( QMouseEvent* event );
+  virtual void wheelEvent( QWheelEvent* event );
+
+  virtual bool event( QEvent* event );
+
+private:
+
+  virtual void onHome();
+  virtual void onResize( int width, int height );
+
+  osgGA::EventQueue* getEventQueue() const;
+
+  osg::ref_ptr<osgViewer::GraphicsWindowEmbedded> graphicsWindow_;
+  osg::ref_ptr<osgWidget::Viewer> viewer_;
+
+  QPoint selectionStart_;
+  QPoint selectionEnd_;
+
+  bool selectionActive_;
+  bool selectionFinished_;
+
+  void processSelection();
+};
+
+#endif

+ 19 - 0
trackpoint-app/include/PickHandler.hpp

@@ -0,0 +1,19 @@
+#ifndef PickHandler_h__
+#define PickHandler_h__
+
+#include <osgGA/GUIEventHandler>
+
+class PickHandler : public osgGA::GUIEventHandler
+{
+public:
+  PickHandler( double devicePixelRatio = 1.0 );
+  virtual ~PickHandler();
+
+  virtual bool handle( const osgGA::GUIEventAdapter&  ea,
+                             osgGA::GUIActionAdapter& aa );
+
+private:
+  double devicePixelRatio_;
+};
+
+#endif

+ 30 - 0
trackpoint-app/src/MainWindow.cpp

@@ -0,0 +1,30 @@
+#include "MainWindow.hpp"
+#include "OSGWidget.hpp"
+
+#include <QDebug>
+#include <QMdiSubWindow>
+#include <QMenuBar>
+
+MainWindow::MainWindow( QWidget* parent, Qt::WindowFlags flags )
+  : QMainWindow( parent, flags ),
+    mdiArea_( new QMdiArea( this ) )
+{
+  QMenuBar* menuBar = this->menuBar();
+
+  QMenu* menu = menuBar->addMenu( "Test" );
+  menu->addAction( "Create view", this, SLOT( onCreateView() ) );
+
+  this->setCentralWidget( mdiArea_ );
+}
+
+MainWindow::~MainWindow()
+{
+}
+
+void MainWindow::onCreateView()
+{
+  OSGWidget* osgWidget     = new OSGWidget( this );
+  QMdiSubWindow* subWindow = mdiArea_->addSubWindow( osgWidget );
+
+  subWindow->show();
+}

+ 496 - 0
trackpoint-app/src/OSGWidget.cpp

@@ -0,0 +1,496 @@
+#include "OSGWidget.hpp"
+#include "PickHandler.hpp"
+
+#include <osg/Camera>
+
+#include <osg/DisplaySettings>
+#include <osg/Geode>
+#include <osg/Material>
+#include <osg/Shape>
+#include <osg/ShapeDrawable>
+#include <osg/StateSet>
+
+#include <osgDB/WriteFile>
+
+#include <osgGA/EventQueue>
+#include <osgGA/TrackballManipulator>
+
+#include <osgUtil/IntersectionVisitor>
+#include <osgUtil/PolytopeIntersector>
+
+#include <osgViewer/View>
+#include <osgViewer/ViewerEventHandlers>
+
+#include <cassert>
+
+#include <stdexcept>
+#include <vector>
+
+#include <QDebug>
+#include <QKeyEvent>
+#include <QPainter>
+#include <QWheelEvent>
+
+namespace
+{
+
+#ifdef WITH_SELECTION_PROCESSING
+QRect makeRectangle( const QPoint& first, const QPoint& second )
+{
+  // Relative to the first point, the second point may be in either one of the
+  // four quadrants of an Euclidean coordinate system.
+  //
+  // We enumerate them in counter-clockwise order, starting from the lower-right
+  // quadrant that corresponds to the default case:
+  //
+  //            |
+  //       (3)  |  (4)
+  //            |
+  //     -------|-------
+  //            |
+  //       (2)  |  (1)
+  //            |
+
+  if( second.x() >= first.x() && second.y() >= first.y() )
+    return QRect( first, second );
+  else if( second.x() < first.x() && second.y() >= first.y() )
+    return QRect( QPoint( second.x(), first.y() ), QPoint( first.x(), second.y() ) );
+  else if( second.x() < first.x() && second.y() < first.y() )
+    return QRect( second, first );
+  else if( second.x() >= first.x() && second.y() < first.y() )
+    return QRect( QPoint( first.x(), second.y() ), QPoint( second.x(), first.y() ) );
+
+  // Should never reach that point...
+  return QRect();
+}
+#endif
+
+}
+
+namespace osgWidget
+{
+  void Viewer::setupThreading()
+  {
+    if( _threadingModel == SingleThreaded )
+    {
+      if(_threadsRunning)
+        stopThreading();
+    }
+    else
+    {
+      if(!_threadsRunning)
+        startThreading();
+    }
+  }
+}
+
+OSGWidget::OSGWidget( QWidget* parent,
+                      Qt::WindowFlags f )
+  : QOpenGLWidget( parent,
+                   f )
+  , graphicsWindow_( new osgViewer::GraphicsWindowEmbedded( this->x(),
+                                                            this->y(),
+                                                            this->width(),
+                                                            this->height() ) )
+  , viewer_( new osgWidget::Viewer )
+  , selectionActive_( false )
+  , selectionFinished_( true )
+{
+  osg::Sphere* sphere    = new osg::Sphere( osg::Vec3( 0.f, 0.f, 0.f ), 0.25f );
+  osg::ShapeDrawable* sd = new osg::ShapeDrawable( sphere );
+  sd->setColor( osg::Vec4( 1.f, 0.f, 0.f, 1.f ) );
+  sd->setName( "A nice sphere" );
+
+  osg::Geode* geode = new osg::Geode;
+  geode->addDrawable( sd );
+
+  // Set material for basic lighting and enable depth tests. Else, the sphere
+  // will suffer from rendering errors.
+  {
+    osg::StateSet* stateSet = geode->getOrCreateStateSet();
+    osg::Material* material = new osg::Material;
+
+    material->setColorMode( osg::Material::AMBIENT_AND_DIFFUSE );
+
+    stateSet->setAttributeAndModes( material, osg::StateAttribute::ON );
+    stateSet->setMode( GL_DEPTH_TEST, osg::StateAttribute::ON );
+  }
+
+  float aspectRatio = static_cast<float>( this->width() / 2 ) / static_cast<float>( this->height() );
+  auto pixelRatio   = this->devicePixelRatio();
+
+  osg::Camera* camera = new osg::Camera;
+  camera->setViewport( 0, 0, this->width() / 2 * pixelRatio, this->height() * pixelRatio );
+  camera->setClearColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
+  camera->setProjectionMatrixAsPerspective( 30.f, aspectRatio, 1.f, 1000.f );
+  camera->setGraphicsContext( graphicsWindow_ );
+
+  osgViewer::View* view = new osgViewer::View;
+  view->setCamera( camera );
+  view->setSceneData( geode );
+  view->addEventHandler( new osgViewer::StatsHandler );
+#ifdef WITH_PICK_HANDLER
+  view->addEventHandler( new PickHandler( this->devicePixelRatio() ) );
+#endif
+
+  osgGA::TrackballManipulator* manipulator = new osgGA::TrackballManipulator;
+  manipulator->setAllowThrow( false );
+
+  view->setCameraManipulator( manipulator );
+
+  osg::Camera* sideCamera = new osg::Camera;
+  sideCamera->setViewport( this->width() /2 * pixelRatio, 0,
+                           this->width() /2 * pixelRatio, this->height() * pixelRatio );
+
+  sideCamera->setClearColor( osg::Vec4( 0.f, 0.f, 1.f, 1.f ) );
+  sideCamera->setProjectionMatrixAsPerspective( 30.f, aspectRatio, 1.f, 1000.f );
+  sideCamera->setGraphicsContext( graphicsWindow_ );
+
+  osgViewer::View* sideView = new osgViewer::View;
+  sideView->setCamera( sideCamera );
+  sideView->setSceneData( geode );
+  sideView->addEventHandler( new osgViewer::StatsHandler );
+  sideView->setCameraManipulator( new osgGA::TrackballManipulator );
+
+  viewer_->addView( view );
+  viewer_->addView( sideView );
+  viewer_->setThreadingModel( osgViewer::CompositeViewer::SingleThreaded );
+  viewer_->realize();
+
+  // This ensures that the widget will receive keyboard events. This focus
+  // policy is not set by default. The default, Qt::NoFocus, will result in
+  // keyboard events that are ignored.
+  this->setFocusPolicy( Qt::StrongFocus );
+  this->setMinimumSize( 100, 100 );
+
+  // Ensures that the widget receives mouse move events even though no
+  // mouse button has been pressed. We require this in order to let the
+  // graphics window switch viewports properly.
+  this->setMouseTracking( true );
+}
+
+OSGWidget::~OSGWidget()
+{
+}
+
+void OSGWidget::paintEvent( QPaintEvent* /* paintEvent */ )
+{
+  this->makeCurrent();
+
+  QPainter painter( this );
+  painter.setRenderHint( QPainter::Antialiasing );
+
+  this->paintGL();
+
+#ifdef WITH_SELECTION_PROCESSING
+  if( selectionActive_ && !selectionFinished_ )
+  {
+    painter.setPen( Qt::black );
+    painter.setBrush( Qt::transparent );
+    painter.drawRect( makeRectangle( selectionStart_, selectionEnd_ ) );
+  }
+#endif
+
+  painter.end();
+
+  this->doneCurrent();
+}
+
+void OSGWidget::paintGL()
+{
+  viewer_->frame();
+}
+
+void OSGWidget::resizeGL( int width, int height )
+{
+  this->getEventQueue()->windowResize( this->x(), this->y(), width, height );
+  graphicsWindow_->resized( this->x(), this->y(), width, height );
+
+  this->onResize( width, height );
+}
+
+void OSGWidget::keyPressEvent( QKeyEvent* event )
+{
+  QString keyString   = event->text();
+  const char* keyData = keyString.toLocal8Bit().data();
+
+  if( event->key() == Qt::Key_S )
+  {
+#ifdef WITH_SELECTION_PROCESSING
+    selectionActive_ = !selectionActive_;
+#endif
+
+    // Further processing is required for the statistics handler here, so we do
+    // not return right away.
+  }
+  else if( event->key() == Qt::Key_D )
+  {
+    osgDB::writeNodeFile( *viewer_->getView(0)->getSceneData(),
+                          "/tmp/sceneGraph.osg" );
+
+    return;
+  }
+  else if( event->key() == Qt::Key_H )
+  {
+    this->onHome();
+    return;
+  }
+
+  this->getEventQueue()->keyPress( osgGA::GUIEventAdapter::KeySymbol( *keyData ) );
+}
+
+void OSGWidget::keyReleaseEvent( QKeyEvent* event )
+{
+  QString keyString   = event->text();
+  const char* keyData = keyString.toLocal8Bit().data();
+
+  this->getEventQueue()->keyRelease( osgGA::GUIEventAdapter::KeySymbol( *keyData ) );
+}
+
+void OSGWidget::mouseMoveEvent( QMouseEvent* event )
+{
+  // Note that we have to check the buttons mask in order to see whether the
+  // left button has been pressed. A call to `button()` will only result in
+  // `Qt::NoButton` for mouse move events.
+  if( selectionActive_ && event->buttons() & Qt::LeftButton )
+  {
+    selectionEnd_ = event->pos();
+
+    // Ensures that new paint events are created while the user moves the
+    // mouse.
+    this->update();
+  }
+  else
+  {
+    auto pixelRatio = this->devicePixelRatio();
+
+    this->getEventQueue()->mouseMotion( static_cast<float>( event->x() * pixelRatio ),
+                                        static_cast<float>( event->y() * pixelRatio ) );
+  }
+}
+
+void OSGWidget::mousePressEvent( QMouseEvent* event )
+{
+  // Selection processing
+  if( selectionActive_ && event->button() == Qt::LeftButton )
+  {
+    selectionStart_    = event->pos();
+    selectionEnd_      = selectionStart_; // Deletes the old selection
+    selectionFinished_ = false;           // As long as this is set, the rectangle will be drawn
+  }
+
+  // Normal processing
+  else
+  {
+    // 1 = left mouse button
+    // 2 = middle mouse button
+    // 3 = right mouse button
+
+    unsigned int button = 0;
+
+    switch( event->button() )
+    {
+    case Qt::LeftButton:
+      button = 1;
+      break;
+
+    case Qt::MiddleButton:
+      button = 2;
+      break;
+
+    case Qt::RightButton:
+      button = 3;
+      break;
+
+    default:
+      break;
+    }
+
+    auto pixelRatio = this->devicePixelRatio();
+
+    this->getEventQueue()->mouseButtonPress( static_cast<float>( event->x() * pixelRatio ),
+                                             static_cast<float>( event->y() * pixelRatio ),
+                                             button );
+    }
+}
+
+void OSGWidget::mouseReleaseEvent(QMouseEvent* event)
+{
+  // Selection processing: Store end position and obtain selected objects
+  // through polytope intersection.
+  if( selectionActive_ && event->button() == Qt::LeftButton )
+  {
+    selectionEnd_      = event->pos();
+    selectionFinished_ = true; // Will force the painter to stop drawing the
+                               // selection rectangle
+
+    this->processSelection();
+  }
+
+  // Normal processing
+  else
+  {
+    // 1 = left mouse button
+    // 2 = middle mouse button
+    // 3 = right mouse button
+
+    unsigned int button = 0;
+
+    switch( event->button() )
+    {
+    case Qt::LeftButton:
+      button = 1;
+      break;
+
+    case Qt::MiddleButton:
+      button = 2;
+      break;
+
+    case Qt::RightButton:
+      button = 3;
+      break;
+
+    default:
+      break;
+    }
+
+    auto pixelRatio = this->devicePixelRatio();
+
+    this->getEventQueue()->mouseButtonRelease( static_cast<float>( pixelRatio * event->x() ),
+                                               static_cast<float>( pixelRatio * event->y() ),
+                                               button );
+  }
+}
+
+void OSGWidget::wheelEvent( QWheelEvent* event )
+{
+  // Ignore wheel events as long as the selection is active.
+  if( selectionActive_ )
+    return;
+
+  event->accept();
+  int delta = event->angleDelta().y();
+
+  osgGA::GUIEventAdapter::ScrollingMotion motion = delta > 0 ?   osgGA::GUIEventAdapter::SCROLL_UP
+                                                               : osgGA::GUIEventAdapter::SCROLL_DOWN;
+
+  this->getEventQueue()->mouseScroll( motion );
+}
+
+bool OSGWidget::event( QEvent* event )
+{
+  bool handled = QOpenGLWidget::event( event );
+
+  // This ensures that the OSG widget is always going to be repainted after the
+  // user performed some interaction. Doing this in the event handler ensures
+  // that we don't forget about some event and prevents duplicate code.
+  switch( event->type() )
+  {
+  case QEvent::KeyPress:
+  case QEvent::KeyRelease:
+  case QEvent::MouseButtonDblClick:
+  case QEvent::MouseButtonPress:
+  case QEvent::MouseButtonRelease:
+  case QEvent::MouseMove:
+  case QEvent::Wheel:
+    this->update();
+    break;
+
+  default:
+    break;
+  }
+
+  return handled;
+}
+
+void OSGWidget::onHome()
+{
+  osgViewer::ViewerBase::Views views;
+  viewer_->getViews( views );
+
+  for( std::size_t i = 0; i < views.size(); i++ )
+  {
+    osgViewer::View* view = views.at(i);
+    view->home();
+  }
+}
+
+void OSGWidget::onResize( int width, int height )
+{
+  std::vector<osg::Camera*> cameras;
+  viewer_->getCameras( cameras );
+
+  assert( cameras.size() == 2 );
+
+  auto pixelRatio = this->devicePixelRatio();
+
+  cameras[0]->setViewport( 0, 0, width / 2 * pixelRatio, height * pixelRatio );
+  cameras[1]->setViewport( width / 2 * pixelRatio, 0, width / 2 * pixelRatio, height * pixelRatio );
+}
+
+osgGA::EventQueue* OSGWidget::getEventQueue() const
+{
+  osgGA::EventQueue* eventQueue = graphicsWindow_->getEventQueue();
+
+  if( eventQueue )
+    return eventQueue;
+  else
+    throw std::runtime_error( "Unable to obtain valid event queue");
+}
+
+void OSGWidget::processSelection()
+{
+#ifdef WITH_SELECTION_PROCESSING
+  QRect selectionRectangle = makeRectangle( selectionStart_, selectionEnd_ );
+  auto widgetHeight        = this->height();
+  auto pixelRatio          = this->devicePixelRatio();
+
+  double xMin = selectionRectangle.left();
+  double xMax = selectionRectangle.right();
+  double yMin = widgetHeight - selectionRectangle.bottom();
+  double yMax = widgetHeight - selectionRectangle.top();
+
+  xMin *= pixelRatio;
+  yMin *= pixelRatio;
+  xMax *= pixelRatio;
+  yMax *= pixelRatio;
+
+  osgUtil::PolytopeIntersector* polytopeIntersector
+      = new osgUtil::PolytopeIntersector( osgUtil::PolytopeIntersector::WINDOW,
+                                          xMin, yMin,
+                                          xMax, yMax );
+
+  // This limits the amount of intersections that are reported by the
+  // polytope intersector. Using this setting, a single drawable will
+  // appear at most once while calculating intersections. This is the
+  // preferred and expected behaviour.
+  polytopeIntersector->setIntersectionLimit( osgUtil::Intersector::LIMIT_ONE_PER_DRAWABLE );
+
+  osgUtil::IntersectionVisitor iv( polytopeIntersector );
+
+  for( unsigned int viewIndex = 0; viewIndex < viewer_->getNumViews(); viewIndex++ )
+  {
+    qDebug() << "View index:" << viewIndex;
+
+    osgViewer::View* view = viewer_->getView( viewIndex );
+
+    if( !view )
+      throw std::runtime_error( "Unable to obtain valid view for selection processing" );
+
+    osg::Camera* camera = view->getCamera();
+
+    if( !camera )
+      throw std::runtime_error( "Unable to obtain valid camera for selection processing" );
+
+    camera->accept( iv );
+
+    if( !polytopeIntersector->containsIntersections() )
+      continue;
+
+    auto intersections = polytopeIntersector->getIntersections();
+
+    for( auto&& intersection : intersections )
+      qDebug() << "Selected a drawable:" << QString::fromStdString( intersection.drawable->getName() );
+  }
+#endif
+}

+ 56 - 0
trackpoint-app/src/PickHandler.cpp

@@ -0,0 +1,56 @@
+#include "PickHandler.hpp"
+
+#include <osg/io_utils>
+
+#include <osgUtil/IntersectionVisitor>
+#include <osgUtil/LineSegmentIntersector>
+
+#include <osgViewer/Viewer>
+
+#include <iostream>
+
+PickHandler::PickHandler( double devicePixelRatio )
+  : devicePixelRatio_( devicePixelRatio )
+{
+}
+
+PickHandler::~PickHandler()
+{
+}
+
+bool PickHandler::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
+{
+  if( ea.getEventType() != osgGA::GUIEventAdapter::RELEASE &&
+      ea.getButton()    != osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON )
+  {
+    return false;
+  }
+
+  osgViewer::View* viewer = dynamic_cast<osgViewer::View*>( &aa );
+
+  if( viewer )
+  {
+    osgUtil::LineSegmentIntersector* intersector
+        = new osgUtil::LineSegmentIntersector( osgUtil::Intersector::WINDOW, ea.getX() * devicePixelRatio_, ea.getY() * devicePixelRatio_ );
+
+    osgUtil::IntersectionVisitor iv( intersector );
+
+    osg::Camera* camera = viewer->getCamera();
+    if( !camera )
+      return false;
+
+    camera->accept( iv );
+
+    if( !intersector->containsIntersections() )
+      return false;
+
+    auto intersections = intersector->getIntersections();
+
+    std::cout << "Got " << intersections.size() << " intersections:\n";
+
+    for( auto&& intersection : intersections )
+      std::cout << "  - Local intersection point = " << intersection.localIntersectionPoint << "\n";
+  }
+
+  return true;
+}

+ 24 - 6
trackpoint-app/src/main.cpp

@@ -1,4 +1,4 @@
-#include <osgViewer/Viewer>
+/*#include <osgViewer/Viewer>
 #include <osg/ShapeDrawable>
 #include <osg/Geode>
 #include <osgDB/ReadFile>
@@ -16,9 +16,14 @@
 #include <string>
 #include <math.h>
 #include "lib3mf_implicit.hpp"
-#include <nlohmann/json.hpp>
+#include <nlohmann/json.hpp>*/
 
-using json = nlohmann::json;
+#include <QApplication>
+#include <QSurfaceFormat>
+
+#include "MainWindow.hpp"
+
+/*using json = nlohmann::json;
 
 const char* openScadBase =
   "$fn = 100;\n"
@@ -306,10 +311,10 @@ void PickHandler::rotateToNormalVector(osg::Vec3f normal) {
 
 void PickHandler::setVisibility(bool mode) {
   _selectionSwitch->setValue(0, mode);
-}
+}*/
 
 int main(int argc, char** argv) {
-    // Root node of the scene
+    /*// Root node of the scene
     osg::ref_ptr<osg::Group> root = new osg::Group;
 
     // Create the viewer
@@ -338,5 +343,18 @@ int main(int argc, char** argv) {
 
     viewer.addEventHandler(picker.get());
 
-    return viewer.run();
+    return viewer.run();*/
+
+    QApplication application( argc, argv );
+
+    QSurfaceFormat format;
+    format.setVersion(2, 1);
+    format.setProfile( QSurfaceFormat::CompatibilityProfile );
+
+    QSurfaceFormat::setDefaultFormat(format);
+
+    MainWindow mainWindow;
+    mainWindow.show();
+
+    return( application.exec() );
 }

+ 18 - 0
trackpoint-app/thirdparty/qt.cmake

@@ -1 +1,19 @@
 # Build Qt
+
+SET(QT_PREFIX qt6)
+
+SET(QT_URL https://download.qt.io/official_releases/qt/6.1/6.1.0/single/qt-everywhere-src-6.1.0.zip)
+
+# Add Qt
+FETCHCONTENT_DECLARE(
+  ${QT_PREFIX}
+  PREFIX ${QT_PREFIX}
+  URL ${QT_URL}
+)
+
+FETCHCONTENT_GETPROPERTIES(${QT_PREFIX})
+
+IF(NOT ${QT_PREFIX}_POPULATED)
+  FETCHCONTENT_POPULATE(${QT_PREFIX})
+  ADD_SUBDIRECTORY(${${QT_PREFIX}_SOURCE_DIR} ${${QT_PREFIX}_BINARY_DIR})
+ENDIF()