// Include own headers #include "OSGWidget.hpp" #include "PickHandler.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 namespace osgWidget { void Viewer::setupThreading() { if (_threadingModel == SingleThreaded) { if (_threadsRunning) { stopThreading(); } } else { if (!_threadsRunning) { startThreading(); } } } } 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.0f, 0.0f, 1.0f, 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); // Add axesNode under root _axesNode = osgDB::readNodeFile("../../testdata/testbutton.stl"); if (!_axesNode) { printf("Origin node not loaded, model not found\n"); } // Set material for basic lighting and enable depth tests. Else, the sphere // will suffer from rendering errors. { osg::StateSet* stateSet = _axesNode->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); } _root->addChild(_axesNode); // 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(); _storeHandler = new StoreHandler(_root); _openScadRenderer = new OpenScadRenderer(); _threeMFWriter = new ThreeMFWriter(); osg::ref_ptr picker = new PickHandler(_storeHandler, _openScadRenderer, _threeMFWriter, _axesNode); _root->addChild(picker->getOrCreateSelectionCylinder()); _view->addEventHandler(picker.get()); // 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*) { 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) { 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_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(pixelRatio * event->position().x()), static_cast(pixelRatio * event->position().y()), 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"); } }