From 3b62f23c0e12b7cf7b620a59c47a084566398021 Mon Sep 17 00:00:00 2001 From: kazu Date: Sat, 3 Feb 2018 23:30:55 +0100 Subject: [PATCH] worked on 3D display some ui changes refactoring new icons --- IndoorMap.pro | 308 +++++++++++----------- MainController.cpp | 72 ++--- MainWindow.ui | 41 --- mapview/3D/MV3DElementFloorObstacleWall.h | 75 +----- mapview/3D/MV3DElementFloorOutline.h | 124 ++------- mapview/3D/MV3DElementStair.h | 50 +++- mapview/3D/MapView3D.cpp | 204 +++++--------- mapview/3D/MapView3D.h | 8 + mapview/3D/misc/Cube.cpp | 235 +++-------------- mapview/3D/misc/Handrail.cpp | 101 +++++++ mapview/3D/misc/Handrail.h | 25 ++ mapview/3D/misc/Outline.cpp | 73 +++++ mapview/3D/misc/Outline.h | 33 +++ mapview/3D/misc/Shader.cpp | 59 ++++- mapview/3D/misc/Shader.h | 5 + mapview/3DNavMesh/NavMeshRenderer.h | 102 +++++-- res.qrc | 4 + res/icons/floorplan.svg | 107 ++++++++ res/icons/grid.svg | 45 ++++ res/icons/mesh.svg | 95 +++++++ res/icons/perspective.svg | 53 ++++ 21 files changed, 1054 insertions(+), 765 deletions(-) create mode 100644 mapview/3D/misc/Handrail.cpp create mode 100644 mapview/3D/misc/Handrail.h create mode 100644 mapview/3D/misc/Outline.cpp create mode 100644 mapview/3D/misc/Outline.h create mode 100644 res/icons/floorplan.svg create mode 100644 res/icons/grid.svg create mode 100644 res/icons/mesh.svg create mode 100644 res/icons/perspective.svg diff --git a/IndoorMap.pro b/IndoorMap.pro index 51964b2..0abd632 100644 --- a/IndoorMap.pro +++ b/IndoorMap.pro @@ -17,168 +17,164 @@ DEFINES += WITH_ASSERTIONS DEFINES += WITH_DEBUG_LOG INCLUDEPATH += \ - ../ + ../ SOURCES += \ - lib/gpc/gpc.cpp \ - main.cpp \ - MainWindow.cpp \ - MainController.cpp \ - mapview/2D/tools/ToolSelector.cpp \ - mapview/2D/tools/Tool.cpp \ - params/ElementParamWidget.cpp \ - params/LayerParamWidget.cpp \ - params/ActionWidget.cpp \ - params/ToolBox.cpp \ - mapview/model/MapModel.cpp \ - tree/MapTreeModel.cpp \ - mapview/3D/MapView3D.cpp \ - params/StairBuilder.cpp \ - mapview/2D/tools/Tools.cpp \ - params/LayerTree.cpp \ - mapview/2D/tools/ToolMeasure.cpp \ - params/MetaEditWidget.cpp \ - params/MetaEditModel.cpp \ - mapview/2D/MapView2D.cpp \ - misc/LINTView.cpp \ - mapview/3DNavMesh/QNavMeshSettings.cpp \ - mapview/3D/misc/Cube.cpp \ - mapview/3D/misc/Shader.cpp \ - mapview/3D/misc/Window.cpp + lib/gpc/gpc.cpp \ + main.cpp \ + MainWindow.cpp \ + MainController.cpp \ + mapview/2D/tools/ToolSelector.cpp \ + mapview/2D/tools/Tool.cpp \ + params/ElementParamWidget.cpp \ + params/LayerParamWidget.cpp \ + params/ActionWidget.cpp \ + params/ToolBox.cpp \ + mapview/model/MapModel.cpp \ + tree/MapTreeModel.cpp \ + mapview/3D/MapView3D.cpp \ + params/StairBuilder.cpp \ + mapview/2D/tools/Tools.cpp \ + params/LayerTree.cpp \ + mapview/2D/tools/ToolMeasure.cpp \ + params/MetaEditWidget.cpp \ + params/MetaEditModel.cpp \ + mapview/2D/MapView2D.cpp \ + misc/LINTView.cpp \ + mapview/3DNavMesh/QNavMeshSettings.cpp \ + mapview/3D/misc/Cube.cpp \ + mapview/3D/misc/Shader.cpp \ + mapview/3D/misc/Window.cpp \ + mapview/3D/misc/Outline.cpp \ + mapview/3D/misc/Handrail.cpp HEADERS += MainWindow.h \ - mapview/model/MapLayer.h \ - mapview/model/MapModel.h \ - tree/MapTreeModel.h \ - MainController.h \ - mapview/model/MapLayers.h \ - params/ElementParamWidget.h \ - params/LayerParamWidget.h \ - params/ActionWidget.h \ - params/ToolBoxWidget.h \ - UIHelper.h \ - mapview/model/MapModelElement.h \ - mapview/model/IHasAttributes.h \ - mapview/model/IHasMAC.h \ - mapview/model/IHasMaterial.h \ - mapview/model/IHasName.h \ - mapview/model/IHasObstacleType.h \ - mapview/model/IHasPosition3D.h \ - mapview/2D/MV2DElementFloorObstacleLine.h \ - mapview/2D/MV2DElement.h \ - mapview/2D/MV2DElementFloorOutlinePolygon.h \ - mapview/2D/MV2DElementBeacon.h \ - mapview/2D/MV2DElementAccessPoint.h \ - mapview/2D/MV2DElementFloorObstacleCircle.h \ - mapview/2D/MV2DElementFloorObstacleDoor.h \ - mapview/2D/MapViewElementHelper.h \ - mapview/2D/MV2DElementFloorUnderlay.h \ - mapview/2D/MV2DElementPOI.h \ - mapview/2D/MV2DElementStair.h \ - mapview/2D/tools/ToolMoveMap.h \ - mapview/2D/tools/Tools.h \ - mapview/2D/tools/ToolRuler.h \ - mapview/2D/tools/ToolMapZoom.h \ - mapview/2D/tools/ToolSelector.h \ - mapview/2D/tools/ToolMapGrid.h \ - mapview/2D/tools/Tool.h \ - mapview/model/MMFloorObstacleCircle.h \ - mapview/model/MMFloorObstacleLine.h \ - mapview/model/MMFloorOutlinePolygon.h \ - mapview/model/MMFloor.h \ - mapview/model/MMFloors.h \ - mapview/model/MMFloorObstacles.h \ - mapview/model/MMFloorOutline.h \ - mapview/model/MMRoot.h \ - mapview/model/MMFloorAccessPoint.h \ - mapview/model/MMFloorBeacon.h \ - mapview/model/MMFloorAccessPoints.h \ - mapview/model/MMFloorBeacons.h \ - mapview/model/IHasFile.h \ - mapview/model/MMFloorUnderlayImage.h \ - mapview/model/IHasParams.h \ - mapview/model/MMFloorUnderlays.h \ - mapview/model/MMFloorPOIs.h \ - mapview/model/MMFloorPOI.h \ - mapview/3D/MapView3D.h \ - mapview/3D/MV3DElement.h \ - mapview/3D/MV3DElementFloorObstacleWall.h \ - mapview/3D/MV3DElementFloorObstaclePillar.h \ - mapview/3D/MV3DElementAccessPoint.h \ - mapview/3D/misc/Cube.h \ - mapview/3D/MV3DElementFloorOutlinePolygon.h \ - mapview/3D/MV3DElementFloorOutline.h \ - mapview/3D/misc/Polygon.h \ - mapview/grid/MapView3DGrid.h \ - mapview/grid/MapModelGrid.h \ - mapview/3DGrid/GridModel.h \ - mapview/3DGrid/GridRenderer.h \ - mapview/3DGrid/MyNode.h \ - mapview/3D/MV3DElementStair.h \ - mapview/model/MMFloorStair.h \ - mapview/model/MMFloorStairs.h \ - params/StairBuilder.h \ - misc/GnuplotExport.h \ - exp.h \ - ray.h \ - mapview/model/MMFloorObstacleDoor.h \ - mapview/elements/MV2DElementFloorObstacleDoor.h \ - mapview/model/IHasDoorType.h \ - mapview/3D/MV3DElementFloorObstacleDoor.h \ - mapview/3D/misc/Plane.h \ - mapview/elements/HasMoveableNodes.h \ - mapview/2D/MapView2D.h \ - mapview/2D/Painter.h \ - mapview/2D/Scaler.h \ - mapview/2D/MV2DElementElevator.h \ - mapview/model/MMFloorElevators.h \ - mapview/model/MMFloorElevator.h \ - params/LayerTree.h \ - params/EditFields.h \ - mapview/2D/ClickDist.h \ - mapview/2D/tools/ToolMeasure.h \ - mapview/model/IHasEditableMeta.h \ - params/MetaEditWidget.h \ - params/MetaEditModel.h \ - mapview/model/MapLayerListener.h \ - mapview/model/MapModelListener.h \ - mapview/model/MMFloorOutlinePolygonCombined.h \ - mapview/model/MMFloorFingerprints.h \ - mapview/model/MMFloorFingerprintLocation.h \ - mapview/2D/MV2DElementFingerprintLocation.h \ + mapview/model/MapLayer.h \ + mapview/model/MapModel.h \ + tree/MapTreeModel.h \ + MainController.h \ + mapview/model/MapLayers.h \ + params/ElementParamWidget.h \ + params/LayerParamWidget.h \ + params/ActionWidget.h \ + params/ToolBoxWidget.h \ + UIHelper.h \ + mapview/model/MapModelElement.h \ + mapview/model/IHasAttributes.h \ + mapview/model/IHasMAC.h \ + mapview/model/IHasMaterial.h \ + mapview/model/IHasName.h \ + mapview/model/IHasObstacleType.h \ + mapview/model/IHasPosition3D.h \ + mapview/2D/MV2DElementFloorObstacleLine.h \ + mapview/2D/MV2DElement.h \ + mapview/2D/MV2DElementFloorOutlinePolygon.h \ + mapview/2D/MV2DElementBeacon.h \ + mapview/2D/MV2DElementAccessPoint.h \ + mapview/2D/MV2DElementFloorObstacleCircle.h \ + mapview/2D/MV2DElementFloorObstacleDoor.h \ + mapview/2D/MapViewElementHelper.h \ + mapview/2D/MV2DElementFloorUnderlay.h \ + mapview/2D/MV2DElementPOI.h \ + mapview/2D/MV2DElementStair.h \ + mapview/2D/tools/ToolMoveMap.h \ + mapview/2D/tools/Tools.h \ + mapview/2D/tools/ToolRuler.h \ + mapview/2D/tools/ToolMapZoom.h \ + mapview/2D/tools/ToolSelector.h \ + mapview/2D/tools/ToolMapGrid.h \ + mapview/2D/tools/Tool.h \ + mapview/model/MMFloorObstacleCircle.h \ + mapview/model/MMFloorObstacleLine.h \ + mapview/model/MMFloorOutlinePolygon.h \ + mapview/model/MMFloor.h \ + mapview/model/MMFloors.h \ + mapview/model/MMFloorObstacles.h \ + mapview/model/MMFloorOutline.h \ + mapview/model/MMRoot.h \ + mapview/model/MMFloorAccessPoint.h \ + mapview/model/MMFloorBeacon.h \ + mapview/model/MMFloorAccessPoints.h \ + mapview/model/MMFloorBeacons.h \ + mapview/model/IHasFile.h \ + mapview/model/MMFloorUnderlayImage.h \ + mapview/model/IHasParams.h \ + mapview/model/MMFloorUnderlays.h \ + mapview/model/MMFloorPOIs.h \ + mapview/model/MMFloorPOI.h \ + mapview/3D/MapView3D.h \ + mapview/3D/MV3DElement.h \ + mapview/3D/MV3DElementFloorObstacleWall.h \ + mapview/3D/MV3DElementFloorObstaclePillar.h \ + mapview/3D/MV3DElementAccessPoint.h \ + mapview/3D/misc/Cube.h \ + mapview/3D/MV3DElementFloorOutlinePolygon.h \ + mapview/3D/MV3DElementFloorOutline.h \ + mapview/3D/misc/Polygon.h \ + mapview/3DGrid/GridModel.h \ + mapview/3DGrid/GridRenderer.h \ + mapview/3DGrid/MyNode.h \ + mapview/3D/MV3DElementStair.h \ + mapview/model/MMFloorStair.h \ + mapview/model/MMFloorStairs.h \ + params/StairBuilder.h \ + misc/GnuplotExport.h \ + mapview/model/MMFloorObstacleDoor.h \ + mapview/model/IHasDoorType.h \ + mapview/3D/MV3DElementFloorObstacleDoor.h \ + mapview/3D/misc/Plane.h \ mapview/2D/MapView2D.h \ + mapview/2D/Painter.h \ + mapview/2D/Scaler.h \ + mapview/2D/MV2DElementElevator.h \ + mapview/model/MMFloorElevators.h \ + mapview/model/MMFloorElevator.h \ + params/LayerTree.h \ + params/EditFields.h \ + mapview/2D/ClickDist.h \ + mapview/2D/tools/ToolMeasure.h \ + mapview/model/IHasEditableMeta.h \ + params/MetaEditWidget.h \ + params/MetaEditModel.h \ + mapview/model/MapLayerListener.h \ + mapview/model/MapModelListener.h \ + mapview/model/MMFloorOutlinePolygonCombined.h \ + mapview/model/MMFloorFingerprints.h \ + mapview/model/MMFloorFingerprintLocation.h \ + mapview/2D/MV2DElementFingerprintLocation.h \ mapview/3D/MV3DElementFingerprintLocation.h \ - mapview/model/MMFloorGroundTruthPoints.h \ - mapview/model/MMFloorGroundTruthPoint.h \ - mapview/2D/MV2DElementGroundTruthPoint.h \ - misc/LINTView.h \ - mapview/2D/tools/ToolNewElement.h \ - mapview/2D/tools/ToolNewDoor.h \ - mapview/2D/tools/ToolNewWall.h \ - mapview/2D/tools/ToolNewStair.h \ - mapview/2D/tools/ToolNewElevator.h \ - mapview/2D/tools/ToolNewOutline.h \ - mapview/model/MMRegistration.h \ - mapview/model/MMRegistrationPoint.h \ - mapview/2D/MV2DElementRegistrationPoint.h \ - mapview/3D/MV3DElementRegistrationPoint.h \ - mapview/2D/tools/ToolNewFingerprint.h \ - mapview/2D/tools/ToolNewAccessPoint.h \ - mapview/2D/tools/ToolNewBeacon.h \ - mapview/2D/tools/ToolNewGroundTruth.h \ - mapview/2D/tools/ToolNewPOI.h \ - mapview/3D/MV3DElementElevator.h \ - mapview/3DNavMesh/NavMeshView.h \ - mapview/3DNavMesh/NavMeshRenderer.h \ - mapview/3DNavMesh/NavMeshModel.h \ - mapview/3DNavMesh/QNavMeshSettings.h \ - fixC11.h \ - mapview/3D/misc/Shader.h \ - mapview/3D/misc/Camera.h \ - mapview/3D/misc/Renderable3D.h \ - mapview/3D/misc/Window.h + mapview/model/MMFloorGroundTruthPoints.h \ + mapview/model/MMFloorGroundTruthPoint.h \ + mapview/2D/MV2DElementGroundTruthPoint.h \ + misc/LINTView.h \ + mapview/2D/tools/ToolNewElement.h \ + mapview/2D/tools/ToolNewDoor.h \ + mapview/2D/tools/ToolNewWall.h \ + mapview/2D/tools/ToolNewStair.h \ + mapview/2D/tools/ToolNewElevator.h \ + mapview/2D/tools/ToolNewOutline.h \ + mapview/model/MMRegistration.h \ + mapview/model/MMRegistrationPoint.h \ + mapview/2D/MV2DElementRegistrationPoint.h \ + mapview/3D/MV3DElementRegistrationPoint.h \ + mapview/2D/tools/ToolNewFingerprint.h \ + mapview/2D/tools/ToolNewAccessPoint.h \ + mapview/2D/tools/ToolNewBeacon.h \ + mapview/2D/tools/ToolNewGroundTruth.h \ + mapview/2D/tools/ToolNewPOI.h \ + mapview/3D/MV3DElementElevator.h \ + mapview/3DNavMesh/NavMeshRenderer.h \ + mapview/3DNavMesh/NavMeshModel.h \ + mapview/3DNavMesh/QNavMeshSettings.h \ + fixC11.h \ + mapview/3D/misc/Shader.h \ + mapview/3D/misc/Camera.h \ + mapview/3D/misc/Renderable3D.h \ + mapview/3D/misc/Window.h \ + mapview/3D/misc/Outline.h \ + mapview/3D/misc/Handrail.h FORMS += MainWindow.ui @@ -188,8 +184,8 @@ SOURCES += \ ../Indoor/lib/Recast/*.cpp RESOURCES += \ - res.qrc + res.qrc DISTFILES += \ - res/icons/polygon.svg + res/icons/polygon.svg diff --git a/MainController.cpp b/MainController.cpp index 6841ed4..e8383e0 100644 --- a/MainController.cpp +++ b/MainController.cpp @@ -93,6 +93,10 @@ MainController::MainController() { connect(mw, SIGNAL(onShow3DGrid(bool)), this, SLOT(onSetShow3DGrid(bool))); connect(mw, SIGNAL(onShow3DNavMesh(bool)), this, SLOT(onSetShow3DNavMesh(bool))); + connect(mapView3D, SIGNAL(onShow3DFloorplan(bool)), this, SLOT(onSetShow3DFloorplan(bool))); + connect(mapView3D, SIGNAL(onShow3DGrid(bool)), this, SLOT(onSetShow3DGrid(bool))); + connect(mapView3D, SIGNAL(onShow3DNavMesh(bool)), this, SLOT(onSetShow3DNavMesh(bool))); + // 3D grid view config connect(mw, &MainWindow::onGridNodeColorImp, [&] () {mw->getMapView3D()->getGridRenderer()->setNodeColorMode(GridRendererColorMode::SHOW_NODE_IMPORTANCE);} ); connect(mw, &MainWindow::onGridNodeColorType, [&] () {mw->getMapView3D()->getGridRenderer()->setNodeColorMode(GridRendererColorMode::SHOW_NODE_TYPE);} ); @@ -104,8 +108,8 @@ MainController::MainController() { //mapModel->load("/apps/map24b.xml"); //mapModel->load("/apps/SHL41.xml"); - //mapModel->load("/mnt/vm/paper/diss/data/maps/SHL41_nm.xml"); - mapModel->load("/apps/paper/diss/data/maps/SHL41_nm.xml"); + mapModel->load("/mnt/vm/paper/diss/data/maps/SHL41_nm.xml"); + //mapModel->load("/apps/paper/diss/data/maps/SHL41_nm.xml"); //mapModel->load("/apps/paper/diss/data/maps/map_elevator2.xml"); //mapModel->load("/apps/paper/diss/data/maps/map_issue6.xml"); @@ -117,7 +121,7 @@ MainController::MainController() { //mapModel->load("../IndoorMap/maps/APs.xml"); //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/CAR/CAR9.xml"); - //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/UAH/UAH9.xml"); + //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/UAH/UAH9.xml"); //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/UJI-TI/UJI-TI4.xml"); //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/UJI-UB/UJI-UB4.xml"); //mapModel->load("/mnt/data/workspaces/Indoor/tests/data/WalkHeadingMap.xml"); @@ -128,26 +132,26 @@ MainController::MainController() { } void MainController::onLayerChanged(MapLayer* layer) { - (void) layer; - mw->getMapView2D()->update(); - mw->getMapView3D()->update(); + (void) layer; + mw->getMapView2D()->update(); + mw->getMapView3D()->update(); mw->getLINT()->update(mapModel->getMap()); } void MainController::onLayerElementAdded(MapLayer* layer, MapModelElement* elem) { - (void) layer; - mapSelector->focus(mw->getMapView2D(), elem); + (void) layer; + mapSelector->focus(mw->getMapView2D(), elem); mw->getLINT()->update(mapModel->getMap()); } void MainController::onLayerElementRemoved(MapLayer* layer, const MapModelElement* elem) { - (void) layer; - (void) elem; + (void) layer; + (void) elem; } void MainController::onLayerVisibilityChanged(MapLayer *layer, const bool visible) { - (void) layer; - (void) visible; + (void) layer; + (void) visible; } void MainController::onSetShow3DFloorplan(bool show) { @@ -163,40 +167,40 @@ void MainController::onSetShow3DNavMesh(bool show) { } void MainController::layerSelected(QModelIndex idx) { - mw->getMapView2D()->layerChange(); - MapLayer* ml = static_cast(idx.internalPointer()); - mapModel->setSelectedLayer(ml); - mw->getMapView2D()->layerChange(); - mw->getMapView3D()->layerChange(); - mw->getLayerParamWidget()->setElement(ml); - mw->getToolBoxWidget()->setSelectedLayer(ml); + mw->getMapView2D()->layerChange(); + MapLayer* ml = static_cast(idx.internalPointer()); + mapModel->setSelectedLayer(ml); + mw->getMapView2D()->layerChange(); + mw->getMapView3D()->layerChange(); + mw->getLayerParamWidget()->setElement(ml); + mw->getToolBoxWidget()->setSelectedLayer(ml); mw->getLINT()->update(mapModel->getMap()); } void MainController::curMapElementChanged() { - mw->getElementParamWidget()->refresh(); + mw->getElementParamWidget()->refresh(); mw->getLINT()->update(mapModel->getMap()); } void MainController::mapElementSelected(MapModelElement* el) { - mw->getElementParamWidget()->setElement(el); + mw->getElementParamWidget()->setElement(el); mw->getLINT()->update(mapModel->getMap()); } void MainController::onMapModelAboutToReset() { - mw->getLayerParamWidget()->setElement(nullptr); - mw->getToolBoxWidget()->setSelectedLayer(nullptr); - mw->getMapView2D()->update(); + mw->getLayerParamWidget()->setElement(nullptr); + mw->getToolBoxWidget()->setSelectedLayer(nullptr); + mw->getMapView2D()->update(); mw->getMapView3D()->update(); //mw->getToolBoxWidget()-> } void MainController::onMapModelNeedsRepaint() { - mw->getMapView2D()->update(); + mw->getMapView2D()->update(); mw->getLINT()->update(mapModel->getMap()); } void MainController::onMapModelReset() { - mw->getTree()->expandAll(); + mw->getTree()->expandAll(); mw->getLINT()->update(mapModel->getMap()); } @@ -209,16 +213,16 @@ void MainController::onNew() { } void MainController::onLoad() { - QString file = QFileDialog::getOpenFileName(mw, "open a map"); - if (file != "") { - mapModel->load(file.toStdString()); + QString file = QFileDialog::getOpenFileName(mw, "open a map"); + if (file != "") { + mapModel->load(file.toStdString()); mw->getLINT()->update(mapModel->getMap()); - } + } } void MainController::onSave() { - QString file = QFileDialog::getSaveFileName(mw, "save the map"); - if (file != "") { - mapModel->save(file.toStdString()); - } + QString file = QFileDialog::getSaveFileName(mw, "save the map"); + if (file != "") { + mapModel->save(file.toStdString()); + } } diff --git a/MainWindow.ui b/MainWindow.ui index c0a76bd..435b1ba 100644 --- a/MainWindow.ui +++ b/MainWindow.ui @@ -17,47 +17,6 @@ - - - - 0 - 0 - 777 - 30 - - - - - &View - - - - &3D - - - - - - - - - - - &Grid - - - - &node color - - - - - - - - - - true diff --git a/mapview/3D/MV3DElementFloorObstacleWall.h b/mapview/3D/MV3DElementFloorObstacleWall.h index a3435f2..ef5dd11 100644 --- a/mapview/3D/MV3DElementFloorObstacleWall.h +++ b/mapview/3D/MV3DElementFloorObstacleWall.h @@ -6,11 +6,10 @@ #include "misc/Cube.h" #include "misc/Window.h" +#include "misc/Handrail.h" #include "MV3DElement.h" - - class MV3DElementFloorObstacleWall : public MV3DElement { Floorplan::Floor* f; @@ -52,9 +51,10 @@ protected: const Point3 pos(cen2.x, cen2.y, atHeight + height/2); // div by 2.01 to prevent overlapps and z-fi - const float sx = from.getDistance(to) / 2.02f; - const float sy = thickness_m / 2.02f; - const float sz = height / 2.02f; // prevent overlaps + const float div = 2.015f; // prevent overlaps + const float sx = from.getDistance(to) / div; + const float sy = thickness_m / div; + const float sz = height / div; const Point3 size(sx, sy, sz); const Point3 rot(0,0,deg); @@ -72,71 +72,6 @@ protected: }; - struct Handrail : public Renderable3D { - - Point2 from; - Point2 to; - float atHeight; - float height; - - Handrail(const Point2 from, const Point2 to, float atHeight, float height) : - from(from), to(to), atHeight(atHeight), height(height) {;} - - void render(const RenderSettings& rs) override { - - float y1 = atHeight; - float y2 = atHeight + height; - - // polygon edges - Point3 p1 = Point3(from.x, y1, from.y); - Point3 p2 = Point3(to.x, y1, to.y); - - Point3 p3 = Point3(from.x, y2, from.y); - Point3 p4 = Point3(to.x, y2, to.y); - - /* - - TODO_GL - - glDisable(GL_LIGHTING); - glBegin(GL_LINES); - - glColor3f(0.9, 0.9, 0.9); - - // top - glVertex3f(p3.x, p3.y, p3.z); - glVertex3f(p4.x, p4.y, p4.z); - - // start bar - glVertex3f(p1.x, p1.y, p1.z); - glVertex3f(p3.x, p3.y, p3.z); - - // end bar - glVertex3f(p2.x, p2.y, p2.z); - glVertex3f(p4.x, p4.y, p4.z); - - glColor3f(0.6, 0.6, 0.6); - - // intermediate bars - const Point3 d1 = p2-p1; - const Point3 d2 = p4-p3; - const int numBars = d2.length() / 1; - for (int i = 1; i < numBars; ++i) { - const Point3 s = p1 + d1 * i / numBars; - const Point3 e = p3 + d2 * i / numBars; - glVertex3f(s.x, s.y, s.z); - glVertex3f(e.x, e.y, e.z); - } - - glEnd(); - glEnable(GL_LIGHTING); - - */ - - } - - }; - /** repaint me */ void render(const RenderSettings& rs) override { diff --git a/mapview/3D/MV3DElementFloorOutline.h b/mapview/3D/MV3DElementFloorOutline.h index 82a1d26..27e8e91 100644 --- a/mapview/3D/MV3DElementFloorOutline.h +++ b/mapview/3D/MV3DElementFloorOutline.h @@ -8,114 +8,44 @@ #include "../../lib/gpc/gpc.h" #include "misc/Polygon.h" - -#include "misc/Shader.h" +#include "misc/Outline.h" class MV3DElementFloorOutline : public MV3DElement { Floorplan::Floor* f; Floorplan::FloorOutline* out; - struct ToRender { + struct Temp { Point2 cacheSum; Polygon* pol = nullptr; std::vector> trias; }; - std::unordered_map elements; + Temp tempIndoor; + Temp tempOutdoor; + + Outline outlineIndoor; + Outline outlineOutdoor; public: /** ctor */ MV3DElementFloorOutline(Floorplan::Floor* f, Floorplan::FloorOutline* out) : f(f), out(out) { - ; + + outlineOutdoor.setColor(0.0, 0.5, 0.0); + outlineIndoor.setColor(0.2, 0.2, 0.2); + } protected: - - - - /** repaint me */ void render(const RenderSettings& rs) override { rebuildIfNeeded(); - rs.shader->bind(); - - glDisable(GL_CULL_FACE); - - QMatrix4x4 mat; - rs.shader->setModelMatrix(mat); - - for (auto& it : elements) { - - //Polygon& pol = it.second->pol; - std::vector>& trias = it.second->trias; - - if (it.first == "outdoor") { - rs.shader->setColor(0.0, 0.5, 0.0); - } else { - rs.shader->setColor(0.2, 0.2, 0.2); - } - - std::vector values; - std::vector normals; - - for (const std::vector& tria : trias) { - for (int i = 2; i < tria.size(); ++i) { - - const Point3 p1 = tria[i-2]; - const Point3 p2 = tria[i-1]; - const Point3 p3 = tria[i-0]; - - values.push_back(p1.x); values.push_back(p1.y); values.push_back(p1.z); - values.push_back(p2.x); values.push_back(p2.y); values.push_back(p2.z); - values.push_back(p3.x); values.push_back(p3.y); values.push_back(p3.z); - - normals.push_back(0); normals.push_back(1); normals.push_back(0); - normals.push_back(0); normals.push_back(1); normals.push_back(0); - normals.push_back(0); normals.push_back(1); normals.push_back(0); - - } - } - - rs.shader->setVertices(values.data()); - rs.shader->setNormals(normals.data()); - - glDrawArrays(GL_TRIANGLES, 0, values.size() / 3); - rs.shader->unsetVertices(); - rs.shader->unsetNormals(); - - /* - - TODO_GL - - if (it.first == "outdoor") { - glColor3f(0.0, 0.5, 0.0); - } else { - glColor3f(0.2, 0.2, 0.2); - } - - glDisable(GL_CULL_FACE); - for (const std::vector& tria : trias) { - glNormal3f(0, 1, 0); - glBegin(GL_TRIANGLE_STRIP); - for (const Point3& p3 : tria) { - glVertex3f(p3.x, p3.z, p3.y); - } - glEnd(); - } - glEnable(GL_CULL_FACE); - - */ - - } - - glEnable(GL_CULL_FACE); - - rs.shader->release(); + outlineIndoor.render(rs); + outlineOutdoor.render(rs); } @@ -124,17 +54,12 @@ protected: auto filterIndoor = [] (const Floorplan::FloorOutlinePolygon* p) {return p->outdoor == false;}; auto filterOutdoor = [] (const Floorplan::FloorOutlinePolygon* p) {return p->outdoor == true;}; - if (elements.empty()) { - elements["indoor"] = new ToRender(); - elements["outdoor"] = new ToRender(); - } - - rebuildIfNeeded(filterIndoor, elements["indoor"]); - rebuildIfNeeded(filterOutdoor, elements["outdoor"]); + rebuildIfNeeded(filterIndoor, outlineIndoor, tempIndoor); + rebuildIfNeeded(filterOutdoor, outlineOutdoor, tempOutdoor); } - template void rebuildIfNeeded(Filter include, ToRender* dst) { + template void rebuildIfNeeded(Filter include, Outline& dst, Temp& tmp) { const std::vector& polys = *out; @@ -148,25 +73,28 @@ protected: } // already up to date? - if (cacheSum == dst->cacheSum) {return;} - dst->cacheSum = cacheSum; + if (cacheSum == tmp.cacheSum) {return;} + tmp.cacheSum = cacheSum; // rebuild std::vector add; std::vector rem; - if (dst->pol) {delete dst->pol;} - dst->pol = new Polygon(); + if (tmp.pol) {delete tmp.pol;} + tmp.pol = new Polygon(); for (Floorplan::FloorOutlinePolygon* poly : polys) { if (!include(poly)) {continue;} switch (poly->method) { - case Floorplan::OutlineMethod::ADD: dst->pol->add(poly->poly); break; - case Floorplan::OutlineMethod::REMOVE: dst->pol->remove(poly->poly); break; + case Floorplan::OutlineMethod::ADD: tmp.pol->add(poly->poly); break; + case Floorplan::OutlineMethod::REMOVE: tmp.pol->remove(poly->poly); break; default: throw 1; } } - dst->trias = dst->pol->get(f->atHeight); + //dst->trias = dst->pol->get(f->atHeight); + std::vector> trias = tmp.pol->get(f->atHeight); + dst.clear(); + dst.add(trias); } diff --git a/mapview/3D/MV3DElementStair.h b/mapview/3D/MV3DElementStair.h index 76e8036..4e9a411 100644 --- a/mapview/3D/MV3DElementStair.h +++ b/mapview/3D/MV3DElementStair.h @@ -5,6 +5,7 @@ #include "misc/Cube.h" #include "MV3DElement.h" +#include "misc/Shader.h" class MV3DElementStair : public MV3DElement { @@ -24,7 +25,54 @@ protected: /** repaint me */ void render(const RenderSettings& rs) override { - glDisable(GL_CULL_FACE); + //glDisable(GL_CULL_FACE); + + + std::vector vertices; + std::vector normals; + + const std::vector parts = stair->getParts(); + const std::vector quads = Floorplan::getQuads(parts, floor); + + for (int i = 0; i < (int) parts.size(); ++i) { + + //const Floorplan::StairPart& part = parts[i]; + const Floorplan::Quad3& quad = quads[i]; + + //const Floorplan::Quad3 quad = part.getQuad(floor); + const Point3 p1 = quad.p2-quad.p1; + const Point3 p2 = quad.p4-quad.p1; + + Point3 n = Math::normal(p1,p2); + if (n.z < 0) {n = -n;} + + vertices.insert(vertices.end(), {quad.p1.x, quad.p1.y, quad.p1.z}); + vertices.insert(vertices.end(), {quad.p2.x, quad.p2.y, quad.p2.z}); + vertices.insert(vertices.end(), {quad.p3.x, quad.p3.y, quad.p3.z}); + + vertices.insert(vertices.end(), {quad.p3.x, quad.p3.y, quad.p3.z}); + vertices.insert(vertices.end(), {quad.p4.x, quad.p4.y, quad.p4.z}); + vertices.insert(vertices.end(), {quad.p1.x, quad.p1.y, quad.p1.z}); + + normals.insert(normals.end(), {n.x, n.y, n.z}); + normals.insert(normals.end(), {n.x, n.y, n.z}); + normals.insert(normals.end(), {n.x, n.y, n.z}); + + normals.insert(normals.end(), {n.x, n.y, n.z}); + normals.insert(normals.end(), {n.x, n.y, n.z}); + normals.insert(normals.end(), {n.x, n.y, n.z}); + + } + + rs.shader->bind(); + rs.shader->setModelMatrix(QMatrix4x4()); + rs.shader->setColor(1.0, 0.55, 0.55); + rs.shader->setVertices(vertices.data()); + rs.shader->setNormals(normals.data()); + glDrawArrays(GL_TRIANGLES, 0, vertices.size()/3); + rs.shader->unsetVertices(); + rs.shader->unsetNormals(); + rs.shader->release(); /* TODO_GL diff --git a/mapview/3D/MapView3D.cpp b/mapview/3D/MapView3D.cpp index e896613..d1e7aab 100644 --- a/mapview/3D/MapView3D.cpp +++ b/mapview/3D/MapView3D.cpp @@ -11,6 +11,8 @@ #include "../3DNavMesh/NavMeshModel.h" #include "../3DNavMesh/NavMeshRenderer.h" +#include + MapView3D::MapView3D(QWidget *parent) : QOpenGLWidget(parent) { rot.x = 0; @@ -21,12 +23,51 @@ MapView3D::MapView3D(QWidget *parent) : QOpenGLWidget(parent) { center.y = 0; center.z = 0; - scale.x = 0.05f; - scale.y = 0.05f; - scale.z = 0.05f; + scale.x = 1.0f; + scale.y = 1.0f; + scale.z = 1.0f; gridRenderer = new GridRenderer(); + 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* 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()); + }); + } @@ -36,53 +77,13 @@ void MapView3D::initializeGL() { QOpenGLWidget::initializeGL(); - glCullFace(GL_FRONT); + // this should be the default!! + glCullFace(GL_BACK); glFrontFace(GL_CCW); - //glDisable(GL_CULL_FACE); glEnable(GL_CULL_FACE); - // culling, lighting, depth-test, ... + // additional settings glEnable(GL_DEPTH_TEST); - //glShadeModel(GL_SMOOTH); - -// glEnable(GL_MULTISAMPLE); -// glEnable(GL_LINE_SMOOTH); - - /* - - TODO_GL - - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_LIGHT1); - - -// GLfloat light0_position [] = {+50, 50, +50, 1}; -// GLfloat light1_position [] = {-50, 50, -50, 1}; - -// glLightfv ( GL_LIGHT0, GL_POSITION, light0_position ); -// glLightfv ( GL_LIGHT1, GL_POSITION, light1_position ); - - GLfloat light_diffuse []={ 0.7, 0.7, 0.7, 1.0 }; - glLightfv ( GL_LIGHT0, GL_DIFFUSE, light_diffuse ); - glLightfv ( GL_LIGHT1, GL_DIFFUSE, light_diffuse ); - - - // otherwise scaling the scene kills lighting normals! - glEnable(GL_NORMALIZE); - - // allow using glColor3(r,g,b) - glEnable(GL_COLOR_MATERIAL); - -// GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; -// GLfloat mat_shininess[] = { 50.0 }; -// glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); -// glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); - - */ - - // background color - //glClearColor(Qt::white); glClearColor(1,1,1,1); } @@ -91,71 +92,13 @@ void MapView3D::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - /* - - TODO_GL - - glLoadIdentity(); - - glScalef(+1, -1, +1); - - // 3) scale - glScalef(scale.x, scale.y, scale.z); - - // 2) rotate around center - glRotatef(rot.x, 1.0, 0.0, 0.0); - glRotatef(rot.z, 0.0, 1.0, 0.0); - glRotatef(rot.y, 0.0, 0.0, 1.0); - - // 1) post translate (mouse moving) - glTranslatef(center.x, center.z, center.y); - - // 0) swap the y axis - //glScalef(+1, -1, +1); - - GLfloat light0_position [] = {100, 50, 100, 1}; - glLightfv ( GL_LIGHT0, GL_POSITION, light0_position ); - GLfloat light1_position [] = {0, 50, 0, 1}; - glLightfv ( GL_LIGHT1, GL_POSITION, light1_position ); - - -// // 1) translate into center -// glTranslatef(tra.x, tra.y, tra.z); - - */ - draw(); - } void MapView3D::resizeGL(int width, int height) { - //int side = qMin(width, height); - //glViewport((width - side) / 2, (height - side) / 2, side, side); - - - - //glViewport(0, 0, width, height); - - /* - TODO_GL - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - //glOrtho(-2, +2, -2, +2, 1.0, 25.0); - //glFrustum(-1,1, -1,1, 0.1,20); - viewport.size.x = 2.0f; - viewport.size.y = 2.0f * height / width; - const float w = viewport.size.x; - const float h = viewport.size.y; - glOrtho(-w, +w, -h, +h, -20, +20); - //glScalef(1,-1,1); - - //glFrustum(+w, -w, -h, +h, -20, +20); - - glMatrixMode(GL_MODELVIEW); - */ + glViewport(0, 0, width, height); } @@ -175,9 +118,9 @@ void MapView3D::mouseMoveEvent(QMouseEvent* e) { rot.x -= dy/2.0f; } else if (mouse.btn == 4) { //Point3 vec(-dx / width() * 2 * viewport.size.x, 0, +dy / height() * 2 * viewport.size.y); - 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 = vec.rot(-rot.x/180*M_PI, -rot.y/180*M_PI, -rot.z/180*M_PI); vec /= scale; center += vec; } @@ -256,45 +199,44 @@ void MapView3D::draw() { static RenderSettings rs = RenderSettings(new Shader()); - glViewport(0, 0, width(), height()); - - //glCullFace(GL_FRONT); - //glFrontFace(GL_CCW); - glEnable(GL_CULL_FACE); - glEnable(GL_DEPTH_TEST); // view QMatrix4x4 V; + + V.translate(0,0,-30); 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(center.x, center.z, center.y); - V.translate(0,0,-50); + V.translate(center.x, center.y, center.z); + // V.lookAt(QVector3D(30,-25,25), QVector3D(30,10,0), QVector3D(0,1,0)); // projection QMatrix4x4 P; float aspect = (float) width() / (float) height(); - float w = 2.0f; - float h = 2.0f * height() / width(); - viewport.size.x = w; - viewport.size.y = h; - //P.ortho(-w, +w, +h, -h, 0.1f, +30); // glOrtho(-w, +w, -h, +h, -20, +20); - P.perspective(45.0f, aspect, 0.01, 100); + 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, 100); + } 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, +100); + } rs.shader->bind(); rs.shader->setViewMatrix(V); rs.shader->setProjectionMatrix(P); - Cube cube(Point3(0,0,0), 1); - cube.setColor(1,0,0); - cube.render(rs); - - /* // solid floorplan parts if (showFloorplan) { @@ -305,12 +247,12 @@ void MapView3D::draw() { } - if (showGrid && gridModel) { - gridRenderer->paintGL(gridModel->getGrid()); - } +// if (showGrid && gridModel) { +// gridRenderer->paintGL(gridModel->getGrid()); +// } if (showNavMesh && navMeshModel) { - navMeshRenderer->paintGL(navMeshModel->getNavMesh(), this); + navMeshRenderer->render(rs, navMeshModel->getNavMesh(), this); } // transparant floorplan parts @@ -322,7 +264,7 @@ void MapView3D::draw() { } } - */ + } diff --git a/mapview/3D/MapView3D.h b/mapview/3D/MapView3D.h index a208675..e14c9cf 100644 --- a/mapview/3D/MapView3D.h +++ b/mapview/3D/MapView3D.h @@ -18,6 +18,8 @@ class MapView3D : public QOpenGLWidget { Q_OBJECT + bool usePerspectiveProjection = false; + public: MapView3D(QWidget* parent = 0); @@ -102,6 +104,12 @@ protected: void mouseReleaseEvent(QMouseEvent* e); void wheelEvent(QWheelEvent* e); +signals: + + void onShow3DFloorplan(bool show); + void onShow3DGrid(bool show); + void onShow3DNavMesh(bool show); + private: void draw(); diff --git a/mapview/3D/misc/Cube.cpp b/mapview/3D/misc/Cube.cpp index b3b3aae..4ba0c3e 100644 --- a/mapview/3D/misc/Cube.cpp +++ b/mapview/3D/misc/Cube.cpp @@ -2,217 +2,52 @@ #include "Shader.h" #include -//static float cube_vertices[] = { -//1 -1.0000, -1.0000, -1.0000, -//2 -1.0000, 1.0000, -1.0000, -//3 1.0000, 1.0000, -1.0000, -//4 1.0000, -1.0000, -1.0000, -//5 -1.0000, -1.0000, 1.0000, -//6 1.0000, -1.0000, 1.0000, -//7 1.0000, 1.0000, 1.0000, -//8 -1.0000, 1.0000, 1.0000, -//}; - -//static int cube_vertex_indices[] = { -// 1, 2, 3, -// 3, 4, 1, -// 5, 6, 7, -// 7, 8, 5, -// 1, 4, 6, -// 6, 5, 1, -// 4, 3, 7, -// 7, 6, 4, -// 3, 2, 8, -// 8, 7, 3, - -// 2, 1, 5, -// 5, 8, 2, -//}; - static float cube_vertices[] = { + -1, -1, -1, -1, +1, -1, +1, +1, -1, + +1, +1, -1, +1, -1, -1, -1, -1, -1, - -1, -1, 0, - +1, -1, 0, - 0, +1, 0, + -1, -1, +1, +1, -1, +1, +1, +1, +1, + +1, +1, +1, -1, +1, +1, -1, -1, +1, -// -1.0000, -1.0000, -1.0000, -1.0000, 1.0000, -1.0000, 1.0000, 1.0000, -1.0000, -// 1.0000, 1.0000, -1.0000, 1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, + -1, -1, -1, +1, -1, -1, +1, -1, +1, + +1, -1, +1, -1, -1, +1, -1, -1, -1, -// -1.0000, -1.0000, 1.0000, 1.0000, -1.0000, 1.0000, 1.0000, 1.0000, 1.0000, -// 1.0000, 1.0000, 1.0000, -1.0000, 1.0000, 1.0000, -1.0000, -1.0000, 1.0000, + +1, -1, -1, +1, +1, -1, +1, +1, +1, + +1, +1, +1, +1, -1, +1, +1, -1, -1, -// -1.0000, -1.0000, -1.0000, 1.0000, -1.0000, -1.0000, 1.0000, -1.0000, 1.0000, -// 1.0000, -1.0000, 1.0000, -1.0000, -1.0000, 1.0000, -1.0000, -1.0000, -1.0000, + +1, +1, -1, -1, +1, -1, -1, +1, +1, + -1, +1, +1, +1, +1, +1, +1, +1, -1, -// 1.0000, -1.0000, -1.0000, 1.0000, 1.0000, -1.0000, 1.0000, 1.0000, 1.0000, -// 1.0000, 1.0000, 1.0000, 1.0000, -1.0000, 1.0000, 1.0000, -1.0000, -1.0000, - -// 1.0000, 1.0000, -1.0000, -1.0000, 1.0000, -1.0000, -1.0000, 1.0000, 1.0000, -// -1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, -1.0000, - -// -1.0000, 1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, -1.0000, 1.0000, -// -1.0000, -1.0000, 1.0000, -1.0000, 1.0000, 1.0000, -1.0000, 1.0000, -1.0000, + -1, +1, -1, -1, -1, -1, -1, -1, +1, + -1, -1, +1, -1, +1, +1, -1, +1, -1, }; -//static float cube_normals[] = { -// 0.0000, 0.0000, -1.0000, -// 0.0000, 0.0000, 1.0000, -// 0.0000, -1.0000, 0.0000, -// 1.0000, 0.0000, 0.0000, -// 0.0000, 1.0000, 0.0000, -// -1.0000, 0.0000, 0.0000, -//}; - static float cube_normals[] = { - 0.0000, 0.0000, -1.0000, 0.0000, 0.0000, -1.0000, 0.0000, 0.0000, -1.0000, - 0.0000, 0.0000, -1.0000, 0.0000, 0.0000, -1.0000, 0.0000, 0.0000, -1.0000, - 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 1.0000, - 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 1.0000, + 0, 0, -1, 0, 0, -1, 0, 0, -1, + 0, 0, -1, 0, 0, -1, 0, 0, -1, - 0.0000, -1.0000, 0.0000, 0.0000, -1.0000, 0.0000, 0.0000, -1.0000, 0.0000, - 0.0000, -1.0000, 0.0000, 0.0000, -1.0000, 0.0000, 0.0000, -1.0000, 0.0000, + 0, 0, +1, 0, 0, +1, 0, 0, +1, + 0, 0, +1, 0, 0, +1, 0, 0, +1, - 1.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, - 1.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, 1.0000, 0.0000, 0.0000, + 0, -1, 0, 0, -1, 0, 0, -1, 0, + 0, -1, 0, 0, -1, 0, 0, -1, 0, - 0.0000, 1.0000, 0.0000,0.0000, 1.0000, 0.0000,0.0000, 1.0000, 0.0000, - 0.0000, 1.0000, 0.0000,0.0000, 1.0000, 0.0000,0.0000, 1.0000, 0.0000, + 1, 0, 0, 1, 0, 0, 1, 0, 0, + 1, 0, 0, 1, 0, 0, 1, 0, 0, + + 0, +1, 0, 0, +1, 0, 0, +1, 0, + 0, +1, 0, 0, +1, 0, 0, +1, 0, + + -1, 0, 0, -1, 0, 0, -1, 0, 0, + -1, 0, 0, -1, 0, 0, -1, 0, 0, - -1.0000, 0.0000, 0.0000,-1.0000, 0.0000, 0.0000,-1.0000, 0.0000, 0.0000, - -1.0000, 0.0000, 0.0000,-1.0000, 0.0000, 0.0000,-1.0000, 0.0000, 0.0000, }; -//static int cube_normal_indices[] = { -// 1, 1, 1, -// 1, 1, 1, -// 2, 2, 2, -// 2, 2, 2, -// 3, 3, 3, -// 3, 3, 3, -// 4, 4, 4, -// 4, 4, 4, -// 5, 5, 5, -// 5, 5, 5, -// 6, 6, 6, -// 6, 6, 6, -//}; - - -//static float cube_vertices[] = { - -// // bottom -// +1, -1, -1, -// +1, -1, +1, -// -1, -1, +1, - -// -1, -1, +1, -// -1, -1, -1, -// +1, -1, -1, - -// // top -// -1, +1, -1, -// -1, +1, +1, -// +1, +1, +1, - -// +1, +1, +1, -// +1, +1, -1, -// -1, +1, -1, - -// // left -// -1, -1, -1, -// -1, -1, +1, -// -1, +1, +1, - -// -1, +1, +1, -// -1, +1, -1, -// -1, -1, -1, - -// // right -// +1, +1, -1, -// +1, +1, +1, -// +1, -1, +1, - -// +1, -1, +1, -// +1, -1, -1, -// +1, +1, -1, - -// // front -// +1, +1, +1, -// -1, +1, +1, -// -1, -1, +1, - -// -1, -1, +1, -// +1, -1, +1, -// +1, +1, +1, - -// // rear -// +1, -1, -1, -// -1, -1, -1, -// -1, +1, -1, - -// -1, +1, -1, -// +1, +1, -1, -// +1, -1, -1, - -//}; - -//static float cube_normals[] = { - -// // bottom -// 0,-1,0, -// 0,-1,0, -// 0,-1,0, -// 0,-1,0, -// 0,-1,0, -// 0,-1,0, - -// // top -// 0,+1,0, -// 0,+1,0, -// 0,+1,0, -// 0,+1,0, -// 0,+1,0, -// 0,+1,0, - -// // left -// -1,0,0, -// -1,0,0, -// -1,0,0, -// -1,0,0, -// -1,0,0, -// -1,0,0, - -// // right -// +1,0,0, -// +1,0,0, -// +1,0,0, -// +1,0,0, -// +1,0,0, -// +1,0,0, - -// // front -// 0,0,+1, -// 0,0,+1, -// 0,0,+1, -// 0,0,+1, -// 0,0,+1, -// 0,0,+1, - -// // rear -// 0,0,-1, -// 0,0,-1, -// 0,0,-1, -// 0,0,-1, -// 0,0,-1, -// 0,0,-1, - -//}; - -static Shader* shader = nullptr; +//static Shader* shader = nullptr; Cube::Cube(Point3 pos, float size) : pos(pos), size(size,size,size), rot(0,0,0) { @@ -228,29 +63,23 @@ void Cube::setColor(float r, float g, float b) { void Cube::render(const RenderSettings& rs) { - - rs.shader->bind(); QMatrix4x4 mat; mat.translate(pos.x, pos.y, pos.z); - mat.rotate(rot.x, 1, 0, 0); - mat.rotate(rot.y, 0, 1, 0); - mat.rotate(rot.z, 0, 0, 1); + mat.rotate(rot.x, +1, 0, 0); + mat.rotate(rot.y, 0, +1, 0); + mat.rotate(rot.z, 0, 0, +1); mat.scale(size.x, size.y, size.z); -// mat.scale(0.1, 0.1, 0.1); rs.shader->setModelMatrix(mat); -// shader->setViewMatrix(V); -// shader->setProjectionMatrix(P); - rs.shader->setColor(color.x, color.y, color.z); rs.shader->setVertices(cube_vertices); rs.shader->setNormals(cube_normals); - glDrawArrays(GL_TRIANGLES, 0, 1*3); - //glDrawElements(GL_TRIANGLES, 12, GL_INT, cube_vertex_indices); + glDrawArrays(GL_TRIANGLES, 0, 12*3); + //glDrawElements(GL_TRIANGLES, +12, GL_INT, cube_vertex_indices); rs.shader->unsetVertices(); rs.shader->unsetNormals(); diff --git a/mapview/3D/misc/Handrail.cpp b/mapview/3D/misc/Handrail.cpp new file mode 100644 index 0000000..a206484 --- /dev/null +++ b/mapview/3D/misc/Handrail.cpp @@ -0,0 +1,101 @@ +#include "Handrail.h" + +#include +#include "Shader.h" + +Handrail::Handrail(const Point2 from, const Point2 to, float atHeight, float height) : + from(from), to(to), atHeight(atHeight), height(height) { + + ; + +} + +void Handrail::render(const RenderSettings& rs) { + + rs.shader->bind(); + rs.shader->setColor(0.8, 0.8, 0.8); + rs.shader->setModelMatrix(QMatrix4x4()); + + const float z1 = atHeight; + const float z2 = atHeight + height; + + // polygon edges + Point3 p1 = Point3(from.x, from.y, z1); + Point3 p2 = Point3(to.x, to.y, z1); + + Point3 p3 = Point3(from.x, from.y, z2); + Point3 p4 = Point3(to.x, to.y, z2); + + std::vector vertices; + + // top + vertices.insert( vertices.end(), {p3.x, p3.y, p3.z} ); + vertices.insert( vertices.end(), {p4.x, p4.y, p4.z} ); + + // start bar + vertices.insert( vertices.end(), {p1.x, p1.y, p1.z} ); + vertices.insert( vertices.end(), {p3.x, p3.y, p3.z} ); + + // end bar + vertices.insert( vertices.end(), {p2.x, p2.y, p2.z} ); + vertices.insert( vertices.end(), {p4.x, p4.y, p4.z} ); + + // intermediate bars + const Point3 d1 = p2-p1; + const Point3 d2 = p4-p3; + const int numBars = d2.length() / 1; + for (int i = 1; i < numBars; ++i) { + const Point3 s = p1 + d1 * i / numBars; + const Point3 e = p3 + d2 * i / numBars; + vertices.insert( vertices.end(), {s.x, s.y, s.z} ); + vertices.insert( vertices.end(), {e.x, e.y, e.z} ); + } + + rs.shader->setVertices(vertices.data()); + glDrawArrays(GL_LINES, 0, vertices.size() / 3); + rs.shader->unsetVertices(); + + rs.shader->release(); + + /* + + TODO_GL + + glDisable(GL_LIGHTING); + glBegin(GL_LINES); + + glColor3f(0.9, 0.9, 0.9); + + // top + glVertex3f(p3.x, p3.y, p3.z); + glVertex3f(p4.x, p4.y, p4.z); + + // start bar + glVertex3f(p1.x, p1.y, p1.z); + glVertex3f(p3.x, p3.y, p3.z); + + // end bar + glVertex3f(p2.x, p2.y, p2.z); + glVertex3f(p4.x, p4.y, p4.z); + + glColor3f(0.6, 0.6, 0.6); + + // intermediate bars + const Point3 d1 = p2-p1; + const Point3 d2 = p4-p3; + const int numBars = d2.length() / 1; + for (int i = 1; i < numBars; ++i) { + const Point3 s = p1 + d1 * i / numBars; + const Point3 e = p3 + d2 * i / numBars; + glVertex3f(s.x, s.y, s.z); + glVertex3f(e.x, e.y, e.z); + } + + glEnd(); + glEnable(GL_LIGHTING); + + */ + + + +} diff --git a/mapview/3D/misc/Handrail.h b/mapview/3D/misc/Handrail.h new file mode 100644 index 0000000..9bdc266 --- /dev/null +++ b/mapview/3D/misc/Handrail.h @@ -0,0 +1,25 @@ +#ifndef HANDRAIL_H +#define HANDRAIL_H + + +#include "Renderable3D.h" +#include + +class Handrail : public Renderable3D { + +private: + + Point2 from; + Point2 to; + float atHeight; + float height; + +public: + + Handrail(const Point2 from, const Point2 to, float atHeight, float height); + + void render(const RenderSettings& rs) override; + +}; + +#endif // HANDRAIL_H diff --git a/mapview/3D/misc/Outline.cpp b/mapview/3D/misc/Outline.cpp new file mode 100644 index 0000000..f34b007 --- /dev/null +++ b/mapview/3D/misc/Outline.cpp @@ -0,0 +1,73 @@ +#include "Outline.h" + +#include +#include +#include +#include "Shader.h" + +Outline::Outline() { + +} + +void Outline::render(const RenderSettings& rs) { + + rs.shader->bind(); + + // identity + QMatrix4x4 mat; + rs.shader->setModelMatrix(mat); + + // show both sides + //glDisable(GL_CULL_FACE); + glEnable(GL_CULL_FACE); + + rs.shader->setColor(color.x, color.y, color.z); + rs.shader->setVertices(vertices.data()); + rs.shader->setNormals(normals.data()); + glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 3); + rs.shader->unsetVertices(); + rs.shader->unsetNormals(); + + + + rs.shader->release(); + +} + +void Outline::setColor(float r, float g, float b) { + color = Point3(r,g,b); +} + +void Outline::clear() { + normals.clear(); + vertices.clear(); +} + +void Outline::add(std::vector>& triangles) { + + for (const std::vector& tria : triangles) { + for (size_t i = 2; i < tria.size(); ++i) { + + const Point3 p1 = tria[i-2]; + const Point3 p2 = tria[i-1]; + const Point3 p3 = tria[i-0]; + + const Point3 n = cross(p2-p1, p3-p1); + if (n.z < 0) { + vertices.push_back(p1.x); vertices.push_back(p1.y); vertices.push_back(p1.z); + vertices.push_back(p3.x); vertices.push_back(p3.y); vertices.push_back(p3.z); + vertices.push_back(p2.x); vertices.push_back(p2.y); vertices.push_back(p2.z); + } else { + vertices.push_back(p1.x); vertices.push_back(p1.y); vertices.push_back(p1.z); + vertices.push_back(p2.x); vertices.push_back(p2.y); vertices.push_back(p2.z); + vertices.push_back(p3.x); vertices.push_back(p3.y); vertices.push_back(p3.z); + } + + normals.push_back(0); normals.push_back(0); normals.push_back(1); + normals.push_back(0); normals.push_back(0); normals.push_back(1); + normals.push_back(0); normals.push_back(0); normals.push_back(1); + + } + } + +} diff --git a/mapview/3D/misc/Outline.h b/mapview/3D/misc/Outline.h new file mode 100644 index 0000000..435f5de --- /dev/null +++ b/mapview/3D/misc/Outline.h @@ -0,0 +1,33 @@ +#ifndef OUTLINE_H +#define OUTLINE_H + + +#include "Renderable3D.h" +#include +#include + +class Outline : public Renderable3D { + +private: + + std::vector vertices; + std::vector normals; + Point3 color; + +public: + + Outline(); + + + + virtual void render(const RenderSettings& rs) override; + + void setColor(float r, float g, float b); + + void clear(); + + void add(std::vector>&); + +}; + +#endif // OUTLINE_H diff --git a/mapview/3D/misc/Shader.cpp b/mapview/3D/misc/Shader.cpp index 042d670..c5525d1 100644 --- a/mapview/3D/misc/Shader.cpp +++ b/mapview/3D/misc/Shader.cpp @@ -6,27 +6,41 @@ Shader::Shader() { addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, R"( attribute highp vec3 a_vertex; attribute highp vec3 a_normal; + attribute lowp vec4 a_color; uniform highp mat4 M; uniform highp mat4 V; uniform highp mat4 P; - varying highp vec3 normal; + varying highp vec3 v_normal; + varying lowp vec4 v_color; + varying lowp vec4 v_vertex; void main() { - gl_Position = vec4(a_vertex, 1.0); - normal = normalize( V*M*vec4(a_normal, 0.0) ); + gl_Position = P * V * M * vec4(a_vertex, 1.0); + v_normal = (V * M * vec4(a_normal, 0.0)).xyz; + v_color = a_color; + v_vertex = V * M * vec4(a_vertex, 1.0); } )"); addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, R"( uniform vec4 color; - varying highp vec3 normal; + uniform bool useNormal; + uniform bool useVertexColor; + varying highp vec3 v_normal; + varying lowp vec4 v_color; + varying lowp vec4 v_vertex; void main() { - float intensity = dot( normal, normalize(vec3(-1,-1,-3)) ); - gl_FragColor.rgb = color.rgb * intensity; - gl_FragColor.a = color.a; + vec3 lightPos = vec3(0,0,0); // camera + //vec3 lightVec = normalize(vec3(0,0,1)); + vec3 lightVec = normalize(lightPos - v_vertex.xyz); + float intensity = useNormal ? 0.3 + 0.7 * dot( normalize(v_normal), lightVec ) : 1.0; // light at camera pos + vec4 col = useVertexColor ? v_color : color; + gl_FragColor.rgb = col.rgb * intensity; + gl_FragColor.a = col.a; } )"); //bindAttributeLocation("vertices", 0); + if (!link()) { std::cout << log().toStdString() << std::endl; throw std::runtime_error("shader link error"); @@ -36,15 +50,25 @@ Shader::Shader() { void Shader::setModelMatrix(const QMatrix4x4& m) { - //setUniformValue(getUniform("M"), m); + setUniformValue(getUniform("M"), m); } void Shader::setViewMatrix(const QMatrix4x4& m) { - //setUniformValue(getUniform("V"), m); + setUniformValue(getUniform("V"), m); } void Shader::setProjectionMatrix(const QMatrix4x4& m) { - //setUniformValue(getUniform("P"), m); + setUniformValue(getUniform("P"), m); +} + +void Shader::setUseNormals(bool use) { + int loc = getUniform("useNormal"); + setUniformValue(loc, use); +} + +void Shader::setUseVertexColor(bool use) { + int loc = getUniform("useVertexColor"); + setUniformValue(loc, use); } int Shader::getUniform(const char* name) { @@ -52,7 +76,6 @@ int Shader::getUniform(const char* name) { if (loc == -1) {throw std::runtime_error("error");} return loc; } - int Shader::getAttribute(const char* name) { int loc = attributeLocation(name); if (loc == -1) {throw std::runtime_error("error");} @@ -78,11 +101,25 @@ void Shader::unsetVertices() { } void Shader::setNormals(const float* values) { + setUseNormals(true); const int loc = getAttribute("a_normal"); enableAttributeArray(loc); setAttributeArray(loc, GL_FLOAT, values, 3); } void Shader::unsetNormals() { + setUseNormals(false); const int loc = getAttribute("a_normal"); disableAttributeArray(loc); } + +void Shader::setVertexColor(const float* values) { + setUseVertexColor(true); + const int loc = getAttribute("a_color"); + enableAttributeArray(loc); + setAttributeArray(loc, GL_FLOAT, values, 4); // RGBA!!! +} +void Shader::unsetVertexColor() { + setUseVertexColor(false); + const int loc = getAttribute("a_color"); + disableAttributeArray(loc); +} diff --git a/mapview/3D/misc/Shader.h b/mapview/3D/misc/Shader.h index 79f3a2d..3ada0eb 100644 --- a/mapview/3D/misc/Shader.h +++ b/mapview/3D/misc/Shader.h @@ -21,9 +21,14 @@ public: void setVertices(const float*); void unsetVertices(); + void setUseNormals(bool use); void setNormals(const float*); void unsetNormals(); + void setVertexColor(const float*); + void unsetVertexColor(); + void setUseVertexColor(bool use); + int getUniform(const char*); int getAttribute(const char*); diff --git a/mapview/3DNavMesh/NavMeshRenderer.h b/mapview/3DNavMesh/NavMeshRenderer.h index 930f497..1f34fda 100644 --- a/mapview/3DNavMesh/NavMeshRenderer.h +++ b/mapview/3DNavMesh/NavMeshRenderer.h @@ -9,12 +9,10 @@ #include #include -#include +#include -//enum class GridRendererColorMode { -// SHOW_NODE_TYPE, -// SHOW_NODE_IMPORTANCE, -//}; +#include "../3D/misc/Renderable3D.h" +#include "../3D/misc/Shader.h" class NavMeshRenderer { @@ -53,10 +51,88 @@ public: /** render the given grid using GL commands */ - void paintGL(NM::NavMesh* navMesh, QGLWidget* dst) { + void render(const RenderSettings& rs, NM::NavMesh* navMesh, QOpenGLWidget* dst) { if (navMesh == nullptr) {return;} + + rs.shader->bind(); + rs.shader->setModelMatrix(QMatrix4x4()); + + std::vector vertices; + std::vector colors; + + for (const NM::NavMeshTriangle* tria : *navMesh) { + + Point3 color; + + switch (tria->getType()) { + case (int) NM::NavMeshType::FLOOR_INDOOR: color = Point3(0.8, 0.8, 0.8); break; + case (int) NM::NavMeshType::FLOOR_OUTDOOR: color = Point3(0.1, 0.8, 0.1); break; + case (int) NM::NavMeshType::DOOR: color = Point3(0.7, 0.7, 0.8); break; + case (int) NM::NavMeshType::STAIR_LEVELED: color = Point3(0.5, 0.5, 0.5); break; + case (int) NM::NavMeshType::STAIR_SKEWED: color = Point3(0.6, 0.6, 0.6); break; + } + + vertices.insert(vertices.end(), {tria->getP1().x, tria->getP1().y, tria->getP1().z}); + vertices.insert(vertices.end(), {tria->getP3().x, tria->getP3().y, tria->getP3().z}); + vertices.insert(vertices.end(), {tria->getP2().x, tria->getP2().y, tria->getP2().z}); + + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + + } + + rs.shader->setVertices(vertices.data()); + rs.shader->setVertexColor(colors.data()); + glDrawArrays(GL_TRIANGLES, 0, vertices.size()/3); + rs.shader->unsetVertices(); + rs.shader->unsetVertexColor(); + + vertices.clear(); + colors.clear(); + + for (const NM::NavMeshTriangle* tria : *navMesh) { + + Point3 color; + + switch (tria->getType()) { + case (int) NM::NavMeshType::FLOOR_INDOOR: color = Point3(0.6, 0.6, 0.6); break; + case (int) NM::NavMeshType::FLOOR_OUTDOOR: color = Point3(0.0, 0.6, 0.0); break; + case (int) NM::NavMeshType::DOOR: color = Point3(0.5, 0.5, 0.6); break; + case (int) NM::NavMeshType::STAIR_LEVELED: color = Point3(0.4, 0.4, 0.4); break; + case (int) NM::NavMeshType::STAIR_SKEWED: color = Point3(0.4, 0.4, 0.4); break; + } + + const float o = 0.001f; + vertices.insert(vertices.end(), {tria->getP1().x, tria->getP1().y, tria->getP1().z+o}); + vertices.insert(vertices.end(), {tria->getP2().x, tria->getP2().y, tria->getP2().z+o}); + + vertices.insert(vertices.end(), {tria->getP2().x, tria->getP2().y, tria->getP2().z+o}); + vertices.insert(vertices.end(), {tria->getP3().x, tria->getP3().y, tria->getP3().z+o}); + + vertices.insert(vertices.end(), {tria->getP3().x, tria->getP3().y, tria->getP3().z+o}); + vertices.insert(vertices.end(), {tria->getP1().x, tria->getP1().y, tria->getP1().z+o}); + + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + colors.insert(colors.end(), {color.x, color.y, color.z, 1}); + + } + + rs.shader->setVertices(vertices.data()); + rs.shader->setVertexColor(colors.data()); + glDrawArrays(GL_LINES, 0, vertices.size()/3); + rs.shader->unsetVertices(); + rs.shader->unsetVertexColor(); + + rs.shader->release(); + + /* TODO_GL @@ -68,21 +144,7 @@ public: glBegin(GL_TRIANGLES); for (const NM::NavMeshTriangle* tria : *navMesh) { -// // get the color to use -// switch(colorMode) { -// case GridRendererColorMode::SHOW_NODE_TYPE: { -// const Color c = colors[n.getType()]; -// glColor3f(c.r, c.g, c.b); -// break; -// } -// case GridRendererColorMode::SHOW_NODE_IMPORTANCE: { -// const float xx = n.navImportance - 0.6; -// glColor3f(xx, xx, xx); -// break; -// } - -// } switch (tria->getType()) { case (int) NM::NavMeshType::FLOOR_INDOOR: glColor3f(0.8, 0.8, 0.8); break; diff --git a/res.qrc b/res.qrc index 6489553..4a98ea4 100644 --- a/res.qrc +++ b/res.qrc @@ -20,5 +20,9 @@ res/icons/fingerprint.svg res/icons/gtp.svg res/icons/registration.svg + res/icons/mesh.svg + res/icons/perspective.svg + res/icons/floorplan.svg + res/icons/grid.svg diff --git a/res/icons/floorplan.svg b/res/icons/floorplan.svg new file mode 100644 index 0000000..5f10eb8 --- /dev/null +++ b/res/icons/floorplan.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/grid.svg b/res/icons/grid.svg new file mode 100644 index 0000000..309e35d --- /dev/null +++ b/res/icons/grid.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/mesh.svg b/res/icons/mesh.svg new file mode 100644 index 0000000..d799ae7 --- /dev/null +++ b/res/icons/mesh.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/perspective.svg b/res/icons/perspective.svg new file mode 100644 index 0000000..76894ed --- /dev/null +++ b/res/icons/perspective.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +