From 832618b84a905ad4d4eae6252b33a0ba4de32897 Mon Sep 17 00:00:00 2001 From: dushyant basson <39452934+dushyant-basson@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:36:45 +0530 Subject: [PATCH] initial camera-orientation; multiple materials in a single element; default material used from IfcGeom; improved triangle node data generation for OSG --- src/qtviewer/IfcViewerWidget.cpp | 76 +++++++---- src/qtviewer/IfcViewerWidget.h | 4 +- src/qtviewer/ParseIfcFile.cpp | 212 +++++++++++++++---------------- src/qtviewer/ParseIfcFile.h | 25 ++-- 4 files changed, 167 insertions(+), 150 deletions(-) diff --git a/src/qtviewer/IfcViewerWidget.cpp b/src/qtviewer/IfcViewerWidget.cpp index 0124d6947d..742d3835a3 100644 --- a/src/qtviewer/IfcViewerWidget.cpp +++ b/src/qtviewer/IfcViewerWidget.cpp @@ -12,6 +12,7 @@ #include "MessageLogger.h" #include "osg/Geode" #include "osg/Geometry" +#include "osg/StateSet" #include "osg/Vec3" #include "osg/ref_ptr" #include "osgGA/TrackballManipulator" @@ -26,11 +27,11 @@ 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); + m_clearColor = osg::Vec4(0.8, 0.8, 0.8, 1.0); //debug - override //std::string filePath = "/Users/onizudb/Downloads/IFC/wallAtOrigin.ifc"; + //std::string filePath = "/Users/onizudb/Downloads/IFC/FromAC_IFC4ref.ifc"; //this->loadFile(filePath); } @@ -50,13 +51,8 @@ void IfcViewerWidget::initializeGL() m_viewer->getCamera()->setClearColor(m_clearColor); m_viewer->getCamera()->setGraphicsContext(m_graphicsWindow); - // Add event handlers - - this->setMouseTracking(false); - - m_mouseHandler->setAllowThrow(false); - - m_viewer->setCameraManipulator(m_mouseHandler); + // set camera manipulator, etc. + setupViewController(); m_viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded); m_viewer->realize(); @@ -136,6 +132,14 @@ unsigned int IfcViewerWidget::getMouseButtonNum(QMouseEvent* event) return button; } +void IfcViewerWidget::setupViewController() +{ + setMouseTracking(false); + m_mouseHandler = new osgGA::TrackballManipulator; + m_mouseHandler->setAllowThrow(false); + m_viewer->setCameraManipulator(m_mouseHandler); +} + void IfcViewerWidget::loadFile(const std::string& filePath) { std::vector> geometries; @@ -152,19 +156,45 @@ void IfcViewerWidget::loadFile(const std::string& filePath) geode->addDrawable(geometry); } - prepareSceneWithGeometry(geode); - - 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); + root->addChild(geode); + + // set initial camera position + orientScene(geode); +} + +void IfcViewerWidget::orientScene(osg::ref_ptr geode) +{ + // Get the bounding-box + osg::ComputeBoundsVisitor cbv; + geode->accept(cbv); + //root.get()->accept(cbv); + const osg::BoundingBox& bb = cbv.getBoundingBox(); + + // Center (average of opposite corners of bb) + osg::Vec3d center = (bb.corner(0) + bb.corner(7)) * 0.5; + + // Radius (distance between the center and the corner) + // assuming corner(7) has maximum x,y,z coords + float radius = (bb.corner(7) - center).length(); + + // Calc. eye position + osg::Vec3d eye = center + osg::Vec3d(radius * 3, -radius * 3, 0.0); + + osg::Vec3d up = osg::Vec3d(0.0, 0.0, 1.0); + + // Set the camera's view matrix + // In case a cameraManipulator is set, lookAt position setting does not work on the camera + // as it is overridden. + // So set it on the manipulator and reapply it to the viewer + m_mouseHandler->setHomePosition(eye, center, up); + m_viewer->setCameraManipulator(m_mouseHandler); + + auto log = [center, radius, eye]() { + MessageLogger::log("center: " + std::to_string(center.length())); + MessageLogger::log("radius: " + std::to_string(radius)); + MessageLogger::log("center: " + std::to_string(center.x()) + ", " + std::to_string(center.y()) + ", " + std::to_string(center.z())); + MessageLogger::log("eye: " + std::to_string(eye.x()) + ", " + std::to_string(eye.y()) + ", " + std::to_string(eye.z())); + }; + //log(); } diff --git a/src/qtviewer/IfcViewerWidget.h b/src/qtviewer/IfcViewerWidget.h index b0419ed9ca..ec9158ed2d 100644 --- a/src/qtviewer/IfcViewerWidget.h +++ b/src/qtviewer/IfcViewerWidget.h @@ -54,7 +54,9 @@ private: osgGA::EventQueue* getEventQueue() const; unsigned int getMouseButtonNum(QMouseEvent* event); - void prepareSceneWithGeometry(osg::ref_ptr geode); + void setupViewController(); + + void orientScene(osg::ref_ptr geode); }; #endif // IFCVIEWERWIDGET_H diff --git a/src/qtviewer/ParseIfcFile.cpp b/src/qtviewer/ParseIfcFile.cpp index 6b2249146f..dfbe059efb 100644 --- a/src/qtviewer/ParseIfcFile.cpp +++ b/src/qtviewer/ParseIfcFile.cpp @@ -6,6 +6,7 @@ #include "osg/Material" #include "osg/PrimitiveSet" #include "osg/StateAttribute" +#include "osg/StateSet" #include "osg/Vec4" #include "osg/ref_ptr" #include @@ -28,6 +29,7 @@ bool ParseIfcFile::Parse( IfcGeom::IteratorSettings settings; settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true); settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false); + settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS, true); IfcGeom::Iterator* it = new IfcGeom::Iterator(settings, &file); if (!it->initialize()) { @@ -39,137 +41,129 @@ bool ParseIfcFile::Parse( do { //const IfcGeom::BRepElement* bRepElem = it->get_native(); const IfcGeom::TriangulationElement* triElem = static_cast(it->get()); - MessageLogger::log(triElem->type() + ": " + triElem->name()); + + // Print element info + std::string elemInfo = triElem->type(); + elemInfo += triElem->name() == "" ? "" : ": " + triElem->name(); + MessageLogger::log(elemInfo); + + // Transformation Matrix + const std::vector& matrixData = triElem->transformation().matrix().data(); + std::string matrixStr = ""; + int di = 0; + for (auto d : matrixData) { + if (di == 4) { + matrixStr += "\n"; + di = 0; + } + matrixStr += std::to_string(d) + " "; + di++; + } + //MessageLogger::log(matrixStr); // Print matrix data + + //MessageLogger::log(triElem->geometry().id()); 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(); - 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()); + const std::vector& elemMatIds = triElemGeom->material_ids(); + + std::map> uniqueMaterials; + + std::map> elemFaces_grouped; + auto fit = elemFaces.begin(); + for (const int& matId : elemMatIds) { + for (int i = 0; i < 3; ++i) { + elemFaces_grouped[matId].push_back(*fit++); + } } - geometries.push_back(elemGeom); + if (fit != elemFaces.end()) + MessageLogger::log("** Failed to map the faces to material IDs!"); + + for (const auto& group : elemFaces_grouped) { + int matId = group.first; + const std::vector& fVertIds = group.second; + + osg::ref_ptr osgVertices = new osg::Vec3Array; + osg::ref_ptr osgNormals = new osg::Vec3Array; + + for (size_t i = 0; i < fVertIds.size(); i += 3) { + buildTriangleNodes( + fVertIds, elemVertices, elemNormals, i, + osgVertices, osgNormals + ); + } + + osg::ref_ptr geometry = new osg::Geometry; + + geometry->setVertexArray(osgVertices); + geometry->setNormalArray(osgNormals); + geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); + + osg::ref_ptr drawArrays = new osg::DrawArrays( + osg::PrimitiveSet::TRIANGLES, 0, osgVertices->size()); + geometry->addPrimitiveSet(drawArrays); + + osg::ref_ptr osgMaterial = getOsgMaterial(elemMats[matId]); + geometry->getOrCreateStateSet()->setAttributeAndModes(osgMaterial.get()); + + geometries.push_back(geometry); + } } 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() +void ParseIfcFile::buildTriangleNodes( + const std::vector& fVertIds, + const std::vector& elemVertices, + const std::vector& elemNormals, + size_t index, + osg::ref_ptr osgVertices, + osg::ref_ptr osgNormals) { - osg::Vec4 osgDiffuseColor(0.4f, 0.4f, 0.8f, 1.0f); - return osgDiffuseColor; + auto addVertexAndNormal = [fVertIds, elemVertices, elemNormals, osgVertices, osgNormals](size_t i) { + + size_t initVertIndex = 3 * fVertIds[i]; + size_t x_vertIndex = initVertIndex; + size_t y_vertIndex = initVertIndex + 1; + size_t z_vertIndex = initVertIndex + 2; + + osgVertices->push_back(osg::Vec3 ( + elemVertices[x_vertIndex], + elemVertices[y_vertIndex], + elemVertices[z_vertIndex] + )); + + osgNormals->push_back(osg::Vec3 ( + elemNormals[x_vertIndex], + elemNormals[y_vertIndex], + elemNormals[z_vertIndex] + )); + }; + + addVertexAndNormal(index); + addVertexAndNormal(index + 1); + addVertexAndNormal(index + 2); } 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; - } + const double* diffuseColor = material.diffuse(); + osg::Vec4 matOsgDiffuseColor( + diffuseColor[0], + diffuseColor[1], + diffuseColor[2], + material.transparency() + ); osg::ref_ptr osgMaterial = new osg::Material; - osgMaterial->setDiffuse(osg::Material::FRONT_AND_BACK, osgDiffuseColor); + osgMaterial->setDiffuse(osg::Material::FRONT_AND_BACK, matOsgDiffuseColor); return osgMaterial; } diff --git a/src/qtviewer/ParseIfcFile.h b/src/qtviewer/ParseIfcFile.h index 7271d6c988..17ed490e72 100644 --- a/src/qtviewer/ParseIfcFile.h +++ b/src/qtviewer/ParseIfcFile.h @@ -10,17 +10,6 @@ #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 { @@ -34,12 +23,14 @@ public: ); private: - std::vector createTriangles( - const std::vector& elemFaces, - const std::vector& elemVertices, - const std::vector& elemNormals - ); - osg::Vec4 getDefaultOsgDiffuseColor(); + void buildTriangleNodes( + const std::vector& fVertIds, + const std::vector& elemVertices, + const std::vector& elemNormals, + size_t index, + osg::ref_ptr osgVertices, + osg::ref_ptr osgNormals); + osg::ref_ptr getOsgMaterial(const IfcGeom::Material& material); };