// Include own headers #include "OSGWidget.hpp" // Include modules #include "PickHandler.hpp" #include "TrackPointRenderer.hpp" #include "HudCallback.hpp" #include "resources.hpp" #include "MeshTools.hpp" // Include dependencies #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace osgWidget { void Viewer::setupThreading() { if (_threadingModel == SingleThreaded) { if (_threadsRunning) { stopThreading(); } } else { if (!_threadsRunning) { startThreading(); } } } } void OSGWidget::fixMaterialState(osg::ref_ptr node, osg::Vec4* color) { osg::StateSet* stateSet = node->getOrCreateStateSet(); osg::Material* material = new osg::Material; material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); if (color) { material->setDiffuse(osg::Material::FRONT_AND_BACK, *color); } stateSet->setAttributeAndModes(material, osg::StateAttribute::ON); stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON); } OSGWidget::OSGWidget(QWidget* parent): QOpenGLWidget(parent), graphicsWindow_(new osgViewer::GraphicsWindowEmbedded(this->x(), this->y(), this->width(), this->height())), _viewer(new osgWidget::Viewer), selectionActive_(false), selectionFinished_(true) { this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // Root node of the scene _root = new osg::Group; // Create the camera float aspectRatio = static_cast(this->width()) / static_cast(this->height()); auto pixelRatio = this->devicePixelRatio(); osg::Camera* camera = new osg::Camera; camera->setViewport(0, 0, this->width() * pixelRatio, this->height() * pixelRatio); camera->setClearColor(osg::Vec4(0.2f, 0.1875f, 0.375f, 1.0f)); camera->setProjectionMatrixAsPerspective(30.0f, aspectRatio, 1.0f, 1000.0f); camera->setGraphicsContext(graphicsWindow_); // Create the viewer _view = new osgViewer::View; _view->setCamera(camera); _view->setSceneData(_root); // Create coordinate axes _coordinateAxes = new osg::Group; osg::Vec4 axesColor = osg::Vec4(0.7f, 0.7f, 0.7f, 1.0f); osg::ref_ptr xRotation = new osg::MatrixTransform; xRotation->setMatrix(osg::Matrix::rotate(osg::Vec3f(0.0f, 0.0f, 1.0f), osg::Vec3f(1.0f, 0.0f, 0.0f))); osg::ref_ptr xAxis = new osg::Geode; osg::ref_ptr xAxisShape = new osg::ShapeDrawable(); xAxisShape->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.0f), 0.05f, 100.0f)); xAxisShape->setColor(axesColor); xAxis->addDrawable(xAxisShape.get()); osg::ref_ptr xConeShape = new osg::ShapeDrawable(); xConeShape->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 50.0f), 0.2f, 1.0f)); xConeShape->setColor(axesColor); xAxis->addDrawable(xConeShape.get()); xRotation->addChild(xAxis.get()); osg::ref_ptr yRotation = new osg::MatrixTransform; yRotation->setMatrix(osg::Matrix::rotate(osg::Vec3f(0.0f, 0.0f, 1.0f), osg::Vec3f(0.0f, 1.0f, 0.0f))); osg::ref_ptr yAxis = new osg::Geode; osg::ref_ptr yAxisShape = new osg::ShapeDrawable(); yAxisShape->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.0f), 0.05f, 100.0f)); yAxisShape->setColor(axesColor); yAxis->addDrawable(yAxisShape.get()); osg::ref_ptr yConeShape = new osg::ShapeDrawable(); yConeShape->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 50.0f), 0.2f, 1.0f)); yConeShape->setColor(axesColor); yAxis->addDrawable(yConeShape.get()); yRotation->addChild(yAxis.get()); osg::ref_ptr zAxis = new osg::Geode; osg::ref_ptr zAxisShape = new osg::ShapeDrawable(); zAxisShape->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.0f), 0.05f, 100.0f)); zAxisShape->setColor(axesColor); osg::ref_ptr zConeShape = new osg::ShapeDrawable(); zConeShape->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 50.0f), 0.2f, 1.0f)); zConeShape->setColor(axesColor); zAxis->addDrawable(zConeShape.get()); zAxis->addDrawable(zAxisShape.get()); _coordinateAxes->addChild(xRotation.get()); _coordinateAxes->addChild(yRotation.get()); _coordinateAxes->addChild(zAxis.get()); OSGWidget::fixMaterialState(_coordinateAxes); _root->addChild(_coordinateAxes); // Add axes preview osg::ref_ptr hudCamera = new osg::Camera; int width = 1024; int height = 1024; hudCamera->setProjectionMatrixAsOrtho(0, width, 0, height, 1, 100); hudCamera->setRenderOrder(osg::Camera::POST_RENDER); hudCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); hudCamera->setClearMask(GL_DEPTH_BUFFER_BIT); osg::ref_ptr axesPreview = createAxesPreview(); osg::MatrixTransform* pTM = new osg::MatrixTransform; pTM->addChild(axesPreview.get()); axesPreview->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); pTM->setMatrix(osg::Matrix::scale(osg::Vec3(width/12, width/12, width/12)) * osg::Matrix::translate(osg::Vec3(width/20, width/20, 1))); pTM->setUpdateCallback(new HudCallback(camera)); hudCamera->addChild(pTM); hudCamera->setViewMatrixAsLookAt(osg::Vec3(0, 0, 1), osg::Vec3(0, 0, 0), osg::Vec3(0, 1, 0)); // opengl default camera position _root->addChild(hudCamera.release()); // Attach a manipulator (it's usually done for us when we use viewer.run()) osg::ref_ptr tm = new osgGA::TrackballManipulator; tm->setAllowThrow(false); _view->setCameraManipulator(tm); _viewer->addView(_view); _viewer->setThreadingModel(osgViewer::CompositeViewer::SingleThreaded); _viewer->realize(); _picker = new PickHandler(this, _root); _root->addChild(_picker->getPickerRoot()); _view->addEventHandler(_picker); // 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); _verifyGroup = new osg::Group; _root->addChild(_verifyGroup.get()); _pointRoot = new osg::Group; _root->addChild(_pointRoot.get()); _pointRenderer = new TrackPointRenderer(this, _pointRoot); } OSGWidget::~OSGWidget() { } void OSGWidget::renderBaseMesh(const osg::ref_ptr vertices, const osg::ref_ptr normals) { _root->removeChild(_mesh); _mesh = new osg::Geode; osg::ref_ptr meshGeometry = new osg::Geometry; meshGeometry->setVertexArray(vertices.get()); meshGeometry->setNormalArray(normals.get(), osg::Array::BIND_PER_VERTEX); meshGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, vertices->getNumElements())); osgUtil::optimizeMesh(meshGeometry.get()); _mesh->addDrawable(meshGeometry.get()); OSGWidget::fixMaterialState(_mesh); _root->addChild(_mesh); _picker->updateRenderer(); } osg::ref_ptr OSGWidget::getMesh() { return _mesh; } osg::ref_ptr OSGWidget::getVerifyGroup() { return _verifyGroup; } PickHandler* OSGWidget::getPicker() { return _picker; } TrackPointRenderer* OSGWidget::getPointRenderer() { return _pointRenderer; } void OSGWidget::loadSteamvrThread() { if (!_steamvrLoaded) { std::vector verticesBuffer; std::vector triangleBuffer; for (unsigned int i = 0; i < sizeof(steamvrthread_VERTICES) / sizeof(float); i += 3) { Lib3MF::sPosition vertex = {steamvrthread_VERTICES[i], steamvrthread_VERTICES[i + 1], steamvrthread_VERTICES[i + 2]}; verticesBuffer.push_back(vertex); } for (unsigned int i = 0; i < sizeof(steamvrthread_TRIANGLES) / sizeof(unsigned int); i += 3) { Lib3MF::sTriangle triangle = {steamvrthread_TRIANGLES[i], steamvrthread_TRIANGLES[i + 1], steamvrthread_TRIANGLES[i + 2]}; triangleBuffer.push_back(triangle); } osg::ref_ptr vertices = new osg::Vec3Array; osg::ref_ptr normals = new osg::Vec3Array; MeshTools::calculateNormals(verticesBuffer, triangleBuffer, vertices, normals); _steamvrThreadMesh = new osg::Geometry; _steamvrThreadMesh->setVertexArray(vertices.get()); _steamvrThreadMesh->setNormalArray(normals.get(), osg::Array::BIND_PER_VERTEX); _steamvrThreadMesh->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, vertices->getNumElements())); osgUtil::optimizeMesh(_steamvrThreadMesh.get()); _steamvrLoaded = true; } } void OSGWidget::loadSteamvrCollision() { if (!_steamvrCollisionLoaded) { std::vector verticesBuffer; std::vector triangleBuffer; for (unsigned int i = 0; i < sizeof(steamvrtracker_VERTICES) / sizeof(float); i += 3) { Lib3MF::sPosition vertex = {steamvrtracker_VERTICES[i], steamvrtracker_VERTICES[i + 1], steamvrtracker_VERTICES[i + 2]}; verticesBuffer.push_back(vertex); } for (unsigned int i = 0; i < sizeof(steamvrtracker_TRIANGLES) / sizeof(unsigned int); i += 3) { Lib3MF::sTriangle triangle = {steamvrtracker_TRIANGLES[i], steamvrtracker_TRIANGLES[i + 1], steamvrtracker_TRIANGLES[i + 2]}; triangleBuffer.push_back(triangle); } osg::ref_ptr vertices = new osg::Vec3Array; osg::ref_ptr normals = new osg::Vec3Array; MeshTools::calculateNormals(verticesBuffer, triangleBuffer, vertices, normals); _steamvrTrackerMesh = new osg::Geometry; _steamvrTrackerMesh->setVertexArray(vertices.get()); _steamvrTrackerMesh->setNormalArray(normals.get(), osg::Array::BIND_PER_VERTEX); _steamvrTrackerMesh->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, vertices->getNumElements())); osgUtil::optimizeMesh(_steamvrTrackerMesh.get()); _steamvrCollisionLoaded = true; } } void OSGWidget::clear() { _root->removeChild(_mesh); } void OSGWidget::paintEvent(QPaintEvent*) { this->makeCurrent(); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); this->paintGL(); painter.end(); this->doneCurrent(); } void OSGWidget::paintGL() { _viewer->frame(); } void OSGWidget::resizeGL(int width, int height) { auto pixelRatio = this->devicePixelRatio(); this->getEventQueue()->windowResize(this->x(), this->y(), width * pixelRatio, height * pixelRatio); graphicsWindow_->resized(this->x(), this->y(), width * pixelRatio, height * pixelRatio); this->onResize(width, height); } void OSGWidget::keyPressEvent(QKeyEvent* event) { QString keyString = event->text(); const char* keyData = keyString.toLocal8Bit().data(); 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) { auto pixelRatio = this->devicePixelRatio(); this->getEventQueue()->mouseMotion(static_cast(event->position().x() * pixelRatio), static_cast(event->position().y() * pixelRatio)); } void OSGWidget::mousePressEvent(QMouseEvent* event) { 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(event->position().x() * pixelRatio), static_cast(event->position().y() * pixelRatio), button); } void OSGWidget::mouseReleaseEvent(QMouseEvent* event) { 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(event->position().x() * pixelRatio), static_cast(event->position().y() * pixelRatio), button); } void OSGWidget::wheelEvent(QWheelEvent* event) { 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; case QEvent::Resize: this->onResize(this->width(), this->height()); 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 cameras; _viewer->getCameras(cameras); assert(cameras.size() == 1); auto pixelRatio = this->devicePixelRatio(); cameras[0]->setViewport(0, 0, width * 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"); } } osg::ref_ptr OSGWidget::createAxesPreview() { osg::ref_ptr axesPreview = new osg::Group; osg::Vec4 red = osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f); osg::Vec4 green = osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f); osg::Vec4 blue = osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f); osg::ref_ptr xRotation = new osg::MatrixTransform; xRotation->setMatrix(osg::Matrix::rotate(osg::Vec3f(0.0f, 0.0f, 1.0f), osg::Vec3f(1.0f, 0.0f, 0.0f))); osg::ref_ptr xAxis = new osg::Geode; osg::ref_ptr xAxisShape = new osg::ShapeDrawable(); xAxisShape->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.5f), 0.01f, 1.0f)); xAxisShape->setColor(red); xAxis->addDrawable(xAxisShape.get()); osg::ref_ptr xConeShape = new osg::ShapeDrawable(); xConeShape->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 1.0f), 0.05f, 0.2f)); xConeShape->setColor(red); xAxis->addDrawable(xConeShape.get()); xRotation->addChild(xAxis.get()); osg::ref_ptr yRotation = new osg::MatrixTransform; yRotation->setMatrix(osg::Matrix::rotate(osg::Vec3f(0.0f, 0.0f, 1.0f), osg::Vec3f(0.0f, 1.0f, 0.0f))); osg::ref_ptr yAxis = new osg::Geode; osg::ref_ptr yAxisShape = new osg::ShapeDrawable(); yAxisShape->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.5f), 0.01f, 1.0f)); yAxisShape->setColor(green); yAxis->addDrawable(yAxisShape.get()); osg::ref_ptr yConeShape = new osg::ShapeDrawable(); yConeShape->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 1.0f), 0.05f, 0.2f)); yConeShape->setColor(green); yAxis->addDrawable(yConeShape.get()); yRotation->addChild(yAxis.get()); osg::ref_ptr zAxis = new osg::Geode; osg::ref_ptr zAxisShape = new osg::ShapeDrawable(); zAxisShape->setShape(new osg::Cylinder(osg::Vec3(0.0f, 0.0f, 0.5f), 0.01f, 1.0f)); zAxisShape->setColor(blue); osg::ref_ptr zConeShape = new osg::ShapeDrawable(); zConeShape->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 1.0f), 0.05f, 0.2f)); zConeShape->setColor(blue); zAxis->addDrawable(zConeShape.get()); zAxis->addDrawable(zAxisShape.get()); axesPreview->addChild(xRotation.get()); axesPreview->addChild(yRotation.get()); axesPreview->addChild(zAxis.get()); OSGWidget::fixMaterialState(axesPreview); return axesPreview; }