From 7b92b5c7e2673d02e6ae95ec6f3dfee6d1066bb5 Mon Sep 17 00:00:00 2001 From: dushyant basson <39452934+dushyant-basson@users.noreply.github.com> Date: Tue, 12 Sep 2023 12:57:40 +0530 Subject: [PATCH] IFC file successfully parsed and loaded in Qt OpenGL widget --- src/qtviewer/CMakeLists.txt | 2 - src/qtviewer/IfcViewerWidget.cpp | 94 ++++++++++--------- src/qtviewer/IfcViewerWidget.h | 21 ++++- src/qtviewer/MainWindow.cpp | 7 +- src/qtviewer/MainWindow.h | 10 +-- src/qtviewer/MouseHandler.cpp | 47 ---------- src/qtviewer/MouseHandler.h | 21 ----- src/qtviewer/ParseIfcFile.cpp | 149 +++++++++++++++++++++++++++++-- src/qtviewer/ParseIfcFile.h | 31 ++++++- 9 files changed, 246 insertions(+), 136 deletions(-) delete mode 100644 src/qtviewer/MouseHandler.cpp delete mode 100644 src/qtviewer/MouseHandler.h diff --git a/src/qtviewer/CMakeLists.txt b/src/qtviewer/CMakeLists.txt index 2bd0267a3b..0e83f1a386 100644 --- a/src/qtviewer/CMakeLists.txt +++ b/src/qtviewer/CMakeLists.txt @@ -64,8 +64,6 @@ set_source_files_properties(${app_icon_macos} PROPERTIES add_executable(${targetName} MessageLogger.h - MouseHandler.h - MouseHandler.cpp IfcViewerWidget.h IfcViewerWidget.cpp ParseIfcFile.h diff --git a/src/qtviewer/IfcViewerWidget.cpp b/src/qtviewer/IfcViewerWidget.cpp index 3d1c928617..0124d6947d 100644 --- a/src/qtviewer/IfcViewerWidget.cpp +++ b/src/qtviewer/IfcViewerWidget.cpp @@ -1,16 +1,20 @@ #include "IfcViewerWidget.h" #include -#include -#include #include #include #include +#include #include #include #include +#include +#include "MainWindow.h" #include "MessageLogger.h" -#include "MouseHandler.h" +#include "osg/Geode" +#include "osg/Geometry" +#include "osg/Vec3" #include "osg/ref_ptr" +#include "osgGA/TrackballManipulator" IfcViewerWidget::IfcViewerWidget(qreal dpiScale, QWidget *parent) : QOpenGLWidget(parent), @@ -21,16 +25,21 @@ IfcViewerWidget::IfcViewerWidget(qreal dpiScale, QWidget *parent) : )), m_viewer(new osgViewer::Viewer), root(new osg::Group) -{ } +{ + m_mouseHandler = new osgGA::TrackballManipulator; + m_clearColor = osg::Vec4(0.7, 0.7, 0.7, 1.0); + + //debug - override + //std::string filePath = "/Users/onizudb/Downloads/IFC/wallAtOrigin.ifc"; + //this->loadFile(filePath); +} void IfcViewerWidget::initializeGL() { + MessageLogger::log("Press `Cmd/Ctrl + o` or use the File menu to open an IFC file"); + // Set up the rendering context, load shaders and other resources, etc.: - MessageLogger::log("devicePixelRatio: " + std::to_string(m_dpiScale)); - - this->buildSceneData(); - // Set the root node as the scene data for the viewer m_viewer->setSceneData(root); @@ -38,20 +47,16 @@ void IfcViewerWidget::initializeGL() state->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON); m_viewer->getCamera()->setViewport(0, 0, this->width(), this->height()); - m_viewer->getCamera()->setClearColor(osg::Vec4(0.8f, 0.8f, 0.8f, 1.0f)); + m_viewer->getCamera()->setClearColor(m_clearColor); m_viewer->getCamera()->setGraphicsContext(m_graphicsWindow); // Add event handlers this->setMouseTracking(false); - - //osg::ref_ptr trackballManipulator = new osgGA::TrackballManipulator; - //trackballManipulator->setAllowThrow(false); - //m_viewer->setCameraManipulator(trackballManipulator); - osg::ref_ptr mouseHandler = new MouseHandler(m_viewer); - mouseHandler->setAllowThrow(false); - m_viewer->setCameraManipulator(mouseHandler); + m_mouseHandler->setAllowThrow(false); + + m_viewer->setCameraManipulator(m_mouseHandler); m_viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded); m_viewer->realize(); @@ -70,16 +75,6 @@ void IfcViewerWidget::resizeGL(int w, int h) void IfcViewerWidget::paintGL() { - // Render geometries from the parsed IFC file - // Draw the scene: - //QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); - //f->glClear(GL_COLOR_BUFFER_BIT); - - //MessageLogger::log("paintGL called"); - - glClear(GL_COLOR_BUFFER_BIT); - //glClearColor(0.2f, 0.6f, 0.9f, 0.5f); - // Render OSG scene m_viewer->frame(); } @@ -141,32 +136,35 @@ unsigned int IfcViewerWidget::getMouseButtonNum(QMouseEvent* event) return button; } -void IfcViewerWidget::buildSceneData() +void IfcViewerWidget::loadFile(const std::string& filePath) { - // Cube - osg::ref_ptr box = new osg::Box(osg::Vec3(0, 0, 0), 1.0f); - osg::ref_ptr shapeDrawable = new osg::ShapeDrawable(box); + std::vector> geometries; - // Set material properties (optional) - osg::ref_ptr material = new osg::Material; - material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); // Red color - shapeDrawable->getOrCreateStateSet()->setAttributeAndModes(material.get()); + if(!m_parser.Parse(filePath, geometries)) { + MessageLogger::log("Failed to prepare IFC geometry"); + return; + } + + MessageLogger::log("\nIFC geometry prepared"); osg::ref_ptr geode = new osg::Geode; - geode->addDrawable(shapeDrawable); + for (auto geometry : geometries) { + geode->addDrawable(geometry); + } - // Apply a rotation to the cube to make it 3D - osg::ref_ptr cubeTransform = new osg::MatrixTransform; - osg::ref_ptr rotationTransform = new osg::MatrixTransform; - rotationTransform->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(45.0), osg::Vec3(1.0, 1.0, 0.0))); - cubeTransform->addChild(geode); - rotationTransform->addChild(cubeTransform); + prepareSceneWithGeometry(geode); - // Translate the cube to a proper position - osg::ref_ptr translationTransform = new osg::MatrixTransform; - translationTransform->setMatrix(osg::Matrix::translate(osg::Vec3(0.0, 0.0, -5.0))); - translationTransform->addChild(rotationTransform); - - // Add the cube to the root node - root->addChild(translationTransform); + m_viewer->setCameraManipulator(m_mouseHandler); +} + +void IfcViewerWidget::prepareSceneWithGeometry(osg::ref_ptr geode) +{ + // Apply a rotation to the model + // MatrixTransform allows applying transformations (translation, rotation, scaling) to its children + osg::ref_ptr rotationTransform = new osg::MatrixTransform; + rotationTransform->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(45.0), osg::Vec3(0.0, 0.0, 1.0))); + rotationTransform->addChild(geode); + + // Add the model to the root node + root->addChild(rotationTransform); } diff --git a/src/qtviewer/IfcViewerWidget.h b/src/qtviewer/IfcViewerWidget.h index 93bce7690b..b0419ed9ca 100644 --- a/src/qtviewer/IfcViewerWidget.h +++ b/src/qtviewer/IfcViewerWidget.h @@ -9,13 +9,25 @@ #include #include #include +#include #include +#include + +#include "ParseIfcFile.h" +#include "osg/Vec4" class IfcViewerWidget : public QOpenGLWidget { + Q_OBJECT public: - IfcViewerWidget(qreal dpiScale, QWidget *parent = nullptr); + IfcViewerWidget( + qreal dpiScale, + QWidget *parent = nullptr + ); + +public slots: + void loadFile(const std::string& filePath); protected: virtual void initializeGL() override; // to set up resources, state @@ -31,13 +43,18 @@ protected: private: qreal m_dpiScale; QMatrix4x4 m_projection; + osg::Vec4 m_clearColor; + osg::ref_ptr m_mouseHandler; osg::ref_ptr m_graphicsWindow; osg::ref_ptr m_viewer; osg::ref_ptr root; // OSG root node + ParseIfcFile m_parser; + osgGA::EventQueue* getEventQueue() const; unsigned int getMouseButtonNum(QMouseEvent* event); - void buildSceneData(); + + void prepareSceneWithGeometry(osg::ref_ptr geode); }; #endif // IFCVIEWERWIDGET_H diff --git a/src/qtviewer/MainWindow.cpp b/src/qtviewer/MainWindow.cpp index 200c372de6..fa46ed2b87 100644 --- a/src/qtviewer/MainWindow.cpp +++ b/src/qtviewer/MainWindow.cpp @@ -84,6 +84,8 @@ void MainWindow::createConnections() //connect(&m_parser, &ParseIfcFile::parsingInfo, this, &MainWindow::appendToOutputText); connect(&MessageLogger::getInstance(), SIGNAL(logMessage(QString)), this, SLOT(appendToOutputText(QString))); + + connect(this, SIGNAL(loadFileInViewerWidget(const std::string&)), m_glWidget, SLOT(loadFile(const std::string&))); } void MainWindow::appendToOutputText(const QString& message) @@ -119,11 +121,10 @@ void MainWindow::openFile() QString message = tr("Opening file: %1\n").arg(filePath); appendToOutputText(message); - m_parser.Parse(filePath.toStdString()); - - m_currentPath = filePath; setWindowTitle(tr("%1 - IFCViewer").arg(fileName)); m_outlineAction->setEnabled(true); m_backgroundAction->setEnabled(true); + + emit loadFileInViewerWidget(filePath.toStdString()); } diff --git a/src/qtviewer/MainWindow.h b/src/qtviewer/MainWindow.h index 34062eee4a..467a48cbdf 100644 --- a/src/qtviewer/MainWindow.h +++ b/src/qtviewer/MainWindow.h @@ -7,8 +7,7 @@ #include #include #include - -#include "ParseIfcFile.h" +#include class MainWindow : public QMainWindow { @@ -17,6 +16,9 @@ class MainWindow : public QMainWindow public: MainWindow(qreal dpiScale, QWidget *parent = nullptr); +signals: + void loadFileInViewerWidget(const std::string& filePath); + public slots: void openFile(); void appendToOutputText(const QString& message); @@ -40,12 +42,8 @@ private: QAction *m_backgroundAction; QAction *m_outlineAction; - QString m_currentPath; - QOpenGLWidget *m_glWidget; QPlainTextEdit *m_outputText; - - ParseIfcFile m_parser; }; #endif // MAINWINDOW_H diff --git a/src/qtviewer/MouseHandler.cpp b/src/qtviewer/MouseHandler.cpp deleted file mode 100644 index 77821b5dc0..0000000000 --- a/src/qtviewer/MouseHandler.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "MouseHandler.h" -#include -#include "MessageLogger.h" - -MouseHandler::MouseHandler(osgViewer::Viewer* viewer) : - osgGA::TrackballManipulator(), - viewer(viewer) -{ - MessageLogger::log("MouseHandler constructed"); -} - -bool MouseHandler::handle ( - const osgGA::GUIEventAdapter& ea, - const osgGA::GUIActionAdapter& aa) -{ - MessageLogger::log("MouseHandler::handle() called"); - switch (ea.getEventType()) { - case (osgGA::GUIEventAdapter::DRAG): - { - if (ea.getButton() == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON) - { // pan view - // Calculate the delta movement - float deltaX = ea.getX() - _ga_t0->getXnormalized(); - float deltaY = ea.getY() - _ga_t1->getYnormalized(); - - // Implement panning logic - osg::Vec3d eye, center, up; - getInverseMatrix().getLookAt(eye, center, up); - osg::Vec3d right = (eye - center) ^ up; - osg::Matrixd rotationMatrix = osg::Matrix::rotate(-deltaX * 0.1, up) * osg::Matrix::rotate(deltaY * 0.1, right); - - osg::Matrixd newMatrix = getMatrix() * rotationMatrix; - setByMatrix(newMatrix); - - return true; // Event handled - } - if (ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON) - { - return false; - } - // If not a right mouse button drag, let the base class handle it - //return osgGA::TrackballManipulator::handle(ea, aa); - } - default: - return false; - } -} diff --git a/src/qtviewer/MouseHandler.h b/src/qtviewer/MouseHandler.h deleted file mode 100644 index 89c3bf6e9f..0000000000 --- a/src/qtviewer/MouseHandler.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef MOUSEHANDLER_H -#define MOUSEHANDLER_H - -#include -#include - -class MouseHandler : public osgGA::TrackballManipulator -{ - public: - MouseHandler(osgViewer::Viewer* viewer); - - using osgGA::GUIEventHandler::handle; - virtual bool handle( - const osgGA::GUIEventAdapter& ea, - const osgGA::GUIActionAdapter& aa - ); - private: - osgViewer::Viewer* viewer; -}; - -#endif // MOUSEHANDLER_H diff --git a/src/qtviewer/ParseIfcFile.cpp b/src/qtviewer/ParseIfcFile.cpp index 115cb10c0c..6b2249146f 100644 --- a/src/qtviewer/ParseIfcFile.cpp +++ b/src/qtviewer/ParseIfcFile.cpp @@ -1,14 +1,28 @@ #include "ParseIfcFile.h" #include "MessageLogger.h" -#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h" +#include "osg/Array" +#include "osg/Geometry" +#include "osg/Material" +#include "osg/PrimitiveSet" +#include "osg/StateAttribute" +#include "osg/Vec4" +#include "osg/ref_ptr" +#include +#include // size_t +#include + +#include +#include ParseIfcFile::ParseIfcFile() {} ParseIfcFile::~ParseIfcFile() {} -void ParseIfcFile::Parse(const std::string& filePath) -{ +bool ParseIfcFile::Parse( + const std::string& filePath, + std::vector>& geometries +) { IfcParse::IfcFile file(filePath); IfcGeom::IteratorSettings settings; @@ -19,7 +33,7 @@ void ParseIfcFile::Parse(const std::string& filePath) if (!it->initialize()) { MessageLogger::log("Error: Iterator failed to initialize! Aborting."); delete it; - return; + return false; } do { @@ -29,10 +43,133 @@ void ParseIfcFile::Parse(const std::string& filePath) const boost::shared_ptr& triElemGeom = triElem->geometry_pointer(); + const std::vector& elemFaces = triElemGeom->faces(); + const std::vector& elemVertices = triElemGeom->verts(); + const std::vector& elemNormals = triElemGeom->normals(); + + std::vector triangles = this->createTriangles(elemFaces, elemVertices, elemNormals); + + osg::ref_ptr osgVertices = new osg::Vec3Array; + osg::ref_ptr osgNormals = new osg::Vec3Array; + + // Combine the triangles from the vector into a single Vec3Array + for (const auto& triangle : triangles) { + osgVertices->push_back(triangle.vn1.vertex); + osgVertices->push_back(triangle.vn2.vertex); + osgVertices->push_back(triangle.vn3.vertex); + + osgNormals->push_back(triangle.vn1.normal); + osgNormals->push_back(triangle.vn2.normal); + osgNormals->push_back(triangle.vn3.normal); + } + + osg::ref_ptr elemGeom = new osg::Geometry; + elemGeom->setVertexArray(osgVertices); + elemGeom->setNormalArray(osgNormals); + elemGeom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); + + osg::ref_ptr drawArrays = new osg::DrawArrays( + osg::PrimitiveSet::TRIANGLES, 0, osgVertices->size()); + elemGeom->addPrimitiveSet(drawArrays); + // materials const std::vector& elemMats = triElemGeom->materials(); - for (auto mat : elemMats) { - MessageLogger::log(" " + mat.original_name()); + std::vector> elemOsgMats; + for (const IfcGeom::Material& mat : elemMats) { + //MessageLogger::log(" " + mat.original_name()); + elemOsgMats.push_back(getOsgMaterial(mat)); } + if (elemOsgMats.size()) { + // Todo: set respective material for each part of the element + elemGeom->getOrCreateStateSet()->setAttributeAndModes(elemOsgMats[0].get()); + } else { // sometimes material values are not available (like for IfcOpeningElement) + osg::ref_ptr osgMaterial = new osg::Material; + osgMaterial->setDiffuse(osg::Material::FRONT_AND_BACK, this->getDefaultOsgDiffuseColor()); + elemGeom->getOrCreateStateSet()->setAttributeAndModes(osgMaterial.get()); + } + + geometries.push_back(elemGeom); + } while (it->next()); + + return true; +} + +std::vector ParseIfcFile::createTriangles( + const std::vector& elemFaces, + const std::vector& elemVertices, + const std::vector& elemNormals +) { + + std::vector triangles; + + for (size_t i = 0; i < elemFaces.size(); i += 3) { + + auto createVertexWithNormal = [elemFaces, elemVertices, elemNormals](size_t fi) { + + VertexWithNormal vn; + + size_t initVertIndex = 3 * elemFaces[fi]; + size_t x_vertIndex = initVertIndex; + size_t y_vertIndex = initVertIndex + 1; + size_t z_vertIndex = initVertIndex + 2; + + vn.vertex = osg::Vec3 ( + elemVertices[x_vertIndex], + elemVertices[y_vertIndex], + elemVertices[z_vertIndex] + ); + + vn.normal = osg::Vec3 ( + elemNormals[x_vertIndex], + elemNormals[y_vertIndex], + elemNormals[z_vertIndex] + ); + + return vn; + }; + + auto createTriangle = [createVertexWithNormal, i]() { + + Triangle triangle; + + triangle.vn1 = createVertexWithNormal(i); + triangle.vn2 = createVertexWithNormal(i + 1); + triangle.vn3 = createVertexWithNormal(i + 2); + + return triangle; + }; + + triangles.push_back(createTriangle()); + } + + return triangles; + +} + +osg::Vec4 ParseIfcFile::getDefaultOsgDiffuseColor() +{ + osg::Vec4 osgDiffuseColor(0.4f, 0.4f, 0.8f, 1.0f); + return osgDiffuseColor; +} + +osg::ref_ptr ParseIfcFile::getOsgMaterial(const IfcGeom::Material& material) +{ + osg::Vec4 osgDiffuseColor = getDefaultOsgDiffuseColor(); + + if(material.hasDiffuse()) { + const double* diffuseColor = material.diffuse(); + osg::Vec4 matOsgDiffuseColor( + diffuseColor[0], + diffuseColor[1], + diffuseColor[2], + material.transparency() + ); + osgDiffuseColor = matOsgDiffuseColor; + } + + osg::ref_ptr osgMaterial = new osg::Material; + osgMaterial->setDiffuse(osg::Material::FRONT_AND_BACK, osgDiffuseColor); + + return osgMaterial; } diff --git a/src/qtviewer/ParseIfcFile.h b/src/qtviewer/ParseIfcFile.h index 36bdda4655..7271d6c988 100644 --- a/src/qtviewer/ParseIfcFile.h +++ b/src/qtviewer/ParseIfcFile.h @@ -4,14 +4,43 @@ #include #include #include +#include +#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h" +#include "osg/Material" +#include "osg/ref_ptr" +#include "osg/Geometry" + +struct VertexWithNormal { + osg::Vec3 vertex; + osg::Vec3 normal; +}; + +struct Triangle { + VertexWithNormal vn1; + VertexWithNormal vn2; + VertexWithNormal vn3; +}; class ParseIfcFile : public QObject { + public: ParseIfcFile(); ~ParseIfcFile(); - void Parse(const std::string& filePath); + bool Parse( + const std::string& filePath, + std::vector>& geometries + ); + +private: + std::vector createTriangles( + const std::vector& elemFaces, + const std::vector& elemVertices, + const std::vector& elemNormals + ); + osg::Vec4 getDefaultOsgDiffuseColor(); + osg::ref_ptr getOsgMaterial(const IfcGeom::Material& material); }; #endif // PARSEIFCFILE_H