/* * © Copyright 2014 – Urheberrechtshinweis * Alle Rechte vorbehalten / All Rights Reserved * * Programmcode ist urheberrechtlich geschuetzt. * Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner. * Keine Verwendung ohne explizite Genehmigung. * (vgl. § 106 ff UrhG / § 97 UrhG) */ #include "../../fixC11.h" #include "MapView3D.h" #include "../model/MapModelElement.h" #include "../model/MapModel.h" #include "../3D/grid/GridModel.h" #include "../3D/grid/GridRenderer.h" #include "../3D/navMesh/NavMeshModel.h" #include "../3D/navMesh/NavMeshRenderer.h" #include "../3D/floorplan/FloorplanRenderer.h" #include "../3D/floorplan/FloorplanRendererModel.h" #include MapView3D::MapView3D(QWidget *parent) : QOpenGLWidget(parent) { rot.x = 0; rot.y = 0; rot.z = 0; center.x = 0; center.y = 0; center.z = 0; scale.x = 1.0f; scale.y = 1.0f; scale.z = 1.0f; gridRenderer = new GridRenderer(); navMeshRenderer = new NavMeshRenderer(); floorplanRenderer = new FloorplanRenderer(); floorplanRendererModel = new FloorplanRendererModel(); QString style = "QPushButton:checked{\ background-color: rgb(200, 200, 230);\ border: none; \ }"; QPushButton* btnFloorplan = new QPushButton(UIHelper::getIcon("floorplan"), "", this); btnFloorplan->setGeometry(16, 16, 32, 32); btnFloorplan->setCheckable(true); btnFloorplan->setChecked(true); btnFloorplan->setStyleSheet(style); connect(btnFloorplan, &QPushButton::toggled, [btnFloorplan,this] () { emit onShow3DFloorplan(btnFloorplan->isChecked()); }); QPushButton* btnPerspective = new QPushButton(UIHelper::getIcon("perspective"), "", this); btnPerspective->setGeometry(16, 16+8+32, 32, 32); connect(btnPerspective, &QPushButton::clicked, [this] () { usePerspectiveProjection = !usePerspectiveProjection; emit update(); }); QPushButton* btnWireframe = new QPushButton(UIHelper::getIcon("wireframe"), "", this); btnWireframe->setCheckable(true); btnWireframe->setStyleSheet(style); btnWireframe->setGeometry(16, 16+8+32+8+32, 32, 32); connect(btnWireframe, &QPushButton::clicked, [this] () { useWireframe = !useWireframe; emit update(); }); QPushButton* btnDoor = new QPushButton(UIHelper::getIcon("door"), "", this); btnDoor->setCheckable(true); btnDoor->setStyleSheet(style); btnDoor->setGeometry(16, 16+8+32+8+32+8+32, 32, 32); connect(btnDoor, &QPushButton::clicked, [this] () { this->floorplanRendererModel->showDoors = !this->floorplanRendererModel->showDoors; layerChange(); emit update(); }); QPushButton* btnExp3D = new QPushButton(UIHelper::getIcon("save"), "", this); btnExp3D->setStyleSheet(style); btnExp3D->setGeometry(16, 16+8+32+8+32+8+32+8+32, 32, 32); connect(btnExp3D, &QPushButton::clicked, [this] () { floorplanRendererModel->getMesh().exportOBJsimple("/tmp/map.obj"); floorplanRendererModel->getMesh().exportOBJcomplex("/tmp/map_complex", "map_complex"); floorplanRendererModel->getMesh().exportPLY("/tmp/map.ply"); QMessageBox::information(this, "Export", "3D Model exported to /tmp/* as .obj and .ply"); }); QPushButton* btnGrid = new QPushButton(UIHelper::getIcon("grid"), "", this); btnGrid->setCheckable(true); btnGrid->setChecked(false); btnGrid->setGeometry(16+16+32, 16, 32, 32); btnGrid->setStyleSheet(style); connect(btnGrid, &QPushButton::toggled, [btnGrid, this] () { emit onShow3DGrid(btnGrid->isChecked()); }); QPushButton* btnNavMesh = new QPushButton(UIHelper::getIcon("mesh"), "", this); btnNavMesh->setCheckable(true); btnNavMesh->setChecked(false); btnNavMesh->setGeometry(16+16+32+16+32, 16, 32, 32); btnNavMesh->setStyleSheet(style); connect(btnNavMesh, &QPushButton::toggled, [btnNavMesh, this] () { emit onShow3DNavMesh(btnNavMesh->isChecked()); }); // android setAttribute(Qt::WA_AcceptTouchEvents, true); grabGesture(Qt::PanGesture); grabGesture(Qt::PinchGesture); auto format = QSurfaceFormat(); format.setRenderableType(QSurfaceFormat::OpenGL); //format.setVersion(3, 4); //format.setSamples(2); //format.setProfile(QSurfaceFormat::CompatibilityProfile); //format.setOption(QSurfaceFormat::DebugContext); format.setSamples(4); setFormat(format); auto ver = format.version(); std::cout << "OpenGL Context Version: " << ver.first << "." << ver.second << std::endl; } void MapView3D::initializeGL() { //setFormat(QGLFormat(QGL::SampleBuffers)); QOpenGLWidget::initializeGL(); initializeOpenGLFunctions(); logger = new QOpenGLDebugLogger(this); if (logger->initialize()) { connect(logger, &QOpenGLDebugLogger::messageLogged, [] (const QOpenGLDebugMessage& debugMessage) { std::cout << debugMessage.message().toStdString() << std::endl; }); logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); } // Print device info const GLubyte* vendor = glGetString(GL_VENDOR); const GLubyte* renderer = glGetString(GL_RENDERER); const GLubyte* version = glGetString(GL_VERSION); std::cout << "Device info: "; if (vendor) { std::cout << (const char*) vendor << " "; } if (renderer) { std::cout << (const char*) renderer << " "; } if (version) { std::cout << (const char*) version << " "; } std::cout << std::endl; // this should be the default!! glCullFace(GL_BACK); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); // additional settings glEnable(GL_DEPTH_TEST); glClearColor(0.5, 0.5, 0.5, 1.0); } void MapView3D::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw(); } void MapView3D::resizeGL(int width, int height) { glViewport(0, 0, width, height); } void MapView3D::mousePressEvent(QMouseEvent* e) { mouse.btn = e->button(); mouse.x = e->x(); mouse.y = e->y(); update(); } void MapView3D::mouseMoveEvent(QMouseEvent* e) { float dx = mouse.x - e->x(); float dy = mouse.y - e->y(); if (mouse.btn == 1) { rot.z -= dx/2.0f; rot.x -= dy/2.0f; } else if (mouse.btn == 4) { moveXY(dx, dy); } mouse.x = e->x(); mouse.y = e->y(); update(); } void MapView3D::moveXY(float dx, float dy) { //Point3 vec(-dx / width() * 2 * viewport.size.x, 0, +dy / height() * 2 * viewport.size.y); Point3 vec(-dx / width() * 2 * viewport.size.x, +dy / height() * 2 * viewport.size.y, 0); //Point3 vec(-dx * 2 / width() , 0, +dy * 2 / height()); vec = vec.rot(-rot.x/180*M_PI, -rot.y/180*M_PI, -rot.z/180*M_PI); vec /= scale; center += vec; } void MapView3D::mouseReleaseEvent(QMouseEvent* e) { (void) e; update(); } void MapView3D::wheelEvent(QWheelEvent* e) { float f = e->delta() / 120.0f; scale *= (f > 0) ? (1.5) : (0.75); update(); } // android bool MapView3D::event(QEvent* event) { if (event->type() == QEvent::Gesture) { return gestureEvent(static_cast(event)); //} else if (event->type() == QEvent::TouchBegin) { // return true; //} else if (event->type() == QEvent::TouchUpdate) { // return true; } else { return QWidget::event(event); } } bool MapView3D::gestureEvent(QGestureEvent* event) { if (QGesture *swipe = event->gesture(Qt::SwipeGesture)) { (void) swipe; //swipeTriggered(static_cast(swipe)); } else if (QGesture *pan = event->gesture(Qt::PanGesture)) { panTriggered(static_cast(pan)); return true; } if (QGesture *pinch = event->gesture(Qt::PinchGesture)) { pinchTriggered(static_cast(pinch)); return true; } return false; } void MapView3D::pinchTriggered(QPinchGesture* gesture) { (void) gesture; update(); } void MapView3D::panTriggered(QPanGesture* gesture) { moveXY(gesture->delta().x(), gesture->delta().y()); update(); } void MapView3D::setShowFloorplan(bool show) { this->showFloorplan = show; if (!show) {update(); return;} // refresh layerChange(); } void MapView3D::setShowGrid(bool show) { this->showGrid = show; if (!show) {update(); return;} // delete the previous grid (if any) if (gridModel) {delete gridModel; gridModel = nullptr;} // build a new model GridModel* gm = new GridModel(); Floorplan::IndoorMap* im = getModel()->getMap(); gm->rebuild(im); // remember this->gridModel = gm; // update UI update(); } void MapView3D::setShowNavMesh(bool show) { this->showNavMesh = show; if (!show) {update(); return;} // delete the previous grid (if any) if (navMeshModel) {delete navMeshModel; navMeshModel = nullptr;} // build a new model NavMeshModel* nm = new NavMeshModel(); Floorplan::IndoorMap* im = getModel()->getMap(); nm->rebuild(im); // remember this->navMeshModel = nm; // update UI update(); } void MapView3D::layerChange() { // todo: layers?? Floorplan::IndoorMap* im = getModel()->getMap(); this->floorplanRendererModel->rebuild(im); update(); } #include "misc/Shader.h" void MapView3D::draw() { static RenderSettings rs = RenderSettings(new Shader(), this); const Point3 c = center - floorplanRendererModel->getBBox().getCenter(); // view QMatrix4x4 V; V.translate(0,0,-50); // above the building V.scale(scale.x, scale.y, scale.z); V.rotate(rot.x, 1.0, 0.0, 0.0); V.rotate(rot.y, 0.0, 1.0, 0.0); V.rotate(rot.z, 0.0, 0.0, 1.0); V.translate(c.x, c.y, c.z); float farPlane = 200; // TODO // projection QMatrix4x4 P; float aspect = (float) width() / (float) height(); if (usePerspectiveProjection) { float w = width() / 30; float h = height() / 30; viewport.size.x = w; viewport.size.y = h; P.perspective(45.0f, aspect, 0.01, farPlane); } else { // default size: 50 * 50/aspect meters float w = 50.0f; float h = 50.0f * height() / width(); viewport.size.x = w; viewport.size.y = h; P.ortho(-w, +w, -h, +h, 0.1f, +farPlane); } rs.shader->bind(); rs.shader->setViewMatrix(V); rs.shader->setProjectionMatrix(P); if (showFloorplan && floorplanRendererModel) { floorplanRenderer->renderSolid(rs, floorplanRendererModel->getTriaSolid(), useWireframe ); } // // solid floorplan parts // if (showFloorplan) { // std::vector elements = getModel()->getVisibleElements(); // for (MapModelElement* el : elements) { // if (el->getMV3D() && !el->getMV3D()->isTransparent()) {el->getMV3D()->render(rs);} // } // } if (showGrid && gridModel) { //gridRenderer->paintGL(gridModel->getGrid()); gridRenderer->render(rs, gridModel); } if (showNavMesh && navMeshModel) { navMeshRenderer->render(rs, navMeshModel); } // // transparant floorplan parts // if (showFloorplan) { // std::vector elements = getModel()->getVisibleElements(); // for (MapModelElement* el : elements) { // if (el->getMV3D() && el->getMV3D()->isTransparent()) {el->getMV3D()->render(rs);} // } // } if (showFloorplan && floorplanRendererModel) { floorplanRenderer->renderTransp(rs, floorplanRendererModel->getTriaTransp(), useWireframe); } }