|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the demonstration applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include <QDebug> |
|
43 #include "scene.h" |
|
44 #include <QtGui/qmatrix4x4.h> |
|
45 #include <QtGui/qvector3d.h> |
|
46 |
|
47 #include "3rdparty/fbm.h" |
|
48 |
|
49 void checkGLErrors(const QString& prefix) |
|
50 { |
|
51 switch (glGetError()) { |
|
52 case GL_NO_ERROR: |
|
53 //qDebug() << prefix << tr("No error."); |
|
54 break; |
|
55 case GL_INVALID_ENUM: |
|
56 qDebug() << prefix << QObject::tr("Invalid enum."); |
|
57 break; |
|
58 case GL_INVALID_VALUE: |
|
59 qDebug() << prefix << QObject::tr("Invalid value."); |
|
60 break; |
|
61 case GL_INVALID_OPERATION: |
|
62 qDebug() << prefix << QObject::tr("Invalid operation."); |
|
63 break; |
|
64 case GL_STACK_OVERFLOW: |
|
65 qDebug() << prefix << QObject::tr("Stack overflow."); |
|
66 break; |
|
67 case GL_STACK_UNDERFLOW: |
|
68 qDebug() << prefix << QObject::tr("Stack underflow."); |
|
69 break; |
|
70 case GL_OUT_OF_MEMORY: |
|
71 qDebug() << prefix << QObject::tr("Out of memory."); |
|
72 break; |
|
73 default: |
|
74 qDebug() << prefix << QObject::tr("Unknown error."); |
|
75 break; |
|
76 } |
|
77 } |
|
78 |
|
79 //============================================================================// |
|
80 // ColorEdit // |
|
81 //============================================================================// |
|
82 |
|
83 ColorEdit::ColorEdit(QRgb initialColor, int id) |
|
84 : m_color(initialColor), m_id(id) |
|
85 { |
|
86 QHBoxLayout *layout = new QHBoxLayout; |
|
87 setLayout(layout); |
|
88 layout->setContentsMargins(0, 0, 0, 0); |
|
89 |
|
90 m_lineEdit = new QLineEdit(QString::number(m_color, 16)); |
|
91 layout->addWidget(m_lineEdit); |
|
92 |
|
93 m_button = new QFrame; |
|
94 QPalette palette = m_button->palette(); |
|
95 palette.setColor(QPalette::Window, QColor(m_color)); |
|
96 m_button->setPalette(palette); |
|
97 m_button->setAutoFillBackground(true); |
|
98 m_button->setMinimumSize(32, 0); |
|
99 m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); |
|
100 m_button->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); |
|
101 layout->addWidget(m_button); |
|
102 |
|
103 connect(m_lineEdit, SIGNAL(editingFinished()), this, SLOT(editDone())); |
|
104 } |
|
105 |
|
106 void ColorEdit::editDone() |
|
107 { |
|
108 bool ok; |
|
109 QRgb newColor = m_lineEdit->text().toUInt(&ok, 16); |
|
110 if (ok) |
|
111 setColor(newColor); |
|
112 } |
|
113 |
|
114 void ColorEdit::mousePressEvent(QMouseEvent *event) |
|
115 { |
|
116 if (event->button() == Qt::LeftButton) { |
|
117 QColor color(m_color); |
|
118 QColorDialog dialog(color, 0); |
|
119 dialog.setOption(QColorDialog::ShowAlphaChannel, true); |
|
120 // The ifdef block is a workaround for the beta, TODO: remove when bug 238525 is fixed |
|
121 #ifdef Q_WS_MAC |
|
122 dialog.setOption(QColorDialog::DontUseNativeDialog, true); |
|
123 #endif |
|
124 dialog.move(280, 120); |
|
125 if (dialog.exec() == QDialog::Rejected) |
|
126 return; |
|
127 QRgb newColor = dialog.selectedColor().rgba(); |
|
128 if (newColor == m_color) |
|
129 return; |
|
130 setColor(newColor); |
|
131 } |
|
132 } |
|
133 |
|
134 void ColorEdit::setColor(QRgb color) |
|
135 { |
|
136 m_color = color; |
|
137 m_lineEdit->setText(QString::number(m_color, 16)); // "Clean up" text |
|
138 QPalette palette = m_button->palette(); |
|
139 palette.setColor(QPalette::Window, QColor(m_color)); |
|
140 m_button->setPalette(palette); |
|
141 emit colorChanged(m_color, m_id); |
|
142 } |
|
143 |
|
144 //============================================================================// |
|
145 // FloatEdit // |
|
146 //============================================================================// |
|
147 |
|
148 FloatEdit::FloatEdit(float initialValue, int id) |
|
149 : m_value(initialValue), m_id(id) |
|
150 { |
|
151 QHBoxLayout *layout = new QHBoxLayout; |
|
152 setLayout(layout); |
|
153 layout->setContentsMargins(0, 0, 0, 0); |
|
154 |
|
155 m_lineEdit = new QLineEdit(QString::number(m_value)); |
|
156 layout->addWidget(m_lineEdit); |
|
157 |
|
158 connect(m_lineEdit, SIGNAL(editingFinished()), this, SLOT(editDone())); |
|
159 } |
|
160 |
|
161 void FloatEdit::editDone() |
|
162 { |
|
163 bool ok; |
|
164 float newValue = m_lineEdit->text().toFloat(&ok); |
|
165 if (ok) { |
|
166 m_value = newValue; |
|
167 m_lineEdit->setText(QString::number(m_value)); // "Clean up" text |
|
168 emit valueChanged(m_value, m_id); |
|
169 } |
|
170 } |
|
171 |
|
172 //============================================================================// |
|
173 // TwoSidedGraphicsWidget // |
|
174 //============================================================================// |
|
175 |
|
176 TwoSidedGraphicsWidget::TwoSidedGraphicsWidget(QGraphicsScene *scene) |
|
177 : QObject(scene) |
|
178 , m_current(0) |
|
179 , m_angle(0) |
|
180 , m_delta(0) |
|
181 { |
|
182 for (int i = 0; i < 2; ++i) |
|
183 m_proxyWidgets[i] = 0; |
|
184 } |
|
185 |
|
186 void TwoSidedGraphicsWidget::setWidget(int index, QWidget *widget) |
|
187 { |
|
188 if (index < 0 || index >= 2) |
|
189 { |
|
190 qWarning("TwoSidedGraphicsWidget::setWidget: Index out of bounds, index == %d", index); |
|
191 return; |
|
192 } |
|
193 |
|
194 GraphicsWidget *proxy = new GraphicsWidget; |
|
195 proxy->setWidget(widget); |
|
196 |
|
197 if (m_proxyWidgets[index]) |
|
198 delete m_proxyWidgets[index]; |
|
199 m_proxyWidgets[index] = proxy; |
|
200 |
|
201 proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache); |
|
202 proxy->setZValue(1e30); // Make sure the dialog is drawn on top of all other (OpenGL) items |
|
203 |
|
204 if (index != m_current) |
|
205 proxy->setVisible(false); |
|
206 |
|
207 qobject_cast<QGraphicsScene *>(parent())->addItem(proxy); |
|
208 } |
|
209 |
|
210 QWidget *TwoSidedGraphicsWidget::widget(int index) |
|
211 { |
|
212 if (index < 0 || index >= 2) |
|
213 { |
|
214 qWarning("TwoSidedGraphicsWidget::widget: Index out of bounds, index == %d", index); |
|
215 return 0; |
|
216 } |
|
217 return m_proxyWidgets[index]->widget(); |
|
218 } |
|
219 |
|
220 void TwoSidedGraphicsWidget::flip() |
|
221 { |
|
222 m_delta = (m_current == 0 ? 9 : -9); |
|
223 animateFlip(); |
|
224 } |
|
225 |
|
226 void TwoSidedGraphicsWidget::animateFlip() |
|
227 { |
|
228 m_angle += m_delta; |
|
229 if (m_angle == 90) { |
|
230 int old = m_current; |
|
231 m_current ^= 1; |
|
232 m_proxyWidgets[old]->setVisible(false); |
|
233 m_proxyWidgets[m_current]->setVisible(true); |
|
234 m_proxyWidgets[m_current]->setGeometry(m_proxyWidgets[old]->geometry()); |
|
235 } |
|
236 |
|
237 QRectF r = m_proxyWidgets[m_current]->boundingRect(); |
|
238 m_proxyWidgets[m_current]->setTransform(QTransform() |
|
239 .translate(r.width() / 2, r.height() / 2) |
|
240 .rotate(m_angle - 180 * m_current, Qt::YAxis) |
|
241 .translate(-r.width() / 2, -r.height() / 2)); |
|
242 |
|
243 if ((m_current == 0 && m_angle > 0) || (m_current == 1 && m_angle < 180)) |
|
244 QTimer::singleShot(25, this, SLOT(animateFlip())); |
|
245 } |
|
246 |
|
247 QVariant GraphicsWidget::itemChange(GraphicsItemChange change, const QVariant &value) |
|
248 { |
|
249 if (change == ItemPositionChange && scene()) { |
|
250 QRectF rect = boundingRect(); |
|
251 QPointF pos = value.toPointF(); |
|
252 QRectF sceneRect = scene()->sceneRect(); |
|
253 if (pos.x() + rect.left() < sceneRect.left()) |
|
254 pos.setX(sceneRect.left() - rect.left()); |
|
255 else if (pos.x() + rect.right() >= sceneRect.right()) |
|
256 pos.setX(sceneRect.right() - rect.right()); |
|
257 if (pos.y() + rect.top() < sceneRect.top()) |
|
258 pos.setY(sceneRect.top() - rect.top()); |
|
259 else if (pos.y() + rect.bottom() >= sceneRect.bottom()) |
|
260 pos.setY(sceneRect.bottom() - rect.bottom()); |
|
261 return pos; |
|
262 } |
|
263 return QGraphicsProxyWidget::itemChange(change, value); |
|
264 } |
|
265 |
|
266 void GraphicsWidget::resizeEvent(QGraphicsSceneResizeEvent *event) |
|
267 { |
|
268 setCacheMode(QGraphicsItem::NoCache); |
|
269 setCacheMode(QGraphicsItem::ItemCoordinateCache); |
|
270 QGraphicsProxyWidget::resizeEvent(event); |
|
271 } |
|
272 |
|
273 void GraphicsWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
|
274 { |
|
275 painter->setRenderHint(QPainter::Antialiasing, false); |
|
276 QGraphicsProxyWidget::paint(painter, option, widget); |
|
277 //painter->setRenderHint(QPainter::Antialiasing, true); |
|
278 } |
|
279 |
|
280 //============================================================================// |
|
281 // RenderOptionsDialog // |
|
282 //============================================================================// |
|
283 |
|
284 RenderOptionsDialog::RenderOptionsDialog() |
|
285 : QDialog(0, Qt::CustomizeWindowHint | Qt::WindowTitleHint) |
|
286 { |
|
287 setWindowOpacity(0.75); |
|
288 setWindowTitle(tr("Options (double click to flip)")); |
|
289 QGridLayout *layout = new QGridLayout; |
|
290 setLayout(layout); |
|
291 layout->setColumnStretch(1, 1); |
|
292 |
|
293 int row = 0; |
|
294 |
|
295 QCheckBox *check = new QCheckBox(tr("Dynamic cube map")); |
|
296 check->setCheckState(Qt::Unchecked); |
|
297 // Dynamic cube maps are only enabled when multi-texturing and render to texture are available. |
|
298 check->setEnabled(glActiveTexture && glGenFramebuffersEXT); |
|
299 connect(check, SIGNAL(stateChanged(int)), this, SIGNAL(dynamicCubemapToggled(int))); |
|
300 layout->addWidget(check, 0, 0, 1, 2); |
|
301 ++row; |
|
302 |
|
303 QPalette palette; |
|
304 |
|
305 // Load all .par files |
|
306 // .par files have a simple syntax for specifying user adjustable uniform variables. |
|
307 QSet<QByteArray> uniforms; |
|
308 QList<QString> filter = QStringList("*.par"); |
|
309 QList<QFileInfo> files = QDir(":/res/boxes/").entryInfoList(filter, QDir::Files | QDir::Readable); |
|
310 |
|
311 foreach (QFileInfo fileInfo, files) { |
|
312 QFile file(fileInfo.absoluteFilePath()); |
|
313 if (file.open(QIODevice::ReadOnly)) { |
|
314 while (!file.atEnd()) { |
|
315 QList<QByteArray> tokens = file.readLine().simplified().split(' '); |
|
316 QList<QByteArray>::const_iterator it = tokens.begin(); |
|
317 if (it == tokens.end()) |
|
318 continue; |
|
319 QByteArray type = *it; |
|
320 if (++it == tokens.end()) |
|
321 continue; |
|
322 QByteArray name = *it; |
|
323 bool singleElement = (tokens.size() == 3); // type, name and one value |
|
324 char counter[10] = "000000000"; |
|
325 int counterPos = 8; // position of last digit |
|
326 while (++it != tokens.end()) { |
|
327 m_parameterNames << name; |
|
328 if (!singleElement) { |
|
329 m_parameterNames.back() += "["; |
|
330 m_parameterNames.back() += counter + counterPos; |
|
331 m_parameterNames.back() += "]"; |
|
332 int j = 8; // position of last digit |
|
333 ++counter[j]; |
|
334 while (j > 0 && counter[j] > '9') { |
|
335 counter[j] = '0'; |
|
336 ++counter[--j]; |
|
337 } |
|
338 if (j < counterPos) |
|
339 counterPos = j; |
|
340 } |
|
341 |
|
342 if (type == "color") { |
|
343 layout->addWidget(new QLabel(m_parameterNames.back())); |
|
344 bool ok; |
|
345 ColorEdit *colorEdit = new ColorEdit(it->toUInt(&ok, 16), m_parameterNames.size() - 1); |
|
346 m_parameterEdits << colorEdit; |
|
347 layout->addWidget(colorEdit); |
|
348 connect(colorEdit, SIGNAL(colorChanged(QRgb,int)), this, SLOT(setColorParameter(QRgb,int))); |
|
349 ++row; |
|
350 } else if (type == "float") { |
|
351 layout->addWidget(new QLabel(m_parameterNames.back())); |
|
352 bool ok; |
|
353 FloatEdit *floatEdit = new FloatEdit(it->toFloat(&ok), m_parameterNames.size() - 1); |
|
354 m_parameterEdits << floatEdit; |
|
355 layout->addWidget(floatEdit); |
|
356 connect(floatEdit, SIGNAL(valueChanged(float,int)), this, SLOT(setFloatParameter(float,int))); |
|
357 ++row; |
|
358 } |
|
359 } |
|
360 } |
|
361 file.close(); |
|
362 } |
|
363 } |
|
364 |
|
365 layout->addWidget(new QLabel(tr("Texture:"))); |
|
366 m_textureCombo = new QComboBox; |
|
367 connect(m_textureCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(textureChanged(int))); |
|
368 layout->addWidget(m_textureCombo); |
|
369 ++row; |
|
370 |
|
371 layout->addWidget(new QLabel(tr("Shader:"))); |
|
372 m_shaderCombo = new QComboBox; |
|
373 connect(m_shaderCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(shaderChanged(int))); |
|
374 layout->addWidget(m_shaderCombo); |
|
375 ++row; |
|
376 |
|
377 layout->setRowStretch(row, 1); |
|
378 } |
|
379 |
|
380 int RenderOptionsDialog::addTexture(const QString &name) |
|
381 { |
|
382 m_textureCombo->addItem(name); |
|
383 return m_textureCombo->count() - 1; |
|
384 } |
|
385 |
|
386 int RenderOptionsDialog::addShader(const QString &name) |
|
387 { |
|
388 m_shaderCombo->addItem(name); |
|
389 return m_shaderCombo->count() - 1; |
|
390 } |
|
391 |
|
392 void RenderOptionsDialog::emitParameterChanged() |
|
393 { |
|
394 foreach (ParameterEdit *edit, m_parameterEdits) |
|
395 edit->emitChange(); |
|
396 } |
|
397 |
|
398 void RenderOptionsDialog::setColorParameter(QRgb color, int id) |
|
399 { |
|
400 emit colorParameterChanged(m_parameterNames[id], color); |
|
401 } |
|
402 |
|
403 void RenderOptionsDialog::setFloatParameter(float value, int id) |
|
404 { |
|
405 emit floatParameterChanged(m_parameterNames[id], value); |
|
406 } |
|
407 |
|
408 void RenderOptionsDialog::mouseDoubleClickEvent(QMouseEvent *event) |
|
409 { |
|
410 if (event->button() == Qt::LeftButton) |
|
411 emit doubleClicked(); |
|
412 } |
|
413 |
|
414 //============================================================================// |
|
415 // ItemDialog // |
|
416 //============================================================================// |
|
417 |
|
418 ItemDialog::ItemDialog() |
|
419 : QDialog(0, Qt::CustomizeWindowHint | Qt::WindowTitleHint) |
|
420 { |
|
421 setWindowTitle(tr("Items (double click to flip)")); |
|
422 setWindowOpacity(0.75); |
|
423 resize(160, 100); |
|
424 |
|
425 QVBoxLayout *layout = new QVBoxLayout; |
|
426 setLayout(layout); |
|
427 QPushButton *button; |
|
428 |
|
429 button = new QPushButton(tr("Add Qt box")); |
|
430 layout->addWidget(button); |
|
431 connect(button, SIGNAL(clicked()), this, SLOT(triggerNewQtBox())); |
|
432 |
|
433 button = new QPushButton(tr("Add circle")); |
|
434 layout->addWidget(button); |
|
435 connect(button, SIGNAL(clicked()), this, SLOT(triggerNewCircleItem())); |
|
436 |
|
437 button = new QPushButton(tr("Add square")); |
|
438 layout->addWidget(button); |
|
439 connect(button, SIGNAL(clicked()), this, SLOT(triggerNewSquareItem())); |
|
440 |
|
441 layout->addStretch(1); |
|
442 } |
|
443 |
|
444 void ItemDialog::triggerNewQtBox() |
|
445 { |
|
446 emit newItemTriggered(QtBoxItem); |
|
447 } |
|
448 |
|
449 void ItemDialog::triggerNewCircleItem() |
|
450 { |
|
451 emit newItemTriggered(CircleItem); |
|
452 } |
|
453 |
|
454 void ItemDialog::triggerNewSquareItem() |
|
455 { |
|
456 emit newItemTriggered(SquareItem); |
|
457 } |
|
458 |
|
459 void ItemDialog::mouseDoubleClickEvent(QMouseEvent *event) |
|
460 { |
|
461 if (event->button() == Qt::LeftButton) |
|
462 emit doubleClicked(); |
|
463 } |
|
464 |
|
465 //============================================================================// |
|
466 // Scene // |
|
467 //============================================================================// |
|
468 |
|
469 const static char environmentShaderText[] = |
|
470 "uniform samplerCube env;" |
|
471 "void main() {" |
|
472 "gl_FragColor = textureCube(env, gl_TexCoord[1].xyz);" |
|
473 "}"; |
|
474 |
|
475 Scene::Scene(int width, int height, int maxTextureSize) |
|
476 : m_distExp(600) |
|
477 , m_frame(0) |
|
478 , m_maxTextureSize(maxTextureSize) |
|
479 , m_currentShader(0) |
|
480 , m_currentTexture(0) |
|
481 , m_dynamicCubemap(false) |
|
482 , m_updateAllCubemaps(true) |
|
483 , m_box(0) |
|
484 , m_vertexShader(0) |
|
485 , m_environmentShader(0) |
|
486 , m_environmentProgram(0) |
|
487 { |
|
488 setSceneRect(0, 0, width, height); |
|
489 |
|
490 m_trackBalls[0] = TrackBall(0.05f, QVector3D(0, 1, 0), TrackBall::Sphere); |
|
491 m_trackBalls[1] = TrackBall(0.005f, QVector3D(0, 0, 1), TrackBall::Sphere); |
|
492 m_trackBalls[2] = TrackBall(0.0f, QVector3D(0, 1, 0), TrackBall::Plane); |
|
493 |
|
494 m_renderOptions = new RenderOptionsDialog; |
|
495 m_renderOptions->move(20, 120); |
|
496 m_renderOptions->resize(m_renderOptions->sizeHint()); |
|
497 |
|
498 connect(m_renderOptions, SIGNAL(dynamicCubemapToggled(int)), this, SLOT(toggleDynamicCubemap(int))); |
|
499 connect(m_renderOptions, SIGNAL(colorParameterChanged(QString,QRgb)), this, SLOT(setColorParameter(QString,QRgb))); |
|
500 connect(m_renderOptions, SIGNAL(floatParameterChanged(QString,float)), this, SLOT(setFloatParameter(QString,float))); |
|
501 connect(m_renderOptions, SIGNAL(textureChanged(int)), this, SLOT(setTexture(int))); |
|
502 connect(m_renderOptions, SIGNAL(shaderChanged(int)), this, SLOT(setShader(int))); |
|
503 |
|
504 m_itemDialog = new ItemDialog; |
|
505 connect(m_itemDialog, SIGNAL(newItemTriggered(ItemDialog::ItemType)), this, SLOT(newItem(ItemDialog::ItemType))); |
|
506 |
|
507 TwoSidedGraphicsWidget *twoSided = new TwoSidedGraphicsWidget(this); |
|
508 twoSided->setWidget(0, m_renderOptions); |
|
509 twoSided->setWidget(1, m_itemDialog); |
|
510 |
|
511 connect(m_renderOptions, SIGNAL(doubleClicked()), twoSided, SLOT(flip())); |
|
512 connect(m_itemDialog, SIGNAL(doubleClicked()), twoSided, SLOT(flip())); |
|
513 |
|
514 addItem(new QtBox(64, width - 64, height - 64)); |
|
515 addItem(new QtBox(64, width - 64, 64)); |
|
516 addItem(new QtBox(64, 64, height - 64)); |
|
517 addItem(new QtBox(64, 64, 64)); |
|
518 |
|
519 initGL(); |
|
520 |
|
521 m_timer = new QTimer(this); |
|
522 m_timer->setInterval(20); |
|
523 connect(m_timer, SIGNAL(timeout()), this, SLOT(update())); |
|
524 m_timer->start(); |
|
525 |
|
526 m_time.start(); |
|
527 } |
|
528 |
|
529 Scene::~Scene() |
|
530 { |
|
531 if (m_box) |
|
532 delete m_box; |
|
533 foreach (GLTexture *texture, m_textures) |
|
534 if (texture) delete texture; |
|
535 if (m_mainCubemap) |
|
536 delete m_mainCubemap; |
|
537 foreach (QGLShaderProgram *program, m_programs) |
|
538 if (program) delete program; |
|
539 if (m_vertexShader) |
|
540 delete m_vertexShader; |
|
541 foreach (QGLShader *shader, m_fragmentShaders) |
|
542 if (shader) delete shader; |
|
543 foreach (GLRenderTargetCube *rt, m_cubemaps) |
|
544 if (rt) delete rt; |
|
545 if (m_environmentShader) |
|
546 delete m_environmentShader; |
|
547 if (m_environmentProgram) |
|
548 delete m_environmentProgram; |
|
549 } |
|
550 |
|
551 void Scene::initGL() |
|
552 { |
|
553 m_box = new GLRoundedBox(0.25f, 1.0f, 10); |
|
554 |
|
555 m_vertexShader = new QGLShader(QGLShader::Vertex); |
|
556 m_vertexShader->compileSourceFile(QLatin1String(":/res/boxes/basic.vsh")); |
|
557 |
|
558 QStringList list; |
|
559 list << ":/res/boxes/cubemap_posx.jpg" << ":/res/boxes/cubemap_negx.jpg" << ":/res/boxes/cubemap_posy.jpg" |
|
560 << ":/res/boxes/cubemap_negy.jpg" << ":/res/boxes/cubemap_posz.jpg" << ":/res/boxes/cubemap_negz.jpg"; |
|
561 m_environment = new GLTextureCube(list, qMin(1024, m_maxTextureSize)); |
|
562 m_environmentShader = new QGLShader(QGLShader::Fragment); |
|
563 m_environmentShader->compileSourceCode(environmentShaderText); |
|
564 m_environmentProgram = new QGLShaderProgram; |
|
565 m_environmentProgram->addShader(m_vertexShader); |
|
566 m_environmentProgram->addShader(m_environmentShader); |
|
567 m_environmentProgram->link(); |
|
568 |
|
569 const int NOISE_SIZE = 128; // for a different size, B and BM in fbm.c must also be changed |
|
570 m_noise = new GLTexture3D(NOISE_SIZE, NOISE_SIZE, NOISE_SIZE); |
|
571 QRgb *data = new QRgb[NOISE_SIZE * NOISE_SIZE * NOISE_SIZE]; |
|
572 memset(data, 0, NOISE_SIZE * NOISE_SIZE * NOISE_SIZE * sizeof(QRgb)); |
|
573 QRgb *p = data; |
|
574 float pos[3]; |
|
575 for (int k = 0; k < NOISE_SIZE; ++k) { |
|
576 pos[2] = k * (0x20 / (float)NOISE_SIZE); |
|
577 for (int j = 0; j < NOISE_SIZE; ++j) { |
|
578 for (int i = 0; i < NOISE_SIZE; ++i) { |
|
579 for (int byte = 0; byte < 4; ++byte) { |
|
580 pos[0] = (i + (byte & 1) * 16) * (0x20 / (float)NOISE_SIZE); |
|
581 pos[1] = (j + (byte & 2) * 8) * (0x20 / (float)NOISE_SIZE); |
|
582 *p |= (int)(128.0f * (noise3(pos) + 1.0f)) << (byte * 8); |
|
583 } |
|
584 ++p; |
|
585 } |
|
586 } |
|
587 } |
|
588 m_noise->load(NOISE_SIZE, NOISE_SIZE, NOISE_SIZE, data); |
|
589 delete[] data; |
|
590 |
|
591 m_mainCubemap = new GLRenderTargetCube(512); |
|
592 |
|
593 QStringList filter; |
|
594 QList<QFileInfo> files; |
|
595 |
|
596 // Load all .png files as textures |
|
597 m_currentTexture = 0; |
|
598 filter = QStringList("*.png"); |
|
599 files = QDir(":/res/boxes/").entryInfoList(filter, QDir::Files | QDir::Readable); |
|
600 |
|
601 foreach (QFileInfo file, files) { |
|
602 GLTexture *texture = new GLTexture2D(file.absoluteFilePath(), qMin(256, m_maxTextureSize), qMin(256, m_maxTextureSize)); |
|
603 if (texture->failed()) { |
|
604 delete texture; |
|
605 continue; |
|
606 } |
|
607 m_textures << texture; |
|
608 m_renderOptions->addTexture(file.baseName()); |
|
609 } |
|
610 |
|
611 if (m_textures.size() == 0) |
|
612 m_textures << new GLTexture2D(qMin(64, m_maxTextureSize), qMin(64, m_maxTextureSize)); |
|
613 |
|
614 // Load all .fsh files as fragment shaders |
|
615 m_currentShader = 0; |
|
616 filter = QStringList("*.fsh"); |
|
617 files = QDir(":/res/boxes/").entryInfoList(filter, QDir::Files | QDir::Readable); |
|
618 foreach (QFileInfo file, files) { |
|
619 QGLShaderProgram *program = new QGLShaderProgram; |
|
620 QGLShader* shader = new QGLShader(QGLShader::Fragment); |
|
621 shader->compileSourceFile(file.absoluteFilePath()); |
|
622 // The program does not take ownership over the shaders, so store them in a vector so they can be deleted afterwards. |
|
623 program->addShader(m_vertexShader); |
|
624 program->addShader(shader); |
|
625 if (!program->link()) { |
|
626 qWarning("Failed to compile and link shader program"); |
|
627 qWarning("Vertex shader log:"); |
|
628 qWarning() << m_vertexShader->log(); |
|
629 qWarning() << "Fragment shader log ( file =" << file.absoluteFilePath() << "):"; |
|
630 qWarning() << shader->log(); |
|
631 qWarning("Shader program log:"); |
|
632 qWarning() << program->log(); |
|
633 |
|
634 delete shader; |
|
635 delete program; |
|
636 continue; |
|
637 } |
|
638 |
|
639 m_fragmentShaders << shader; |
|
640 m_programs << program; |
|
641 m_renderOptions->addShader(file.baseName()); |
|
642 |
|
643 program->bind(); |
|
644 m_cubemaps << ((program->uniformLocation("env") != -1) ? new GLRenderTargetCube(qMin(256, m_maxTextureSize)) : 0); |
|
645 program->release(); |
|
646 } |
|
647 |
|
648 if (m_programs.size() == 0) |
|
649 m_programs << new QGLShaderProgram; |
|
650 |
|
651 m_renderOptions->emitParameterChanged(); |
|
652 } |
|
653 |
|
654 static void loadMatrix(const QMatrix4x4& m) |
|
655 { |
|
656 // static to prevent glLoadMatrixf to fail on certain drivers |
|
657 static GLfloat mat[16]; |
|
658 const qreal *data = m.constData(); |
|
659 for (int index = 0; index < 16; ++index) |
|
660 mat[index] = data[index]; |
|
661 glLoadMatrixf(mat); |
|
662 } |
|
663 |
|
664 static void multMatrix(const QMatrix4x4& m) |
|
665 { |
|
666 // static to prevent glMultMatrixf to fail on certain drivers |
|
667 static GLfloat mat[16]; |
|
668 const qreal *data = m.constData(); |
|
669 for (int index = 0; index < 16; ++index) |
|
670 mat[index] = data[index]; |
|
671 glMultMatrixf(mat); |
|
672 } |
|
673 |
|
674 // If one of the boxes should not be rendered, set excludeBox to its index. |
|
675 // If the main box should not be rendered, set excludeBox to -1. |
|
676 void Scene::renderBoxes(const QMatrix4x4 &view, int excludeBox) |
|
677 { |
|
678 QMatrix4x4 invView = view.inverted(); |
|
679 |
|
680 // If multi-texturing is supported, use three saplers. |
|
681 if (glActiveTexture) { |
|
682 glActiveTexture(GL_TEXTURE0); |
|
683 m_textures[m_currentTexture]->bind(); |
|
684 glActiveTexture(GL_TEXTURE2); |
|
685 m_noise->bind(); |
|
686 glActiveTexture(GL_TEXTURE1); |
|
687 } else { |
|
688 m_textures[m_currentTexture]->bind(); |
|
689 } |
|
690 |
|
691 glDisable(GL_LIGHTING); |
|
692 glDisable(GL_CULL_FACE); |
|
693 |
|
694 QMatrix4x4 viewRotation(view); |
|
695 viewRotation(3, 0) = viewRotation(3, 1) = viewRotation(3, 2) = 0.0f; |
|
696 viewRotation(0, 3) = viewRotation(1, 3) = viewRotation(2, 3) = 0.0f; |
|
697 viewRotation(3, 3) = 1.0f; |
|
698 loadMatrix(viewRotation); |
|
699 glScalef(20.0f, 20.0f, 20.0f); |
|
700 |
|
701 // Don't render the environment if the environment texture can't be set for the correct sampler. |
|
702 if (glActiveTexture) { |
|
703 m_environment->bind(); |
|
704 m_environmentProgram->bind(); |
|
705 m_environmentProgram->setUniformValue("tex", GLint(0)); |
|
706 m_environmentProgram->setUniformValue("env", GLint(1)); |
|
707 m_environmentProgram->setUniformValue("noise", GLint(2)); |
|
708 m_box->draw(); |
|
709 m_environmentProgram->release(); |
|
710 m_environment->unbind(); |
|
711 } |
|
712 |
|
713 loadMatrix(view); |
|
714 |
|
715 glEnable(GL_CULL_FACE); |
|
716 glEnable(GL_LIGHTING); |
|
717 |
|
718 for (int i = 0; i < m_programs.size(); ++i) { |
|
719 if (i == excludeBox) |
|
720 continue; |
|
721 |
|
722 glPushMatrix(); |
|
723 QMatrix4x4 m; |
|
724 m.rotate(m_trackBalls[1].rotation()); |
|
725 multMatrix(m); |
|
726 |
|
727 glRotatef(360.0f * i / m_programs.size(), 0.0f, 0.0f, 1.0f); |
|
728 glTranslatef(2.0f, 0.0f, 0.0f); |
|
729 glScalef(0.3f, 0.6f, 0.6f); |
|
730 |
|
731 if (glActiveTexture) { |
|
732 if (m_dynamicCubemap && m_cubemaps[i]) |
|
733 m_cubemaps[i]->bind(); |
|
734 else |
|
735 m_environment->bind(); |
|
736 } |
|
737 m_programs[i]->bind(); |
|
738 m_programs[i]->setUniformValue("tex", GLint(0)); |
|
739 m_programs[i]->setUniformValue("env", GLint(1)); |
|
740 m_programs[i]->setUniformValue("noise", GLint(2)); |
|
741 m_programs[i]->setUniformValue("view", view); |
|
742 m_programs[i]->setUniformValue("invView", invView); |
|
743 m_box->draw(); |
|
744 m_programs[i]->release(); |
|
745 |
|
746 if (glActiveTexture) { |
|
747 if (m_dynamicCubemap && m_cubemaps[i]) |
|
748 m_cubemaps[i]->unbind(); |
|
749 else |
|
750 m_environment->unbind(); |
|
751 } |
|
752 glPopMatrix(); |
|
753 } |
|
754 |
|
755 if (-1 != excludeBox) { |
|
756 QMatrix4x4 m; |
|
757 m.rotate(m_trackBalls[0].rotation()); |
|
758 multMatrix(m); |
|
759 |
|
760 if (glActiveTexture) { |
|
761 if (m_dynamicCubemap) |
|
762 m_mainCubemap->bind(); |
|
763 else |
|
764 m_environment->bind(); |
|
765 } |
|
766 |
|
767 m_programs[m_currentShader]->bind(); |
|
768 m_programs[m_currentShader]->setUniformValue("tex", GLint(0)); |
|
769 m_programs[m_currentShader]->setUniformValue("env", GLint(1)); |
|
770 m_programs[m_currentShader]->setUniformValue("noise", GLint(2)); |
|
771 m_programs[m_currentShader]->setUniformValue("view", view); |
|
772 m_programs[m_currentShader]->setUniformValue("invView", invView); |
|
773 m_box->draw(); |
|
774 m_programs[m_currentShader]->release(); |
|
775 |
|
776 if (glActiveTexture) { |
|
777 if (m_dynamicCubemap) |
|
778 m_mainCubemap->unbind(); |
|
779 else |
|
780 m_environment->unbind(); |
|
781 } |
|
782 } |
|
783 |
|
784 if (glActiveTexture) { |
|
785 glActiveTexture(GL_TEXTURE2); |
|
786 m_noise->unbind(); |
|
787 glActiveTexture(GL_TEXTURE0); |
|
788 } |
|
789 m_textures[m_currentTexture]->unbind(); |
|
790 } |
|
791 |
|
792 void Scene::setStates() |
|
793 { |
|
794 //glClearColor(0.25f, 0.25f, 0.5f, 1.0f); |
|
795 |
|
796 glEnable(GL_DEPTH_TEST); |
|
797 glEnable(GL_CULL_FACE); |
|
798 glEnable(GL_LIGHTING); |
|
799 //glEnable(GL_COLOR_MATERIAL); |
|
800 glEnable(GL_TEXTURE_2D); |
|
801 glEnable(GL_NORMALIZE); |
|
802 |
|
803 glMatrixMode(GL_PROJECTION); |
|
804 glPushMatrix(); |
|
805 glLoadIdentity(); |
|
806 |
|
807 glMatrixMode(GL_MODELVIEW); |
|
808 glPushMatrix(); |
|
809 glLoadIdentity(); |
|
810 |
|
811 setLights(); |
|
812 |
|
813 float materialSpecular[] = {0.5f, 0.5f, 0.5f, 1.0f}; |
|
814 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, materialSpecular); |
|
815 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 32.0f); |
|
816 } |
|
817 |
|
818 void Scene::setLights() |
|
819 { |
|
820 glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); |
|
821 //float lightColour[] = {1.0f, 1.0f, 1.0f, 1.0f}; |
|
822 float lightDir[] = {0.0f, 0.0f, 1.0f, 0.0f}; |
|
823 //glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColour); |
|
824 //glLightfv(GL_LIGHT0, GL_SPECULAR, lightColour); |
|
825 glLightfv(GL_LIGHT0, GL_POSITION, lightDir); |
|
826 glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, 1.0f); |
|
827 glEnable(GL_LIGHT0); |
|
828 } |
|
829 |
|
830 void Scene::defaultStates() |
|
831 { |
|
832 //glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
|
833 |
|
834 glDisable(GL_DEPTH_TEST); |
|
835 glDisable(GL_CULL_FACE); |
|
836 glDisable(GL_LIGHTING); |
|
837 //glDisable(GL_COLOR_MATERIAL); |
|
838 glDisable(GL_TEXTURE_2D); |
|
839 glDisable(GL_LIGHT0); |
|
840 glDisable(GL_NORMALIZE); |
|
841 |
|
842 glMatrixMode(GL_MODELVIEW); |
|
843 glPopMatrix(); |
|
844 |
|
845 glMatrixMode(GL_PROJECTION); |
|
846 glPopMatrix(); |
|
847 |
|
848 glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, 0.0f); |
|
849 float defaultMaterialSpecular[] = {0.0f, 0.0f, 0.0f, 1.0f}; |
|
850 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, defaultMaterialSpecular); |
|
851 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f); |
|
852 } |
|
853 |
|
854 void Scene::renderCubemaps() |
|
855 { |
|
856 // To speed things up, only update the cubemaps for the small cubes every N frames. |
|
857 const int N = (m_updateAllCubemaps ? 1 : 3); |
|
858 |
|
859 QMatrix4x4 mat; |
|
860 GLRenderTargetCube::getProjectionMatrix(mat, 0.1f, 100.0f); |
|
861 |
|
862 glMatrixMode(GL_PROJECTION); |
|
863 glPushMatrix(); |
|
864 loadMatrix(mat); |
|
865 |
|
866 glMatrixMode(GL_MODELVIEW); |
|
867 glPushMatrix(); |
|
868 |
|
869 QVector3D center; |
|
870 |
|
871 for (int i = m_frame % N; i < m_cubemaps.size(); i += N) { |
|
872 if (0 == m_cubemaps[i]) |
|
873 continue; |
|
874 |
|
875 float angle = 2.0f * PI * i / m_cubemaps.size(); |
|
876 |
|
877 center = m_trackBalls[1].rotation().rotatedVector(QVector3D(cos(angle), sin(angle), 0.0f)); |
|
878 |
|
879 for (int face = 0; face < 6; ++face) { |
|
880 m_cubemaps[i]->begin(face); |
|
881 |
|
882 GLRenderTargetCube::getViewMatrix(mat, face); |
|
883 QVector4D v = QVector4D(-center.x(), -center.y(), -center.z(), 1.0); |
|
884 mat.setColumn(3, mat * v); |
|
885 |
|
886 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|
887 renderBoxes(mat, i); |
|
888 |
|
889 m_cubemaps[i]->end(); |
|
890 } |
|
891 } |
|
892 |
|
893 for (int face = 0; face < 6; ++face) { |
|
894 m_mainCubemap->begin(face); |
|
895 GLRenderTargetCube::getViewMatrix(mat, face); |
|
896 |
|
897 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|
898 renderBoxes(mat, -1); |
|
899 |
|
900 m_mainCubemap->end(); |
|
901 } |
|
902 |
|
903 glPopMatrix(); |
|
904 |
|
905 glMatrixMode(GL_PROJECTION); |
|
906 glPopMatrix(); |
|
907 |
|
908 m_updateAllCubemaps = false; |
|
909 } |
|
910 |
|
911 void Scene::drawBackground(QPainter *painter, const QRectF &) |
|
912 { |
|
913 float width = float(painter->device()->width()); |
|
914 float height = float(painter->device()->height()); |
|
915 |
|
916 painter->beginNativePainting(); |
|
917 setStates(); |
|
918 |
|
919 if (m_dynamicCubemap) |
|
920 renderCubemaps(); |
|
921 |
|
922 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|
923 |
|
924 glMatrixMode(GL_PROJECTION); |
|
925 gluPerspective(60.0, width / height, 0.01, 15.0); |
|
926 |
|
927 glMatrixMode(GL_MODELVIEW); |
|
928 |
|
929 QMatrix4x4 view; |
|
930 view.rotate(m_trackBalls[2].rotation()); |
|
931 view(2, 3) -= 2.0f * exp(m_distExp / 1200.0f); |
|
932 renderBoxes(view); |
|
933 |
|
934 defaultStates(); |
|
935 ++m_frame; |
|
936 |
|
937 painter->endNativePainting(); |
|
938 } |
|
939 |
|
940 QPointF Scene::pixelPosToViewPos(const QPointF& p) |
|
941 { |
|
942 return QPointF(2.0 * float(p.x()) / width() - 1.0, |
|
943 1.0 - 2.0 * float(p.y()) / height()); |
|
944 } |
|
945 |
|
946 void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) |
|
947 { |
|
948 QGraphicsScene::mouseMoveEvent(event); |
|
949 if (event->isAccepted()) |
|
950 return; |
|
951 |
|
952 if (event->buttons() & Qt::LeftButton) { |
|
953 m_trackBalls[0].move(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate()); |
|
954 event->accept(); |
|
955 } else { |
|
956 m_trackBalls[0].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate()); |
|
957 } |
|
958 |
|
959 if (event->buttons() & Qt::RightButton) { |
|
960 m_trackBalls[1].move(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate()); |
|
961 event->accept(); |
|
962 } else { |
|
963 m_trackBalls[1].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate()); |
|
964 } |
|
965 |
|
966 if (event->buttons() & Qt::MidButton) { |
|
967 m_trackBalls[2].move(pixelPosToViewPos(event->scenePos()), QQuaternion()); |
|
968 event->accept(); |
|
969 } else { |
|
970 m_trackBalls[2].release(pixelPosToViewPos(event->scenePos()), QQuaternion()); |
|
971 } |
|
972 } |
|
973 |
|
974 void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event) |
|
975 { |
|
976 QGraphicsScene::mousePressEvent(event); |
|
977 if (event->isAccepted()) |
|
978 return; |
|
979 |
|
980 if (event->buttons() & Qt::LeftButton) { |
|
981 m_trackBalls[0].push(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate()); |
|
982 event->accept(); |
|
983 } |
|
984 |
|
985 if (event->buttons() & Qt::RightButton) { |
|
986 m_trackBalls[1].push(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate()); |
|
987 event->accept(); |
|
988 } |
|
989 |
|
990 if (event->buttons() & Qt::MidButton) { |
|
991 m_trackBalls[2].push(pixelPosToViewPos(event->scenePos()), QQuaternion()); |
|
992 event->accept(); |
|
993 } |
|
994 } |
|
995 |
|
996 void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) |
|
997 { |
|
998 QGraphicsScene::mouseReleaseEvent(event); |
|
999 if (event->isAccepted()) |
|
1000 return; |
|
1001 |
|
1002 if (event->button() == Qt::LeftButton) { |
|
1003 m_trackBalls[0].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate()); |
|
1004 event->accept(); |
|
1005 } |
|
1006 |
|
1007 if (event->button() == Qt::RightButton) { |
|
1008 m_trackBalls[1].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate()); |
|
1009 event->accept(); |
|
1010 } |
|
1011 |
|
1012 if (event->button() == Qt::MidButton) { |
|
1013 m_trackBalls[2].release(pixelPosToViewPos(event->scenePos()), QQuaternion()); |
|
1014 event->accept(); |
|
1015 } |
|
1016 } |
|
1017 |
|
1018 void Scene::wheelEvent(QGraphicsSceneWheelEvent * event) |
|
1019 { |
|
1020 QGraphicsScene::wheelEvent(event); |
|
1021 if (!event->isAccepted()) { |
|
1022 m_distExp += event->delta(); |
|
1023 if (m_distExp < -8 * 120) |
|
1024 m_distExp = -8 * 120; |
|
1025 if (m_distExp > 10 * 120) |
|
1026 m_distExp = 10 * 120; |
|
1027 event->accept(); |
|
1028 } |
|
1029 } |
|
1030 |
|
1031 void Scene::setShader(int index) |
|
1032 { |
|
1033 if (index >= 0 && index < m_fragmentShaders.size()) |
|
1034 m_currentShader = index; |
|
1035 } |
|
1036 |
|
1037 void Scene::setTexture(int index) |
|
1038 { |
|
1039 if (index >= 0 && index < m_textures.size()) |
|
1040 m_currentTexture = index; |
|
1041 } |
|
1042 |
|
1043 void Scene::toggleDynamicCubemap(int state) |
|
1044 { |
|
1045 if ((m_dynamicCubemap = (state == Qt::Checked))) |
|
1046 m_updateAllCubemaps = true; |
|
1047 } |
|
1048 |
|
1049 void Scene::setColorParameter(const QString &name, QRgb color) |
|
1050 { |
|
1051 // set the color in all programs |
|
1052 foreach (QGLShaderProgram *program, m_programs) { |
|
1053 program->bind(); |
|
1054 program->setUniformValue(program->uniformLocation(name), QColor(color)); |
|
1055 program->release(); |
|
1056 } |
|
1057 } |
|
1058 |
|
1059 void Scene::setFloatParameter(const QString &name, float value) |
|
1060 { |
|
1061 // set the color in all programs |
|
1062 foreach (QGLShaderProgram *program, m_programs) { |
|
1063 program->bind(); |
|
1064 program->setUniformValue(program->uniformLocation(name), value); |
|
1065 program->release(); |
|
1066 } |
|
1067 } |
|
1068 |
|
1069 void Scene::newItem(ItemDialog::ItemType type) |
|
1070 { |
|
1071 QSize size = sceneRect().size().toSize(); |
|
1072 switch (type) { |
|
1073 case ItemDialog::QtBoxItem: |
|
1074 addItem(new QtBox(64, rand() % (size.width() - 64) + 32, rand() % (size.height() - 64) + 32)); |
|
1075 break; |
|
1076 case ItemDialog::CircleItem: |
|
1077 addItem(new CircleItem(64, rand() % (size.width() - 64) + 32, rand() % (size.height() - 64) + 32)); |
|
1078 break; |
|
1079 case ItemDialog::SquareItem: |
|
1080 addItem(new SquareItem(64, rand() % (size.width() - 64) + 32, rand() % (size.height() - 64) + 32)); |
|
1081 break; |
|
1082 default: |
|
1083 break; |
|
1084 } |
|
1085 } |