source: trunk/demos/boxes/scene.cpp@ 350

Last change on this file since 350 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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