Browse Source

Rendering in thread (Closes #7)

Johannes Kreutz 2 years ago
parent
commit
a1ea016cb5

+ 1 - 0
trackpoint-app/CMakeLists.txt

@@ -64,6 +64,7 @@ QT_ADD_EXECUTABLE(TrackpointApp
   include/OSGWidget.hpp
   src/OSGWidget.cpp
   src/ProjectStore.cpp
+  include/ProjectStore.hpp
   src/PickHandler.cpp
   src/TrackPoint.cpp
   src/OptiTrackPoint.cpp

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

@@ -39,6 +39,7 @@ private:
   void close();
   void cleanup();
   bool saveChangesPopup();
+  bool isRenderingPopup();
   Ui::MainWindow* ui;
   OSGWidget* osgWidget;
   NoMeshWidget* noMeshWidget;

+ 21 - 2
trackpoint-app/include/ProjectStore.hpp

@@ -13,13 +13,16 @@
 // Include dependencies
 #include <string>
 #include <nlohmann/json.hpp>
+#include <QThread>
 
 using json = nlohmann::json;
 
-class ProjectStore {
+class ProjectStore: public QObject {
+  Q_OBJECT
+
 public:
   // Create an empty project
-  ProjectStore();
+  ProjectStore(QObject* parent = nullptr);
   // Destructor
   ~ProjectStore();
   // Load a mesh
@@ -38,6 +41,8 @@ public:
   void closeProject();
   // Is current project modified
   bool isModified();
+  // Is a rendering in progress
+  bool isRendering();
   // Set project modification status
   void projectModified();
   // UNIVERSAL
@@ -93,6 +98,9 @@ public:
   // Check if an identifier is already in use
   unsigned int actionPointIdentifierInUse(std::string candidate, int current);
 
+private slots:
+  void renderThreadDone();
+
 private:
   bool _projectLoaded;
   bool _projectModified;
@@ -110,11 +118,22 @@ private:
   osg::Vec3 _normalModifier = osg::Vec3(0.0f, 0.0f, 0.0f);
   float _normalRotation = 0.0f;
   bool _compensation = true;
+  QThread* _optiTrackRenderThread;
+  QThread* _emfTrackRenderThread;
+  QThread* _steamVrTrackRenderThread;
+  bool _exportRunning = false;
+  ExportSettings _currentExportSettings;
+  int _wantedExports;
+  int _doneExports;
+  std::string _exportPath;
   void load3mfLib();
   void reset();
   void render3MFMesh();
   void updateMetaData();
   void loadMetaData();
+  static void renderOptiTrackInThread(std::vector<OptiTrackPoint*> optiTrackPoints);
+  static void renderEMFTrackInThread(std::vector<EMFTrackPoint*> emfTrackPoints);
+  static void renderSteamVRTrackInThread(std::vector<SteamVRTrackPoint*> steamVrTrackPoints);
   std::vector<float> osgVecToStdVec(osg::Vec3f input);
   osg::Vec3f stdVecToOsgVec(std::vector<float> input);
 };

+ 1 - 1
trackpoint-app/src/EditWidget.cpp

@@ -193,7 +193,7 @@ void EditWidget::setExportAvailable(bool available) {
 void EditWidget::setExportStatus(int jobs, int done) {
   ui->exportProgress->setValue(done);
   std::stringstream text;
-  text << "Export running: " << ++done << " of " << jobs << ".";
+  text << "Export running: " << done << " of " << jobs << " finished.";
   ui->exportLabel->setText(QString::fromUtf8(text.str().c_str()));
 }
 

+ 37 - 8
trackpoint-app/src/MainWindow.cpp

@@ -107,22 +107,31 @@ void MainWindow::showErrorMessage(std::string message, std::string title) {
 }
 
 void MainWindow::closeEvent(QCloseEvent *event) {
-  if (MainWindow::getInstance()->getStore()->isModified()) {
+  if (projectStore->isModified()) {
     if (!saveChangesPopup()) {
       event->ignore();
       return;
     }
   }
-  MainWindow::getInstance()->getStore()->closeProject();
+  if (projectStore->isRendering()) {
+    if (!isRenderingPopup()) {
+      event->ignore();
+      return;
+    }
+  }
+  projectStore->closeProject();
   event->accept();
 }
 
 void MainWindow::newFile() {
-  if (MainWindow::getInstance()->getStore()->isProjectOpen()) {
-    if (MainWindow::getInstance()->getStore()->isModified()) {
+  if (projectStore->isProjectOpen()) {
+    if (projectStore->isModified()) {
       if (!saveChangesPopup()) return;
     }
-    MainWindow::getInstance()->getStore()->closeProject();
+    if (projectStore->isRendering()) {
+      if (!isRenderingPopup()) return;
+    }
+    projectStore->closeProject();
   }
   renderView(NoMesh);
   cleanup();
@@ -132,7 +141,7 @@ void MainWindow::newFile() {
 void MainWindow::load() {
   QString fileName = QFileDialog::getOpenFileName(this, tr("Open a TrackpointApp Project"), "", tr("TrackpointApp Projects (*.trackproj)"));
   std::string projectFile = fileName.toUtf8().constData();
-  MainWindow::getInstance()->getStore()->loadProject(projectFile);
+  projectStore->loadProject(projectFile);
 }
 
 bool MainWindow::save() {
@@ -153,10 +162,13 @@ bool MainWindow::saveAs() {
 }
 
 void MainWindow::close() {
-  if (MainWindow::getInstance()->getStore()->isModified()) {
+  if (projectStore->isModified()) {
     if (!saveChangesPopup()) return;
   }
-  MainWindow::getInstance()->getStore()->closeProject();
+  if (projectStore->isRendering()) {
+    if (!isRenderingPopup()) return;
+  }
+  projectStore->closeProject();
   renderView(NoMesh);
   cleanup();
 }
@@ -185,3 +197,20 @@ bool MainWindow::saveChangesPopup() {
       return false;
   }
 }
+
+bool MainWindow::isRenderingPopup() {
+  QMessageBox msgBox;
+  msgBox.setText("A rendering is in progress.");
+  msgBox.setInformativeText("Do you really want to cancel the rendering?");
+  msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+  msgBox.setDefaultButton(QMessageBox::No);
+  int ret = msgBox.exec();
+  switch(ret) {
+    case QMessageBox::Yes:
+      return true;
+    case QMessageBox::No:
+      return false;
+    default:
+      return false;
+  }
+}

+ 172 - 114
trackpoint-app/src/ProjectStore.cpp

@@ -16,7 +16,7 @@
 
 #define META_NAMESPACE "tk-ar-tracking"
 
-ProjectStore::ProjectStore() {
+ProjectStore::ProjectStore(QObject* parent): QObject() {
   _projectLoaded = false;
   _projectModified = false;
   load3mfLib();
@@ -92,128 +92,38 @@ bool ProjectStore::exportProject(std::string path, ExportSettings settings) {
   if (path == "") {
     return false;
   }
-  OpenScadRenderer* renderer = new OpenScadRenderer();
+  MainWindow::getInstance()->getEditWiget()->setExportAvailable(false);
+  _exportPath = path;
   // Base for rendering mesh
   Lib3MF::PWriter writer = _project->QueryWriter("3mf");
   writer->WriteToFile(std::filesystem::temp_directory_path().u8string() + fileDelimiter + "trackpointapp_export.3mf");
-  // Export file
-  Lib3MF::PModel exportModel = _wrapper->CreateModel();
-  Lib3MF::PMetaDataGroup metaData = exportModel->GetMetaDataGroup();
+  // Store export settings
+  _wantedExports = 0;
+  _doneExports = 0;
+  _currentExportSettings = settings;
+  if (settings.OptiTrack) _wantedExports++;
+  if (settings.EMFTrack) _wantedExports++;
+  if (settings.SteamVRTrack) _wantedExports++;
+  MainWindow::getInstance()->getEditWiget()->setExportStatus(_wantedExports, _doneExports);
   if (settings.OptiTrack) {
-    renderer->renderOptiTrack(_optiTrackPoints);
-    Lib3MF::PModel optiTrackModel = _wrapper->CreateModel();
-    Lib3MF::PReader reader = optiTrackModel->QueryReader("3mf");
-    reader->ReadFromFile(std::filesystem::temp_directory_path().u8string() + fileDelimiter + "trackpointapp_render_optitrack.3mf");
-    Lib3MF::PMeshObjectIterator meshIterator = optiTrackModel->GetMeshObjects();
-    if (meshIterator->Count() != 1) {
-      return false;
-    }
-    meshIterator->MoveNext();
-    Lib3MF::PMeshObject renderedMesh = meshIterator->GetCurrentMeshObject();
-    Lib3MF::PMeshObject exportMesh = exportModel->AddMeshObject();
-    exportMesh->SetName("optitrack");
-    std::vector<Lib3MF::sPosition> verticesBuffer;
-    std::vector<Lib3MF::sTriangle> triangleBuffer;
-    renderedMesh->GetVertices(verticesBuffer);
-    renderedMesh->GetTriangleIndices(triangleBuffer);
-    exportMesh->SetGeometry(verticesBuffer, triangleBuffer);
-    json trackpointData = json::array();
-    for (OptiTrackPoint* point: _optiTrackPoints) {
-      osg::Vec3 trackPoint = point->getTrackPoint();
-      osg::Vec3 trackNormal = point->getNormal();
-      json pointData = {
-        {"point", {trackPoint.x(), trackPoint.y(), trackPoint.z()}},
-        {"normal", {trackNormal.x(), trackNormal.y(), trackNormal.z()}}
-      };
-      trackpointData.push_back(pointData);
-    }
-
-    Lib3MF::PMetaDataGroup optiMetaData = exportMesh->GetMetaDataGroup();
-    optiMetaData->AddMetaData(META_NAMESPACE, "trackpoints-optitrack", trackpointData.dump(), "string", true);
-    exportModel->AddBuildItem(exportMesh.get(), _wrapper->GetIdentityTransform());
+    _optiTrackRenderThread = QThread::create(renderOptiTrackInThread, _optiTrackPoints);
+    _optiTrackRenderThread->setObjectName("TrackpointApp OptiTrack Renderer");
+    QObject::connect(_optiTrackRenderThread, &QThread::finished, this, &ProjectStore::renderThreadDone);
+    _optiTrackRenderThread->start();
   }
   if (settings.EMFTrack) {
-    renderer->renderEMFTrack(_emfTrackPoints);
-    Lib3MF::PModel emfTrackModel = _wrapper->CreateModel();
-    Lib3MF::PReader reader = emfTrackModel->QueryReader("3mf");
-    reader->ReadFromFile(std::filesystem::temp_directory_path().u8string() + fileDelimiter + "trackpointapp_render_emftrack.3mf");
-    Lib3MF::PMeshObjectIterator meshIterator = emfTrackModel->GetMeshObjects();
-    if (meshIterator->Count() != 1) {
-      return false;
-    }
-    meshIterator->MoveNext();
-    Lib3MF::PMeshObject renderedMesh = meshIterator->GetCurrentMeshObject();
-    Lib3MF::PMeshObject exportMesh = exportModel->AddMeshObject();
-    exportMesh->SetName("emftrack");
-    std::vector<Lib3MF::sPosition> verticesBuffer;
-    std::vector<Lib3MF::sTriangle> triangleBuffer;
-    renderedMesh->GetVertices(verticesBuffer);
-    renderedMesh->GetTriangleIndices(triangleBuffer);
-    exportMesh->SetGeometry(verticesBuffer, triangleBuffer);
-    json trackpointData = json::array();
-    for (EMFTrackPoint* point: _emfTrackPoints) {
-      osg::Vec3 moveToMid = point->getNormal().operator*(-(point->getDepth() / 2));
-      osg::Vec3 trackPoint = point->getTrackPoint().operator+(moveToMid);
-      osg::Vec3 trackNormal = point->getNormal();
-      json pointData = {
-        {"point", {trackPoint.x(), trackPoint.y(), trackPoint.z()}},
-        {"normal", {trackNormal.x(), trackNormal.y(), trackNormal.z()}},
-        {"rotation", point->getNormalRotation()}
-      };
-      trackpointData.push_back(pointData);
-    }
-
-    Lib3MF::PMetaDataGroup emfMetaData = exportMesh->GetMetaDataGroup();
-    emfMetaData->AddMetaData(META_NAMESPACE, "trackpoints-emftrack", trackpointData.dump(), "string", true);
-    exportModel->AddBuildItem(exportMesh.get(), _wrapper->GetIdentityTransform());
+    _emfTrackRenderThread = QThread::create(renderEMFTrackInThread, _emfTrackPoints);
+    _emfTrackRenderThread->setObjectName("TrackpointApp EMFTrack Renderer");
+    QObject::connect(_emfTrackRenderThread, &QThread::finished, this, &ProjectStore::renderThreadDone);
+    _emfTrackRenderThread->start();
   }
   if (settings.SteamVRTrack) {
-    renderer->renderSteamVRTrack(_steamVrTrackPoints);
-    Lib3MF::PModel steamVrTrackModel = _wrapper->CreateModel();
-    Lib3MF::PReader reader = steamVrTrackModel->QueryReader("3mf");
-    reader->ReadFromFile(std::filesystem::temp_directory_path().u8string() + fileDelimiter + "trackpointapp_render_steamvrtrack.3mf");
-    Lib3MF::PMeshObjectIterator meshIterator = steamVrTrackModel->GetMeshObjects();
-    if (meshIterator->Count() != 1) {
-      return false;
-    }
-    meshIterator->MoveNext();
-    Lib3MF::PMeshObject renderedMesh = meshIterator->GetCurrentMeshObject();
-    Lib3MF::PMeshObject exportMesh = exportModel->AddMeshObject();
-    exportMesh->SetName("steamvrtrack");
-    std::vector<Lib3MF::sPosition> verticesBuffer;
-    std::vector<Lib3MF::sTriangle> triangleBuffer;
-    renderedMesh->GetVertices(verticesBuffer);
-    renderedMesh->GetTriangleIndices(triangleBuffer);
-    exportMesh->SetGeometry(verticesBuffer, triangleBuffer);
-    json trackpointData = json::array();
-    for (SteamVRTrackPoint* point: _steamVrTrackPoints) {
-      osg::Vec3 trackPoint = point->getTrackPoint();
-      osg::Vec3 trackNormal = point->getNormal();
-      json pointData = {
-        {"point", {trackPoint.x(), trackPoint.y(), trackPoint.z()}},
-        {"normal", {trackNormal.x(), trackNormal.y(), trackNormal.z()}}
-      };
-      trackpointData.push_back(pointData);
-    }
-
-    Lib3MF::PMetaDataGroup steamVrMetaData = exportMesh->GetMetaDataGroup();
-    steamVrMetaData->AddMetaData(META_NAMESPACE, "trackpoints-steamvrtrack", trackpointData.dump(), "string", true);
-    exportModel->AddBuildItem(exportMesh.get(), _wrapper->GetIdentityTransform());
-  }
-  delete renderer;
-  // Export action point metadata
-  json actionPointData;
-  for (ActionPoint* point: _actionPoints) {
-    osg::Vec3 translation = point->getTranslation();
-    osg::Vec3 normal = point->getNormal();
-    actionPointData[point->getIdentifier()] = {
-      {"point", {translation.x(), translation.y(), translation.z()}},
-      {"normal", {normal.x(), normal.y(), normal.z()}}
-    };
+    _steamVrTrackRenderThread = QThread::create(renderSteamVRTrackInThread, _steamVrTrackPoints);
+    _steamVrTrackRenderThread->setObjectName("TrackpointApp SteamVR Track Renderer");
+    QObject::connect(_steamVrTrackRenderThread, &QThread::finished, this, &ProjectStore::renderThreadDone);
+    _steamVrTrackRenderThread->start();
   }
-  metaData->AddMetaData(META_NAMESPACE, "trackpoints-actionpoints", actionPointData.dump(), "string", true);
-  Lib3MF::PWriter exportWriter = exportModel->QueryWriter("3mf");
-  exportWriter->WriteToFile(path);
+  _exportRunning = true;
   return true;
 }
 
@@ -230,6 +140,10 @@ bool ProjectStore::isModified() {
   return _projectModified;
 }
 
+bool ProjectStore::isRendering() {
+  return _exportRunning;
+}
+
 void ProjectStore::projectModified() {
   _projectModified = true;
 }
@@ -403,6 +317,132 @@ unsigned int ProjectStore::actionPointIdentifierInUse(std::string candidate, int
   return count;
 }
 
+void ProjectStore::renderThreadDone() {
+  _doneExports++;
+  MainWindow::getInstance()->getEditWiget()->setExportStatus(_wantedExports, _doneExports);
+  if (_wantedExports == _doneExports) {
+    // Export file
+    Lib3MF::PModel exportModel = _wrapper->CreateModel();
+    Lib3MF::PMetaDataGroup metaData = exportModel->GetMetaDataGroup();
+    if (_currentExportSettings.OptiTrack) {
+      Lib3MF::PModel optiTrackModel = _wrapper->CreateModel();
+      Lib3MF::PReader reader = optiTrackModel->QueryReader("3mf");
+      reader->ReadFromFile(std::filesystem::temp_directory_path().u8string() + fileDelimiter + "trackpointapp_render_optitrack.3mf");
+      Lib3MF::PMeshObjectIterator meshIterator = optiTrackModel->GetMeshObjects();
+      if (meshIterator->Count() == 1) {
+        meshIterator->MoveNext();
+        Lib3MF::PMeshObject renderedMesh = meshIterator->GetCurrentMeshObject();
+        Lib3MF::PMeshObject exportMesh = exportModel->AddMeshObject();
+        exportMesh->SetName("optitrack");
+        std::vector<Lib3MF::sPosition> verticesBuffer;
+        std::vector<Lib3MF::sTriangle> triangleBuffer;
+        renderedMesh->GetVertices(verticesBuffer);
+        renderedMesh->GetTriangleIndices(triangleBuffer);
+        exportMesh->SetGeometry(verticesBuffer, triangleBuffer);
+        json trackpointData = json::array();
+        for (OptiTrackPoint* point: _optiTrackPoints) {
+          osg::Vec3 trackPoint = point->getTrackPoint();
+          osg::Vec3 trackNormal = point->getNormal();
+          json pointData = {
+            {"point", {trackPoint.x(), trackPoint.y(), trackPoint.z()}},
+            {"normal", {trackNormal.x(), trackNormal.y(), trackNormal.z()}}
+          };
+          trackpointData.push_back(pointData);
+        }
+
+        Lib3MF::PMetaDataGroup optiMetaData = exportMesh->GetMetaDataGroup();
+        optiMetaData->AddMetaData(META_NAMESPACE, "trackpoints-optitrack", trackpointData.dump(), "string", true);
+        exportModel->AddBuildItem(exportMesh.get(), _wrapper->GetIdentityTransform());
+      } else {
+        MainWindow::getInstance()->showErrorMessage("An error occured while rendering OptiTrack model: inconsistent data.", "Error exporting project.");
+      }
+    }
+    if (_currentExportSettings.EMFTrack) {
+      Lib3MF::PModel emfTrackModel = _wrapper->CreateModel();
+      Lib3MF::PReader reader = emfTrackModel->QueryReader("3mf");
+      reader->ReadFromFile(std::filesystem::temp_directory_path().u8string() + fileDelimiter + "trackpointapp_render_emftrack.3mf");
+      Lib3MF::PMeshObjectIterator meshIterator = emfTrackModel->GetMeshObjects();
+      if (meshIterator->Count() == 1) {
+        meshIterator->MoveNext();
+        Lib3MF::PMeshObject renderedMesh = meshIterator->GetCurrentMeshObject();
+        Lib3MF::PMeshObject exportMesh = exportModel->AddMeshObject();
+        exportMesh->SetName("emftrack");
+        std::vector<Lib3MF::sPosition> verticesBuffer;
+        std::vector<Lib3MF::sTriangle> triangleBuffer;
+        renderedMesh->GetVertices(verticesBuffer);
+        renderedMesh->GetTriangleIndices(triangleBuffer);
+        exportMesh->SetGeometry(verticesBuffer, triangleBuffer);
+        json trackpointData = json::array();
+        for (EMFTrackPoint* point: _emfTrackPoints) {
+          osg::Vec3 moveToMid = point->getNormal().operator*(-(point->getDepth() / 2));
+          osg::Vec3 trackPoint = point->getTrackPoint().operator+(moveToMid);
+          osg::Vec3 trackNormal = point->getNormal();
+          json pointData = {
+            {"point", {trackPoint.x(), trackPoint.y(), trackPoint.z()}},
+            {"normal", {trackNormal.x(), trackNormal.y(), trackNormal.z()}},
+            {"rotation", point->getNormalRotation()}
+          };
+          trackpointData.push_back(pointData);
+        }
+
+        Lib3MF::PMetaDataGroup emfMetaData = exportMesh->GetMetaDataGroup();
+        emfMetaData->AddMetaData(META_NAMESPACE, "trackpoints-emftrack", trackpointData.dump(), "string", true);
+        exportModel->AddBuildItem(exportMesh.get(), _wrapper->GetIdentityTransform());
+      } else {
+        MainWindow::getInstance()->showErrorMessage("An error occured while rendering EMFTrack model: inconsistent data.", "Error exporting project.");
+      }
+    }
+    if (_currentExportSettings.SteamVRTrack) {
+      Lib3MF::PModel steamVrTrackModel = _wrapper->CreateModel();
+      Lib3MF::PReader reader = steamVrTrackModel->QueryReader("3mf");
+      reader->ReadFromFile(std::filesystem::temp_directory_path().u8string() + fileDelimiter + "trackpointapp_render_steamvrtrack.3mf");
+      Lib3MF::PMeshObjectIterator meshIterator = steamVrTrackModel->GetMeshObjects();
+      if (meshIterator->Count() == 1) {
+        meshIterator->MoveNext();
+        Lib3MF::PMeshObject renderedMesh = meshIterator->GetCurrentMeshObject();
+        Lib3MF::PMeshObject exportMesh = exportModel->AddMeshObject();
+        exportMesh->SetName("steamvrtrack");
+        std::vector<Lib3MF::sPosition> verticesBuffer;
+        std::vector<Lib3MF::sTriangle> triangleBuffer;
+        renderedMesh->GetVertices(verticesBuffer);
+        renderedMesh->GetTriangleIndices(triangleBuffer);
+        exportMesh->SetGeometry(verticesBuffer, triangleBuffer);
+        json trackpointData = json::array();
+        for (SteamVRTrackPoint* point: _steamVrTrackPoints) {
+          osg::Vec3 trackPoint = point->getTrackPoint();
+          osg::Vec3 trackNormal = point->getNormal();
+          json pointData = {
+            {"point", {trackPoint.x(), trackPoint.y(), trackPoint.z()}},
+            {"normal", {trackNormal.x(), trackNormal.y(), trackNormal.z()}}
+          };
+          trackpointData.push_back(pointData);
+        }
+
+        Lib3MF::PMetaDataGroup steamVrMetaData = exportMesh->GetMetaDataGroup();
+        steamVrMetaData->AddMetaData(META_NAMESPACE, "trackpoints-steamvrtrack", trackpointData.dump(), "string", true);
+        exportModel->AddBuildItem(exportMesh.get(), _wrapper->GetIdentityTransform());
+      } else {
+        MainWindow::getInstance()->showErrorMessage("An error occured while rendering SteamVR Track model: inconsistent data.", "Error exporting project.");
+      }
+    }
+    // Export action point metadata
+    json actionPointData;
+    for (ActionPoint* point: _actionPoints) {
+      osg::Vec3 translation = point->getTranslation();
+      osg::Vec3 normal = point->getNormal();
+      actionPointData[point->getIdentifier()] = {
+        {"point", {translation.x(), translation.y(), translation.z()}},
+        {"normal", {normal.x(), normal.y(), normal.z()}}
+      };
+    }
+    metaData->AddMetaData(META_NAMESPACE, "trackpoints-actionpoints", actionPointData.dump(), "string", true);
+    Lib3MF::PWriter exportWriter = exportModel->QueryWriter("3mf");
+    exportWriter->WriteToFile(_exportPath);
+    _exportRunning = false;
+    MainWindow::getInstance()->getEditWiget()->setExportAvailable(true);
+  }
+}
+
 void ProjectStore::load3mfLib() {
   _wrapper = Lib3MF::CWrapper::loadLibrary();
   _project = _wrapper->CreateModel();
@@ -599,6 +639,24 @@ void ProjectStore::loadMetaData() {
   MainWindow::getInstance()->getEditWiget()->updateTrackpointCount();
 }
 
+void ProjectStore::renderOptiTrackInThread(std::vector<OptiTrackPoint*> optiTrackPoints) {
+  OpenScadRenderer* renderer = new OpenScadRenderer();
+  renderer->renderOptiTrack(optiTrackPoints);
+  delete renderer;
+}
+
+void ProjectStore::renderEMFTrackInThread(std::vector<EMFTrackPoint*> emfTrackPoints) {
+  OpenScadRenderer* renderer = new OpenScadRenderer();
+  renderer->renderEMFTrack(emfTrackPoints);
+  delete renderer;
+}
+
+void ProjectStore::renderSteamVRTrackInThread(std::vector<SteamVRTrackPoint*> steamVrTrackPoints) {
+  OpenScadRenderer* renderer = new OpenScadRenderer();
+  renderer->renderSteamVRTrack(steamVrTrackPoints);
+  delete renderer;
+}
+
 std::vector<float> ProjectStore::osgVecToStdVec(osg::Vec3f input) {
   std::vector<float> vector;
   vector.push_back(input.x());