#include #include #include "gui/QGLViewerWidget.h" #define INERTIA_MAX 5.0f #define INERTIA_THRESHOLD 0.2f using namespace gmtl; static void dom2vp(const QDomElement& parent, gmtl::Vec3f& center, gmtl::Quatf& rot, float& distance) { if (parent.isNull()) return; center[0] = parent.attribute("centerx", "0").toFloat(); center[1] = parent.attribute("centery", "0").toFloat(); center[2] = parent.attribute("centerz", "0").toFloat(); rot[0] = parent.attribute("rotx", "0").toFloat(); rot[1] = parent.attribute("roty", "0").toFloat(); rot[2] = parent.attribute("rotz", "0").toFloat(); rot[3] = parent.attribute("rotw", "1").toFloat(); distance = parent.attribute("distance", "1").toFloat(); } static QDomElement vp2dom(QDomDocument& doc, const gmtl::Vec3f& center, const gmtl::Quatf& rot, float distance) { QDomElement cm = doc.createElement("Camera"); cm.setAttribute("centerx", QString::number(center[0])); cm.setAttribute("centery", QString::number(center[1])); cm.setAttribute("centerz", QString::number(center[2])); cm.setAttribute("rotx", QString::number(rot[0])); cm.setAttribute("roty", QString::number(rot[1])); cm.setAttribute("rotz", QString::number(rot[2])); cm.setAttribute("rotw", QString::number(rot[3])); cm.setAttribute("distance", QString::number(distance)); return cm; } QGLViewerWidget::QGLViewerWidget(QWidget* pParent, const QGLWidget* pShareWidget, Qt::WindowFlags f) : QGLWidget(pParent, pShareWidget, f) { m_bPerspective = true; m_fovy = 45.0f; m_znear = 0.01f; m_zfar = 100.0f; m_bInertia = true; m_animX = 0.0f; m_animY = 0.0f; m_bAnimating = false; m_clearColor = QColor(64, 64, 64); m_sceneSize = 10.0f; m_bGridVisible = false; m_bAxisVisible = false; m_gridSize = 10.0f; m_gridSubdiv = 10; viewAll(); m_timer.setInterval(30); connect(&m_timer, SIGNAL(timeout()), this, SLOT(timeout())); m_timer.start(); setFocusPolicy(Qt::StrongFocus); } void QGLViewerWidget::setView(const gmtl::Vec3f& center, const gmtl::Quatf& rot, float distance) { m_center = center; m_rot = rot; m_distance = distance; m_matrix = makeTrans(m_center) * makeRot(m_rot) * makeTrans(Vec3f(0.0f, 0.0f, m_distance)); invert(m_matrix); emit cameraChanged(center, rot, distance); } void QGLViewerWidget::init() { } void QGLViewerWidget::draw() { } void QGLViewerWidget::idle() { } void QGLViewerWidget::stop() { m_pivotCenter = m_center; m_pivotRot = m_rot; m_pivotDistance = m_distance; m_pivotOrthoScale = m_orthoScale; m_animX = 0.0f; m_animY = 0.0f; m_bAnimating = false; } void QGLViewerWidget::centerView() { stop(); m_center = Vec3f(0.0f, 0.0f, 0.0f); m_rot = Quatf(); m_distance = 0.0f; m_orthoScale = 0.25f; setView(m_center, m_rot, m_distance); updateGL(); } void QGLViewerWidget::viewAll() { stop(); m_center = Vec3f(0.0f, 0.0f, 0.0f); if (m_bPerspective) m_distance = m_sceneSize * 0.5f; else m_distance = 0.0f; m_orthoScale = 0.25f; setView(m_center, m_rot, m_distance); updateGL(); } void QGLViewerWidget::drawGrid() { if (m_gridSubdiv == 0) return; glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glLineWidth(1.0f); glColor4f(0.5f, 0.5f, 0.5f, 1.0f); glBegin(GL_LINES); for (int x=0; x 0) aspect = (float)width()/height(); if (m_bPerspective) gluPerspective(m_fovy, aspect, m_znear, m_zfar); else { float size = m_sceneSize * m_orthoScale; glOrtho(-size*aspect, size*aspect, -size, size, -m_sceneSize*4.0f, m_sceneSize*4.0f); } glMatrixMode(GL_MODELVIEW); glLoadMatrixf(m_matrix.getData()); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); Vec3f pos = Vec3f(1.0f, 1.0f, 1.0f); if (!_isnan(m_rot[0])) pos = m_rot * pos; GLfloat lposition[] = { pos[0], pos[1], pos[2], 0.0f }; GLfloat lambient[] = { 0.1f, 0.1f, 0.1f, 1.0f }; GLfloat ldiffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; glLightfv(GL_LIGHT0, GL_POSITION, lposition); glLightfv(GL_LIGHT0, GL_AMBIENT, lambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, ldiffuse); glEnable(GL_DEPTH_TEST); glEnable(GL_COLOR_MATERIAL); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); draw(); glDisable(GL_LIGHTING); if (m_bGridVisible) drawGrid(); if (m_bAxisVisible) drawAxis(); glFlush(); } void QGLViewerWidget::mousePressEvent(QMouseEvent* pEvent) { m_timer.stop(); stop(); m_lastMousePos = pEvent->pos(); m_lastTime = QTime::currentTime(); setMouseTracking(true); } void QGLViewerWidget::mouseMoveEvent(QMouseEvent* pEvent) { if (m_bPerspective && (pEvent->buttons() & Qt::LeftButton)) { float delta = (float)m_lastTime.msecsTo(QTime::currentTime()) / 1000.0f; float deltaX = ((float)(pEvent->x()-m_lastMousePos.x())/width()*90.0f) / 180.0f * Math::PI; float deltaY = ((float)(pEvent->y()-m_lastMousePos.y())/height()*90.0f) / 180.0f * Math::PI; if (delta != 0.0f && (fabs(deltaX / delta) > INERTIA_THRESHOLD || fabs(deltaY / delta) > INERTIA_THRESHOLD)) { m_animX = deltaX / delta; m_animY = deltaY / delta; float length = sqrt(m_animX*m_animX+m_animY*m_animY); if (length > INERTIA_MAX) { m_animX = m_animX / length * INERTIA_MAX; m_animY = m_animY / length * INERTIA_MAX; } } else { m_animX = 0.0f; m_animY = 0.0f; } m_rot = m_pivotRot * make(AxisAnglef(-deltaX, Vec3f(0.0f, 1.0f, 0.0f))) * make(AxisAnglef(-deltaY, Vec3f(1.0f, 0.0f, 0.0f))); } else if (pEvent->buttons() & Qt::MidButton || (!m_bPerspective && pEvent->buttons() & Qt::LeftButton)) { float deltaX = (float)(pEvent->x()-m_lastMousePos.x())/width(); float deltaY = (float)(pEvent->y()-m_lastMousePos.y())/height(); Vec3f panX = m_rot * Vec3f(-deltaX, 0.0f, 0.0f); Vec3f panY = m_rot * Vec3f(0.0f, deltaY, 0.0f); m_center = m_pivotCenter + panX + panY; } else if (m_bPerspective && (pEvent->buttons() & Qt::RightButton)) { if (m_distance > 0.0f) { m_distance = m_pivotDistance - m_pivotDistance * (float)(pEvent->y()-m_lastMousePos.y())/height() * 1.0f; if (m_distance < m_znear*2.0f) m_distance = m_znear*2.0f; } } else if (!m_bPerspective && (pEvent->buttons() & Qt::RightButton)) { m_orthoScale = m_pivotOrthoScale - m_pivotOrthoScale * (float)(pEvent->y()-m_lastMousePos.y())/height() * 1.0f; if (m_orthoScale < 0.01f) m_orthoScale = 0.01f; } setView(m_center, m_rot, m_distance); m_lastTime = QTime::currentTime(); updateGL(); } void QGLViewerWidget::mouseReleaseEvent(QMouseEvent* pEvent) { Q_UNUSED(pEvent); if (m_lastTime.msecsTo(QTime::currentTime()) > 50 || !m_bInertia) { m_animX = 0.0f; m_animY = 0.0f; m_bAnimating = false; } if (m_animX != 0.0f || m_animY != 0.0f) m_bAnimating = true; setMouseTracking(false); m_timer.start(); } void QGLViewerWidget::wheelEvent(QWheelEvent* pEvent) { if (m_bPerspective) { if (pEvent->delta() > 0.0f) setView(m_center, m_rot, m_distance * 1.1f); else setView(m_center, m_rot, m_distance / 1.1f); } else { if (pEvent->delta() > 0.0f) m_orthoScale *= 1.1f; else m_orthoScale /= 1.1f; if (m_orthoScale < 0.01f) m_orthoScale = 0.01f; } updateGL(); } void QGLViewerWidget::keyReleaseEvent(QKeyEvent* pEvent) { QGLWidget::keyReleaseEvent(pEvent); switch (pEvent->key()) { case Qt::Key_A: setAxisVisible(!m_bAxisVisible); updateGL(); break; case Qt::Key_G: setGridVisible(!m_bGridVisible); updateGL(); break; case Qt::Key_R: viewAll(); break; default: return; } } void QGLViewerWidget::timeout() { if (m_bAnimating) { float delta = (float)m_lastTime.msecsTo(QTime::currentTime()) / 1000.0f; Quatf deltaRot = make(AxisAnglef(-m_animX*delta, Vec3f(0.0f, 1.0f, 0.0f))) * make(AxisAnglef(-m_animY*delta, Vec3f(1.0f, 0.0f, 0.0f))); Quatf rot = m_rot * deltaRot; if (!_isnan(rot[0])) setView(m_center, rot, m_distance); m_lastTime = QTime::currentTime(); } idle(); if (m_bAnimating) updateGL(); } void QGLViewerWidget::initFromDOMElement(const QDomElement& element) { if (!element.isNull()) { QDomElement camera = element.firstChildElement("Camera"); dom2vp(camera, m_center, m_rot, m_distance); m_bPerspective = (element.attribute("perspective", "true")=="true"); m_fovy = element.attribute("fovy", QString::number(45.0f)).toFloat(); m_orthoScale = element.attribute("orthoScale", QString::number(0.25f)).toFloat(); m_znear = element.attribute("near", QString::number(0.01f)).toFloat(); m_zfar = element.attribute("far", QString::number(100.0f)).toFloat(); m_bInertia = (element.attribute("inertia", "true")=="true"); m_clearColor = QColor(element.attribute("clearColor", QColor(64, 64, 64).name())); m_bGridVisible = (element.attribute("grid", "true")=="true"); m_bAxisVisible = (element.attribute("axis", "true")=="true"); m_sceneSize = element.attribute("sceneSize", QString::number(10.0f)).toFloat(); if (!parentWidget()) { move(element.attribute("x", QString::number(x())).toInt(), element.attribute("y", QString::number(y())).toInt()); resize(element.attribute("width", QString::number(width())).toInt(), element.attribute("height", QString::number(height())).toInt()); } setView(m_center, m_rot, m_distance); stop(); } } QDomElement QGLViewerWidget::domElement(const QString& name, QDomDocument& doc) const { QDomElement de = doc.createElement(name); de.appendChild(vp2dom(doc, m_center, m_rot, m_distance)); de.setAttribute("perspective", m_bPerspective?"true":"false"); de.setAttribute("fovy", m_fovy); de.setAttribute("orthoScale", m_orthoScale); de.setAttribute("near", m_znear); de.setAttribute("far", m_zfar); de.setAttribute("inertia", m_bInertia?"true":"false"); de.setAttribute("clearColor", m_clearColor.name()); de.setAttribute("grid", m_bGridVisible?"true":"false"); de.setAttribute("axis", m_bAxisVisible?"true":"false"); de.setAttribute("sceneSize", m_sceneSize); if (!parentWidget()) { de.setAttribute("x", x()); de.setAttribute("y", y()); de.setAttribute("width", width()); de.setAttribute("height", height()); } return de; }