/* * OpenSCAD (www.openscad.org) * Copyright (C) 2009-2011 Clifford Wolf and * Marius Kintel * * This program 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. * * As a special exception, you have permission to link this program * with the CGAL library and distribute executables, as long as you * follow the requirements of the GNU GPL in regard to all of the * software in the executable aside from CGAL. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "QGLView.h" #include "Preferences.h" #include "renderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "OpenCSGWarningDialog.h" #include "mathc99.h" #include #ifdef ENABLE_OPENCSG # include #endif QGLView::QGLView(QWidget *parent) : QGLWidget(parent) { init(); } QGLView::QGLView(const QGLFormat & format, QWidget *parent) : QGLWidget(format, parent) { init(); } static bool running_under_wine = false; void QGLView::init() { cam.type = Camera::GIMBAL; resetView(); this->mouse_drag_active = false; this->statusLabel = NULL; setMouseTracking(true); // see paintGL() + issue160 + wine FAQ #ifdef _WIN32 #include HMODULE hntdll = GetModuleHandle(L"ntdll.dll"); if (hntdll) if ( (void *)GetProcAddress(hntdll, "wine_get_version") ) running_under_wine = true; #endif } void QGLView::resetView() { cam.object_rot << 35, 0, -25; cam.object_trans << 0, 0, 0; cam.viewer_distance = 500; } void QGLView::initializeGL() { GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); } GLView::initializeGL(); } std::string QGLView::getRendererInfo() const { std::string glewinfo = glew_dump(); std::string glextlist = glew_extensions_dump(); return glewinfo + std::string("\nUsing QGLWidget\n\n") + glextlist; } #ifdef ENABLE_OPENCSG void QGLView::display_opencsg_warning() { if (Preferences::inst()->getValue("advanced/opencsg_show_warning").toBool()) { QTimer::singleShot(0, this, SLOT(display_opencsg_warning_dialog())); } } void QGLView::display_opencsg_warning_dialog() { OpenCSGWarningDialog *dialog = new OpenCSGWarningDialog(this); QString message; if (this->is_opencsg_capable) { message += "Warning: You may experience OpenCSG rendering errors.\n\n"; } else { message += "Warning: Missing OpenGL capabilities for OpenCSG - OpenCSG has been disabled.\n\n"; dialog->enableOpenCSGBox->hide(); } message += "It is highly recommended to use OpenSCAD on a system with " "OpenGL 2.0 or later.\n" "Your renderer information is as follows:\n"; QString rendererinfo; rendererinfo.sprintf("GLEW version %s\n" "%s (%s)\n" "OpenGL version %s\n", glewGetString(GLEW_VERSION), glGetString(GL_RENDERER), glGetString(GL_VENDOR), glGetString(GL_VERSION)); message += rendererinfo; dialog->setText(message); dialog->enableOpenCSGBox->setChecked(Preferences::inst()->getValue("advanced/enable_opencsg_opengl1x").toBool()); dialog->exec(); opencsg_support = this->is_opencsg_capable && Preferences::inst()->getValue("advanced/enable_opencsg_opengl1x").toBool(); } #endif void QGLView::resizeGL(int w, int h) { GLView::resizeGL(w,h); GLView::setupGimbalCamPerspective(); } void QGLView::paintGL() { GLView::gimbalCamPaintGL(); if (statusLabel) { QString msg; Camera nc( cam ); nc.gimbalDefaultTranslate(); msg.sprintf("Viewport: translate = [ %.2f %.2f %.2f ], rotate = [ %.2f %.2f %.2f ], distance = %.2f", nc.object_trans.x(), nc.object_trans.y(), nc.object_trans.z(), nc.object_rot.x(), nc.object_rot.y(), nc.object_rot.z(), nc.viewer_distance ); statusLabel->setText(msg); } if (running_under_wine) swapBuffers(); } void QGLView::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Plus: // On many keyboards, this requires to press Shift-equals case Qt::Key_Equal: // ...so simplify this a bit. cam.viewer_distance *= 0.9; updateGL(); break; case Qt::Key_Minus: cam.viewer_distance /= 0.9; updateGL(); break; case Qt::Key_C: // 'center' cam.object_trans << 0, 0, 0; updateGL(); break; } } void QGLView::wheelEvent(QWheelEvent *event) { cam.viewer_distance *= pow(0.9, event->delta() / 120.0); updateGL(); } void QGLView::mousePressEvent(QMouseEvent *event) { setFocus(); mouse_drag_active = true; last_mouse = event->globalPos(); } void QGLView::normalizeAngle(GLdouble& angle) { while(angle < 0) angle += 360; while(angle > 360) angle -= 360; } void QGLView::mouseMoveEvent(QMouseEvent *event) { QPoint this_mouse = event->globalPos(); double dx = (this_mouse.x()-last_mouse.x()) * 0.7; double dy = (this_mouse.y()-last_mouse.y()) * 0.7; if (mouse_drag_active) { if (event->buttons() & Qt::LeftButton #ifdef Q_WS_MAC && !(event->modifiers() & Qt::MetaModifier) #endif ) { // Left button rotates in xz, Shift-left rotates in xy // On Mac, Ctrl-Left is handled as right button on other platforms cam.object_rot.x() += dy; if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) cam.object_rot.y() += dx; else cam.object_rot.z() += dx; normalizeAngle(cam.object_rot.x()); normalizeAngle(cam.object_rot.y()); normalizeAngle(cam.object_rot.z()); } else { // Right button pans in the xz plane // Middle button pans in the xy plane // Shift-right and Shift-middle zooms if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) { cam.viewer_distance += (GLdouble)dy; } else { double mx = +(dx) * cam.viewer_distance/1000; double mz = -(dy) * cam.viewer_distance/1000; double my = 0; #if (QT_VERSION < QT_VERSION_CHECK(4, 7, 0)) if (event->buttons() & Qt::MidButton) { #else if (event->buttons() & Qt::MiddleButton) { #endif my = mz; mz = 0; // actually lock the x-position // (turns out to be easier to use than xy panning) mx = 0; } Matrix3d aax, aay, aaz, tm3; aax = Eigen::AngleAxisd(-(cam.object_rot.x()/180) * M_PI, Vector3d::UnitX()); aay = Eigen::AngleAxisd(-(cam.object_rot.y()/180) * M_PI, Vector3d::UnitY()); aaz = Eigen::AngleAxisd(-(cam.object_rot.z()/180) * M_PI, Vector3d::UnitZ()); tm3 = Matrix3d::Identity(); tm3 = aaz * (aay * (aax * tm3)); Matrix4d tm; tm = Matrix4d::Identity(); for (int i=0;i<3;i++) for (int j=0;j<3;j++) tm(j,i)=tm3(j,i); Matrix4d vec; vec << 0, 0, 0, mx, 0, 0, 0, my, 0, 0, 0, mz, 0, 0, 0, 1 ; tm = tm * vec; cam.object_trans.x() += tm(0,3); cam.object_trans.y() += tm(1,3); cam.object_trans.z() += tm(2,3); } } updateGL(); emit doAnimateUpdate(); } last_mouse = this_mouse; } void QGLView::mouseReleaseEvent(QMouseEvent*) { mouse_drag_active = false; releaseMouse(); } bool QGLView::save(const char *filename) { QImage img = grabFrameBuffer(); return img.save(filename, "PNG"); }