diff options
Diffstat (limited to 'src/designer/src/plugins/tools/view3d/view3d.cpp')
-rw-r--r-- | src/designer/src/plugins/tools/view3d/view3d.cpp | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/src/designer/src/plugins/tools/view3d/view3d.cpp b/src/designer/src/plugins/tools/view3d/view3d.cpp new file mode 100644 index 000000000..06718f433 --- /dev/null +++ b/src/designer/src/plugins/tools/view3d/view3d.cpp @@ -0,0 +1,492 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <QtGui> +#include <QtOpenGL> + +#include "abstractformeditor.h" +#include "abstractmetadatabase.h" +#include "abstractformwindow.h" +#include "view3d.h" + +#define SELECTION_BUFSIZE 512 + +/******************************************************************************* +** QView3DWidget +*/ + +class QView3DWidget : public QGLWidget +{ + Q_OBJECT +public: + QView3DWidget(QWidget *parent); + virtual void initializeGL(); + virtual void resizeGL(int w, int h); + virtual void paintGL(); + void clear(); + + void addTexture(QWidget *w, const QPixmap &pm); + + void beginAddingWidgets(QWidget *form); + void addWidget(int depth, QWidget *w); + void endAddingWidgets(); + + QWidget *widgetAt(const QPoint &pos); + +signals: + void updateForm(); + +protected: + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void wheelEvent(QWheelEvent *); + void keyReleaseEvent(QKeyEvent *); + + void contextMenuEvent(QContextMenuEvent *); + +private: + QWidget *m_form; + QPoint m_old_pos; + bool m_layer_coloring; + bool m_use_mipmaps; + GLuint m_form_list_id; + + typedef QMap<QWidget*, GLuint> TextureMap; + TextureMap m_texture_map; + + typedef QMap<GLuint, QWidget*> WidgetNameMap; + GLuint m_next_widget_name; + WidgetNameMap m_widget_name_map; +}; + +QView3DWidget::QView3DWidget(QWidget *parent) + : QGLWidget(parent) + , m_layer_coloring(true) + , m_form_list_id(0) + , m_next_widget_name(0) +{ + setFocusPolicy(Qt::StrongFocus); +} + +static int nearestSize(int v) +{ + int n = 0, last = 0; + for (int s = 0; s < 32; ++s) { + if (((v>>s) & 1) == 1) { + ++n; + last = s; + } + } + if (n > 1) + return 1 << (last+1); + return 1 << last; +} + +// static int pm_cnt = 0; + +void QView3DWidget::addTexture(QWidget *w, const QPixmap &pm) +{ + int tx_w = nearestSize(pm.width()); + int tx_h = nearestSize(pm.height()); + + QPixmap tmp(tx_w, tx_h); + tmp.fill(QColor(0,0,0)); + QPainter p(&tmp); + p.drawPixmap(0, tx_h - pm.height(), pm); + p.end(); + QImage tex = tmp.toImage(); + +// QString file_name = QString("pixmapDump%1.png").arg(pm_cnt++); +// qDebug() << "grabWidget():" << file_name << tex.save(file_name, "PNG"); + + tex = QGLWidget::convertToGLFormat(tex); + + GLuint tx_id; + glGenTextures(1, &tx_id); + glBindTexture(GL_TEXTURE_2D, tx_id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (m_use_mipmaps) { + //glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); + //glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.f); + } else { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex.width(), tex.height(), 0, GL_RGBA, + GL_UNSIGNED_BYTE, tex.bits()); + m_texture_map[w] = tx_id; +} + +void QView3DWidget::addWidget(int depth, QWidget *widget) +{ + TextureMap::const_iterator it = m_texture_map.find(widget); + Q_ASSERT(it != m_texture_map.end()); + + makeCurrent(); + + int w = m_form->size().width(); + int h = m_form->size().height(); + int max = qMax(w, h); + QRect r = widget->rect(); + QPoint pos = widget->mapToGlobal(QPoint(0, 0)); + r.moveTopLeft(m_form->mapFromGlobal(pos)); + + float s = r.width()/float(nearestSize(r.width())); + float t = r.height()/float(nearestSize(r.height())); + + if (m_layer_coloring) + glColor4f(1.0 - depth/10.0, 1.0 - depth/10.0, 1.0, 1.0 - depth/20.0); + else + glColor4f(1.0, 1.0, 1.0, 1.0); + + glBindTexture(GL_TEXTURE_2D, *it); + glBegin(GL_QUADS); + glLoadName(m_next_widget_name); + glTexCoord2f(0.0, 0.0); glVertex3f(r.left() - w/2, r.bottom() - h/2, depth*max/8); + glTexCoord2f(s, 0.0); glVertex3f(r.right() - w/2, r.bottom()- h/2, depth*max/8); + glTexCoord2f(s, t); glVertex3f(r.right() - w/2, r.top() - h/2, depth*max/8); + glTexCoord2f(0.0, t); glVertex3f(r.left() - w/2, r.top() - h/2, depth*max/8); + glEnd(); + + m_widget_name_map[m_next_widget_name++] = widget; +} + +void QView3DWidget::clear() +{ + makeCurrent(); + glDeleteLists(m_form_list_id, 1); + foreach (GLuint id, m_texture_map) + glDeleteTextures(1, &id); + m_texture_map.clear(); + m_widget_name_map.clear(); + m_next_widget_name = 0; +} + +void QView3DWidget::beginAddingWidgets(QWidget *form) +{ + makeCurrent(); + m_form = form; + m_form_list_id = glGenLists(1); + glNewList(m_form_list_id, GL_COMPILE); + glInitNames(); + glPushName(-1); + m_next_widget_name = 0; +} + +void QView3DWidget::endAddingWidgets() +{ + makeCurrent(); + glEndList(); +} + +void QView3DWidget::initializeGL() +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + qglClearColor(palette().color(QPalette::Window).dark()); + glColor3f (1.0, 1.0, 1.0); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + + glShadeModel(GL_FLAT); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + QString extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS))); + m_use_mipmaps = false;// extensions.contains("GL_SGIS_generate_mipmap"); +} + +void QView3DWidget::resizeGL(int width, int height) +{ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-width/2, width/2, height/2, -height/2, -999999, 999999); +} + +void QView3DWidget::paintGL() +{ + glColor4f(1.0, 1.0, 1.0, 1.0); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glCallList(m_form_list_id); + + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(-width()/2, -height()/2, 0.0); + + QFontMetrics fm(font()); + glColor4f(0.4, 0.4, 0.4, 0.7); + glRecti(0, height() - fm.lineSpacing()*2.5, width(), height()); + + glColor3f(1.0, 1.0, 1.0); + renderText(10, height() - fm.lineSpacing()*1.5, + "Press and hold left/right mouse button = tilt the view."); + renderText(10, height() - fm.lineSpacing()*0.5, + "Mouse wheel = zoom. 't' = toggle layer coloring. 'r' = reset transform."); + glPopMatrix(); + glPopAttrib(); +} + +QWidget *QView3DWidget::widgetAt(const QPoint &pos) +{ + makeCurrent(); + GLuint selectBuf[SELECTION_BUFSIZE]; + glSelectBuffer(SELECTION_BUFSIZE, selectBuf); + glRenderMode (GL_SELECT); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glCallList(m_form_list_id); + return 0; +} + +void QView3DWidget::keyReleaseEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_T) { + m_layer_coloring = !m_layer_coloring; + emit updateForm(); + } else if (e->key() == Qt::Key_R) { + makeCurrent(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } + + updateGL(); +} + +void QView3DWidget::mousePressEvent(QMouseEvent *e) +{ + m_old_pos = e->pos(); +} + +void QView3DWidget::mouseReleaseEvent(QMouseEvent *e) +{ + m_old_pos = e->pos(); +} + +void QView3DWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (e->buttons() & (Qt::LeftButton | Qt::RightButton)) { + GLfloat rx = (GLfloat) (e->x() - m_old_pos.x()) / width(); + GLfloat ry = (GLfloat) (e->y() - m_old_pos.y()) / height(); + + makeCurrent(); + glMatrixMode(GL_MODELVIEW); + if (e->buttons() & Qt::LeftButton) { + // Left button down - rotate around X and Y axes + glRotatef(-180*ry, 1, 0, 0); + glRotatef(180*rx, 0, 1, 0); + } else if (e->buttons() & Qt::RightButton) { + // Right button down - rotate around X and Z axes + glRotatef(-180*ry, 1, 0, 0); + glRotatef(-180*rx, 0, 0, 1); + } + updateGL(); + m_old_pos = e->pos(); + } else { + + } +} + +void QView3DWidget::wheelEvent(QWheelEvent *e) +{ + makeCurrent(); + glMatrixMode(GL_MODELVIEW); + if (e->delta() < 0) + glScalef(0.9, 0.9, 0.9); + else + glScalef(1.1, 1.1, 1.1); + updateGL(); +} + +void QView3DWidget::contextMenuEvent(QContextMenuEvent *e) +{ + e->accept(); +} + +/******************************************************************************* +** Misc tools +*/ + +class WalkWidgetTreeFunction +{ +public: + virtual void operator () (int depth, QWidget *widget) const = 0; +}; + +static bool skipWidget(QDesignerFormEditorInterface *core, QWidget *widget) +{ + QDesignerMetaDataBaseItemInterface *item = core->metaDataBase()->item(widget); + if (item == 0) + return true; + QString name = widget->metaObject()->className(); + if (name == "QLayoutWidget") + return true; + + return false; +} + +static void walkWidgetTree(QDesignerFormEditorInterface *core, int depth, QWidget *widget, const WalkWidgetTreeFunction &func) +{ + if (widget == 0) + return; + if (!widget->isVisible()) + return; + + if (!skipWidget(core, widget)) + func(depth++, widget); + + QObjectList child_obj_list = widget->children(); + foreach (QObject *child_obj, child_obj_list) { + QWidget *child = qobject_cast<QWidget*>(child_obj); + if (child != 0) + walkWidgetTree(core, depth, child, func); + } +} + +static void grabWidget_helper(QWidget *widget, QPixmap &res, QPixmap &buf, + const QRect &r, const QPoint &offset, QDesignerFormEditorInterface *core) +{ + buf.fill(widget, r.topLeft()); + QPainter::setRedirected(widget, &buf, r.topLeft()); + QPaintEvent e(r & widget->rect()); + QApplication::sendEvent(widget, &e); + QPainter::restoreRedirected(widget); + { + QPainter pt(&res); + pt.drawPixmap(offset.x(), offset.y(), buf, 0, 0, r.width(), r.height()); + } + + const QObjectList children = widget->children(); + for (int i = 0; i < children.size(); ++i) { + QWidget *child = qobject_cast<QWidget*>(children.at(i)); + if (child == 0 || child->isWindow()) + continue; + if (child->isHidden() || !child->geometry().intersects(r)) + continue; + if (core->metaDataBase()->item(child) != 0) + continue; + QRect cr = r & child->geometry(); + cr.translate(-child->pos()); + grabWidget_helper(child, res, buf, cr, offset + child->pos(), core); + } +} + +static QPixmap grabWidget(QWidget * widget, QDesignerFormEditorInterface *core) +{ + if (!widget) + return QPixmap(); + + QRect r = widget->rect(); + QSize s = widget->size(); + + QPixmap res(s), buf(s); + + grabWidget_helper(widget, res, buf, r, QPoint(), core); + + return res; +} + +/******************************************************************************* +** QView3D +*/ + +class AddTexture : public WalkWidgetTreeFunction +{ +public: + inline AddTexture(QDesignerFormEditorInterface *core, QView3DWidget *w) + : m_core(core), m_3d_widget(w) {} + inline virtual void operator ()(int, QWidget *w) const + { m_3d_widget->addTexture(w, ::grabWidget(w, m_core)); } + QDesignerFormEditorInterface *m_core; + QView3DWidget *m_3d_widget; +}; + +class AddWidget : public WalkWidgetTreeFunction +{ +public: + inline AddWidget(QView3DWidget *w) : m_3d_widget(w) {} + inline virtual void operator ()(int depth, QWidget *w) const + { m_3d_widget->addWidget(depth, w); } + QView3DWidget *m_3d_widget; +}; + +QView3D::QView3D(QDesignerFormWindowInterface *form_window, QWidget *parent) + : QWidget(parent) +{ + m_form_window = form_window; + m_3d_widget = new QView3DWidget(this); + connect(m_3d_widget, SIGNAL(updateForm()), this, SLOT(updateForm())); + + QGridLayout *layout = new QGridLayout(this); + layout->setMargin(0); + layout->addWidget(m_3d_widget, 0, 0, 1, 1); + + updateForm(); +} + +void QView3D::updateForm() +{ + QWidget *w = m_form_window->mainContainer(); + if (w == 0) + return; + + m_3d_widget->clear(); + + walkWidgetTree(m_form_window->core(), 0, w, AddTexture(m_form_window->core(), m_3d_widget)); + m_3d_widget->beginAddingWidgets(w); + walkWidgetTree(m_form_window->core(), 0, w, AddWidget(m_3d_widget)); + m_3d_widget->endAddingWidgets(); +} + +#include "view3d.moc" |