/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010-2012 University of Bonn. All rights reserved. * Copyright (C) 2013 Frederik Heber. All rights reserved. * * * This file is part of MoleCuilder. * * MoleCuilder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * MoleCuilder 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MoleCuilder. If not, see . */ /* * GLWorldView.cpp * * Created on: Aug 1, 2010 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "GLWorldView.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "GLWorldScene.hpp" //#include "CodePatterns/MemDebug.hpp" #include "Atom/AtomObserver.hpp" #include "Atom/atom_observable.hpp" #include "Box.hpp" #include "CodePatterns/Log.hpp" #include "CodePatterns/Observer/Notification.hpp" #include "CodePatterns/Observer/ObserverLog.hpp" #include "Descriptors/MoleculeIdDescriptor.hpp" #include "molecule.hpp" #include "Shapes/ShapeRegistry.hpp" #include "World.hpp" #include "WorldTime.hpp" GLWorldView::GLWorldView( QtObservedInstanceBoard * _board, QWidget *parent) : QGLView(parent), Observer("GLWorldView"), worldscene(NULL), changesPresent(false), needsRedraw(false) { worldscene = new GLWorldScene(_board, this); setOption(QGLView::ObjectPicking, true); setOption(QGLView::CameraNavigation, false); setFocusPolicy(Qt::StrongFocus); setCameraControlMode(Rotate); defaultEyeSeparation = 4.0; createDomainBox(); createDreiBein(); //changeMaterials(false); qRegisterMetaType("atomId_t"); qRegisterMetaType("moleculeId_t"); connect(this, SIGNAL(ShapeAdded(const std::string &)), worldscene, SLOT(addShape(const std::string &))); connect(this, SIGNAL(ShapeRemoved(const std::string &)), worldscene, SLOT(removeShape(const std::string &))); // connect(this, SIGNAL(TimeChanged()), worldscene, SIGNAL(updated())); connect(worldscene, SIGNAL(changeOccured()), this, SLOT(changeSignalled())); connect(this, SIGNAL(changed()), this, SLOT(changeSignalled())); connect(worldscene, SIGNAL(hoverChanged(const atomId_t)), this, SLOT(sceneHoverSignalled(const atomId_t))); connect(worldscene, SIGNAL(hoverChanged(const moleculeId_t, int)), this, SLOT(sceneHoverSignalled(const moleculeId_t, int))); //connect(this, SIGNAL(changed()), this, SLOT(updateGL())); connect(worldscene, SIGNAL(changed()), this, SLOT(sceneChangeSignalled())); connect(this, SIGNAL(moleculesVisibilityChanged(ObservedValue_Index_t,bool)), worldscene, SLOT(moleculesVisibilityChanged(ObservedValue_Index_t,bool))); // sign on to changes in the world WorldTime::getInstance().signOn(this, WorldTime::TimeChanged); AtomObserver::getInstance().signOn(this, AtomObservable::PositionChanged); AtomObserver::getInstance().signOn(this, AtomObservable::VelocityChanged); AtomObserver::getInstance().signOn(this, AtomObservable::ForceChanged); ShapeRegistry::getInstance().signOn(this); ShapeRegistry::getInstance().signOn(this, ShapeRegistry::ShapeInserted); ShapeRegistry::getInstance().signOn(this, ShapeRegistry::ShapeRemoved); ShapeRegistry::getInstance().signOn(this, ShapeRegistry::SelectionChanged); redrawTimer = new QTimer(this); } GLWorldView::~GLWorldView() { QSettings settings; settings.beginGroup("WorldView"); settings.setValue("domainBoxEnabled", (meshDomainBox->options() & QGLSceneNode::HideNode) == 0); settings.setValue("dreiBeinEnabled", (meshDreiBein->options() & QGLSceneNode::HideNode) == 0); settings.endGroup(); WorldTime::getInstance().signOff(this, WorldTime::TimeChanged); AtomObserver::getInstance().signOff(this, AtomObservable::PositionChanged); AtomObserver::getInstance().signOff(this, AtomObservable::VelocityChanged); AtomObserver::getInstance().signOff(this, AtomObservable::ForceChanged); ShapeRegistry::getInstance().signOff(this); ShapeRegistry::getInstance().signOff(this, ShapeRegistry::ShapeInserted); ShapeRegistry::getInstance().signOff(this, ShapeRegistry::ShapeRemoved); ShapeRegistry::getInstance().signOff(this, ShapeRegistry::SelectionChanged); delete worldscene; delete(domainBoxMaterial); for (int i=0;i<3;i++) delete(dreiBeinMaterial[i]); } /** * Add some widget specific actions to the toolbar: * - camera rotation/translation mode * - camera fit to domain */ void GLWorldView::addToolBarActions(QToolBar *toolbar) { // camera control mode toolbar->addSeparator(); QAction *transAction = new QAction(QIcon::fromTheme("forward"), tr("camera translation mode"), this); connect(transAction, SIGNAL(triggered()), this, SLOT(setCameraControlModeTranslation())); toolbar->addAction(transAction); QAction *rotAction = new QAction(QIcon::fromTheme("object-rotate-left"), tr("camera rotation mode"), this); connect(rotAction, SIGNAL(triggered()), this, SLOT(setCameraControlModeRotation())); toolbar->addAction(rotAction); QAction *fitAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("camera fit to domain"), this); connect(fitAction, SIGNAL(triggered()), this, SLOT(fitCameraToDomain())); toolbar->addAction(fitAction); // stereo mode QToolButton *stereoButton = new QToolButton(toolbar); QMenu *stereoMenu = new QMenu(); QAction *stereoDisableAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("disable"), this); connect(stereoDisableAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeDisable())); stereoMenu->addAction(stereoDisableAction); QAction *stereoHardwareAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("hardware"), this); connect(stereoHardwareAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeHardware())); stereoMenu->addAction(stereoHardwareAction); QAction *stereoLeftRightAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("left right"), this); connect(stereoLeftRightAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeLeftRight())); stereoMenu->addAction(stereoLeftRightAction); QAction *stereoRightLeftAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("right left"), this); connect(stereoRightLeftAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeRightLeft())); stereoMenu->addAction(stereoRightLeftAction); QAction *stereoTopBottomAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("top bottom"), this); connect(stereoTopBottomAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeTopBottom())); stereoMenu->addAction(stereoTopBottomAction); QAction *stereoBottomTopAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("bottom top"), this); connect(stereoBottomTopAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeBottomTop())); stereoMenu->addAction(stereoBottomTopAction); QAction *stereoAnaglyphAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("anaglyph"), this); connect(stereoAnaglyphAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeAnaglyph())); stereoMenu->addAction(stereoAnaglyphAction); stereoButton->setMenu(stereoMenu); stereoButton->setIcon(QIcon(QPixmap(":/icon_view_stereo.png"))); stereoButton->setPopupMode(QToolButton::InstantPopup); toolbar->addWidget(stereoButton); // selection mode toolbar->addSeparator(); QAction *selAtomAction = new QAction(QIcon(QPixmap(":/icon_select_atom.png")), tr("select atom by clicking"), this); connect(selAtomAction, SIGNAL(triggered()), worldscene, SLOT(setSelectionModeAtom())); toolbar->addAction(selAtomAction); QAction *selMolAction = new QAction(QIcon(QPixmap(":/icon_select_molecule.png")), tr("select molecule by clicking"), this); connect(selMolAction, SIGNAL(triggered()), worldscene, SLOT(setSelectionModeMolecule())); toolbar->addAction(selMolAction); // dreiBein/domain enabler toolbar->addSeparator(); QAction *seldreiBein = new QAction(QIcon(QPixmap(":/icon_dreiBein.png")), tr("enable/disable dreiBein"), this); connect(seldreiBein, SIGNAL(triggered()), this, SLOT(changeDreiBein())); toolbar->addAction(seldreiBein); QAction *seldomain = new QAction(QIcon(QPixmap(":/icon_domain.png")), tr("enable/disable domain box"), this); connect(seldomain, SIGNAL(triggered()), this, SLOT(changeDomain())); toolbar->addAction(seldomain); } void GLWorldView::createDomainBox() { QSettings settings; settings.beginGroup("WorldView"); QColor colorFrame = settings.value("domainBoxColorFrame", QColor(150,160,200,255)).value(); QColor colorAmbient = settings.value("domainBoxColorAmbient", QColor(50,60,100,255)).value(); QColor colorDiffuse = settings.value("domainBoxColorDiffuse", QColor(150,160,200,180)).value(); settings.setValue("domainBoxColorFrame", colorFrame); settings.setValue("domainBoxColorAmbient", colorAmbient); settings.setValue("domainBoxColorDiffuse", colorDiffuse); const bool status = settings.value("domainBoxEnabled").toBool(); settings.endGroup(); domainBoxMaterial = new QGLMaterial; domainBoxMaterial->setAmbientColor(QColor(0,0,0,255)); domainBoxMaterial->setDiffuseColor(QColor(0,0,0,255)); domainBoxMaterial->setEmittedLight(colorFrame); QGLMaterial *material = new QGLMaterial; material->setAmbientColor(colorAmbient); material->setDiffuseColor(colorDiffuse); QGLBuilder builder; builder << QGL::Faceted; builder << QGLCube(-1.0); // "inverted" => inside faces are used as front. meshDomainBox = builder.finalizedSceneNode(); QMatrix4x4 mat; mat.translate(0.5f, 0.5f, 0.5f); meshDomainBox->setLocalTransform(mat); meshDomainBox->setMaterial(material); setDomainStatus( status ); } void GLWorldView::createDreiBein() { QSettings settings; settings.beginGroup("WorldView"); QColor colorX = settings.value("dreiBeinColorX", QColor(255,50,50,255)).value(); QColor colorY = settings.value("dreiBeinColorY", QColor(50,255,50,255)).value(); QColor colorZ = settings.value("dreiBeinColorZ", QColor(50,50,255,255)).value(); settings.setValue("dreiBeinColorX", colorX); settings.setValue("dreiBeinColorY", colorY); settings.setValue("dreiBeinColorZ", colorZ); const bool status = settings.value("dreiBeinEnabled").toBool(); settings.endGroup(); // Create 3 color for the 3 axes. dreiBeinMaterial[0] = new QGLMaterial; dreiBeinMaterial[0]->setColor(colorX); dreiBeinMaterial[1] = new QGLMaterial; dreiBeinMaterial[1]->setColor(colorY); dreiBeinMaterial[2] = new QGLMaterial; dreiBeinMaterial[2]->setColor(colorZ); // Create the basic meshes (cylinder and cone). QGLBuilder builderCyl; builderCyl << QGLCylinder(.15,.15,1.6,16); QGLSceneNode *cyl = builderCyl.finalizedSceneNode(); QGLBuilder builderCone; builderCone << QGLCylinder(0,.4,0.4,16); QGLSceneNode *cone = builderCone.finalizedSceneNode(); { QMatrix4x4 mat; mat.translate(0.0f, 0.0f, 1.0f); cone->setLocalTransform(mat); } // Create a scene node from the 3 axes. meshDreiBein = new QGLSceneNode(this); // X-direction QGLSceneNode *node = new QGLSceneNode(meshDreiBein); node->setMaterial(dreiBeinMaterial[0]); node->addNode(cyl); node->setPosition(QVector3D(.8f, 0.f, 0.f)); node->addNode(cone); { QMatrix4x4 mat; mat.rotate(90, 0.0f, 1.0f, 0.0f); node->setLocalTransform(mat); } // Y-direction node = new QGLSceneNode(meshDreiBein); node->setMaterial(dreiBeinMaterial[1]); node->addNode(cyl); node->addNode(cone); { QMatrix4x4 mat; mat.rotate(-90, 1.0f, 0.0f, 0.0f); node->setLocalTransform(mat); } node->setPosition(QVector3D(0.f, .8f, 0.f)); // Z-direction node = new QGLSceneNode(meshDreiBein); node->setMaterial(dreiBeinMaterial[2]); node->addNode(cyl); node->addNode(cone); node->setPosition(QVector3D(0.f, 0.f, .8f)); setdreiBeinStatus( status ); } /** * Update operation which can be invoked by the observable (which should be the * change tracker here). */ void GLWorldView::update(Observable *publisher) { // emit changed(); } /** * The observable can tell when it dies. */ void GLWorldView::subjectKilled(Observable *publisher) { // world never dies } /** Listen to specific changes to the world. * * @param publisher ref to observable. * @param notification type of notification */ void GLWorldView::recieveNotification(Observable *publisher, Notification_ptr notification) { if (static_cast(publisher) == WorldTime::getPointer()) { switch (notification->getChannelNo()) { case WorldTime::TimeChanged: { #ifdef LOG_OBSERVER observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast(this)) << " received notification that WorldTime's time has changed."; #endif needsRedraw = false; emit changed(); emit TimeChanged(); break; } default: ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for WorldTime."); break; } } else if (static_cast(publisher) == ShapeRegistry::getPointer()) { switch (notification->getChannelNo()) { case ShapeRegistry::ShapeInserted: { needsRedraw = false; emit ShapeAdded(ShapeRegistry::getInstance().lastChanged()->getName()); break; } case ShapeRegistry::ShapeRemoved: { needsRedraw = false; emit ShapeRemoved(ShapeRegistry::getInstance().lastChanged()->getName()); break; } case ShapeRegistry::SelectionChanged: { needsRedraw = false; worldscene->updateSelectedShapes(); break; } default: ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for ShapeRegistry."); break; } } } void GLWorldView::checkChanges() { needsRedraw = false; updateGL(); } void GLWorldView::changeDreiBein() { // invert to new status const bool status = ((meshDreiBein->options() & QGLSceneNode::HideNode) == 0); setdreiBeinStatus(!status); // realize needsRedraw = true; emit changed(); } void GLWorldView::setdreiBeinStatus(const bool status) { if (status) meshDreiBein->setOptions( meshDreiBein->options() & (255-QGLSceneNode::HideNode) ); else meshDreiBein->setOptions( meshDreiBein->options() | QGLSceneNode::HideNode ); } void GLWorldView::changeDomain() { // invert to new status const bool status = ((meshDomainBox->options() & QGLSceneNode::HideNode) == 0); setDomainStatus(!status); // realize needsRedraw = true; emit changed(); } void GLWorldView::setDomainStatus(const bool status) { if (status) meshDomainBox->setOptions( meshDomainBox->options() & (255-QGLSceneNode::HideNode) ); else meshDomainBox->setOptions( meshDomainBox->options() | QGLSceneNode::HideNode ); } void GLWorldView::sceneChangeSignalled() { if (needsRedraw){ redrawTimer->singleShot(0, this, SLOT(checkChanges())); needsRedraw = true; redrawTimer->start(); } } void GLWorldView::initializeGL(QGLPainter *painter) { worldscene->initialize(this, painter); changesPresent = false; } void GLWorldView::paintGL(QGLPainter *painter) { if (changesPresent) initializeGL(painter); QVector3D cameraDir = camera()->center() - camera()->eye(); cameraDir.normalize(); QVector4D cameraPlane(cameraDir, QVector3D::dotProduct(cameraDir, camera()->eye())); worldscene->draw(painter, cameraPlane); drawDreiBein(painter); // Domain box has to be last because of its transparency. drawDomainBox(painter); } void GLWorldView::keyPressEvent(QKeyEvent *e) { // Find the distance between the eye and focus point. QVector3D d = camera()->eye() - camera()->center(); // LOG(1, "Distance vector eye and center is " // << d.x() << "," << d.y() << "," << d.z()); // scale the move unit by the eye <-> domain center distance const double key_move_unit = 0.04*(d.length()/50.); if (e->key() == Qt::Key_Tab) { // The Tab key turns the ShowPicking option on and off, // which helps show what the pick buffer looks like. setOption(QGLView::ShowPicking, ((options() & QGLView::ShowPicking) == 0)); updateGL(); } else if ((e->key() == Qt::Key_Minus) || (e->key() == Qt::Key_Plus)) { // Scale the distance. if (e->key() == Qt::Key_Minus) d *= 1.2; else if (e->key() == Qt::Key_Plus) d /= 1.2; // Set new eye position. camera()->setEye(camera()->center() + d); } else if ((e->key() == Qt::Key_Left) || (e->key() == Qt::Key_Right)) { // Translate the camera. const double d = (e->key() == Qt::Key_Left) ? -key_move_unit : key_move_unit; camera()->translateCenter( d, 0., 0); camera()->translateEye( d, 0., 0); } else if ((e->key() == Qt::Key_Up) || (e->key() == Qt::Key_Down)) { // Translate the camera. const double d = (e->key() == Qt::Key_Up) ? -key_move_unit : key_move_unit; camera()->translateCenter( 0., d, 0); camera()->translateEye( 0., d, 0); } else if ((e->key() == Qt::Key_PageUp) || (e->key() == Qt::Key_PageDown)) { // Translate the camera. const double d = (e->key() == Qt::Key_PageUp) ? -key_move_unit : key_move_unit; camera()->translateCenter( 0., 0., d); camera()->translateEye( 0., 0., d); } QGLView::keyPressEvent(e); } void GLWorldView::changeSignalled() { changesPresent = true; updateGL(); } /** * Set the current camera control mode. */ void GLWorldView::setCameraControlMode(GLWorldView::CameraControlModeType mode) { cameraControlMode = mode; } void GLWorldView::setCameraControlModeRotation() { setCameraControlMode(Rotate); } void GLWorldView::setCameraControlModeTranslation() { setCameraControlMode(Translate); } /** * Returns the current camera control mode. * This needs to be invertable (rotation - translation), if the shift key is pressed. */ GLWorldView::CameraControlModeType GLWorldView::getCameraControlMode(bool inverted) { if (inverted){ if (cameraControlMode == Rotate) return Translate; if (cameraControlMode == Translate) return Rotate; return Rotate; }else return cameraControlMode; } /** * Set the camera so it can oversee the whole domain. */ void GLWorldView::fitCameraToDomain() { // Move the camera focus point to the center of the domain box. Vector v = World::getInstance().getDomain().translateIn(Vector(0.5, 0.5, 0.5)); camera()->setCenter(QVector3D(v[0], v[1], v[2])); // Guess some eye distance. double dist = v.Norm() * 3; camera()->setEye(QVector3D(v[0], v[1], v[2] + dist)); camera()->setUpVector(QVector3D(0, 1, 0)); } void GLWorldView::setCameraStereoModeDisable() { setStereoType(QGLView::Hardware); camera()->setEyeSeparation(0.0); emit changed(); } void GLWorldView::setCameraStereoModeHardware() { setStereoType(QGLView::Hardware); camera()->setEyeSeparation(defaultEyeSeparation); emit changed(); } void GLWorldView::setCameraStereoModeLeftRight() { setStereoType(QGLView::LeftRight); camera()->setEyeSeparation(defaultEyeSeparation); emit changed(); } void GLWorldView::setCameraStereoModeRightLeft() { setStereoType(QGLView::RightLeft); camera()->setEyeSeparation(defaultEyeSeparation); emit changed(); } void GLWorldView::setCameraStereoModeTopBottom() { setStereoType(QGLView::TopBottom); camera()->setEyeSeparation(defaultEyeSeparation); emit changed(); } void GLWorldView::setCameraStereoModeBottomTop() { setStereoType(QGLView::BottomTop); camera()->setEyeSeparation(defaultEyeSeparation); emit changed(); } void GLWorldView::setCameraStereoModeAnaglyph() { setStereoType(QGLView::RedCyanAnaglyph); camera()->setEyeSeparation(defaultEyeSeparation); emit changed(); } void GLWorldView::mousePressEvent(QMouseEvent *event) { QGLView::mousePressEvent(event); // Reset the saved mouse position. lastMousePos = event->posF(); } /** * Handle a mouse move event. * This is used to control the camera (rotation and translation) when the left button is being pressed. */ void GLWorldView::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton){ // Find the mouse distance since the last event. QPointF d = event->posF() - lastMousePos; lastMousePos = event->posF(); // Rotate or translate? (inverted by shift key) CameraControlModeType mode = getCameraControlMode(event->modifiers() & Qt::ShiftModifier); if (mode == Rotate){ // Rotate the camera. d *= 0.3; camera()->tiltPanRollCenter(- d.y(), - d.x(), 0); }else if (mode == Translate){ // Translate the camera. d *= 0.02; camera()->translateCenter(- d.x(), d.y(), 0); camera()->translateEye(- d.x(), d.y(), 0); } }else{ // Without this Qt would not test for hover events (i.e. mouse over an atom). QGLView::mouseMoveEvent(event); } } /** * When the mouse wheel is used, zoom in or out. */ void GLWorldView::wheelEvent(QWheelEvent *event) { // Find the distance between the eye and focus point. QVector3D d = camera()->eye() - camera()->center(); // Scale the distance. if (event->delta() < 0) d *= 1.2; else if (event->delta() > 0) d /= 1.2; // Set new eye position. camera()->setEye(camera()->center() + d); } /** * Draw a transparent cube representing the domain. */ void GLWorldView::drawDomainBox(QGLPainter *painter) const { // Apply the domain matrix. RealSpaceMatrix m = World::getInstance().getDomain().getM(); painter->modelViewMatrix().push(); painter->modelViewMatrix() *= QMatrix4x4(m.at(0,0), m.at(0,1), m.at(0,2), 0.0, m.at(1,0), m.at(1,1), m.at(1,2), 0.0, m.at(2,0), m.at(2,1), m.at(2,2), 0.0, 0.0, 0.0, 0.0, 1.0); // Draw the transparent cube. painter->setStandardEffect(QGL::LitMaterial); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glDepthMask(0); //glDisable(GL_DEPTH_TEST); meshDomainBox->draw(painter); //glEnable(GL_DEPTH_TEST); glDepthMask(1); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); // Draw the outlines (if we have drawn the box itself) if ((meshDomainBox->options() & QGLSceneNode::HideNode) == 0) { painter->setFaceMaterial(QGL::AllFaces, domainBoxMaterial); //glEnable(GL_LINE_SMOOTH); QVector3DArray array; array.append(0, 0, 0); array.append(1, 0, 0); array.append(1, 0, 0); array.append(1, 1, 0); array.append(1, 1, 0); array.append(0, 1, 0); array.append(0, 1, 0); array.append(0, 0, 0); array.append(0, 0, 1); array.append(1, 0, 1); array.append(1, 0, 1); array.append(1, 1, 1); array.append(1, 1, 1); array.append(0, 1, 1); array.append(0, 1, 1); array.append(0, 0, 1); array.append(0, 0, 0); array.append(0, 0, 1); array.append(1, 0, 0); array.append(1, 0, 1); array.append(0, 1, 0); array.append(0, 1, 1); array.append(1, 1, 0); array.append(1, 1, 1); painter->clearAttributes(); painter->setVertexAttribute(QGL::Position, array); painter->draw(QGL::Lines, 24); } painter->modelViewMatrix().pop(); } void GLWorldView::drawDreiBein(QGLPainter *painter) { painter->modelViewMatrix().push(); painter->modelViewMatrix().translate(camera()->center()); painter->setStandardEffect(QGL::LitMaterial); painter->setFaceMaterial(QGL::FrontFaces, NULL); meshDreiBein->draw(painter); painter->modelViewMatrix().pop(); } void GLWorldView::sceneHoverSignalled(const atomId_t _id) { needsRedraw = true; emit changed(); emit hoverChanged(_id); } void GLWorldView::sceneHoverSignalled(const moleculeId_t _id, int _i) { needsRedraw = true; emit changed(); emit hoverChanged(_id, _i); }