/*************************************************************************** * Copyright (c) 2017 Kustaa Nyholm * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ # include # include # ifdef FC_OS_WIN32 # include # endif # ifdef FC_OS_MACOSX # include # else # include # endif # include # include # include # include # include # include # include # include # include # include #endif #include #include #include "NaviCube.h" #include "Application.h" #include "Command.h" #include "MainWindow.h" #include "View3DInventorViewer.h" #include "View3DInventor.h" using namespace Eigen; using namespace std; using namespace Gui; // TODO // ortho / persp // stay in window // corner angle // menu actions // size // XYZ position // menu feedback // colors // antialise cube icon // translation // DONE // - permanent menu ("NaviCube_Menu" // - improved hit testing // - improved graphics (text now black) // - first stab at supporting translations class Face { public: int m_FirstVertex; int m_VertexCount; GLuint m_TextureId; QColor m_Color; int m_PickId; int m_PickTexId; GLuint m_PickTextureId; int m_RenderPass; Face( int firstVertex, int vertexCount, GLuint textureId, int pickId, int pickTexId, GLuint pickTextureId, const QColor& color, int renderPass ) { m_FirstVertex = firstVertex; m_VertexCount = vertexCount; m_TextureId = textureId; m_PickId = pickId; m_PickTexId = pickTexId; m_PickTextureId = pickTextureId; m_Color = color; m_RenderPass = renderPass; } }; class NaviCubeImplementation : public ParameterGrp::ObserverType { public: explicit NaviCubeImplementation(Gui::View3DInventorViewer*); ~NaviCubeImplementation() override; int getDefaultFontSize(); void drawNaviCube(); void createContextMenu(const std::vector& cmd); /// Observer message from the ParameterGrp void OnChange(ParameterGrp::SubjectType& rCaller, ParameterGrp::MessageType Reason) override; void applySettings(ParameterGrp&); bool processSoEvent(const SoEvent* ev); private: bool mousePressed(short x, short y); bool mouseReleased(short x, short y); bool mouseMoved(short x, short y); int pickFace(short x, short y); bool inDragZone(short x, short y); void handleResize(); void handleMenu(); void setHilite(int); void initNaviCube(QtGLWidget*); void addFace(float gap, const Vector3f&, const Vector3f&, int, int, int, bool flag = false); GLuint createCubeFaceTex(QtGLWidget* gl, float gap, const char* text, int shape); GLuint createButtonTex(QtGLWidget*, int); GLuint createMenuTex(QtGLWidget*, bool); SbRotation setView(float, float) const; SbRotation rotateView(SbRotation, int axis, float rotAngle, SbVec3f customAxis = SbVec3f(0, 0, 0)) const; void rotateView(const SbRotation&); QString str(const char* str); char* enum2str(int); QMenu* createNaviCubeMenu(); public: enum { // TEX_FRONT = 1, // 0 is reserved for 'nothing picked' TEX_REAR, TEX_TOP, TEX_BOTTOM, TEX_LEFT, TEX_RIGHT, TEX_FRONT_FACE, TEX_CORNER_FACE, TEX_EDGE_FACE, TEX_FRONT_TOP, TEX_FRONT_BOTTOM, TEX_FRONT_LEFT, TEX_FRONT_RIGHT, TEX_REAR_TOP, TEX_REAR_BOTTOM, TEX_REAR_LEFT, TEX_REAR_RIGHT, TEX_TOP_LEFT, TEX_TOP_RIGHT, TEX_BOTTOM_LEFT, TEX_BOTTOM_RIGHT, TEX_BOTTOM_RIGHT_REAR, TEX_BOTTOM_FRONT_RIGHT, TEX_BOTTOM_LEFT_FRONT, TEX_BOTTOM_REAR_LEFT, TEX_TOP_RIGHT_FRONT, TEX_TOP_FRONT_LEFT, TEX_TOP_LEFT_REAR, TEX_TOP_REAR_RIGHT, TEX_ARROW_NORTH, TEX_ARROW_SOUTH, TEX_ARROW_EAST, TEX_ARROW_WEST, TEX_ARROW_RIGHT, TEX_ARROW_LEFT, TEX_DOT_BACKSIDE, TEX_VIEW_MENU_ICON, TEX_VIEW_MENU_FACE }; enum { DIR_UP, DIR_RIGHT, DIR_OUT }; enum { SHAPE_SQUARE, SHAPE_EDGE, SHAPE_CORNER }; Gui::View3DInventorViewer* m_View3DInventorViewer; void drawNaviCube(bool picking); int m_OverSample = 4; int m_CubeWidgetSize = 0; int m_CubeWidgetPosX = 0; int m_CubeWidgetPosY = 0; int m_PrevWidth = 0; int m_PrevHeight = 0; QColor m_TextColor; QColor m_HiliteColor; QColor m_ButtonColor; QColor m_FrontColor; QColor m_BorderColor; int m_HiliteId = 0; bool m_MouseDown = false; bool m_Dragging = false; bool m_MightDrag = false; double m_BorderWidth; NaviCube::Corner m_Corner = NaviCube::TopRightCorner; bool m_RotateToNearest = true; int m_NaviStepByTurn = 8; int m_CubeTextSize = 0; std::string m_CubeTextFont; QtGLFramebufferObject* m_PickingFramebuffer; bool m_NaviCubeInitialised = false; vector m_IndexArray; vector m_TextureCoordArray; vector m_VertexArray; map> m_VertexArrays2; map m_Textures; vector m_Faces; vector m_Buttons; vector m_glTextures; static vector m_commands; static vector m_labels; QMenu* m_Menu; }; NaviCube::NaviCube(Gui::View3DInventorViewer* viewer) { m_NaviCubeImplementation = new NaviCubeImplementation(viewer); } NaviCube::~NaviCube() { delete m_NaviCubeImplementation; } void NaviCube::drawNaviCube() { m_NaviCubeImplementation->drawNaviCube(); } void NaviCube::createContextMenu(const std::vector& cmd) { m_NaviCubeImplementation->createContextMenu(cmd); } bool NaviCube::processSoEvent(const SoEvent* ev) { return m_NaviCubeImplementation->processSoEvent(ev); } vector NaviCubeImplementation::m_commands; vector NaviCubeImplementation::m_labels; void NaviCube::setCorner(Corner c) { m_NaviCubeImplementation->m_Corner = c; m_NaviCubeImplementation->m_PrevWidth = 0; m_NaviCubeImplementation->m_PrevHeight = 0; } void NaviCube::setSize(int size) { m_NaviCubeImplementation->m_CubeWidgetSize = size; } void NaviCube::setNaviRotateToNearest(bool toNearest) { m_NaviCubeImplementation->m_RotateToNearest = toNearest; } void NaviCube::setNaviStepByTurn(int steps) { m_NaviCubeImplementation->m_NaviStepByTurn = steps; } void NaviCube::setFont(std::string font) { m_NaviCubeImplementation->m_CubeTextFont = font; } void NaviCube::setFontSize(int size) { m_NaviCubeImplementation->m_CubeTextSize = size; } void NaviCube::setTextColor(QColor TextColor) { m_NaviCubeImplementation->m_TextColor = TextColor; } void NaviCube::setFrontColor(QColor FrontColor) { m_NaviCubeImplementation->m_FrontColor = FrontColor; } void NaviCube::setHiliteColor(QColor HiliteColor) { m_NaviCubeImplementation->m_HiliteColor = HiliteColor; } void NaviCube::setButtonColor(QColor ButtonColor) { m_NaviCubeImplementation->m_ButtonColor = ButtonColor; } void NaviCube::setBorderWidth(double BorderWidth) { m_NaviCubeImplementation->m_BorderWidth = BorderWidth; } void NaviCube::setBorderColor(QColor BorderColor) { m_NaviCubeImplementation->m_BorderColor = BorderColor; } QString NaviCube::getDefaultSansserifFont() { // Windows versions since 2017 have the 'Bahnschrift' font (a condensed // sans serif font, optimized for readability despite being condensed), // we first check for that. QFont font(QStringLiteral("Bahnschrift")); if (!font.exactMatch()) // On systems without 'Bahnschrift' we check for 'Helvetica' or its closest match // as default sans serif font. (For Windows 7 this will e.g. result in 'Arial'.) font = QFont(QStringLiteral("Helvetica")); font.setStyleHint(QFont::SansSerif); // QFontInfo is required to get the actually matched font family return QFontInfo(font).family(); } int NaviCube::getDefaultFontSize() { return this->m_NaviCubeImplementation->getDefaultFontSize(); } NaviCubeImplementation::NaviCubeImplementation( Gui::View3DInventorViewer* viewer) { m_View3DInventorViewer = viewer; auto hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/NaviCube"); hGrp->Attach(this); applySettings(*hGrp); m_PickingFramebuffer = nullptr; m_Menu = createNaviCubeMenu(); } NaviCubeImplementation::~NaviCubeImplementation() { auto hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/NaviCube"); hGrp->Detach(this); delete m_Menu; if (m_PickingFramebuffer) delete m_PickingFramebuffer; for (vector::iterator f = m_Faces.begin(); f != m_Faces.end(); f++) delete* f; for (vector::iterator t = m_glTextures.begin(); t != m_glTextures.end(); t++) delete* t; } int NaviCubeImplementation::getDefaultFontSize() { int texSize = m_CubeWidgetSize * m_OverSample; return int(0.18 * texSize); } void NaviCubeImplementation::applySettings(ParameterGrp& rGrp) { NaviCubeImplementation::OnChange(rGrp, "TextColor"); NaviCubeImplementation::OnChange(rGrp, "FrontColor"); NaviCubeImplementation::OnChange(rGrp, "HiliteColor"); NaviCubeImplementation::OnChange(rGrp, "ButtonColor"); NaviCubeImplementation::OnChange(rGrp, "CornerNaviCube"); NaviCubeImplementation::OnChange(rGrp, "CubeSize"); NaviCubeImplementation::OnChange(rGrp, "NaviRotateToNearest"); NaviCubeImplementation::OnChange(rGrp, "NaviStepByTurn"); NaviCubeImplementation::OnChange(rGrp, "BorderWidth"); NaviCubeImplementation::OnChange(rGrp, "BorderColor"); NaviCubeImplementation::OnChange(rGrp, "FontSize"); NaviCubeImplementation::OnChange(rGrp, "FontString"); } void NaviCubeImplementation::OnChange(ParameterGrp::SubjectType& rCaller, ParameterGrp::MessageType reason) { const auto& rGrp = static_cast(rCaller); if (strcmp(reason, "TextColor") == 0) { // the colors are stored in the form RRGGBBAA as unsigned long // QColor expects the form AARRGGBB therefore we must make a shift in writing to QColor unsigned long col = rGrp.GetUnsigned(reason, 255); // 255 is RRR,GGG,BBB,AAA: 0,0,0,255 QColor textColor((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff); m_TextColor = textColor; } else if (strcmp(reason, "FrontColor") == 0) { unsigned long col = rGrp.GetUnsigned(reason, 3806916544); // 3236096495 is RRR,GGG,BBB,AAA: 226,233,239,192 QColor frontColor((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff); m_FrontColor = frontColor; } else if (strcmp(reason, "HiliteColor") == 0) { unsigned long col = rGrp.GetUnsigned(reason, 2867003391); // 2867003391 is RRR,GGG,BBB,AAA: 170,226,255,255 QColor hiliteColor((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff); m_HiliteColor = hiliteColor; } else if (strcmp(reason, "ButtonColor") == 0) { unsigned long col = rGrp.GetUnsigned(reason, 3806916480); // 3806916480 is RRR,GGG,BBB,AAA: 226,233,239,128 QColor buttonColor( (col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff); m_ButtonColor = buttonColor; } else if (strcmp(reason, "CornerNaviCube") == 0) { m_Corner = static_cast(rGrp.GetInt(reason, 1)); } else if (strcmp(reason, "CubeSize") == 0) { m_CubeWidgetSize = rGrp.GetInt(reason, 132); } else if (strcmp(reason, "NaviRotateToNearest") == 0) { m_RotateToNearest = rGrp.GetBool(reason, true); } else if (strcmp(reason, "NaviStepByTurn") == 0) { m_NaviStepByTurn = rGrp.GetInt(reason, 8); } else if (strcmp(reason, "BorderWidth") == 0) { m_BorderWidth = rGrp.GetFloat(reason, 1.1); } else if (strcmp(reason, "BorderColor") == 0) { unsigned long col = rGrp.GetUnsigned(reason, 842150655); // 842150655 is RRR,GGG,BBB,AAA: 50,50,50,255 QColor borderColor((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff); m_BorderColor = borderColor; } else if (strcmp(reason, "FontSize") == 0) { m_CubeTextSize = rGrp.GetInt(reason, getDefaultFontSize()); } else if (strcmp(reason, "FontString") == 0) { m_CubeTextFont = (rGrp.GetASCII( reason, NaviCube::getDefaultSansserifFont().toStdString().c_str())); } } char* NaviCubeImplementation::enum2str(int positionEnum) { switch (positionEnum) { default: return "???"; case TEX_FRONT: return "TEX_FRONT"; case TEX_REAR: return "TEX_REAR"; case TEX_TOP: return "TEX_TOP"; case TEX_BOTTOM: return "TEX_BOTTOM"; case TEX_RIGHT: return "TEX_RIGHT"; case TEX_LEFT: return "TEX_LEFT"; case TEX_FRONT_FACE: return "TEX_FRONT_FACE"; case TEX_CORNER_FACE: return "TEX_CORNER_FACE"; case TEX_EDGE_FACE: return "TEX_EDGE_FACE"; case TEX_FRONT_TOP: return "TEX_FRONT_TOP"; case TEX_FRONT_BOTTOM: return "TEX_FRONT_BOTTOM"; case TEX_FRONT_LEFT: return "TEX_FRONT_LEFT"; case TEX_FRONT_RIGHT: return "TEX_FRONT_RIGHT"; case TEX_REAR_TOP: return "TEX_REAR_TOP"; case TEX_REAR_BOTTOM: return "TEX_REAR_BOTTOM"; case TEX_REAR_LEFT: return "TEX_REAR_LEFT"; case TEX_REAR_RIGHT: return "TEX_REAR_RIGHT"; case TEX_BOTTOM_RIGHT_REAR: return "TEX_BOTTOM_RIGHT_REAR"; case TEX_BOTTOM_FRONT_RIGHT: return "TEX_BOTTOM_FRONT_RIGHT"; case TEX_BOTTOM_LEFT_FRONT: return "TEX_BOTTOM_LEFT_FRONT"; case TEX_BOTTOM_REAR_LEFT: return "TEX_BOTTOM_REAR_LEFT"; case TEX_TOP_RIGHT_FRONT: return "TEX_TOP_RIGHT_FRONT"; case TEX_TOP_FRONT_LEFT: return "TEX_TOP_FRONT_LEFT"; case TEX_TOP_LEFT_REAR: return "TEX_TOP_LEFT_REAR"; case TEX_TOP_REAR_RIGHT: return "TEX_TOP_REAR_RIGHT"; case TEX_ARROW_NORTH: return "TEX_ARROW_NORTH"; case TEX_ARROW_SOUTH: return "TEX_ARROW_SOUTH"; case TEX_ARROW_EAST: return "TEX_ARROW_EAST"; case TEX_ARROW_WEST: return "TEX_ARROW_WEST"; case TEX_ARROW_RIGHT: return "TEX_ARROW_RIGHT"; case TEX_ARROW_LEFT: return "TEX_ARROW_LEFT"; case TEX_DOT_BACKSIDE: return "TEX_DOT_BACKSIDE"; case TEX_VIEW_MENU_ICON: return "TEX_VIEW_MENU_ICON"; case TEX_VIEW_MENU_FACE: return "TEX_VIEW_MENU"; } } auto convertWeights = [](int weight) -> QFont::Weight { if (weight >= 87) return QFont::Black; if (weight >= 81) return QFont::ExtraBold; if (weight >= 75) return QFont::Bold; if (weight >= 63) return QFont::DemiBold; if (weight >= 57) return QFont::Medium; if (weight >= 50) return QFont::Normal; if (weight >= 25) return QFont::Light; if (weight >= 12) return QFont::ExtraLight; return QFont::Thin; }; GLuint NaviCubeImplementation::createCubeFaceTex(QtGLWidget* gl, float gap, const char* text, int shape) { int texSize = m_CubeWidgetSize * m_OverSample; float gapi = texSize * gap; QImage image(texSize, texSize, QImage::Format_ARGB32); image.fill(qRgba(255, 255, 255, 0)); QPainter paint; paint.begin(&image); paint.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); if (text) { ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/NaviCube"); paint.setPen(Qt::white); QFont sansFont; // check the user settings QString fontString = QString::fromStdString(m_CubeTextFont); // Override fromString if (hGrp->GetInt("FontWeight") > 0) { sansFont.setWeight(convertWeights(hGrp->GetInt("FontWeight"))); } if (hGrp->GetInt("FontStretch") > 0) { sansFont.setStretch(hGrp->GetInt("FontStretch")); } sansFont.fromString(fontString); sansFont.setPointSize(m_CubeTextSize); paint.setFont(sansFont); paint.drawText( QRect(0, 0, texSize, texSize), Qt::AlignCenter, qApp->translate("Gui::NaviCube", text)); } else if (shape == SHAPE_SQUARE) { QPainterPath pathSquare; auto rectSquare = QRectF(gapi, gapi, (qreal)texSize - 2.0 * gapi, (qreal)texSize - 2.0 * gapi); // Qt's coordinate system is x->left y->down, // this must be taken into account on operations pathSquare.moveTo(rectSquare.left() , rectSquare.bottom() - gapi); pathSquare.lineTo(rectSquare.left() + gapi , rectSquare.bottom()); pathSquare.lineTo(rectSquare.right() - gapi , rectSquare.bottom()); pathSquare.lineTo(rectSquare.right() , rectSquare.bottom() - gapi); pathSquare.lineTo(rectSquare.right() , rectSquare.top() + gapi); pathSquare.lineTo(rectSquare.right() - gapi , rectSquare.top()); pathSquare.lineTo(rectSquare.left() + gapi , rectSquare.top()); pathSquare.lineTo(rectSquare.left() , rectSquare.top() + gapi); pathSquare.closeSubpath(); paint.fillPath(pathSquare, Qt::white); } else if (shape == SHAPE_CORNER) { QPainterPath pathCorner; // the hexagon edges are of length sqrt(2) * gapi const auto hexWidth = 2 * sqrt(2) * gapi; // hexagon vertex to vertex distance const auto hexHeight = sqrt(3) * sqrt(2) * gapi; // edge to edge distance auto rectCorner = QRectF((texSize - hexWidth) / 2, (texSize - hexHeight) / 2, hexWidth, hexHeight); // Qt's coordinate system is x->left y->down, this must be taken into account on operations pathCorner.moveTo(rectCorner.left() , rectCorner.bottom() - hexHeight / 2); // left middle vertex pathCorner.lineTo(rectCorner.left() + hexWidth * 0.25 , rectCorner.bottom()); // left lower pathCorner.lineTo(rectCorner.left() + hexWidth * 0.75 , rectCorner.bottom()); // right lower pathCorner.lineTo(rectCorner.right() , rectCorner.bottom() - hexHeight / 2); // right middle pathCorner.lineTo(rectCorner.left() + hexWidth * 0.75 , rectCorner.top()); // right upper pathCorner.lineTo(rectCorner.left() + hexWidth * 0.25 , rectCorner.top()); // left upper pathCorner.closeSubpath(); paint.fillPath(pathCorner, Qt::white); } else if (shape == SHAPE_EDGE) { QPainterPath pathEdge; // since the gap is 0.12, the rect must be geometriclly shifted up with a factor pathEdge.addRect(QRectF(2 * gapi, ((qreal)texSize - sqrt(2) * gapi) * 0.5, (qreal)texSize - 4.0 * gapi, sqrt(2) * gapi)); paint.fillPath(pathEdge, Qt::white); } paint.end(); Q_UNUSED(gl); auto texture = new QOpenGLTexture(image.mirrored()); m_glTextures.push_back(texture); texture->generateMipMaps(); texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); texture->setMagnificationFilter(QOpenGLTexture::Linear); return texture->textureId(); } GLuint NaviCubeImplementation::createButtonTex(QtGLWidget* gl, int button) { int texSize = m_CubeWidgetSize * m_OverSample; QImage image(texSize, texSize, QImage::Format_ARGB32); image.fill(qRgba(255, 255, 255, 0)); QPainter painter; painter.begin(&image); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); QTransform transform; transform.translate(texSize / 2, texSize / 2); transform.scale(texSize / 2, texSize / 2); painter.setTransform(transform); QPainterPath path; float as1 = 0.18f; // arrow size float as3 = as1 / 3; switch (button) { default: break; case TEX_ARROW_RIGHT: case TEX_ARROW_LEFT: { QRectF r(-1.00, -1.00, 2.00, 2.00); QRectF r0(r); r.adjust(as3, as3, -as3, -as3); QRectF r1(r); r.adjust(as3, as3, -as3, -as3); QRectF r2(r); r.adjust(as3, as3, -as3, -as3); QRectF r3(r); r.adjust(as3, as3, -as3, -as3); QRectF r4(r); float a0 = 72; float a1 = 45; float a2 = 32; if (TEX_ARROW_LEFT == button) { a0 = 180 - a0; a1 = 180 - a1; a2 = 180 - a2; } path.arcMoveTo(r0, a1); QPointF p0 = path.currentPosition(); path.arcMoveTo(r2, a2); QPointF p1 = path.currentPosition(); path.arcMoveTo(r4, a1); QPointF p2 = path.currentPosition(); path.arcMoveTo(r1, a0); path.arcTo(r1, a0, -(a0 - a1)); path.lineTo(p0); path.lineTo(p1); path.lineTo(p2); path.arcTo(r3, a1, +(a0 - a1)); break; } case TEX_ARROW_EAST: { path.moveTo(1, 0); path.lineTo(1 - as1, +as1); path.lineTo(1 - as1, -as1); break; } case TEX_ARROW_WEST: { path.moveTo(-1, 0); path.lineTo(-1 + as1, -as1); path.lineTo(-1 + as1, +as1); break; } case TEX_ARROW_SOUTH: { path.moveTo(0, 1); path.lineTo(-as1, 1 - as1); path.lineTo(+as1, 1 - as1); break; } case TEX_ARROW_NORTH: { path.moveTo(0, -1); path.lineTo(+as1, -1 + as1); path.lineTo(-as1, -1 + as1); break; } case TEX_DOT_BACKSIDE: { path.arcTo(QRectF(1 - as1, -1, as1, as1), 0, 360); break; } } painter.fillPath(path, Qt::white); painter.end(); //image.save(str(enum2str(button))+str(".png")); Q_UNUSED(gl); auto texture = new QOpenGLTexture(image.mirrored()); m_glTextures.push_back(texture); texture->generateMipMaps(); texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); texture->setMagnificationFilter(QOpenGLTexture::Linear); return texture->textureId(); } GLuint NaviCubeImplementation::createMenuTex(QtGLWidget* gl, bool forPicking) { int texSize = m_CubeWidgetSize * m_OverSample; QImage image(texSize, texSize, QImage::Format_ARGB32); image.fill(qRgba(0, 0, 0, 0)); QPainter painter; painter.begin(&image); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); QTransform transform; transform.translate(texSize * 12 / 16, texSize * 13 / 16); transform.scale(texSize / 200.0, texSize / 200.0); // 200 == size at which this was designed painter.setTransform(transform); QPainterPath path; if (forPicking) { path.addRoundedRect(-25, -8, 75, 45, 6, 6); painter.fillPath(path, Qt::white); } else { // top path.moveTo(0, 0); path.lineTo(15, 5); path.lineTo(0, 10); path.lineTo(-15, 5); painter.fillPath(path, QColor(240, 240, 240)); // left QPainterPath path2; path2.lineTo(0, 10); path2.lineTo(-15, 5); path2.lineTo(-15, 25); path2.lineTo(0, 30); painter.fillPath(path2, QColor(190, 190, 190)); // right QPainterPath path3; path3.lineTo(0, 10); path3.lineTo(15, 5); path3.lineTo(15, 25); path3.lineTo(0, 30); painter.fillPath(path3, QColor(220, 220, 220)); // outline QPainterPath path4; path4.moveTo(0, 0); path4.lineTo(15, 5); path4.lineTo(15, 25); path4.lineTo(0, 30); path4.lineTo(-15, 25); path4.lineTo(-15, 5); path4.lineTo(0, 0); painter.strokePath(path4, QColor(128, 128, 128)); // menu triangle QPainterPath path5; path5.moveTo(20, 10); path5.lineTo(40, 10); path5.lineTo(30, 20); path5.lineTo(20, 10); painter.fillPath(path5, QColor(64, 64, 64)); } painter.end(); Q_UNUSED(gl); auto texture = new QOpenGLTexture(image.mirrored()); m_glTextures.push_back(texture); texture->generateMipMaps(); texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); texture->setMagnificationFilter(QOpenGLTexture::Linear); return texture->textureId(); } void NaviCubeImplementation::addFace(float gap, const Vector3f& x, const Vector3f& z, int frontTex, int pickTex, int pickId, bool text) { Vector3f y = x.cross(-z); y = y / y.norm() * x.norm(); int t = m_VertexArray.size(); m_VertexArray.emplace_back(z - x - y); m_TextureCoordArray.emplace_back(0, 0); m_VertexArray.emplace_back(z + x - y); m_TextureCoordArray.emplace_back(1, 0); m_VertexArray.emplace_back(z + x + y); m_TextureCoordArray.emplace_back(1, 1); m_VertexArray.emplace_back(z - x + y); m_TextureCoordArray.emplace_back(0, 1); if (pickTex == TEX_FRONT_FACE) { auto x2 = x * (1 - gap * 2); auto y2 = y * (1 - gap * 2); auto x4 = x * (1 - gap * 4); auto y4 = y * (1 - gap * 4); m_VertexArrays2[pickId].reserve(8); m_VertexArrays2[pickId].emplace_back(z - x2 - y4); m_VertexArrays2[pickId].emplace_back(z - x4 - y2); m_VertexArrays2[pickId].emplace_back(z + x4 - y2); m_VertexArrays2[pickId].emplace_back(z + x2 - y4); m_VertexArrays2[pickId].emplace_back(z + x2 + y4); m_VertexArrays2[pickId].emplace_back(z + x4 + y2); m_VertexArrays2[pickId].emplace_back(z - x4 + y2); m_VertexArrays2[pickId].emplace_back(z - x2 + y4); } else if (pickTex == TEX_EDGE_FACE) { auto x4 = x * (1 - gap * 4); auto y_sqrt2 = y * sqrt(2) * gap; m_VertexArrays2[pickId].reserve(4); m_VertexArrays2[pickId].emplace_back(z - x4 - y_sqrt2); m_VertexArrays2[pickId].emplace_back(z + x4 - y_sqrt2); m_VertexArrays2[pickId].emplace_back(z + x4 + y_sqrt2); m_VertexArrays2[pickId].emplace_back(z - x4 + y_sqrt2); } else if (pickTex == TEX_CORNER_FACE) { auto x_sqrt2 = x * sqrt(2) * gap; auto y_sqrt6 = y * sqrt(6) * gap; m_VertexArrays2[pickId].reserve(6); m_VertexArrays2[pickId].emplace_back(z - 2 * x_sqrt2); m_VertexArrays2[pickId].emplace_back(z - x_sqrt2 - y_sqrt6); m_VertexArrays2[pickId].emplace_back(z + x_sqrt2 - y_sqrt6); m_VertexArrays2[pickId].emplace_back(z + 2 * x_sqrt2); m_VertexArrays2[pickId].emplace_back(z + x_sqrt2 + y_sqrt6); m_VertexArrays2[pickId].emplace_back(z - x_sqrt2 + y_sqrt6); } // TEX_TOP, TEX_FRONT_FACE, TEX_TOP // TEX_TOP frontTex, // TEX_FRONT_FACE pickTex, // TEX_TOP pickId Face* FaceFront = new Face( m_IndexArray.size(), 4, m_Textures[pickTex], pickId, pickTex, m_Textures[pickTex], m_FrontColor, 1); m_Faces.push_back(FaceFront); if (text) { Face* FaceText = new Face( m_IndexArray.size(), 4, m_Textures[frontTex], pickId, pickTex, m_Textures[pickTex], m_TextColor, 2); m_Faces.push_back(FaceText); } for (int i = 0; i < 4; i++) m_IndexArray.push_back(t + i); } void NaviCubeImplementation::initNaviCube(QtGLWidget* gl) { Vector3f x(1, 0, 0); Vector3f y(0, 1, 0); Vector3f z(0, 0, 1); float cs, sn; cs = cos(90 * M_PI / 180); sn = sin(90 * M_PI / 180); Matrix3f r90x; r90x << 1, 0, 0, 0, cs, -sn, 0, sn, cs; Matrix3f r90y; r90y << cs, 0, sn, 0, 1, 0, -sn, 0, cs; Matrix3f r90z; r90z << cs, sn, 0, -sn, cs, 0, 0, 0, 1; cs = cos(45 * M_PI / 180); sn = sin(45 * M_PI / 180); Matrix3f r45x; r45x << 1, 0, 0, 0, cs, -sn, 0, sn, cs; Matrix3f r45z; r45z << cs, sn, 0, -sn, cs, 0, 0, 0, 1; // first create front and backside of faces float gap = 0.12f; m_Textures[TEX_FRONT_FACE] = createCubeFaceTex(gl, gap, nullptr, SHAPE_SQUARE); vector labels = NaviCubeImplementation::m_labels; if (labels.size() != 6) { labels.clear(); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/NaviCube"); labels.push_back(hGrp->GetASCII("TextFront", "FRONT")); labels.push_back(hGrp->GetASCII("TextRear", "REAR")); labels.push_back(hGrp->GetASCII("TextTop", "TOP")); labels.push_back(hGrp->GetASCII("TextBottom", "BOTTOM")); labels.push_back(hGrp->GetASCII("TextRight", "RIGHT")); labels.push_back(hGrp->GetASCII("TextLeft", "LEFT")); } // create the main faces m_Textures[TEX_FRONT] = createCubeFaceTex(gl, gap, labels[0].c_str(), SHAPE_SQUARE); m_Textures[TEX_REAR] = createCubeFaceTex(gl, gap, labels[1].c_str(), SHAPE_SQUARE); m_Textures[TEX_TOP] = createCubeFaceTex(gl, gap, labels[2].c_str(), SHAPE_SQUARE); m_Textures[TEX_BOTTOM] = createCubeFaceTex(gl, gap, labels[3].c_str(), SHAPE_SQUARE); m_Textures[TEX_RIGHT] = createCubeFaceTex(gl, gap, labels[4].c_str(), SHAPE_SQUARE); m_Textures[TEX_LEFT] = createCubeFaceTex(gl, gap, labels[5].c_str(), SHAPE_SQUARE); // create the arrows m_Textures[TEX_ARROW_NORTH] = createButtonTex(gl, TEX_ARROW_NORTH); m_Textures[TEX_ARROW_SOUTH] = createButtonTex(gl, TEX_ARROW_SOUTH); m_Textures[TEX_ARROW_EAST] = createButtonTex(gl, TEX_ARROW_EAST); m_Textures[TEX_ARROW_WEST] = createButtonTex(gl, TEX_ARROW_WEST); m_Textures[TEX_ARROW_LEFT] = createButtonTex(gl, TEX_ARROW_LEFT); m_Textures[TEX_ARROW_RIGHT] = createButtonTex(gl, TEX_ARROW_RIGHT); m_Textures[TEX_DOT_BACKSIDE] = createButtonTex(gl, TEX_DOT_BACKSIDE); m_Textures[TEX_VIEW_MENU_ICON] = createMenuTex(gl, false); m_Textures[TEX_VIEW_MENU_FACE] = createMenuTex(gl, true); // front,back,pick,pickid addFace(gap, x, z, TEX_TOP, TEX_FRONT_FACE, TEX_TOP, true); x = r90x * x; z = r90x * z; addFace(gap, x, z, TEX_FRONT, TEX_FRONT_FACE, TEX_FRONT, true); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_LEFT, TEX_FRONT_FACE, TEX_LEFT, true); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_REAR, TEX_FRONT_FACE, TEX_REAR, true); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_RIGHT, TEX_FRONT_FACE, TEX_RIGHT, true); x = r90x * r90z * x; z = r90x * r90z * z; addFace(gap, x, z, TEX_BOTTOM, TEX_FRONT_FACE, TEX_BOTTOM, true); // add corner faces m_Textures[TEX_CORNER_FACE] = createCubeFaceTex(gl, gap, nullptr, SHAPE_CORNER); // we need to rotate to the edge, thus matrix for rotation angle of 54.7 deg cs = cos(atan(sqrt(2.0))); sn = sin(atan(sqrt(2.0))); Matrix3f r54x; r54x << 1, 0, 0, 0, cs, -sn, 0, sn, cs; z = r45z * r54x * z; x = r45z * r54x * x; z *= sqrt(3) * (1 - 2 * gap); // corner face position along the cube diagonal addFace(gap, x, z, TEX_CORNER_FACE, TEX_CORNER_FACE, TEX_BOTTOM_RIGHT_REAR); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_CORNER_FACE, TEX_CORNER_FACE, TEX_BOTTOM_FRONT_RIGHT); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_CORNER_FACE, TEX_CORNER_FACE, TEX_BOTTOM_LEFT_FRONT); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_CORNER_FACE, TEX_CORNER_FACE, TEX_BOTTOM_REAR_LEFT); x = r90x * r90x * r90z * x; z = r90x * r90x * r90z * z; addFace(gap, x, z, TEX_CORNER_FACE, TEX_CORNER_FACE, TEX_TOP_RIGHT_FRONT); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_CORNER_FACE, TEX_CORNER_FACE, TEX_TOP_FRONT_LEFT); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_CORNER_FACE, TEX_CORNER_FACE, TEX_TOP_LEFT_REAR); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_CORNER_FACE, TEX_CORNER_FACE, TEX_TOP_REAR_RIGHT); // add edge faces m_Textures[TEX_EDGE_FACE] = createCubeFaceTex(gl, gap, nullptr, SHAPE_EDGE); // first back to top side x[0] = 1; x[1] = 0; x[2] = 0; z[0] = 0; z[1] = 0; z[2] = 1; // rotate 45 degrees up z = r45x * z; x = r45x * x; z *= sqrt(2) * (1 - gap); addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_FRONT_TOP); x = r90x * x; z = r90x * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_FRONT_BOTTOM); x = r90x * x; z = r90x * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_REAR_BOTTOM); x = r90x * x; z = r90x * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_REAR_TOP); x = r90y * x; z = r90y * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_REAR_RIGHT); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_FRONT_RIGHT); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_FRONT_LEFT); x = r90z * x; z = r90z * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_REAR_LEFT); x = r90x * x; z = r90x * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_TOP_LEFT); x = r90y * x; z = r90y * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_TOP_RIGHT); x = r90y * x; z = r90y * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_BOTTOM_RIGHT); x = r90y * x; z = r90y * z; addFace(gap, x, z, TEX_EDGE_FACE, TEX_EDGE_FACE, TEX_BOTTOM_LEFT); m_Buttons.push_back(TEX_ARROW_NORTH); m_Buttons.push_back(TEX_ARROW_SOUTH); m_Buttons.push_back(TEX_ARROW_EAST); m_Buttons.push_back(TEX_ARROW_WEST); m_Buttons.push_back(TEX_ARROW_LEFT); m_Buttons.push_back(TEX_ARROW_RIGHT); m_Buttons.push_back(TEX_DOT_BACKSIDE); m_PickingFramebuffer = new QtGLFramebufferObject(2 * m_CubeWidgetSize, 2 * m_CubeWidgetSize, QtGLFramebufferObject::CombinedDepthStencil); } void NaviCubeImplementation::drawNaviCube() { glViewport(m_CubeWidgetPosX - m_CubeWidgetSize / 2, m_CubeWidgetPosY - m_CubeWidgetSize / 2, m_CubeWidgetSize, m_CubeWidgetSize); drawNaviCube(false); } void NaviCubeImplementation::createContextMenu(const std::vector& cmd) { CommandManager& rcCmdMgr = Application::Instance->commandManager(); m_Menu->clear(); for (const auto & i : cmd) { Command* cmd = rcCmdMgr.getCommandByName(i.c_str()); if (cmd) cmd->addTo(m_Menu); } } void NaviCubeImplementation::handleResize() { SbVec2s view = m_View3DInventorViewer->getSoRenderManager()->getSize(); if ((m_PrevWidth != view[0]) || (m_PrevHeight != view[1])) { if ((m_PrevWidth > 0) && (m_PrevHeight > 0)) { // maintain position relative to closest edge if (m_CubeWidgetPosX > m_PrevWidth / 2) m_CubeWidgetPosX = view[0] - (m_PrevWidth - m_CubeWidgetPosX); if (m_CubeWidgetPosY > m_PrevHeight / 2) m_CubeWidgetPosY = view[1] - (m_PrevHeight - m_CubeWidgetPosY); } else { // initial position ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/NaviCube"); int m_CubeWidgetOffsetX = hGrp->GetInt("OffsetX", 0); int m_CubeWidgetOffsetY = hGrp->GetInt("OffsetY", 0); switch (m_Corner) { case NaviCube::TopLeftCorner: m_CubeWidgetPosX = m_CubeWidgetSize * 1.1 / 2 + m_CubeWidgetOffsetX; m_CubeWidgetPosY = view[1] - m_CubeWidgetSize * 1.1 / 2 - m_CubeWidgetOffsetY; break; case NaviCube::TopRightCorner: m_CubeWidgetPosX = view[0] - m_CubeWidgetSize * 1.1 / 2 - m_CubeWidgetOffsetX; m_CubeWidgetPosY = view[1] - m_CubeWidgetSize * 1.1 / 2 - m_CubeWidgetOffsetY; break; case NaviCube::BottomLeftCorner: m_CubeWidgetPosX = m_CubeWidgetSize * 1.1 / 2 + m_CubeWidgetOffsetX; m_CubeWidgetPosY = m_CubeWidgetSize * 1.1 / 2 + m_CubeWidgetOffsetY; break; case NaviCube::BottomRightCorner: m_CubeWidgetPosX = view[0] - m_CubeWidgetSize * 1.1 / 2 - m_CubeWidgetOffsetX; m_CubeWidgetPosY = m_CubeWidgetSize * 1.1 / 2 + m_CubeWidgetOffsetY; break; } } m_PrevWidth = view[0]; m_PrevHeight = view[1]; m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw(); } } void NaviCubeImplementation::drawNaviCube(bool pickMode) { // initializes stuff here when we actually have a context // FIXME actually now that we have Qt5, we could probably do this earlier // (as we do not need the opengl context) if (!m_NaviCubeInitialised) { auto gl = static_cast(m_View3DInventorViewer->viewport()); if (!gl) return; initNaviCube(gl); m_NaviCubeInitialised = true; } SoCamera* cam = m_View3DInventorViewer->getSoRenderManager()->getCamera(); if (!cam) return; handleResize(); // Store GL state. glPushAttrib(GL_ALL_ATTRIB_BITS); GLfloat depthrange[2]; glGetFloatv(GL_DEPTH_RANGE, depthrange); GLdouble projectionmatrix[16]; glGetDoublev(GL_PROJECTION_MATRIX, projectionmatrix); glDepthMask(GL_TRUE); glDepthRange(0.0, 1.0); glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glLineWidth(2.0); glDisable(GL_LIGHTING); //glDisable(GL_BLEND); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE); glDepthMask(GL_TRUE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glShadeModel(GL_SMOOTH); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW); glAlphaFunc(GL_GREATER, 0.25); glEnable(GL_ALPHA_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); const float NEARVAL = 0.1f; const float FARVAL = 10.0f; const float dim = NEARVAL * float(tan(M_PI / 8.0)) * 1.2; glFrustum(-dim, dim, -dim, dim, NEARVAL, FARVAL); SbMatrix mx; mx = cam->orientation.getValue(); mx = mx.inverse(); mx[3][2] = -5.0; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadMatrixf((float*)mx); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); if (pickMode) { glDisable(GL_BLEND); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glShadeModel(GL_FLAT); glDisable(GL_DITHER); glDisable(GL_POLYGON_SMOOTH); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } glClear(GL_DEPTH_BUFFER_BIT); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, (void*)m_VertexArray.data()); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, m_TextureCoordArray.data()); if (!pickMode) { // Draw the axes ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/NaviCube"); bool ShowCS = hGrp->GetBool("ShowCS", 1); if (ShowCS) { glDisable(GL_TEXTURE_2D); float a = 1.1f; glColor3f(1, 0, 0); glBegin(GL_LINES); glVertex3f(-1.1f, -1.1f, -1.1f); glVertex3f(+0.5f, -1.1f, -1.1f); glEnd(); glRasterPos3d(a, -a, -a); glColor3f(0, 1, 0); glBegin(GL_LINES); glVertex3f(-1.1f, -1.1f, -1.1f); glVertex3f(-1.1f, +0.5f, -1.1f); glEnd(); glRasterPos3d(-a, a, -a); glColor3f(0, 0, 1); glBegin(GL_LINES); glVertex3f(-1.1f, -1.1f, -1.1f); glVertex3f(-1.1f, -1.1f, +0.5f); glEnd(); glRasterPos3d(-a, -a, a); glEnable(GL_TEXTURE_2D); } } // Draw the cube faces if (pickMode) { for (vector::iterator f = m_Faces.begin(); f != m_Faces.end(); f++) { glColor3ub((*f)->m_PickId, 0, 0); glBindTexture(GL_TEXTURE_2D, (*f)->m_PickTextureId); glDrawElements(GL_TRIANGLE_FAN, (*f)->m_VertexCount, GL_UNSIGNED_BYTE, (void*)&m_IndexArray[(*f)->m_FirstVertex]); } } else { for (int pass = 0; pass < 3; pass++) { for (vector::iterator f = m_Faces.begin(); f != m_Faces.end(); f++) { //if (pickMode) { // pick should not be drawn in tree passes // glColor3ub((*f)->m_PickId, 0, 0); // glBindTexture(GL_TEXTURE_2D, (*f)->m_PickTextureId); //} else { if (pass != (*f)->m_RenderPass) continue; QColor& c = (m_HiliteId == (*f)->m_PickId) && (pass < 2) ? m_HiliteColor : (*f)->m_Color; glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()); glBindTexture(GL_TEXTURE_2D, (*f)->m_TextureId); //} glDrawElements(GL_TRIANGLE_FAN, (*f)->m_VertexCount, GL_UNSIGNED_BYTE, (void*)&m_IndexArray[(*f)->m_FirstVertex]); } } } // Draw the rotate buttons glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); if (!pickMode && m_BorderWidth >= 1.0f) { glDisable(GL_DEPTH_TEST); glDisable(GL_TEXTURE_2D); const auto& c = m_BorderColor; glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()); glLineWidth(m_BorderWidth); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); for (int pass = 0; pass < 3; pass++) { for (const auto& f : m_Faces) { if (pass != f->m_RenderPass) continue; if (f->m_TextureId == f->m_PickTextureId) { if (f->m_PickTexId == TEX_FRONT_FACE || f->m_PickTexId == TEX_EDGE_FACE || f->m_PickTexId == TEX_CORNER_FACE) { glBegin(GL_POLYGON); for (const Vector3f& v : m_VertexArrays2[f->m_PickId]) { glVertex3f(v[0], v[1], v[2]); } glEnd(); } } } } glEnable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } glDisable(GL_DEPTH_TEST); glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 1, 1, 0, 0, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); for (vector::iterator b = m_Buttons.begin(); b != m_Buttons.end(); b++) { if (pickMode) glColor3ub(*b, 0, 0); else { QColor& c = (m_HiliteId == (*b)) ? m_HiliteColor : m_ButtonColor; glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()); } glBindTexture(GL_TEXTURE_2D, m_Textures[*b]); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex3f(0.0f, 1.0f, 0.0f); glTexCoord2f(1, 0); glVertex3f(1.0f, 1.0f, 0.0f); glTexCoord2f(1, 1); glVertex3f(1.0f, 0.0f, 0.0f); glTexCoord2f(0, 1); glVertex3f(0.0f, 0.0f, 0.0f); glEnd(); } // Draw the view menu icon if (pickMode) { glColor3ub(TEX_VIEW_MENU_FACE, 0, 0); glBindTexture(GL_TEXTURE_2D, m_Textures[TEX_VIEW_MENU_FACE]); } else { if (m_HiliteId == TEX_VIEW_MENU_FACE) { QColor& c = m_HiliteColor; glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()); glBindTexture(GL_TEXTURE_2D, m_Textures[TEX_VIEW_MENU_FACE]); glBegin(GL_QUADS); // DO THIS WITH VERTEX ARRAYS glTexCoord2f(0, 0); glVertex3f(0.0f, 1.0f, 0.0f); glTexCoord2f(1, 0); glVertex3f(1.0f, 1.0f, 0.0f); glTexCoord2f(1, 1); glVertex3f(1.0f, 0.0f, 0.0f); glTexCoord2f(0, 1); glVertex3f(0.0f, 0.0f, 0.0f); glEnd(); } QColor& c = m_ButtonColor; glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF()); glBindTexture(GL_TEXTURE_2D, m_Textures[TEX_VIEW_MENU_ICON]); } glBegin(GL_QUADS); // FIXME do this with vertex arrays glTexCoord2f(0, 0); glVertex3f(0.0f, 1.0f, 0.0f); glTexCoord2f(1, 0); glVertex3f(1.0f, 1.0f, 0.0f); glTexCoord2f(1, 1); glVertex3f(1.0f, 0.0f, 0.0f); glTexCoord2f(0, 1); glVertex3f(0.0f, 0.0f, 0.0f); glEnd(); glPopMatrix(); // Restore original state. glDepthRange(depthrange[0], depthrange[1]); glMatrixMode(GL_PROJECTION); glLoadMatrixd(projectionmatrix); glPopAttrib(); } int NaviCubeImplementation::pickFace(short x, short y) { GLubyte pixels[4] = { 0 }; if (m_PickingFramebuffer) { m_PickingFramebuffer->bind(); glViewport(0, 0, 2 * m_CubeWidgetSize, 2 * m_CubeWidgetSize); glLoadIdentity(); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawNaviCube(true); glFinish(); glReadPixels(2 * (x - (m_CubeWidgetPosX - m_CubeWidgetSize / 2)), 2 * (y - (m_CubeWidgetPosY - m_CubeWidgetSize / 2)), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixels); m_PickingFramebuffer->release(); //QImage image = m_PickingFramebuffer->toImage(); //image.save(QLatin1String("pickimage.png")); } return pixels[3] == 255 ? pixels[0] : 0; } bool NaviCubeImplementation::mousePressed(short x, short y) { m_MouseDown = true; m_Dragging = false; m_MightDrag = inDragZone(x, y); int pick = pickFace(x, y); // cerr << enum2str(pick) << endl; setHilite(pick); return pick != 0; } SbRotation NaviCubeImplementation::setView(float rotZ, float rotX) const { SbRotation rz, rx, t; rz.setValue(SbVec3f(0, 0, 1), rotZ * M_PI / 180); rx.setValue(SbVec3f(1, 0, 0), rotX * M_PI / 180); return rx * rz; } SbRotation NaviCubeImplementation::rotateView(SbRotation viewRot, int axis, float rotAngle, SbVec3f customAxis) const { SbVec3f up; viewRot.multVec(SbVec3f(0, 1, 0), up); SbVec3f out; viewRot.multVec(SbVec3f(0, 0, 1), out); SbVec3f right; viewRot.multVec(SbVec3f(1, 0, 0), right); SbVec3f direction; switch (axis) { default: return viewRot; case DIR_UP: direction = up; break; case DIR_OUT: direction = out; break; case DIR_RIGHT: direction = right; break; } if (customAxis != SbVec3f(0, 0, 0)) direction = customAxis; SbRotation rot(direction, -rotAngle * M_PI / 180.0); SbRotation newViewRot = viewRot * rot; return newViewRot; } void NaviCubeImplementation::rotateView(const SbRotation& rot) { m_View3DInventorViewer->setCameraOrientation(rot); } void NaviCubeImplementation::handleMenu() { m_Menu->exec(QCursor::pos()); } bool NaviCubeImplementation::mouseReleased(short x, short y) { setHilite(0); m_MouseDown = false; // get the current view SbMatrix ViewRotMatrix; SbRotation CurrentViewRot = m_View3DInventorViewer->getCameraOrientation(); CurrentViewRot.getValue(ViewRotMatrix); if (!m_Dragging) { float rot = 45; float tilt = 90 - Base::toDegrees(atan(sqrt(2.0))); int pick = pickFace(x, y); long step = Base::clamp(long(m_NaviStepByTurn), 4L, 36L); float rotStepAngle = 360.0f / step; bool applyRotation = true; SbRotation viewRot = CurrentViewRot; switch (pick) { default: return false; break; case TEX_FRONT: viewRot = setView(0, 90); // we don't want to dumb rotate to the same view since depending on from where the user clicked on FRONT // we have one of four suitable end positions. // we use here the same rotation logic used by other programs using OCC like "CAD Assistant" // when current matrix's 0,0 entry is larger than its |1,0| entry, we already have the final result // otherwise rotate around y if (m_RotateToNearest) { if (ViewRotMatrix[0][0] < 0 && abs(ViewRotMatrix[0][0]) >= abs(ViewRotMatrix[1][0])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][0] > 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, 90); else if (ViewRotMatrix[1][0] < 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, -90); } break; case TEX_REAR: viewRot = setView(180, 90); if (m_RotateToNearest) { if (ViewRotMatrix[0][0] > 0 && abs(ViewRotMatrix[0][0]) >= abs(ViewRotMatrix[1][0])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][0] > 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[1][0] < 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_LEFT: viewRot = setView(270, 90); if (m_RotateToNearest) { if (ViewRotMatrix[0][1] > 0 && abs(ViewRotMatrix[0][1]) >= abs(ViewRotMatrix[1][1])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][1] > 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[1][1] < 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_RIGHT: viewRot = setView(90, 90); if (m_RotateToNearest) { if (ViewRotMatrix[0][1] < 0 && abs(ViewRotMatrix[0][1]) >= abs(ViewRotMatrix[1][1])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][1] > 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, 90); else if (ViewRotMatrix[1][1] < 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, -90); } break; case TEX_TOP: viewRot = setView(0, 0); if (m_RotateToNearest) { if (ViewRotMatrix[0][0] < 0 && abs(ViewRotMatrix[0][0]) >= abs(ViewRotMatrix[1][0])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][0] > 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, 90); else if (ViewRotMatrix[1][0] < 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, -90); } break; case TEX_BOTTOM: viewRot = setView(0, 180); if (m_RotateToNearest) { if (ViewRotMatrix[0][0] < 0 && abs(ViewRotMatrix[0][0]) >= abs(ViewRotMatrix[1][0])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][0] > 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, 90); else if (ViewRotMatrix[1][0] < 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, -90); } break; case TEX_FRONT_TOP: // set to FRONT then rotate viewRot = setView(0, 90); viewRot = rotateView(viewRot, 1, 45); if (m_RotateToNearest) { if (ViewRotMatrix[0][0] < 0 && abs(ViewRotMatrix[0][0]) >= abs(ViewRotMatrix[1][0])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][0] > 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, 90); else if (ViewRotMatrix[1][0] < 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, -90); } break; case TEX_FRONT_BOTTOM: // set to FRONT then rotate viewRot = setView(0, 90); viewRot = rotateView(viewRot, 1, -45); if (m_RotateToNearest) { if (ViewRotMatrix[0][0] < 0 && abs(ViewRotMatrix[0][0]) >= abs(ViewRotMatrix[1][0])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][0] > 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, 90); else if (ViewRotMatrix[1][0] < 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, -90); } break; case TEX_REAR_BOTTOM: // set to REAR then rotate viewRot = setView(180, 90); viewRot = rotateView(viewRot, 1, -45); if (m_RotateToNearest) { if (ViewRotMatrix[0][0] > 0 && abs(ViewRotMatrix[0][0]) >= abs(ViewRotMatrix[1][0])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][0] > 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[1][0] < 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_REAR_TOP: // set to REAR then rotate viewRot = setView(180, 90); viewRot = rotateView(viewRot, 1, 45); if (m_RotateToNearest) { if (ViewRotMatrix[0][0] > 0 && abs(ViewRotMatrix[0][0]) >= abs(ViewRotMatrix[1][0])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][0] > 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[1][0] < 0 && abs(ViewRotMatrix[1][0]) > abs(ViewRotMatrix[0][0])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_FRONT_LEFT: // set to FRONT then rotate viewRot = setView(0, 90); viewRot = rotateView(viewRot, 0, 45); if (m_RotateToNearest) { if (ViewRotMatrix[1][2] < 0 && abs(ViewRotMatrix[1][2]) >= abs(ViewRotMatrix[0][2])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[0][2] > 0 && abs(ViewRotMatrix[0][2]) > abs(ViewRotMatrix[1][2])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[0][2] < 0 && abs(ViewRotMatrix[0][2]) > abs(ViewRotMatrix[1][2])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_FRONT_RIGHT: // set to FRONT then rotate viewRot = setView(0, 90); viewRot = rotateView(viewRot, 0, -45); if (m_RotateToNearest) { if (ViewRotMatrix[1][2] < 0 && abs(ViewRotMatrix[1][2]) >= abs(ViewRotMatrix[0][2])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[0][2] > 0 && abs(ViewRotMatrix[0][2]) > abs(ViewRotMatrix[1][2])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[0][2] < 0 && abs(ViewRotMatrix[0][2]) > abs(ViewRotMatrix[1][2])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_REAR_RIGHT: // set to REAR then rotate viewRot = setView(180, 90); viewRot = rotateView(viewRot, 0, 45); if (m_RotateToNearest) { if (ViewRotMatrix[1][2] < 0 && abs(ViewRotMatrix[1][2]) >= abs(ViewRotMatrix[0][2])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[0][2] > 0 && abs(ViewRotMatrix[0][2]) > abs(ViewRotMatrix[1][2])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[0][2] < 0 && abs(ViewRotMatrix[0][2]) > abs(ViewRotMatrix[1][2])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_REAR_LEFT: // set to REAR then rotate viewRot = setView(180, 90); viewRot = rotateView(viewRot, 0, -45); if (ViewRotMatrix[1][2] < 0 && abs(ViewRotMatrix[1][2]) >= abs(ViewRotMatrix[0][2])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[0][2] > 0 && abs(ViewRotMatrix[0][2]) > abs(ViewRotMatrix[1][2])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[0][2] < 0 && abs(ViewRotMatrix[0][2]) > abs(ViewRotMatrix[1][2])) viewRot = rotateView(viewRot, 2, 90); break; case TEX_TOP_LEFT: // set to LEFT then rotate viewRot = setView(270, 90); viewRot = rotateView(viewRot, 1, 45); if (m_RotateToNearest) { if (ViewRotMatrix[0][1] > 0 && abs(ViewRotMatrix[0][1]) >= abs(ViewRotMatrix[1][1])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][1] > 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[1][1] < 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_TOP_RIGHT: // set to RIGHT then rotate viewRot = setView(90, 90); viewRot = rotateView(viewRot, 1, 45); if (m_RotateToNearest) { if (ViewRotMatrix[0][1] < 0 && abs(ViewRotMatrix[0][1]) >= abs(ViewRotMatrix[1][1])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][1] > 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, 90); else if (ViewRotMatrix[1][1] < 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, -90); } break; case TEX_BOTTOM_RIGHT: // set to RIGHT then rotate viewRot = setView(90, 90); viewRot = rotateView(viewRot, 1, -45); if (m_RotateToNearest) { if (ViewRotMatrix[0][1] < 0 && abs(ViewRotMatrix[0][1]) >= abs(ViewRotMatrix[1][1])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][1] > 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, 90); else if (ViewRotMatrix[1][1] < 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, -90); } break; case TEX_BOTTOM_LEFT: // set to LEFT then rotate viewRot = setView(270, 90); viewRot = rotateView(viewRot, 1, -45); if (m_RotateToNearest) { if (ViewRotMatrix[0][1] > 0 && abs(ViewRotMatrix[0][1]) >= abs(ViewRotMatrix[1][1])) viewRot = rotateView(viewRot, 2, 180); else if (ViewRotMatrix[1][1] > 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, -90); else if (ViewRotMatrix[1][1] < 0 && abs(ViewRotMatrix[1][1]) > abs(ViewRotMatrix[0][1])) viewRot = rotateView(viewRot, 2, 90); } break; case TEX_BOTTOM_LEFT_FRONT: viewRot = setView(rot - 90, 90 + tilt); // we have 3 possible end states: // - z-axis is not rotated larger than 120 deg from (0, 1, 0) -> we are already there // - y-axis is not rotated larger than 120 deg from (0, 1, 0) // - x-axis is not rotated larger than 120 deg from (0, 1, 0) if (m_RotateToNearest) { if (ViewRotMatrix[1][0] > 0.4823) viewRot = rotateView(viewRot, 0, -120, SbVec3f(1, 1, 1)); else if (ViewRotMatrix[1][1] > 0.4823) viewRot = rotateView(viewRot, 0, 120, SbVec3f(1, 1, 1)); } break; case TEX_BOTTOM_FRONT_RIGHT: viewRot = setView(90 + rot - 90, 90 + tilt); if (m_RotateToNearest) { if (ViewRotMatrix[1][0] < -0.4823) viewRot = rotateView(viewRot, 0, 120, SbVec3f(-1, 1, 1)); else if (ViewRotMatrix[1][1] > 0.4823) viewRot = rotateView(viewRot, 0, -120, SbVec3f(-1, 1, 1)); } break; case TEX_BOTTOM_RIGHT_REAR: viewRot = setView(180 + rot - 90, 90 + tilt); if (m_RotateToNearest) { if (ViewRotMatrix[1][0] < -0.4823) viewRot = rotateView(viewRot, 0, -120, SbVec3f(-1, -1, 1)); else if (ViewRotMatrix[1][1] < -0.4823) viewRot = rotateView(viewRot, 0, 120, SbVec3f(-1, -1, 1)); } break; case TEX_BOTTOM_REAR_LEFT: viewRot = setView(270 + rot - 90, 90 + tilt); if (m_RotateToNearest) { if (ViewRotMatrix[1][0] > 0.4823) viewRot = rotateView(viewRot, 0, 120, SbVec3f(1, -1, 1)); else if (ViewRotMatrix[1][1] < -0.4823) viewRot = rotateView(viewRot, 0, -120, SbVec3f(1, -1, 1)); } break; case TEX_TOP_RIGHT_FRONT: viewRot = setView(rot, 90 - tilt); if (m_RotateToNearest) { if (ViewRotMatrix[1][0] > 0.4823) viewRot = rotateView(viewRot, 0, -120, SbVec3f(-1, 1, -1)); else if (ViewRotMatrix[1][1] < -0.4823) viewRot = rotateView(viewRot, 0, 120, SbVec3f(-1, 1, -1)); } break; case TEX_TOP_FRONT_LEFT: viewRot = setView(rot - 90, 90 - tilt); if (m_RotateToNearest) { if (ViewRotMatrix[1][0] < -0.4823) viewRot = rotateView(viewRot, 0, 120, SbVec3f(1, 1, -1)); else if (ViewRotMatrix[1][1] < -0.4823) viewRot = rotateView(viewRot, 0, -120, SbVec3f(1, 1, -1)); } break; case TEX_TOP_LEFT_REAR: viewRot = setView(rot - 180, 90 - tilt); if (m_RotateToNearest) { if (ViewRotMatrix[1][0] < -0.4823) viewRot = rotateView(viewRot, 0, -120, SbVec3f(1, -1, -1)); else if (ViewRotMatrix[1][1] > 0.4823) viewRot = rotateView(viewRot, 0, 120, SbVec3f(1, -1, -1)); } break; case TEX_TOP_REAR_RIGHT: viewRot = setView(rot - 270, 90 - tilt); if (m_RotateToNearest) { if (ViewRotMatrix[1][0] > 0.4823) viewRot = rotateView(viewRot, 0, 120, SbVec3f(-1, -1, -1)); else if (ViewRotMatrix[1][1] > 0.4823) viewRot = rotateView(viewRot, 0, -120, SbVec3f(-1, -1, -1)); } break; case TEX_ARROW_LEFT: viewRot = rotateView(viewRot, DIR_OUT, rotStepAngle); break; case TEX_ARROW_RIGHT: viewRot = rotateView(viewRot, DIR_OUT, -rotStepAngle); break; case TEX_ARROW_WEST: viewRot = rotateView(viewRot, DIR_UP, -rotStepAngle); break; case TEX_ARROW_EAST: viewRot = rotateView(viewRot, DIR_UP, rotStepAngle); break; case TEX_ARROW_NORTH: viewRot = rotateView(viewRot, DIR_RIGHT, -rotStepAngle); break; case TEX_ARROW_SOUTH: viewRot = rotateView(viewRot, DIR_RIGHT, rotStepAngle); break; case TEX_DOT_BACKSIDE: viewRot = rotateView(viewRot, DIR_UP, 180); break; case TEX_VIEW_MENU_FACE: handleMenu(); applyRotation = false; break; } if (applyRotation) rotateView(viewRot); } return true; } void NaviCubeImplementation::setHilite(int hilite) { if (hilite != m_HiliteId) { m_HiliteId = hilite; //cerr << "m_HiliteFace " << m_HiliteId << endl; m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw(); } } bool NaviCubeImplementation::inDragZone(short x, short y) { int dx = x - m_CubeWidgetPosX; int dy = y - m_CubeWidgetPosY; int limit = m_CubeWidgetSize / 4; return abs(dx) < limit && abs(dy) < limit; } bool NaviCubeImplementation::mouseMoved(short x, short y) { setHilite(pickFace(x, y)); if (m_MouseDown) { if (m_MightDrag && !m_Dragging && !inDragZone(x, y)) m_Dragging = true; if (m_Dragging) { setHilite(0); SbVec2s view = m_View3DInventorViewer->getSoRenderManager()->getSize(); int width = view[0]; int height = view[1]; int len = m_CubeWidgetSize / 2; m_CubeWidgetPosX = std::min(std::max(static_cast(x), len), width - len); m_CubeWidgetPosY = std::min(std::max(static_cast(y), len), height - len); this->m_View3DInventorViewer->getSoRenderManager()->scheduleRedraw(); return true; } } return false; } bool NaviCubeImplementation::processSoEvent(const SoEvent* ev) { short x, y; ev->getPosition().getValue(x, y); // FIXME find out why do we need to hack the cursor position to get // 2019-02-17 // The above comment is truncated; don't know what it's about // The two hacked lines changing the cursor position are responsible for // parts of the navigational cluster not being active. // Commented them out and everything seems to be working // y += 4; // x -= 2; if (ev->getTypeId().isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) { const auto mbev = static_cast(ev); if (mbev->isButtonPressEvent(mbev, SoMouseButtonEvent::BUTTON1)) return mousePressed(x, y); if (mbev->isButtonReleaseEvent(mbev, SoMouseButtonEvent::BUTTON1)) return mouseReleased(x, y); } if (ev->getTypeId().isDerivedFrom(SoLocation2Event::getClassTypeId())) return mouseMoved(x, y); return false; } QString NaviCubeImplementation::str(const char* str) { return QString::fromLatin1(str); } void NaviCube::setNaviCubeCommands(const std::vector& cmd) { NaviCubeImplementation::m_commands = cmd; } void NaviCube::setNaviCubeLabels(const std::vector& labels) { NaviCubeImplementation::m_labels = labels; } DEF_3DV_CMD(ViewIsometricCmd) ViewIsometricCmd::ViewIsometricCmd() : Command("ViewIsometricCmd") { sGroup = ""; sMenuText = QT_TR_NOOP("Isometric"); sToolTipText = QT_TR_NOOP("Set NaviCube to Isometric mode"); sWhatsThis = ""; sStatusTip = sToolTipText; sPixmap = ""; sAccel = ""; eType = Alter3DView; } void ViewIsometricCmd::activated(int iMsg) { Q_UNUSED(iMsg); Command::doCommand(Command::Gui, "Gui.activeDocument().activeView().viewIsometric()"); } DEF_3DV_CMD(ViewOrthographicCmd) ViewOrthographicCmd::ViewOrthographicCmd() : Command("ViewOrthographicCmd") { sGroup = ""; sMenuText = QT_TR_NOOP("Orthographic"); sToolTipText = QT_TR_NOOP("Set View to Orthographic mode"); sWhatsThis = ""; sStatusTip = sToolTipText; sPixmap = ""; sAccel = ""; eType = Alter3DView; } void ViewOrthographicCmd::activated(int iMsg) { Q_UNUSED(iMsg); Command::doCommand(Command::Gui, "Gui.activeDocument().activeView().setCameraType(\"Orthographic\")"); } DEF_3DV_CMD(ViewPerspectiveCmd) ViewPerspectiveCmd::ViewPerspectiveCmd() : Command("ViewPerspectiveCmd") { sGroup = ""; sMenuText = QT_TR_NOOP("Perspective"); sToolTipText = QT_TR_NOOP("Set View to Perspective mode"); sWhatsThis = ""; sStatusTip = sToolTipText; sPixmap = ""; sAccel = ""; eType = Alter3DView; } void ViewPerspectiveCmd::activated(int iMsg) { Q_UNUSED(iMsg); Command::doCommand(Command::Gui, "Gui.activeDocument().activeView().setCameraType(\"Perspective\")"); } DEF_3DV_CMD(ViewZoomToFitCmd) ViewZoomToFitCmd::ViewZoomToFitCmd() : Command("ViewZoomToFit") { sGroup = ""; sMenuText = QT_TR_NOOP("Zoom to fit"); sToolTipText = QT_TR_NOOP("Zoom so that model fills the view"); sWhatsThis = ""; sStatusTip = sToolTipText; sPixmap = ""; sAccel = ""; eType = Alter3DView; } void ViewZoomToFitCmd::activated(int iMsg) { Q_UNUSED(iMsg); Command::doCommand(Command::Gui, "Gui.SendMsgToActiveView(\"ViewFit\")"); } QMenu* NaviCubeImplementation::createNaviCubeMenu() { auto menu = new QMenu(getMainWindow()); menu->setObjectName(str("NaviCube_Menu")); CommandManager& rcCmdMgr = Application::Instance->commandManager(); static bool init = true; if (init) { init = false; rcCmdMgr.addCommand(new ViewOrthographicCmd); rcCmdMgr.addCommand(new ViewPerspectiveCmd); rcCmdMgr.addCommand(new ViewIsometricCmd); rcCmdMgr.addCommand(new ViewZoomToFitCmd); } vector commands = NaviCubeImplementation::m_commands; if (commands.empty()) { commands.emplace_back("ViewOrthographicCmd"); commands.emplace_back("ViewPerspectiveCmd"); commands.emplace_back("ViewIsometricCmd"); commands.emplace_back("Separator"); commands.emplace_back("ViewZoomToFit"); } for (const auto & command : commands) { if (command == "Separator") { menu->addSeparator(); } else { Command* cmd = rcCmdMgr.getCommandByName(command.c_str()); if (cmd) cmd->addTo(menu); } } return menu; }