Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
view3d.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7#include "view3d.h"
8
9#include <QKeyEvent>
10#include <QGLFunctions>
11#include <QApplication>
12#include <QGridLayout>
13
14#define SELECTION_BUFSIZE 512
15
16#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
17# define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
18#endif
19
20/*******************************************************************************
21** QView3DWidget
22*/
23
24class QView3DWidget : public QGLWidget
25{
26 Q_OBJECT
27public:
28 QView3DWidget(QWidget *parent);
29 virtual void initializeGL();
30 void resizeGL(int w, int h) override;
31 virtual void paintGL();
32 void clear();
33
34 void addTexture(QWidget *w, const QPixmap &pm);
35
36 void beginAddingWidgets(QWidget *form);
37 void addWidget(int depth, QWidget *w);
38 void endAddingWidgets();
39
40 QWidget *widgetAt(const QPoint &pos);
41
42signals:
43 void updateForm();
44
45protected:
46 void mousePressEvent(QMouseEvent *);
47 void mouseReleaseEvent(QMouseEvent *);
48 void mouseMoveEvent(QMouseEvent *);
49 void wheelEvent(QWheelEvent *);
50 void keyReleaseEvent(QKeyEvent *);
51
52 void contextMenuEvent(QContextMenuEvent *);
53
54private:
55 QWidget *m_form;
56 QPoint m_old_pos;
57 bool m_layer_coloring;
58 bool m_use_mipmaps;
59 GLuint m_form_list_id;
60
61 typedef QMap<QWidget*, GLuint> TextureMap;
62 TextureMap m_texture_map;
63
64 typedef QMap<GLuint, QWidget*> WidgetNameMap;
65 GLuint m_next_widget_name;
66 WidgetNameMap m_widget_name_map;
67};
68
69QView3DWidget::QView3DWidget(QWidget *parent)
70 : QGLWidget(parent)
71 , m_layer_coloring(true)
72 , m_form_list_id(0)
73 , m_next_widget_name(0)
74{
75 setFocusPolicy(Qt::StrongFocus);
76}
77
78static int nearestSize(int v)
79{
80 int n = 0, last = 0;
81 for (int s = 0; s < 32; ++s) {
82 if (((v>>s) & 1) == 1) {
83 ++n;
84 last = s;
85 }
86 }
87 if (n > 1)
88 return 1 << (last+1);
89 return 1 << last;
90}
91
92// static int pm_cnt = 0;
93
94void QView3DWidget::addTexture(QWidget *w, const QPixmap &pm)
95{
96 int tx_w = nearestSize(pm.width());
97 int tx_h = nearestSize(pm.height());
98
99 QPixmap tmp(tx_w, tx_h);
100 tmp.fill(QColor(0,0,0));
101 QPainter p(&tmp);
102 p.drawPixmap(0, tx_h - pm.height(), pm);
103 p.end();
104 QImage tex = tmp.toImage();
105
106// QString file_name = QString("pixmapDump%1.png").arg(pm_cnt++);
107// qDebug() << "grabWidget():" << file_name << tex.save(file_name, "PNG");
108
109 tex = QGLWidget::convertToGLFormat(tex);
110
111 GLuint tx_id;
112 glGenTextures(1, &tx_id);
113 glBindTexture(GL_TEXTURE_2D, tx_id);
114 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
115 if (m_use_mipmaps) {
116 //glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
117 //glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
118 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
119 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.f);
120 } else {
121 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
122 }
123 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex.width(), tex.height(), 0, GL_RGBA,
124 GL_UNSIGNED_BYTE, tex.bits());
125 m_texture_map[w] = tx_id;
126}
127
128void QView3DWidget::addWidget(int depth, QWidget *widget)
129{
130 const auto it = m_texture_map.find(widget);
131 Q_ASSERT(it != m_texture_map.end());
132
133 makeCurrent();
134
135 int w = m_form->size().width();
136 int h = m_form->size().height();
137 int max = qMax(w, h);
138 QRect r = widget->rect();
139 QPoint pos = widget->mapToGlobal(QPoint(0, 0));
140 r.moveTopLeft(m_form->mapFromGlobal(pos));
141
142 float s = r.width()/float(nearestSize(r.width()));
143 float t = r.height()/float(nearestSize(r.height()));
144
145 if (m_layer_coloring)
146 glColor4f(1.0 - depth/10.0, 1.0 - depth/10.0, 1.0, 1.0 - depth/20.0);
147 else
148 glColor4f(1.0, 1.0, 1.0, 1.0);
149
150 glBindTexture(GL_TEXTURE_2D, *it);
151 glBegin(GL_QUADS);
152 glLoadName(m_next_widget_name);
153 glTexCoord2f(0.0, 0.0); glVertex3f(r.left() - w/2, r.bottom() - h/2, depth*max/8);
154 glTexCoord2f(s, 0.0); glVertex3f(r.right() - w/2, r.bottom()- h/2, depth*max/8);
155 glTexCoord2f(s, t); glVertex3f(r.right() - w/2, r.top() - h/2, depth*max/8);
156 glTexCoord2f(0.0, t); glVertex3f(r.left() - w/2, r.top() - h/2, depth*max/8);
157 glEnd();
158
159 m_widget_name_map[m_next_widget_name++] = widget;
160}
161
162void QView3DWidget::clear()
163{
164 makeCurrent();
165 glDeleteLists(m_form_list_id, 1);
166 for (auto it = m_texture_map.begin(), end = m_texture_map.end(); it != end; ++it)
167 glDeleteTextures(1, &(it.value()));
168 m_texture_map.clear();
169 m_widget_name_map.clear();
170 m_next_widget_name = 0;
171}
172
173void QView3DWidget::beginAddingWidgets(QWidget *form)
174{
175 makeCurrent();
176 m_form = form;
177 m_form_list_id = glGenLists(1);
178 glNewList(m_form_list_id, GL_COMPILE);
179 glInitNames();
180 glPushName(-1);
181 m_next_widget_name = 0;
182}
183
184void QView3DWidget::endAddingWidgets()
185{
186 makeCurrent();
187 glEndList();
188}
189
190void QView3DWidget::initializeGL()
191{
192 glMatrixMode(GL_MODELVIEW);
193 glLoadIdentity();
194
195 qglClearColor(palette().color(QPalette::Window).darker());
196 glColor3f (1.0, 1.0, 1.0);
197 glEnable(GL_DEPTH_TEST);
198 glDepthFunc(GL_LEQUAL);
199
200 glShadeModel(GL_FLAT);
201 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
202 QString extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
203 m_use_mipmaps = false;// extensions.contains("GL_SGIS_generate_mipmap");
204}
205
206void QView3DWidget::resizeGL(int width, int height)
207{
208 glViewport(0, 0, width, height);
209 glMatrixMode(GL_PROJECTION);
210 glLoadIdentity();
211 glOrtho(-width/2, width/2, height/2, -height/2, -999999, 999999);
212}
213
214void QView3DWidget::paintGL()
215{
216 glColor4f(1.0, 1.0, 1.0, 1.0);
217 glEnable(GL_TEXTURE_2D);
218 glEnable(GL_BLEND);
219 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
220 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
221 glCallList(m_form_list_id);
222
223 glPushAttrib(GL_ENABLE_BIT);
224 glDisable(GL_DEPTH_TEST);
225 glDisable(GL_LIGHTING);
226 glDisable(GL_TEXTURE_2D);
227 glMatrixMode(GL_MODELVIEW);
228 glPushMatrix();
229 glLoadIdentity();
230 glTranslatef(-width()/2, -height()/2, 0.0);
231
232 QFontMetrics fm(font());
233 glColor4f(0.4, 0.4, 0.4, 0.7);
234 glRecti(0, height() - fm.lineSpacing()*2.5, width(), height());
235
236 glColor3f(1.0, 1.0, 1.0);
237 renderText(10, height() - fm.lineSpacing()*1.5,
238 "Press and hold left/right mouse button = tilt the view.");
239 renderText(10, height() - fm.lineSpacing()*0.5,
240 "Mouse wheel = zoom. 't' = toggle layer coloring. 'r' = reset transform.");
241 glPopMatrix();
242 glPopAttrib();
243}
244
245QWidget *QView3DWidget::widgetAt(const QPoint & /* pos */)
246{
247 makeCurrent();
248 GLuint selectBuf[SELECTION_BUFSIZE];
249 glSelectBuffer(SELECTION_BUFSIZE, selectBuf);
250 glRenderMode (GL_SELECT);
251
252 glMatrixMode(GL_PROJECTION);
253 glPushMatrix();
254 glLoadIdentity();
255
256 glCallList(m_form_list_id);
257 return 0;
258}
259
260void QView3DWidget::keyReleaseEvent(QKeyEvent *e)
261{
262 if (e->key() == Qt::Key_T) {
263 m_layer_coloring = !m_layer_coloring;
264 emit updateForm();
265 } else if (e->key() == Qt::Key_R) {
266 makeCurrent();
267 glMatrixMode(GL_MODELVIEW);
268 glLoadIdentity();
269 }
270
271 updateGL();
272}
273
274void QView3DWidget::mousePressEvent(QMouseEvent *e)
275{
276 m_old_pos = e->pos();
277}
278
279void QView3DWidget::mouseReleaseEvent(QMouseEvent *e)
280{
281 m_old_pos = e->pos();
282}
283
284void QView3DWidget::mouseMoveEvent(QMouseEvent *e)
285{
286 if (e->buttons() & (Qt::LeftButton | Qt::RightButton)) {
287 GLfloat rx = (GLfloat) (e->x() - m_old_pos.x()) / width();
288 GLfloat ry = (GLfloat) (e->y() - m_old_pos.y()) / height();
289
290 makeCurrent();
291 glMatrixMode(GL_MODELVIEW);
292 if (e->buttons() & Qt::LeftButton) {
293 // Left button down - rotate around X and Y axes
294 glRotatef(-180*ry, 1, 0, 0);
295 glRotatef(180*rx, 0, 1, 0);
296 } else if (e->buttons() & Qt::RightButton) {
297 // Right button down - rotate around X and Z axes
298 glRotatef(-180*ry, 1, 0, 0);
299 glRotatef(-180*rx, 0, 0, 1);
300 }
301 updateGL();
302 m_old_pos = e->pos();
303 } else {
304
305 }
306}
307
308void QView3DWidget::wheelEvent(QWheelEvent *e)
309{
310 makeCurrent();
311 glMatrixMode(GL_MODELVIEW);
312 if (e->delta() < 0)
313 glScalef(0.9, 0.9, 0.9);
314 else
315 glScalef(1.1, 1.1, 1.1);
316 updateGL();
317}
318
319void QView3DWidget::contextMenuEvent(QContextMenuEvent *e)
320{
321 e->accept();
322}
323
324/*******************************************************************************
325** Misc tools
326*/
327
329{
330public:
331 virtual void operator () (int depth, QWidget *widget) const = 0;
332};
333
334static bool skipWidget(QDesignerFormEditorInterface *core, QWidget *widget)
335{
336 QDesignerMetaDataBaseItemInterface *item = core->metaDataBase()->item(widget);
337 if (item == 0)
338 return true;
339 QString name = widget->metaObject()->className();
340 if (name == "QLayoutWidget")
341 return true;
342
343 return false;
344}
345
346static void walkWidgetTree(QDesignerFormEditorInterface *core, int depth, QWidget *widget, const WalkWidgetTreeFunction &func)
347{
348 if (widget == 0)
349 return;
350 if (!widget->isVisible())
351 return;
352
353 if (!skipWidget(core, widget))
354 func(depth++, widget);
355
356 const QObjectList child_obj_list = widget->children();
357 for (QObject *child_obj : child_obj_list) {
358 QWidget *child = qobject_cast<QWidget*>(child_obj);
359 if (child != 0)
360 walkWidgetTree(core, depth, child, func);
361 }
362}
363
364static void grabWidget_helper(QWidget *widget, QPixmap &res, QPixmap &buf,
365 const QRect &r, const QPoint &offset, QDesignerFormEditorInterface *core)
366{
367 buf.fill(widget, r.topLeft());
368 QPainter::setRedirected(widget, &buf, r.topLeft());
369 QPaintEvent e(r & widget->rect());
370 QApplication::sendEvent(widget, &e);
371 QPainter::restoreRedirected(widget);
372 {
373 QPainter pt(&res);
374 pt.drawPixmap(offset.x(), offset.y(), buf, 0, 0, r.width(), r.height());
375 }
376
377 for (auto *o : widget->children()) {
378 QWidget *child = qobject_cast<QWidget*>(o);
379 if (child == 0 || child->isWindow())
380 continue;
381 if (child->isHidden() || !child->geometry().intersects(r))
382 continue;
383 if (core->metaDataBase()->item(child) != 0)
384 continue;
385 QRect cr = r & child->geometry();
386 cr.translate(-child->pos());
387 grabWidget_helper(child, res, buf, cr, offset + child->pos(), core);
388 }
389}
390
391static QPixmap grabWidget(QWidget * widget, QDesignerFormEditorInterface *core)
392{
393 if (!widget)
394 return QPixmap();
395
396 QRect r = widget->rect();
397 QSize s = widget->size();
398
399 QPixmap res(s), buf(s);
400
401 grabWidget_helper(widget, res, buf, r, QPoint(), core);
402
403 return res;
404}
405
406/*******************************************************************************
407** QView3D
408*/
409
411{
412public:
413 inline AddTexture(QDesignerFormEditorInterface *core, QView3DWidget *w)
414 : m_core(core), m_3d_widget(w) {}
415 inline virtual void operator ()(int, QWidget *w) const
416 { m_3d_widget->addTexture(w, ::grabWidget(w, m_core)); }
417 QDesignerFormEditorInterface *m_core;
418 QView3DWidget *m_3d_widget;
419};
420
422{
423public:
424 inline AddWidget(QView3DWidget *w) : m_3d_widget(w) {}
425 inline virtual void operator ()(int depth, QWidget *w) const
426 { m_3d_widget->addWidget(depth, w); }
427 QView3DWidget *m_3d_widget;
428};
429
430QView3D::QView3D(QDesignerFormWindowInterface *form_window, QWidget *parent)
431 : QWidget(parent)
432{
433 m_form_window = form_window;
434 m_3d_widget = new QView3DWidget(this);
435 connect(m_3d_widget, SIGNAL(updateForm()), this, SLOT(updateForm()));
436
437 QGridLayout *layout = new QGridLayout(this);
438 layout->setContentsMargins(QMargins());
439 layout->addWidget(m_3d_widget, 0, 0, 1, 1);
440
441 updateForm();
442}
443
444void QView3D::updateForm()
445{
446 QWidget *w = m_form_window->mainContainer();
447 if (w == 0)
448 return;
449
450 m_3d_widget->clear();
451
452 walkWidgetTree(m_form_window->core(), 0, w, AddTexture(m_form_window->core(), m_3d_widget));
453 m_3d_widget->beginAddingWidgets(w);
454 walkWidgetTree(m_form_window->core(), 0, w, AddWidget(m_3d_widget));
455 m_3d_widget->endAddingWidgets();
456}
457
458#include "view3d.moc"
QView3DWidget * m_3d_widget
Definition view3d.cpp:418
virtual void operator()(int, QWidget *w) const
Definition view3d.cpp:415
QDesignerFormEditorInterface * m_core
Definition view3d.cpp:417
AddTexture(QDesignerFormEditorInterface *core, QView3DWidget *w)
Definition view3d.cpp:413
virtual void operator()(int depth, QWidget *w) const
Definition view3d.cpp:425
AddWidget(QView3DWidget *w)
Definition view3d.cpp:424
QView3DWidget * m_3d_widget
Definition view3d.cpp:427
The QDesignerMetaDataBaseItemInterface class provides an interface to individual items in \QD's meta ...
friend class QWidget
Definition qpainter.h:421
virtual void operator()(int depth, QWidget *widget) const =0
#define GL_TEXTURE_MAX_ANISOTROPY_EXT
static void grabWidget_helper(QWidget *widget, QPixmap &res, QPixmap &buf, const QRect &r, const QPoint &offset, QDesignerFormEditorInterface *core)
Definition view3d.cpp:364
#define SELECTION_BUFSIZE
Definition view3d.cpp:14
static int nearestSize(int v)
Definition view3d.cpp:78
static bool skipWidget(QDesignerFormEditorInterface *core, QWidget *widget)
Definition view3d.cpp:334
static QPixmap grabWidget(QWidget *widget, QDesignerFormEditorInterface *core)
Definition view3d.cpp:391
static void walkWidgetTree(QDesignerFormEditorInterface *core, int depth, QWidget *widget, const WalkWidgetTreeFunction &func)
Definition view3d.cpp:346