mitsuba/src/mtsgui/mainwindow.cpp

2100 lines
71 KiB
C++

/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "rendersettingsdlg.h"
#include "previewsettingsdlg.h"
#include "programsettingsdlg.h"
#include "sceneinfodlg.h"
#include "sceneloader.h"
#include "logwidget.h"
#include "aboutdlg.h"
#include "importdlg.h"
#include "loaddlg.h"
#include "updatedlg.h"
#include "server.h"
#include "save.h"
#include "upgrade.h"
#include <QtNetwork/QtNetwork>
#include <mitsuba/core/sched_remote.h>
#include <mitsuba/core/sstream.h>
#include <mitsuba/core/sshstream.h>
#include <mitsuba/core/plugin.h>
#include <mitsuba/core/fresolver.h>
#include <mitsuba/core/fstream.h>
#if !defined(WIN32)
#include <QtGui/QX11Info>
#include <pwd.h>
#endif
#if defined(__OSX__)
#include "previewsettingsdlg_cocoa.h"
#else
#include "previewsettingsdlg.h"
#endif
extern bool create_symlinks();
static int localWorkerCtr = 0, remoteWorkerCtr = 0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), ui(new Ui::MainWindow),
m_networkReply(NULL), m_activeWindowHack(false) {
Logger *logger = Thread::getThread()->getLogger();
#if defined(__OSX__)
m_previewSettings = NULL;
#endif
QSettings settings;
logger->setLogLevel((ELogLevel) settings.value("verbosity", EDebug).toInt());
m_logWidget = new LogWidget(NULL);
m_consoleAppender = new QConsoleAppender();
logger->addAppender(m_consoleAppender);
connect(m_consoleAppender, SIGNAL(textMessage(ELogLevel, const QString &)),
m_logWidget, SLOT(onTextMessage(ELogLevel, const QString &)), Qt::QueuedConnection);
connect(m_consoleAppender, SIGNAL(criticalError(const QString &)),
m_logWidget, SLOT(onCriticalError(const QString &)), Qt::QueuedConnection);
SLog(EInfo, "Mitsuba version %s, Copyright (c) " MTS_YEAR " Wenzel Jakob",
Version(MTS_VERSION).toStringComplete().c_str());
#if SPECTRUM_SAMPLES == 3
SLog(EInfo, "Configured for RGB-based rendering");
#else
std::ostringstream oss;
oss << std::fixed;
oss.precision(2);
for (int i=0; i<SPECTRUM_SAMPLES; i++) {
std::pair<Float, Float> bin = Spectrum::getBinCoverage(i);
if (i < SPECTRUM_SAMPLES) {
oss << bin.first << "-" << bin.second << " nm";
if (i+1<SPECTRUM_SAMPLES)
oss << ", ";
}
}
SLog(EInfo, "Configured for spectral rendering using "
"the wavelength discretization %s", oss.str().c_str());
#endif
m_currentChild = NULL;
ui->setupUi(this);
isActive();
m_lastTab = NULL;
ui->glView->setScrollBars(ui->hScrollBar, ui->vScrollBar);
ui->hScrollBar->setVisible(false);
ui->vScrollBar->setVisible(false);
ui->actionUpdateCheck->setMenuRole(QAction::ApplicationSpecificRole);
ui->actionFeedback->setMenuRole(QAction::ApplicationSpecificRole);
ui->actionReportBug->setMenuRole(QAction::ApplicationSpecificRole);
ui->actionEnableCommandLine->setMenuRole(QAction::ApplicationSpecificRole);
#if !defined(__OSX__)
ui->actionEnableCommandLine->setVisible(false);
#endif
m_progressWidget = new QWidget(centralWidget());
m_progressLabel = new QLabel(m_progressWidget);
m_progress = new QProgressBar(m_progressWidget);
m_progressWidget->setObjectName("progressWidget");
QHBoxLayout *hlayout = new QHBoxLayout();
hlayout->addWidget(m_progressLabel);
hlayout->addWidget(m_progress);
m_progressWidget->setLayout(hlayout);
#if defined(__OSX__)
m_progressWidget->setStyleSheet("QWidget#progressWidget {background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
"stop:0 palette(dark), stop: 1 palette(mid)); border-top: 1px solid palette(mid); margin: 0px; spacing: 15px; }");
m_progress->setMaximumSize(SHRT_MAX,18);
m_progress->setMinimumSize(10,18);
hlayout->setContentsMargins(10, 8, 10, 3);
m_progress->setAttribute(Qt::WA_MacSmallSize, true);
m_progressLabel->setAttribute(Qt::WA_MacSmallSize, true);
#endif
m_serverWidget = NULL;
m_contextIndex = -1;
for (int i = 0; i < MAX_RECENT_FILES; ++i) {
m_actRecent[i] = new QAction(this);
m_actRecent[i]->setVisible(false);
connect(m_actRecent[i], SIGNAL(triggered()),
this, SLOT(onOpenRecent()));
ui->menuOpen_Recent->addAction(m_actRecent[i]);
}
m_clearRecent = new QAction(tr("Clear Menu"), this);
connect(m_clearRecent, SIGNAL(triggered()),
this, SLOT(onClearRecent()));
ui->menuOpen_Recent->addAction(m_clearRecent);
#if defined(__OSX__)
/* Make this the default menu bar */
ui->menuBar->setParent(NULL);
#endif
updateRecentFileActions();
ui->tabBar->setDocumentMode(true);
ui->tabBar->setElideMode(Qt::ElideRight);
ui->tabBar->setUsesScrollButtons(true);
ui->tabBar->setTabsClosable(true);
ui->tabBar->setMovable(true);
ui->tabBar->setContextMenuPolicy(Qt::CustomContextMenu);
on_tabBar_currentChanged(-1);
connect(ui->glView, SIGNAL(quit()), this, SLOT(on_actionExit_triggered()));
connect(ui->glView, SIGNAL(beginRendering()), this, SLOT(on_actionRender_triggered()));
connect(ui->glView, SIGNAL(stopRendering()), this, SLOT(updateUI()));
connect(ui->glView, SIGNAL(statusMessage(const QString &)), this, SLOT(onStatusMessage(const QString &)),
Qt::QueuedConnection);
connect(ui->glView, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));
/* Load defaults from app settings file */
ui->glView->setInvertMouse(settings.value("invertMouse", false).toBool());
ui->glView->setMouseSensitivity(settings.value("mouseSensitivity", 3).toInt());
ui->glView->setNavigationMode((ENavigationMode) settings.value("navMode",
EStandard).toInt());
m_searchPaths = settings.value("searchPaths", QStringList()).toStringList();
m_blockSize = settings.value("blockSize", 32).toInt();
m_listenPort = settings.value("listenPort", MTS_DEFAULT_PORT).toInt();
m_nodeName = settings.value("nodeName", getFQDN().c_str()).toString();
m_renderQueue = new RenderQueue();
m_renderListener = new QRenderListener();
m_renderQueue->registerListener(m_renderListener);
connect(m_renderListener, SIGNAL(jobFinished(const RenderJob *, bool)),
this, SLOT(onJobFinished(const RenderJob *, bool)), Qt::QueuedConnection);
connect(m_renderListener, SIGNAL(workEnd(const RenderJob *, const ImageBlock *)),
this, SLOT(onWorkEnd(const RenderJob *, const ImageBlock *)), Qt::DirectConnection);
connect(m_renderListener, SIGNAL(workCanceled(const RenderJob *, const Point2i &, const Vector2i &)),
this, SLOT(onWorkCanceled(const RenderJob *, const Point2i &, const Vector2i &)), Qt::DirectConnection);
connect(m_renderListener, SIGNAL(workBegin(const RenderJob *, const RectangularWorkUnit *, int)),
this, SLOT(onWorkBegin(const RenderJob *, const RectangularWorkUnit *, int)), Qt::DirectConnection);
connect(m_renderListener, SIGNAL(refresh()), this, SLOT(onRefresh()), Qt::QueuedConnection);
connect(m_consoleAppender,
SIGNAL(progressMessage(const RenderJob *, const QString &, float, const QString &)),
this, SLOT(onProgressMessage(const RenderJob *, const QString &, float, const QString &)),
Qt::QueuedConnection);
connect(this, SIGNAL(updateView()), ui->glView, SLOT(onUpdateView()));
QPoint windowPos;
if (settings.contains("pos")) {
windowPos = settings.value("pos").toPoint();
} else {
QDesktopWidget *desktop = QApplication::desktop();
QRect geo = desktop->screenGeometry();
windowPos = QPoint(
geo.left() + (geo.width() - width()) / 2,
geo.top() + (geo.height() - height())/2
);
}
#if defined(__OSX__)
ui->toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
QToolButton *previewButton = static_cast<QToolButton *>(ui->toolBar->widgetForAction(ui->actionPreviewSettings));
previewButton->setStyleSheet("margin-left: -5px; margin-right:-5px");
/* Weird Qt/OSX bug -- moving while a window while it is invisible causes
it to appear move up by 65 pixels (this is related to the unified toolbar) */
move(windowPos + QPoint(0, 65));
#else
move(windowPos);
#endif
show();
/* Move again just to be sure ... */
move(windowPos);
updateUI();
adjustSize();
m_networkManager = new QNetworkAccessManager(this);
if (ui->glView->isUsingSoftwareFallback() &&
!ui->glView->getErrorString().isEmpty()) {
QMessageBox::warning(this, tr("Insufficient OpenGL capabilities"),
ui->glView->getErrorString(), QMessageBox::Ok);
}
connect(m_networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(onNetworkFinished(QNetworkReply *)));
m_checkForUpdates = settings.value("checkForUpdates", true).toBool();
if (m_checkForUpdates)
checkForUpdates(false);
}
MainWindow::~MainWindow() {
m_renderQueue->unregisterListener(m_renderListener);
ref<Scheduler> scheduler = Scheduler::getInstance();
if (scheduler->isRunning())
scheduler->pause();
for (int i=0; i<m_connections.size(); ++i) {
ServerConnection &c = m_connections[i];
scheduler->unregisterWorker(c.worker);
}
if (m_networkReply)
m_networkReply->abort();
#if defined(__OSX__)
delete ui->menuBar;
#endif
delete ui;
}
bool MainWindow::initWorkersProcessArgv() {
QSettings settings;
QStringList args = qApp->arguments(), toBeLoaded;
int localWorkerCount = settings.value("localWorkers", getCoreCount()).toInt();
ref<Scheduler> scheduler = Scheduler::getInstance();
for (int i=1; i<args.count(); ++i) {
if (args[i].startsWith("-D")) {
QString value = args[i].mid(2);
if (value.length() == 0 && i+1<args.count())
value = args[++i];
QStringList list = value.split("=");
if (list.length() == 2)
m_parameters[list[0].toStdString()] = list[1].toStdString();
} else if (args[i].startsWith("-a")) {
ref<FileResolver> resolver = Thread::getThread()->getFileResolver();
QString value = args[i].mid(2);
if (value.length() == 0 && i+1<args.count())
value = args[++i];
resolver->appendPath(value.toStdString());
} else if (args[i].startsWith("-p")) {
QString value = args[i].mid(2);
if (value.length() == 0 && i+1<args.count())
value = args[++i];
bool ok = false;
unsigned int numCores = value.toUInt(&ok, 10);
if (ok)
localWorkerCount = numCores;
} else if (args[i] == "-h") {
cout << "Mitsuba version " << Version(MTS_VERSION).toStringComplete()
<< ", Copyright (c) " MTS_YEAR " Wenzel Jakob" << endl;
cout << "Usage: mtsgui [options] <One or more scene XML files>" << endl;
cout << "Options/Arguments:" << endl;
cout << " -h Display this help text" << endl << endl;
cout << " -D key=val Define a constant, which can referenced as \"$key\" in the scene" << endl << endl;
cout << " -a p1;p2;.. Add one or more entries to the resource search path" << endl << endl;
cout << " -p count Override the detected number of processors." << endl << endl;
cout << " For documentation, please refer to http://www.mitsuba-renderer.org/docs.html" << endl;
return false;
} else if (args[i].startsWith("-")) {
cerr << "Unknown option \"" << args[i].toStdString() << "\"" << endl;
} else {
toBeLoaded.append(args[i]);
}
}
m_workerPriority = (Thread::EThreadPriority)
settings.value("workerPriority", (int) Thread::ELowPriority).toInt();
for (int i=0; i<localWorkerCount; ++i)
scheduler->registerWorker(new LocalWorker(i, formatString("wrk%i", localWorkerCtr++), m_workerPriority));
int networkConnections = 0;
QList<QVariant> connectionData = settings.value("connections").toList();
if (connectionData.size() > 0) {
QDialog *dialog = new NonClosableDialog(this);
dialog->setWindowModality(Qt::WindowModal);
QVBoxLayout *layout = new QVBoxLayout(dialog);
QLabel *label = new QLabel(tr("Establishing network connections ..."), dialog);
label->setAlignment(Qt::AlignCenter);
layout->addWidget(label);
QProgressBar *progressBar = new QProgressBar(dialog);
progressBar->setTextVisible(false);
dialog->resize(200, 50);
layout->addWidget(progressBar);
progressBar->setTextVisible(false);
// weird, Qt/Win needs this to get a busy indicator
progressBar->setValue(1);
progressBar->setRange(0, 0);
dialog->show();
for (int i=0; i<connectionData.size(); ++i) {
ServerConnection conn;
conn.fromByteArray(connectionData[i].toByteArray());
for (int j=0; j<10; ++j)
qApp->processEvents();
if (conn.createWorker(this)) {
++networkConnections;
conn.isRegistered = true;
scheduler->registerWorker(conn.worker);
m_connections.append(conn);
}
}
/* Update, this removes connection failures */
connectionData.clear();
for (int i=0; i<m_connections.size(); ++i)
connectionData.append(m_connections[i].toByteArray());
settings.setValue("connections", connectionData);
dialog->hide();
delete dialog;
}
if (networkConnections + localWorkerCount == 0) {
QMessageBox::warning(this, tr("Scheduler warning"),
tr("There must be at least one worker thread -- forcing creation of one."),
QMessageBox::Ok);
scheduler->registerWorker(new LocalWorker(0, formatString("wrk%i", localWorkerCtr++), m_workerPriority));
}
for (int i=0; i<toBeLoaded.size(); ++i)
loadFile(toBeLoaded[i]);
scheduler->start();
raise();
return true;
}
void MainWindow::adjustSize() {
/* Like QWidget::adjustSize, but clamps the size to 4/5ths of the screen */
ensurePolished();
QSize s = sizeHint();
#if defined(Q_WS_X11)
QRect screen = QApplication::desktop()->screenGeometry(x11Info().screen());
#else // all others
QRect screen = QApplication::desktop()->screenGeometry(pos());
#endif
s.setWidth(qMin(s.width(), screen.width()*4/5));
s.setHeight(qMin(s.height(), screen.height()*4/5));
layout()->activate();
if (s.isValid())
resize(s);
}
void MainWindow::checkForUpdates(bool manualUpdateCheck) {
m_manualUpdateCheck = manualUpdateCheck;
m_networkReply = m_networkManager->get(QNetworkRequest(QUrl("http://www.mitsuba-renderer.org/version")));
}
void MainWindow::onNetworkFinished(QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NoError) {
try {
QSettings settings;
Version remote(QString(reply->readAll()).toStdString());
Version ignoredVersion(settings.value("ignoredVersion", "0.0.0").toString().toStdString());
Version local(MTS_VERSION);
if (local < remote) {
if (!m_manualUpdateCheck && remote == ignoredVersion)
return;
UpdateDialog *dialog = new UpdateDialog(this, local, remote);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowModality(Qt::WindowModal);
dialog->show();
} else if (m_manualUpdateCheck) {
QMessageBox::information(this, tr("Installed version is current"),
QString("<p>You're up to date!</p>"
"<p>Mitsuba <b>%1</b> is still the newest version available.</p>")
.arg(local.toString().c_str()), QMessageBox::Ok);
}
} catch (const std::exception &e) {
/* Got something weird and couldn't parse the version string --
very likely due to some Internet cafe login page. Ignore unless
a manual update check was done. */
if (m_manualUpdateCheck)
throw e;
}
} else {
if (m_manualUpdateCheck)
QMessageBox::warning(this, tr("Unable to determine the newest version"),
QString("<p>Unable to determine the newest Mitsuba version.</p><p>"
"Perhaps you are not connected to the Internet?</p>"),
QMessageBox::Ok);
}
}
void MainWindow::on_actionImport_triggered() {
#if defined(MTS_HAS_COLLADA)
ref<FileResolver> resolver = Thread::getThread()->getFileResolver();
ref<FileResolver> newResolver = resolver->clone();
for (int i=(int) m_searchPaths.size()-1; i>=0; --i)
newResolver->prependPath(toFsPath(m_searchPaths[i]));
ImportDialog *dialog = new ImportDialog(this, newResolver);
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, SIGNAL(finished(int)), this, SLOT(onImportDialogClose(int)));
m_currentChild = dialog;
// prevent a tab drawing artifact on Qt/OSX
m_activeWindowHack = true;
dialog->show();
qApp->processEvents();
m_activeWindowHack = false;
#else
QMessageBox::critical(this, tr("Importer disabled"),
tr("The importer is disabled in this build. To use it, you will need "
"to install COLLADA-DOM and recompile Mitsuba -- please see the "
"documentation for more details."),
QMessageBox::Ok);
#endif
}
void MainWindow::onImportDialogClose(int reason) {
m_currentChild = NULL;
}
void MainWindow::on_actionDuplicateTab_triggered() {
int currentIndex = ui->tabBar->currentIndex();
if (m_contextIndex != -1)
currentIndex = m_contextIndex;
SceneContext *currentContext = m_context[currentIndex];
SceneContext *newContext = new SceneContext(currentContext);
if (currentContext->renderJob)
newContext->windowSize -= currentContext->sizeIncrease;
m_contextMutex.lock();
m_context.append(newContext);
m_contextMutex.unlock();
ui->tabBar->addTab(newContext->shortName);
int index = ui->tabBar->count()-1;
if (ui->tabBar->currentIndex() != index)
ui->tabBar->setCurrentIndex(index);
}
void MainWindow::on_actionUpdateCheck_triggered() {
checkForUpdates(true);
}
void MainWindow::on_actionShowKDTree_triggered() {
int currentIndex = ui->tabBar->currentIndex();
if (currentIndex == -1)
return;
SceneContext *currentContext = m_context[currentIndex];
bool checked = ui->actionShowKDTree->isChecked();
currentContext->showKDTree = checked;
if (currentContext->previewMethod != EOpenGL)
ui->glView->setPreviewMethod(EOpenGL);
else
ui->glView->resetPreview();
}
void MainWindow::on_actionFocusSelected_triggered() {
QKeyEvent event(QEvent::KeyPress, Qt::Key_F, Qt::NoModifier);
ui->glView->keyPressEvent(&event);
}
void MainWindow::on_actionFocusAll_triggered() {
QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
ui->glView->keyPressEvent(&event);
}
void MainWindow::on_actionSceneDescription_triggered() {
int currentIndex = ui->tabBar->currentIndex();
if (currentIndex == -1)
return;
SceneContext *context= m_context[currentIndex];
SceneInformationDialog *dialog = new SceneInformationDialog(this,
context->scene);
/* Center the dialog */
QDesktopWidget *desktop = QApplication::desktop();
QRect geo = desktop->screenGeometry(geometry().center());
QPoint windowPos(
geo.left() + (geo.width() - dialog->width()) / 2,
geo.top() + (geo.height() - dialog->height())/2
);
dialog->move(windowPos);
connect(dialog, SIGNAL(finished(int)), this, SLOT(onSceneInformationClose(int)));
m_currentChild = dialog;
// prevent a tab drawing artifact on Qt/OSX
m_activeWindowHack = true;
dialog->show();
qApp->processEvents();
m_activeWindowHack = false;
}
void MainWindow::onSceneInformationClose(int reason) {
m_currentChild = NULL;
}
void MainWindow::changeEvent(QEvent *e) {
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
SceneContext *MainWindow::getContext(const RenderJob *job, bool failIfNotFound) {
m_contextMutex.lock();
for (int i=0; i<m_context.size(); ++i) {
if (m_context[i]->renderJob == job) {
m_contextMutex.unlock();
return m_context[i];
}
}
m_contextMutex.unlock();
if (failIfNotFound)
SLog(EError, "Internal error: could not find render context!");
return NULL;
}
void MainWindow::onProgressMessage(const RenderJob *job, const QString &name,
float progress, const QString &eta) {
SceneContext *context = getContext(job, false);
if (context == NULL)
return;
context->eta = eta;
context->progress = progress;
context->progressName = name + ": ";
updateUI();
}
#if MTSGUI_STATIC_QFILEDIALOG
void MainWindow::on_actionOpen_triggered() {
QSettings settings;
QStringList fileNames = QFileDialog::getOpenFileNames(this, QString(),
settings.value("fileDir").toString(),
tr("All supported formats (*.xml *.exr *.rgbe *.hdr *.pfm *.png *.jpg *.jpeg);;"
"Mitsuba scenes (*.xml);;High dynamic-range images (*.exr *.rgbe *.hdr *.pfm);;"
"Low dynamic-range images (*.png *.jpg *.jpeg)"));
QStringList::ConstIterator it = fileNames.constBegin();
if (it != fileNames.constEnd()) {
QFileInfo info(*it);
settings.setValue("fileDir", info.absolutePath());
}
for ( ; it != fileNames.constEnd(); ++it) {
loadFile(*it);
}
}
void MainWindow::onOpenDialogClose(int reason) { /* unused */ }
#else // MTSGUI_STATIC_QFILEDIALOG
void MainWindow::on_actionOpen_triggered() {
QFileDialog *dialog = new QFileDialog(this, Qt::Sheet);
dialog->setNameFilter(tr("All supported formats (*.xml *.exr *.rgbe *.hdr *.pfm *.png *.jpg *.jpeg);;"
"Mitsuba scenes (*.xml);;High dynamic-range images (*.exr *.rgbe *.hdr *.pfm);;Low "
"dynamic-range images (*.png *.jpg *.jpeg)"));
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setAcceptMode(QFileDialog::AcceptOpen);
dialog->setViewMode(QFileDialog::Detail);
dialog->setFileMode(QFileDialog::ExistingFiles);
dialog->setWindowModality(Qt::WindowModal);
QSettings settings;
dialog->restoreState(settings.value("fileDialogState").toByteArray());
connect(dialog, SIGNAL(finished(int)), this, SLOT(onOpenDialogClose(int)));
m_currentChild = dialog;
// prevent a tab drawing artifact on Qt/OSX
m_activeWindowHack = true;
dialog->show();
qApp->processEvents();
m_activeWindowHack = false;
}
void MainWindow::onOpenDialogClose(int reason) {
QSettings settings;
QFileDialog *dialog = static_cast<QFileDialog *>(sender());
m_currentChild = NULL;
if (reason == QDialog::Accepted) {
QStringList fileNames = dialog->selectedFiles();
settings.setValue("fileDialogState", dialog->saveState());
for (int i=0; i<fileNames.size(); ++i)
loadFile(fileNames[i]);
}
}
#endif // MTSGUI_STATIC_QFILEDIALOG
void MainWindow::on_actionExit_triggered() {
qApp->closeAllWindows();
}
void MainWindow::onOpenRecent() {
QAction *action = qobject_cast<QAction *>(sender());
if (action)
loadFile(action->data().toString());
}
void MainWindow::onClearRecent() {
QSettings settings;
for (int j = 0; j < MAX_RECENT_FILES; ++j)
m_actRecent[j]->setVisible(false);
settings.setValue("recentFileList", QStringList());
}
SceneContext *MainWindow::loadScene(const QString &qFileName) {
ref<FileResolver> resolver = Thread::getThread()->getFileResolver();
fs::path filename = resolver->resolve(toFsPath(qFileName));
fs::path filePath = fs::absolute(filename).parent_path();
ref<FileResolver> newResolver = resolver->clone();
for (int i=(int) m_searchPaths.size()-1; i>=0; --i)
newResolver->prependPath(toFsPath(m_searchPaths[i]));
newResolver->prependPath(filePath);
LoadDialog *loaddlg = new LoadDialog(this);
SceneContext *result = NULL;
ref<SceneLoader> loadingThread;
loaddlg->setAttribute(Qt::WA_DeleteOnClose);
loaddlg->setWindowModality(Qt::ApplicationModal);
loaddlg->setWindowTitle("Loading ...");
loaddlg->show();
retry:
loadingThread = new SceneLoader(newResolver, filename, m_parameters);
loadingThread->start();
while (loadingThread->isRunning()) {
QCoreApplication::processEvents();
loadingThread->wait(20);
}
loadingThread->join();
result = loadingThread->getResult();
if (result == NULL) {
if (loadingThread->isVersionError()) {
Version version = loadingThread->getVersion();
int ret;
if (version.isValid()) {
ret = QMessageBox::question(this, tr("Version mismatch -- update scene file?"),
QString("The requested scene file is from an older version of Mitsuba "
"(%1). To work with version %2, it will need to be updated. If you "
"continue, Mitsuba will perform a fully automated upgrade (note that a "
"backup copy will be made).\n\nProceed?")
.arg(version.toString().c_str())
.arg(MTS_VERSION), QMessageBox::Yes | QMessageBox::Cancel);
} else {
QMessageBox box(QMessageBox::Question, tr("Version mismatch -- update scene file?"),
(loadingThread->getError() + "\n\nAlternatively, if this file is from version 0.2.1 "
"(the last release without explicit version numbers), you can perform a fully "
"automated upgrade from this version. A backup copy will be made in this case.").c_str());
QPushButton *version021Button = box.addButton(tr("Assume version 0.2.1?"), QMessageBox::YesRole);
box.addButton(tr("Cancel"), QMessageBox::RejectRole);
ret = box.exec();
if (box.clickedButton() == version021Button) {
version = Version(0, 2, 1);
ret = QMessageBox::Yes;
}
}
if (ret == QMessageBox::Yes) {
loaddlg->expand();
UpgradeManager upgradeMgr(newResolver);
try {
upgradeMgr.performUpgrade(fromFsPath(filename), version);
goto retry;
} catch (const std::exception &ex) {
QMessageBox::critical(this, tr("Unable to update %1").arg(qFileName),
QString::fromUtf8(ex.what()), QMessageBox::Ok);
}
} else {
QMessageBox::critical(this, tr("Unable to load %1").arg(qFileName),
QString("No upgrade was performed -- giving up."), QMessageBox::Ok);
}
} else {
QMessageBox::critical(this, tr("Unable to load %1").arg(qFileName),
QString::fromUtf8(loadingThread->getError().c_str()),
QMessageBox::Ok);
}
}
loaddlg->close();
return result;
}
void MainWindow::loadFile(QString filename) {
QFileInfo fInfo(filename);
fInfo.makeAbsolute();
SceneContext *context = loadScene(filename);
if (context == NULL)
return;
m_contextMutex.lock();
m_context.append(context);
m_contextMutex.unlock();
addRecentFile(context->fileName);
ui->tabBar->addTab(context->shortName);
/* Select the newly loaded scene */
int index = ui->tabBar->count()-1;
if (ui->tabBar->currentIndex() != index)
ui->tabBar->setCurrentIndex(index);
updateUI();
adjustSize();
}
void MainWindow::addRecentFile(QString fileName) {
/* Update recent files list */
QSettings settings;
QStringList files = settings.value("recentFileList").toStringList();
files.removeAll(fileName);
files.prepend(fileName);
while (files.size() > MAX_RECENT_FILES)
files.removeLast();
settings.setValue("recentFileList", files);
foreach (QWidget *widget, QApplication::topLevelWidgets()) {
MainWindow *mainWin = qobject_cast<MainWindow *>(widget);
if (mainWin)
mainWin->updateRecentFileActions();
}
}
void MainWindow::updateRecentFileActions() {
QSettings settings;
QStringList files = settings.value("recentFileList").toStringList();
int numRecentFiles = qMin(files.size(), MAX_RECENT_FILES);
for (int i = 0; i < numRecentFiles; ++i) {
m_actRecent[i]->setText(QFileInfo(files[i]).fileName());
m_actRecent[i]->setData(files[i]);
m_actRecent[i]->setVisible(true);
}
for (int j = numRecentFiles; j < MAX_RECENT_FILES; ++j)
m_actRecent[j]->setVisible(false);
}
void MainWindow::onSelectionChanged() {
int index = ui->tabBar->currentIndex();
SceneContext *context = index != -1 ? m_context[index] : NULL;
bool isInactiveScene = (context && context->scene) ? context->renderJob == NULL : false;
ui->actionFocusSelected->setEnabled(isInactiveScene && ui->glView->hasSelection());
}
void MainWindow::updateUI() {
int index = ui->tabBar->currentIndex();
bool hasTab = (index != -1);
SceneContext *context = hasTab ? m_context[index] : NULL;
bool isRendering = hasTab ? context->renderJob != NULL : false;
bool isShowingRendering = hasTab ? context->mode == ERender : false;
bool hasScene = hasTab && context->scene != NULL;
bool isInactive = hasTab ? context->renderJob == NULL : false;
bool isInactiveScene = (hasTab && hasScene) ? context->renderJob == NULL : false;
bool slowFallback = ui->glView->isUsingSoftwareFallback() &&
!ui->glView->hasFastSoftwareFallback();
ui->actionStop->setEnabled(isShowingRendering);
if (isShowingRendering && !isRendering)
ui->actionStop->setToolTip(tr("Return to the realtime preview"));
else
ui->actionStop->setToolTip(tr("Stop rendering"));
ui->actionRender->setEnabled(isInactiveScene);
ui->actionRefresh->setEnabled(isInactive);
ui->actionRenderSettings->setEnabled(isInactiveScene);
ui->actionSave->setEnabled(hasScene);
ui->actionSaveAs->setEnabled(hasScene);
ui->actionExportImage->setEnabled(hasTab);
ui->actionClose->setEnabled(hasTab);
ui->actionDuplicateTab->setEnabled(hasTab);
ui->actionAdjustSize->setEnabled(hasTab);
ui->actionShowKDTree->setEnabled(hasScene);
ui->actionShowKDTree->setChecked(hasScene && context->showKDTree);
ui->actionSceneDescription->setEnabled(hasScene);
ui->actionResetView->setEnabled(hasScene);
ui->actionMagnify->setEnabled(hasScene);
ui->actionCrop->setEnabled(hasScene);
ui->menuCamera->setEnabled(hasScene);
ui->actionFocusAll->setEnabled(isInactiveScene);
ui->actionFocusSelected->setEnabled(isInactiveScene && ui->glView->hasSelection());
#if !defined(__OSX__)
ui->actionPreviewSettings->setEnabled(!slowFallback && hasTab);
#else
bool isVisible = m_previewSettings != NULL && m_previewSettings->isVisible();
ui->actionPreviewSettings->setEnabled(hasTab && !isVisible && !slowFallback);
#endif
if (isRendering) {
if (!m_progress->isVisible()) {
static_cast<QGridLayout *>(centralWidget()->layout())->
addWidget(m_progressWidget, 3, 0, 1, 3);
m_progressWidget->show();
}
m_progress->setValue(context->progress);
QString etaString;
if (context->eta != "")
etaString = QString(" (ETA: ") + context->eta + QString(")");
if (context->progressName != "")
m_progressLabel->setText(QString("<b>%1</b> %2%%3")
.arg(context->progressName)
.arg(context->progress, 0, 'f', 1)
.arg(etaString));
else
m_progressLabel->setText("");
} else {
if (m_progress->isVisible()) {
QLayout *centralLayout = centralWidget()->layout();
m_progressWidget->hide();
centralLayout->removeWidget(m_progressWidget);
}
}
centralWidget()->updateGeometry();
layout()->activate();
}
void MainWindow::resizeEvent(QResizeEvent *event) {
QMainWindow::resizeEvent(event);
ui->glView->updateScrollBars();
}
void MainWindow::on_tabBar_customContextMenuRequested(const QPoint &pt) {
if (pt.isNull())
return;
int tabIndex = ui->tabBar->tabAt(pt);
if (tabIndex == -1)
return;
m_contextIndex = tabIndex;
QMenu menu(this);
menu.addAction(ui->actionDuplicateTab);
if (tabIndex == ui->tabBar->currentIndex())
menu.addAction(ui->actionAdjustSize);
menu.addAction(ui->actionClose);
menu.exec(ui->tabBar->mapToGlobal(pt));
m_contextIndex = -1;
}
void MainWindow::on_tabBar_tabMoved(int from, int to) {
m_contextMutex.lock();
m_context.move(from, to);
m_contextMutex.unlock();
}
void MainWindow::on_tabBar_currentChanged(int index) {
if (m_lastTab != NULL) {
m_lastTab->windowSize = size();
m_lastTab->wasRendering = m_lastTab->renderJob != NULL;
}
ui->glView->ignoreResizeEvents(true);
if (ui->tabBar->currentIndex() != -1)
ui->tabBar->show();
else
ui->tabBar->hide();
ui->glView->ignoreResizeEvents(false);
if (index != -1) {
if (m_context[index] != m_lastTab) {
ui->glView->setScene(m_context[index]);
#if defined(__OSX__)
if (m_previewSettings)
m_previewSettings->setContext(m_context[index]);
#endif
}
} else {
ui->glView->setScene(NULL);
#if defined(__OSX__)
if (m_previewSettings && m_previewSettings->isVisible())
m_previewSettings->hide();
#endif
}
m_statusMessage = "";
updateStatus();
updateUI();
ui->menuCamera->clear();
if (index != -1) {
SceneContext *context = m_context[index];
QSize windowSize = context->windowSize;
if (windowSize.isValid()) {
if (context->wasRendering && !context->renderJob)
windowSize -= context->sizeIncrease;
#if defined(__LINUX__)
int error = (sizeHint()-windowSize).height();
if (error > 0 && error <= 5)
resize(windowSize + QSize(0, error));
else
resize(windowSize);
#else
resize(windowSize);
#endif
} else {
adjustSize();
}
m_lastTab = context;
const Scene *scene = context->scene;
if (scene) {
const ref_vector<Sensor> &sensors = scene->getSensors();
for (size_t i = 0; i < sensors.size(); ++i) {
const Sensor *sensor = sensors[i].get();
const Properties &props = sensor->getProperties();
QAction *act = new QAction(ui->menuCamera);
act->setText(formatString("\"%s\" (%s)",
props.getID().c_str(), props.getPluginName().c_str()).c_str());
act->setData(qVariantFromValue((void *) sensor));
ui->menuCamera->addAction(act);
connect(act, SIGNAL(triggered()), this, SLOT(onActivateCamera()));
}
}
} else {
adjustSize();
m_lastTab = NULL;
ui->menuCamera->clear();
}
ui->glView->setFocus();
}
void MainWindow::on_actionClose_triggered() {
int index = ui->tabBar->currentIndex();
if (m_contextIndex != -1)
index = m_contextIndex;
on_tabBar_tabCloseRequested(index);
}
void MainWindow::on_actionAdjustSize_triggered() {
adjustSize();
}
bool MainWindow::on_tabBar_tabCloseRequested(int index) {
SceneContext *context = m_context[index];
if (context->renderJob != NULL) {
QMessageBox box(QMessageBox::Question, tr("Really close?"),
tr("Rendering of scene \"%1\" is unfinished - all progress "
"will be lost. Are you sure you want to continue?").arg(context->shortName),
QMessageBox::Yes | QMessageBox::No, this,
Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::Sheet);
box.setWindowModality(Qt::WindowModal);
if (box.exec() == QMessageBox::No)
return false;
}
m_contextMutex.lock();
m_context.removeAt(index);
m_contextMutex.unlock();
ui->tabBar->removeTab(index);
if (context->renderJob != NULL) {
context->renderJob->cancel();
context->renderJob->join();
}
ui->glView->makeCurrent();
delete context;
return true;
}
void MainWindow::closeEvent(QCloseEvent *event) {
QSettings settings;
settings.setValue("pos", pos());
for (int i=ui->tabBar->count()-1; i>=0; --i) {
if (!on_tabBar_tabCloseRequested(i)) {
event->ignore();
return;
}
}
ui->glView->shutdown();
QMainWindow::closeEvent(event);
m_logWidget->hide();
Logger *logger = Thread::getThread()->getLogger();
logger->removeAppender(m_consoleAppender);
event->accept();
}
void MainWindow::on_actionCrop_triggered() {
ui->glView->startCrop(GLWidget::ECrop);
}
void MainWindow::on_actionMagnify_triggered() {
ui->glView->startCrop(GLWidget::ECropAndMagnify);
}
void MainWindow::on_glView_crop(int type, int x, int y, int width, int height) {
int currentIndex = ui->tabBar->currentIndex();
if (currentIndex < 0)
return;
SceneContext *context = m_context[currentIndex];
Scene *scene = context->scene;
ref<Sensor> oldSensor = scene->getSensor();
ref<Film> oldFilm = scene->getFilm();
Properties sensorProps = oldSensor->getProperties();
Properties filmProps = oldSensor->getFilm()->getProperties();
Vector2i oldSize = oldFilm->getSize();
Point2i oldCropOffset = oldFilm->getCropOffset();
Vector2i oldCropSize = oldFilm->getCropSize();
if (type == GLWidget::ECrop) {
filmProps.setInteger("cropOffsetX", x + oldCropOffset.x, false);
filmProps.setInteger("cropOffsetY", y + oldCropOffset.y, false);
filmProps.setInteger("cropWidth", width, false);
filmProps.setInteger("cropHeight", height, false);
} else if (type == GLWidget::ECropAndMagnify) {
int maxOldSize = std::max(oldCropSize.x, oldCropSize.y);
int maxNewSize = std::max(width, height);
Float magnification = maxOldSize / (Float) maxNewSize;
width = floorToInt(width*magnification);
height = floorToInt(height*magnification);
filmProps.setInteger("cropOffsetX", floorToInt(magnification
* (x + oldCropOffset.x)), false);
filmProps.setInteger("cropOffsetY", floorToInt(magnification
* (y + oldCropOffset.y)), false);
filmProps.setInteger("cropWidth", width);
filmProps.setInteger("cropHeight", height);
filmProps.setInteger("width", ceilToInt(oldSize.x*magnification), false);
filmProps.setInteger("height", ceilToInt(oldSize.y*magnification), false);
} else {
width = context->originalSize.x;
height = context->originalSize.y;
filmProps.removeProperty("cropOffsetX");
filmProps.removeProperty("cropOffsetY");
filmProps.removeProperty("cropWidth");
filmProps.removeProperty("cropHeight");
filmProps.setInteger("width", width, false);
filmProps.setInteger("height", height, false);
}
if (oldCropOffset == Point2i(0) &&
Vector2i(width, height) == oldCropSize)
return;
on_tabBar_currentChanged(-1);
ref<PluginManager> pluginMgr = PluginManager::getInstance();
ref<Sensor> newSensor = static_cast<Sensor *>
(pluginMgr->createObject(MTS_CLASS(Sensor), sensorProps));
ref<Film> newFilm = static_cast<Film *> (
pluginMgr->createObject(MTS_CLASS(Film), filmProps));
newFilm->addChild(oldFilm->getReconstructionFilter());
newFilm->configure();
newSensor->addChild(oldSensor->getSampler());
newSensor->addChild(newFilm);
newSensor->setMedium(oldSensor->getMedium());
newSensor->configure();
scene->removeSensor(oldSensor);
scene->addSensor(newSensor);
scene->setSensor(newSensor);
context->framebuffer = new Bitmap(Bitmap::ERGBA,
Bitmap::EFloat32, Vector2i(width, height));
context->framebuffer->clear();
context->scrollOffset = Vector2i(0, 0);
context->mode = EPreview;
context->windowSize = QSize();
if (context->previewBuffer.buffer) {
context->previewBuffer.buffer->decRef();
context->previewBuffer.buffer = NULL;
context->previewBuffer.vplSampleOffset = 0;
}
qApp->processEvents();
on_tabBar_currentChanged(currentIndex);
adjustSize();
}
void MainWindow::on_actionResetView_triggered() {
on_glView_crop(GLWidget::ENone);
}
bool MainWindow::isActive() {
if (isActiveWindow() || m_activeWindowHack)
return true;
else if (m_currentChild != NULL && m_currentChild->isActiveWindow())
return true;
#if defined(__OSX__)
if (m_previewSettings != NULL && m_previewSettings->isActiveWindow())
return true;
#endif
return false;
}
void MainWindow::drawHLine(SceneContext *ctx, int x1, int y, int x2, const float *color) {
y = std::max(0, std::min(y, ctx->framebuffer->getHeight()-1));
x1 = std::max(0, x1); x2 = std::min(x2, ctx->framebuffer->getWidth()-1);
float *framebuffer = ctx->framebuffer->getFloat32Data();
int fbOffset = (x1 + y*ctx->framebuffer->getWidth())*4;
for (int x=x1; x<=x2; x++) {
framebuffer[fbOffset] = color[0];
framebuffer[fbOffset+1] = color[1];
framebuffer[fbOffset+2] = color[2];
fbOffset+=4;
}
}
void MainWindow::drawVLine(SceneContext *ctx, int x, int y1, int y2, const float *color) {
x = std::max(0, std::min(x, ctx->framebuffer->getWidth()-1));
y1 = std::max(0, y1); y2 = std::min(y2, ctx->framebuffer->getHeight()-1);
float *framebuffer = ctx->framebuffer->getFloat32Data();
int width = ctx->framebuffer->getWidth(), fbOffset = (x + y1*width)*4;
for (int y=y1; y<=y2; y++) {
framebuffer[fbOffset] = color[0];
framebuffer[fbOffset+1] = color[1];
framebuffer[fbOffset+2] = color[2];
fbOffset += width * 4;
}
}
void MainWindow::on_actionRenderSettings_triggered() {
int currentIndex = ui->tabBar->currentIndex();
SceneContext *context = m_context[currentIndex];
RenderSettingsDialog *dialog = new RenderSettingsDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowModality(Qt::WindowModal);
dialog->load(context);
connect(dialog, SIGNAL(finished(int)), this, SLOT(onRenderSettingsClose(int)));
m_currentChild = dialog;
// prevent a tab drawing artifact on Qt/OSX
m_activeWindowHack = true;
dialog->show();
qApp->processEvents();
m_activeWindowHack = false;
}
void MainWindow::onRenderSettingsClose(int reason) {
RenderSettingsDialog *dialog = static_cast<RenderSettingsDialog *>(sender());
int currentIndex = ui->tabBar->currentIndex();
SceneContext *context = m_context[currentIndex];
m_currentChild = NULL;
if (reason == QDialog::Accepted) {
Scene *scene = context->scene;
scene->incRef();
dialog->apply(context);
ui->glView->setPathLength(context->detectPathLength());
if (dialog->resolutionHasChanged()) {
ui->glView->refreshScene();
updateUI();
adjustSize();
}
scene->decRef();
#if defined(__OSX__)
if (m_previewSettings)
m_previewSettings->setContext(context);
#endif
}
}
void MainWindow::on_actionPreviewSettings_triggered() {
#if !defined(__OSX__)
SceneContext *context = m_context[ui->tabBar->currentIndex()];
PreviewSettingsDialog d(this, context, ui->glView->getRendererCapabilities());
connect(&d, SIGNAL(pathLengthChanged(int)), ui->glView, SLOT(setPathLength(int)));
connect(&d, SIGNAL(clampingChanged(Float)), ui->glView, SLOT(setClamping(Float)));
connect(&d, SIGNAL(shadowMapResolutionChanged(int)), ui->glView, SLOT(setShadowMapResolution(int)));
connect(&d, SIGNAL(gammaChanged(bool, Float)), ui->glView, SLOT(setGamma(bool, Float)));
connect(&d, SIGNAL(exposureChanged(Float)), ui->glView, SLOT(setExposure(Float)));
connect(&d, SIGNAL(reinhardKeyChanged(Float)), ui->glView, SLOT(setReinhardKey(Float)));
connect(&d, SIGNAL(reinhardBurnChanged(Float)), ui->glView, SLOT(setReinhardBurn(Float)));
connect(&d, SIGNAL(previewMethodChanged(EPreviewMethod)), ui->glView, SLOT(setPreviewMethod(EPreviewMethod)));
connect(&d, SIGNAL(toneMappingMethodChanged(EToneMappingMethod)), ui->glView, SLOT(setToneMappingMethod(EToneMappingMethod)));
connect(&d, SIGNAL(diffuseReceiversChanged(bool)), ui->glView, SLOT(setDiffuseReceivers(bool)));
connect(&d, SIGNAL(diffuseSourcesChanged(bool)), ui->glView, SLOT(setDiffuseSources(bool)));
d.setPreviewEnabled(!ui->glView->isUsingSoftwareFallback());
d.setMaximumSize(d.minimumSize());
d.exec();
QSettings settings;
settings.setValue("preview_sRGB", context->srgb);
settings.setValue("preview_gamma", context->gamma);
settings.setValue("preview_exposure", context->exposure);
settings.setValue("preview_shadowMapResolution", context->shadowMapResolution);
settings.setValue("preview_reinhardKey", context->reinhardKey);
settings.setValue("preview_reinhardBurn", context->reinhardBurn);
settings.setValue("preview_clamping", context->clamping);
settings.setValue("preview_method", context->previewMethod);
settings.setValue("preview_toneMappingMethod", context->toneMappingMethod);
settings.setValue("preview_diffuseReceivers", context->diffuseReceivers);
settings.setValue("preview_diffuseSources", context->diffuseSources);
#else
if (!m_previewSettings) {
m_previewSettings = new PreviewSettingsDlg(this);
connect(m_previewSettings, SIGNAL(pathLengthChanged(int)), ui->glView, SLOT(setPathLength(int)));
connect(m_previewSettings, SIGNAL(clampingChanged(Float)), ui->glView, SLOT(setClamping(Float)));
connect(m_previewSettings, SIGNAL(shadowMapResolutionChanged(int)), ui->glView, SLOT(setShadowMapResolution(int)));
connect(m_previewSettings, SIGNAL(gammaChanged(bool, Float)), ui->glView, SLOT(setGamma(bool, Float)));
connect(m_previewSettings, SIGNAL(exposureChanged(Float)), ui->glView, SLOT(setExposure(Float)));
connect(m_previewSettings, SIGNAL(reinhardKeyChanged(Float)), ui->glView, SLOT(setReinhardKey(Float)));
connect(m_previewSettings, SIGNAL(reinhardBurnChanged(Float)), ui->glView, SLOT(setReinhardBurn(Float)));
connect(m_previewSettings, SIGNAL(previewMethodChanged(EPreviewMethod)), ui->glView, SLOT(setPreviewMethod(EPreviewMethod)));
connect(m_previewSettings, SIGNAL(toneMappingMethodChanged(EToneMappingMethod)), ui->glView, SLOT(setToneMappingMethod(EToneMappingMethod)));
connect(m_previewSettings, SIGNAL(close()), this, SLOT(onPreviewSettingsClose()));
connect(m_previewSettings, SIGNAL(diffuseReceiversChanged(bool)), ui->glView, SLOT(setDiffuseReceivers(bool)));
connect(m_previewSettings, SIGNAL(diffuseSourcesChanged(bool)), ui->glView, SLOT(setDiffuseSources(bool)));
m_previewSettings->setPreviewEnabled(!ui->glView->isUsingSoftwareFallback());
}
SceneContext *ctx = NULL;
if (ui->tabBar->currentIndex() != -1)
ctx = m_context[ui->tabBar->currentIndex()];
m_previewSettings->setContext(ctx);
m_previewSettings->show();
ui->actionPreviewSettings->setEnabled(false);
#endif
}
void MainWindow::onPreviewSettingsClose() {
int index = ui->tabBar->currentIndex();
bool hasTab = index != -1;
ui->actionPreviewSettings->setEnabled(hasTab);
if (hasTab) {
SceneContext *context = m_context[index];
QSettings settings;
settings.setValue("preview_sRGB", context->srgb);
settings.setValue("preview_gamma", context->gamma);
settings.setValue("preview_exposure", context->exposure);
settings.setValue("preview_shadowMapResolution", context->shadowMapResolution);
settings.setValue("preview_reinhardKey", context->reinhardKey);
settings.setValue("preview_reinhardBurn", context->reinhardBurn);
settings.setValue("preview_clamping", context->clamping);
settings.setValue("preview_method", context->previewMethod);
settings.setValue("preview_toneMappingMethod", context->toneMappingMethod);
settings.setValue("preview_diffuseSources", context->diffuseSources);
settings.setValue("preview_diffuseReceivers", context->diffuseReceivers);
}
}
void MainWindow::on_actionSettings_triggered() {
Logger *logger = Thread::getThread()->getLogger();
ref<Scheduler> sched = Scheduler::getInstance();
std::vector<Worker *> localWorkers;
if (m_renderQueue->getJobCount() != 0) {
QMessageBox::warning(this, tr("Rendering in progress"),
tr("The program settings cannot be changed while a rendering is in progress."),
QMessageBox::Ok);
return;
}
size_t workerCount = sched->getWorkerCount();
for (size_t i=0; i<workerCount; ++i) {
Worker *worker = sched->getWorker((int) i);
if (worker->getClass()->derivesFrom(MTS_CLASS(LocalWorker)))
localWorkers.push_back(worker);
}
ProgramSettingsDialog d(this);
d.setWindowModality(Qt::ApplicationModal);
d.setLogLevel(logger->getLogLevel());
d.setWorkerPriority(m_workerPriority);
d.setInvertMouse(ui->glView->getInvertMouse());
d.setNavigationMode(ui->glView->getNavigationMode());
d.setMouseSensitivity(ui->glView->getMouseSensitivity());
d.setBlockSize(m_blockSize);
d.setSearchPaths(m_searchPaths);
d.setLocalWorkerCount(localWorkers.size());
d.setConnections(m_connections);
d.setCheckForUpdates(m_checkForUpdates);
d.setListenPort(m_listenPort);
d.setNodeName(m_nodeName);
if (d.exec()) {
QList<ServerConnection> &connections = d.getConnections();
QList<QVariant> connectionData;
for (int i=0; i<connections.size(); ++i)
connectionData.append(connections[i].toByteArray());
QSettings settings;
settings.setValue("verbosity", d.getLogLevel());
settings.setValue("invertMouse", d.getInvertMouse());
settings.setValue("blockSize", d.getBlockSize());
settings.setValue("searchPaths", d.getSearchPaths());
settings.setValue("localWorkers", d.getLocalWorkerCount());
settings.setValue("connections", connectionData);
settings.setValue("checkForUpdates", d.getCheckForUpdates());
settings.setValue("mouseSensitivity", d.getMouseSensitivity());
settings.setValue("listenPort", d.getListenPort());
settings.setValue("nodeName", d.getNodeName());
settings.setValue("navMode", (int) d.getNavigationMode());
settings.setValue("workerPriority", d.getWorkerPriority());
logger->setLogLevel(d.getLogLevel());
ui->glView->setInvertMouse(d.getInvertMouse());
ui->glView->setMouseSensitivity(d.getMouseSensitivity());
ui->glView->setNavigationMode(d.getNavigationMode());
m_blockSize = d.getBlockSize();
m_searchPaths = d.getSearchPaths();
m_checkForUpdates = d.getCheckForUpdates();
m_listenPort = d.getListenPort();
m_nodeName = d.getNodeName();
m_workerPriority = d.getWorkerPriority();
bool localWorkersChanged = (int) localWorkers.size() != d.getLocalWorkerCount();
if (localWorkersChanged || m_connections != d.getConnections()) {
ref<Scheduler> sched = Scheduler::getInstance();
sched->pause();
while (d.getLocalWorkerCount() > (int) localWorkers.size()) {
LocalWorker *worker = new LocalWorker(localWorkerCtr, formatString("wrk%i", localWorkerCtr), m_workerPriority);
localWorkerCtr++;
sched->registerWorker(worker);
localWorkers.push_back(worker);
}
while (d.getLocalWorkerCount() < (int) localWorkers.size()) {
Worker *worker = localWorkers.back();
sched->unregisterWorker(worker);
localWorkers.pop_back();
}
QList<ServerConnection> removeList,
&newConnections = d.getConnections();
for (int i=0; i<m_connections.size(); ++i) {
ServerConnection &c = m_connections[i];
if (!newConnections.contains(c))
removeList.append(c);
}
for (int i=0; i<newConnections.size(); ++i) {
ServerConnection &c = newConnections[i];
if (!m_connections.contains(c)) {
sched->registerWorker(c.worker);
c.isRegistered = true;
c.worker->decRef();
m_connections.append(c);
}
}
for (int i=0; i<removeList.size(); ++i) {
ServerConnection &c = removeList[i];
sched->unregisterWorker(c.worker);
m_connections.removeAll(c);
}
sched->start();
}
}
}
void MainWindow::on_actionStop_triggered() {
SceneContext *context = m_context[ui->tabBar->currentIndex()];
m_contextMutex.lock();
context->workUnits.clear();
if (context->renderJob) {
context->cancelled = true;
ref<RenderJob> renderJob = context->renderJob;
m_contextMutex.unlock();
renderJob->cancel();
} else if (context->mode == ERender) {
context->mode = EPreview;
m_contextMutex.unlock();
}
ui->glView->resumePreview();
updateUI();
}
void MainWindow::on_actionShowLog_triggered() {
m_logWidget->show();
}
void MainWindow::on_actionRender_triggered() {
int index = ui->tabBar->currentIndex();
if (index == -1)
return;
SceneContext *context = m_context[index];
Scene *scene = context->scene;
if (context->renderJob != NULL || scene == NULL)
return;
scene->setBlockSize(m_blockSize);
context->renderJob = new RenderJob("rend", scene, m_renderQueue,
context->sceneResID, -1, -1, false, true);
context->cancelMode = ERender;
if (context->mode != ERender)
ui->glView->downloadFramebuffer();
context->cancelled = false;
context->progress = 0;
context->eta = "";
context->progressName = "";
context->mode = ERender;
m_statusMessage = "";
updateUI();
context->sizeIncrease = QSize(0, m_progressWidget->sizeHint().height());
#if defined(__LINUX__)
/* Workaround: on Linux, a few pixels get lost somehow, which
otherwise causes an unnecessary scrollbar to appear */
int error = (sizeHint()-size()).height() - context->sizeIncrease.height();
if (error > 0 && error <= 5)
resize(size() + context->sizeIncrease + QSize(0, error));
else
resize(size() + context->sizeIncrease);
#else
resize(size() + context->sizeIncrease);
#endif
updateStatus();
context->renderJob->start();
}
void MainWindow::on_actionRefresh_triggered() {
int index = ui->tabBar->currentIndex();
if (index < 0)
return;
SceneContext *context = m_context[index];
on_tabBar_currentChanged(-1);
SceneContext *newContext = loadScene(context->fileName);
if (newContext == NULL)
return;
delete context;
m_contextMutex.lock();
m_context[index] = newContext;
m_contextMutex.unlock();
qApp->processEvents();
on_tabBar_currentChanged(index);
}
inline float toSRGB(float value) {
if (value < 0.0031308f)
return 12.92f * value;
return 1.055f * std::pow(value, 0.41666f) - 0.055f;
}
#if MTSGUI_STATIC_QFILEDIALOG
void MainWindow::on_actionExportImage_triggered() {
QSettings settings;
const QString fileName = QFileDialog::getSaveFileName(this,
tr("Export image ..."), settings.value("exportFileDir").toString(),
tr("All supported formats (*.exr *.hdr *.rgbe *.pfm *.png *.jpg *.jpeg);;"
"High dynamic range OpenEXR image (*.exr);;"
"High dynamic range Radiance RGBE image (*.rgbe *.hdr);;"
"High dynamic range Portable Float Map image (*.pfm);;"
"Tonemapped low dynamic range image (*.png *.jpg *.jpeg)"));
if (!fileName.isEmpty()) {
QSettings settings;
settings.setValue("exportFileDir", QFileInfo(fileName).absolutePath());
exportImage(fileName);
}
}
void MainWindow::onExportDialogClose(int reason) { /* unused */ }
#else // MTSGUI_STATIC_QFILEDIALOG
void MainWindow::on_actionExportImage_triggered() {
QFileDialog *dialog = new QFileDialog(this, tr("Export image ..."),
"", tr("All supported formats (*.exr *.hdr *.rgbe *.pfm *.png *.jpg *.jpeg);;"
"High dynamic range OpenEXR image (*.exr);;"
"High dynamic range Radiance RGBE image (*.rgbe *.hdr);;"
"High dynamic range Portable Float Map image (*.pfm);;"
"Tonemapped low dynamic range image (*.png *.jpg *.jpeg)"));
QSettings settings;
dialog->setViewMode(QFileDialog::Detail);
dialog->setAcceptMode(QFileDialog::AcceptSave);
#if defined(__OSX__)
dialog->setOption(QFileDialog::DontUseNativeDialog, true);
#endif
dialog->restoreState(settings.value("fileDialogState").toByteArray());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowModality(Qt::WindowModal);
connect(dialog, SIGNAL(finished(int)), this, SLOT(onExportDialogClose(int)));
m_currentChild = dialog;
// prevent a tab drawing artifact on Qt/OSX
m_activeWindowHack = true;
dialog->show();
qApp->processEvents();
m_activeWindowHack = false;
}
void MainWindow::onExportDialogClose(int reason) {
QSettings settings;
QFileDialog *dialog = static_cast<QFileDialog *>(sender());
m_currentChild = NULL;
if (reason == QDialog::Accepted) {
QString fileName = dialog->selectedFiles().value(0);
settings.setValue("fileDialogState", dialog->saveState());
exportImage(fileName);
}
}
#endif // MTSGUI_STATIC_QFILEDIALOG
void MainWindow::exportImage(const QString &fileName) {
if (!fileName.isEmpty()) {
Bitmap::EFileFormat format;
bool isHDR = true;
if (fileName.endsWith(".exr")) {
format = Bitmap::EOpenEXR;
} else if (fileName.endsWith(".png")) {
format = Bitmap::EPNG;
isHDR = false;
} else if (fileName.endsWith(".hdr") || fileName.endsWith(".rgbe")) {
format = Bitmap::ERGBE;
} else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
format = Bitmap::EJPEG;
isHDR = false;
} else if (fileName.endsWith(".pfm")) {
format = Bitmap::EPFM;
} else {
SLog(EError, "Unknown file type -- the filename must end in either"
" .exr, .rgbe, .hdr, .pfm, .png, .jpg, or .jpeg");
return;
}
ref<FileStream> fs = new FileStream(toFsPath(fileName),
FileStream::ETruncReadWrite);
const int currentIndex = ui->tabBar->currentIndex();
const SceneContext *ctx = m_context[currentIndex];
if (ctx->mode == EPreview)
ui->glView->downloadFramebuffer();
ref<Bitmap> bitmap = ctx->framebuffer;
if (!isHDR) {
/* Tonemap the image */
if (ctx->toneMappingMethod == EReinhard) {
Float logAvgLuminance = 0, maxLuminance = 0; /* Unused */
Float burn = (ctx->reinhardBurn + 10) / 20.0f;
bitmap = bitmap->clone();
bitmap->tonemapReinhard(logAvgLuminance, maxLuminance,
ctx->reinhardKey, burn);
}
bitmap = bitmap->convert(Bitmap::ERGB, Bitmap::EUInt8,
ctx->srgb ? (Float) -1 : ctx->gamma,
ctx->toneMappingMethod == EReinhard
? (Float) 1.0f : std::pow((Float) 2, ctx->exposure));
}
bitmap->write(format, fs);
}
}
void MainWindow::on_actionSave_triggered() {
SceneContext *context = m_context[ui->tabBar->currentIndex()];
saveScene(this, context, context->fileName);
}
#if MTSGUI_STATIC_QFILEDIALOG
void MainWindow::on_actionSaveAs_triggered() {
SceneContext *context = m_context[ui->tabBar->currentIndex()];
QString fileDir = QFileInfo(context->fileName).absolutePath();
QString fileName = QFileDialog::getSaveFileName(this,
tr("Save scene as ..."), fileDir, tr("Mitsuba scenes (*.xml)"));
if (!fileName.isEmpty()) {
QSettings settings;
settings.setValue("fileDir", QFileInfo(fileName).absolutePath());
saveSceneAs(fileName);
}
}
void MainWindow::onSaveAsDialogClose(int reason) { /* unused */ }
#else // MTSGUI_STATIC_QFILEDIALOG
void MainWindow::on_actionSaveAs_triggered() {
QFileDialog *dialog = new QFileDialog(this, tr("Save scene as ..."),
"", tr("Mitsuba scenes (*.xml)"));
m_currentChild = dialog;
QSettings settings;
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setViewMode(QFileDialog::Detail);
dialog->setAcceptMode(QFileDialog::AcceptSave);
dialog->restoreState(settings.value("fileDialogState").toByteArray());
dialog->setWindowModality(Qt::WindowModal);
connect(dialog, SIGNAL(finished(int)), this, SLOT(onSaveAsDialogClose(int)));
m_currentChild = dialog;
// prevent a tab drawing artifact on Qt/OSX
m_activeWindowHack = true;
dialog->show();
qApp->processEvents();
m_activeWindowHack = false;
}
void MainWindow::onSaveAsDialogClose(int reason) {
QSettings settings;
QFileDialog *dialog = static_cast<QFileDialog *>(sender());
m_currentChild = NULL;
if (reason == QDialog::Accepted) {
QString fileName = dialog->selectedFiles().value(0);
settings.setValue("fileDialogState", dialog->saveState());
saveSceneAs(fileName);
}
}
#endif // MTSGUI_STATIC_QFILEDIALOG
void MainWindow::saveSceneAs(const QString &fileName) {
if (!fileName.isEmpty()) {
int currentIndex = ui->tabBar->currentIndex();
SceneContext *context = m_context[currentIndex];
saveScene(this, context, fileName);
fs::path pathName = toFsPath(fileName),
complete = fs::absolute(pathName),
baseName = pathName.stem();
context->fileName = fileName;
context->shortName = QFileInfo(fileName).fileName();
context->scene->setSourceFile(pathName);
context->scene->setDestinationFile(baseName);
ui->tabBar->setTabText(currentIndex, context->shortName);
addRecentFile(fromFsPath(complete));
}
}
void MainWindow::on_actionReferenceManual_triggered() {
QDesktopServices::openUrl(QUrl("http://www.mitsuba-renderer.org/docs.html"));
}
void MainWindow::on_actionAbout_triggered() {
AboutDialog about(this);
about.exec();
}
void MainWindow::onJobFinished(const RenderJob *job, bool cancelled) {
SceneContext *context = getContext(job, false);
if (context == NULL)
return;
m_renderQueue->join();
context->workUnits.clear();
if (cancelled) {
if (!context->cancelled) {
QMessageBox::critical(this, tr("Error while rendering"),
tr("The rendering job did not complete successfully. Please check the log."),
QMessageBox::Ok);
} else {
context->mode = context->cancelMode;
if (ui->tabBar->currentIndex() != -1 &&
m_context[ui->tabBar->currentIndex()] == context)
ui->glView->resumePreview();
}
}
refresh(job);
context->renderJob = NULL;
updateUI();
if (ui->tabBar->currentIndex() != -1 &&
m_context[ui->tabBar->currentIndex()] == context)
resize(size() - context->sizeIncrease);
}
void MainWindow::onStatusMessage(const QString &status) {
m_statusMessage = status;
updateStatus();
}
void MainWindow::updateStatus() {
if (m_statusMessage == "")
setWindowTitle(tr("Mitsuba renderer"));
else
setWindowTitle(tr("Mitsuba renderer [%1]").arg(m_statusMessage));
}
void MainWindow::on_actionStartServer_triggered() {
m_serverWidget = new ServerWidget(NULL, m_nodeName, m_listenPort);
ui->actionStartServer->setEnabled(false);
connect(m_serverWidget, SIGNAL(closed()), this, SLOT(onServerClosed()));
m_serverWidget->show();
}
void MainWindow::on_actionEnableCommandLine_triggered() {
if (QMessageBox::question(this, tr("Enable command line access"),
tr("<p>If you proceed, Mitsuba will create symbolic links in <tt>/usr/bin</tt> and <tt>/Library/Python/{2.6,2.7}/site-packages</tt>, as well as an entry in .bashrc, "
"which enable command line and Python usage. Note that you will have to "
"repeat this process every time the Mitsuba application is moved.</p>"
"<p>Create links?</p>"),
QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return;
if (!create_symlinks())
QMessageBox::critical(this, tr("Authentication error"),
tr("Unable to create the symbolic links!"), QMessageBox::Ok);
}
void MainWindow::on_actionReportBug_triggered() {
QDesktopServices::openUrl(QUrl("https://www.mitsuba-renderer.org/bugtracker/projects/mitsuba"));
}
void MainWindow::on_actionFeedback_triggered() {
QDesktopServices::openUrl(QUrl("mailto:Wenzel%20Jakob%20<wenzel@cs.cornell.edu>?subject=Feedback%20on%20Mitsuba"));
}
void MainWindow::onServerClosed() {
delete m_serverWidget;
m_serverWidget = NULL;
ui->actionStartServer->setEnabled(true);
}
void MainWindow::onWorkBegin(const RenderJob *job, const RectangularWorkUnit *wu, int worker) {
SceneContext *context = getContext(job, false);
if (context == NULL)
return;
VisualWorkUnit vwu(wu->getOffset(), wu->getSize(), worker);
/* This is not executed in the event loop -- take some precautions */
m_contextMutex.lock();
context->workUnits.insert(vwu);
drawVisualWorkUnit(context, vwu);
bool isCurrentView = ui->tabBar->currentIndex() < m_context.size() &&
m_context[ui->tabBar->currentIndex()] == context;
m_contextMutex.unlock();
if (isCurrentView)
emit updateView();
}
void MainWindow::drawVisualWorkUnit(SceneContext *context, const VisualWorkUnit &vwu) {
int ox = vwu.offset.x, oy = vwu.offset.y,
ex = ox + vwu.size.x, ey = oy + vwu.size.y;
if (vwu.size.x < 3 || vwu.size.y < 3)
return;
const float *color = NULL;
/* Use desaturated colors to highlight the host
responsible for rendering the current image vwu */
const float white[] = { 1.0f, 1.0f, 1.0f };
const float red[] = { 1.0f, 0.3f, 0.3f };
const float green[] = { 0.3f, 1.0f, 0.3f };
const float blue[] = { 0.3f, 0.3f, 1.0f };
const float gray[] = { 0.5f, 0.5f, 0.5f };
const float yellow[] = { 1.0f, 1.0f, 0.0f };
const float magenta[] = { 1.0f, 0.3f, 1.0f };
const float turquoise[] = { 0.3f, 1.0f, 1.0f };
switch (vwu.worker % 8) {
case 1: color = green; break;
case 2: color = yellow; break;
case 3: color = blue; break;
case 4: color = gray; break;
case 5: color = red; break;
case 6: color = magenta; break;
case 7: color = turquoise; break;
case 0:
default:
color = white;
break;
}
float scale = .7f * (color[0] * 0.212671f + color[1] * 0.715160f + color[2] * 0.072169f);
float ncol[3] = {
color[0]*scale,
color[1]*scale,
color[2]*scale
};
drawHLine(context, ox, oy, ox + 3, ncol);
drawHLine(context, ex - 4, oy, ex - 1, ncol);
drawHLine(context, ox, ey - 1, ox + 3, ncol);
drawHLine(context, ex - 4, ey - 1, ex - 1, ncol);
drawVLine(context, ox, oy, oy + 3, ncol);
drawVLine(context, ex - 1, oy, oy + 3, ncol);
drawVLine(context, ex - 1, ey - 4, ey - 1, ncol);
drawVLine(context, ox, ey - 4, ey - 1, ncol);
}
void MainWindow::onWorkCanceled(const RenderJob *job, const Point2i &offset, const Vector2i &size) {
SceneContext *context = getContext(job, false);
if (context == NULL)
return;
VisualWorkUnit vwu(offset, size);
if (context->workUnits.find(vwu) != context->workUnits.end())
context->workUnits.erase(vwu);
m_contextMutex.lock();
m_contextMutex.unlock();
}
void MainWindow::onWorkEnd(const RenderJob *job, const ImageBlock *block) {
SceneContext *context = getContext(job, false);
if (context == NULL)
return;
// int border = block->getBorderSize();
int border = 0; // produces a more appealing preview
Point2i offset(
std::max(0, block->getOffset().x - border),
std::max(0, block->getOffset().y - border));
Bitmap *target = context->framebuffer;
Vector2i size(
std::min(target->getWidth(), offset.x + block->getSize().x + 2 * border)-offset.x,
std::min(target->getHeight(), offset.y + block->getSize().y + 2 * border)-offset.y);
context->scene->getFilm()->develop(offset, size, offset, target);
/* This is executed by worker threads -- take some precautions */
m_contextMutex.lock();
bool isCurrentView = ui->tabBar->currentIndex() < m_context.size() &&
m_context[ui->tabBar->currentIndex()] == context;
VisualWorkUnit vwu(block->getOffset(), block->getSize());
if (context->workUnits.find(vwu) != context->workUnits.end()) {
context->workUnits.erase(vwu);
} else if (!context->cancelled) {
SLog(EWarn, "Internal error: unable to find previously scheduled"
" rectangular work unit.");
}
for (std::set<VisualWorkUnit, block_comparator>::const_iterator it =
context->workUnits.begin(); it != context->workUnits.end(); ++it)
drawVisualWorkUnit(context, *it);
m_contextMutex.unlock();
if (isCurrentView)
emit updateView();
}
void MainWindow::onRefresh() {
const RenderJob *req = m_renderListener->acquireRefreshRequest();
if (req)
refresh(req);
m_renderListener->releaseRefreshRequest();
}
void MainWindow::refresh(const RenderJob *job) {
SceneContext *context = getContext(job, false);
if (context == NULL)
return;
Bitmap *target = context->framebuffer;
context->scene->getFilm()->develop(Point2i(0, 0),
target->getSize(), Point2i(0, 0), target);
/* This is executed by worker threads -- take some precautions */
m_contextMutex.lock();
bool isCurrentView = ui->tabBar->currentIndex() < m_context.size() &&
m_context[ui->tabBar->currentIndex()] == context;
for (std::set<VisualWorkUnit, block_comparator>::const_iterator it =
context->workUnits.begin(); it != context->workUnits.end(); ++it)
drawVisualWorkUnit(context, *it);
m_contextMutex.unlock();
if (isCurrentView)
emit updateView();
}
void MainWindow::onActivateCamera() {
int index = ui->tabBar->currentIndex();
if (index == -1 || m_context[index]->scene == NULL)
return;
QAction *action = qobject_cast<QAction *>(sender());
Sensor *sensor = static_cast<Sensor *>(action->data().value<void *>());
if (!sensor)
return;
SceneContext *context = m_context[index];
on_tabBar_currentChanged(-1);
Vector2i size = sensor->getFilm()->getSize();
context->scene->setSensor(sensor);
context->framebuffer = new Bitmap(Bitmap::ERGBA,
Bitmap::EFloat32, size);
context->framebuffer->clear();
context->scrollOffset = Vector2i(0, 0);
context->mode = EPreview;
context->windowSize = QSize();
if (context->previewBuffer.buffer) {
context->previewBuffer.buffer->decRef();
context->previewBuffer.buffer = NULL;
context->previewBuffer.vplSampleOffset = 0;
}
on_tabBar_currentChanged(index);
updateUI();
adjustSize();
}
void MainWindow::on_glView_loadFileRequest(const QString &string) {
loadFile(string);
}
bool ServerConnection::createWorker(QWidget *parent) {
ref<Stream> stream;
try {
if (type == EDirectConnection) {
stream = new SocketStream(hostName.toStdString(), port);
} else {
std::vector<std::string> cmdLine;
cmdLine.push_back(formatString("bash -c 'cd %s; . setpath.sh; mtssrv -ls'", instDir.toLatin1().constData()));
stream = new SSHStream(userName.toStdString(),
hostName.toStdString(), cmdLine, port);
}
worker = new RemoteWorker(formatString("net%i", remoteWorkerCtr++), stream);
return true;
} catch (const std::exception &e) {
QString extra;
if (type == ESSHConnection && !QString(e.what()).contains("configuration mismatch"))
extra = parent->tr(" - Please make sure that you can log in manually using the "
"command line and that <a href=\"http://www.debian-administration.org/articles/152\">"
"passwordless authentication</a> is active.");
QMessageBox::critical(parent, parent->tr("Unable to connect to %1").arg(hostName),
QString("Unable to create a connection to \"%1\": %2%3")
.arg(hostName).arg(e.what()).arg(extra), QMessageBox::Ok);
return false;
}
}
QSize MainWindow::sizeHint() const {
QSize hint = QMainWindow::sizeHint();
/* Don't include scroll bars in the size hint */
if (ui->hScrollBar->isVisible())
hint -= QSize(0, ui->hScrollBar->sizeHint().height());
if (ui->vScrollBar->isVisible())
hint -= QSize(ui->vScrollBar->sizeHint().width(), 0);
return hint;
}
QString ServerConnection::toString() const {
return QString("%1 (%2, %3 cores)")
.arg(worker->getNodeName().c_str())
.arg(type == EDirectConnection ? "direct" : "ssh")
.arg(worker->getCoreCount());
}
SceneContext::SceneContext(SceneContext *ctx) {
if (ctx->scene) {
/* Temporarily set up a new file resolver */
ref<Thread> thread = Thread::getThread();
ref<FileResolver> oldResolver = thread->getFileResolver();
ref<FileResolver> newResolver = oldResolver->clone();
newResolver->prependPath(fs::absolute(ctx->scene->getSourceFile()).parent_path());
thread->setFileResolver(newResolver);
scene = new Scene(ctx->scene);
ref<PluginManager> pluginMgr = PluginManager::getInstance();
ref<Sensor> oldSensor = static_cast<Sensor *>(ctx->scene->getSensor());
ref<Sensor> newSensor = static_cast<Sensor *>
(pluginMgr->createObject(MTS_CLASS(Sensor), oldSensor->getProperties()));
ref<Sampler> sampler = static_cast<Sampler *>
(pluginMgr->createObject(MTS_CLASS(Sampler), ctx->scene->getSampler()->getProperties()));
ref<Film> film = static_cast<Film *>
(pluginMgr->createObject(MTS_CLASS(Film), oldSensor->getFilm()->getProperties()));
const Integrator *oldIntegrator = ctx->scene->getIntegrator();
ref<Integrator> currentIntegrator;
int depth = 0;
std::vector<Integrator *> integratorList;
while (oldIntegrator != NULL) {
ref<Integrator> integrator = static_cast<Integrator *> (pluginMgr->createObject(
MTS_CLASS(Integrator), oldIntegrator->getProperties()));
if (depth++ == 0)
scene->setIntegrator(integrator);
else
currentIntegrator->addChild(integrator);
currentIntegrator = integrator;
integratorList.push_back(integrator);
oldIntegrator = oldIntegrator->getSubIntegrator();
}
for (int i=(int) integratorList.size()-1; i>=0; --i)
integratorList[i]->configure();
ref<ReconstructionFilter> rfilter = static_cast<ReconstructionFilter *>
(pluginMgr->createObject(MTS_CLASS(ReconstructionFilter), oldSensor->getFilm()->
getReconstructionFilter()->getProperties()));
rfilter->configure();
film->addChild(rfilter);
film->configure();
sampler->configure();
newSensor->addChild(sampler);
newSensor->setMedium(oldSensor->getMedium());
newSensor->addChild(film);
newSensor->configure();
scene->removeSensor(oldSensor);
scene->addSensor(newSensor);
scene->setSensor(newSensor);
scene->setSampler(sampler);
scene->configure();
sceneResID = ctx->sceneResID;
Scheduler::getInstance()->retainResource(sceneResID);
thread->setFileResolver(oldResolver);
} else {
sceneResID = -1;
renderJob = NULL;
}
fileName = ctx->fileName;
shortName = ctx->shortName;
movementScale = ctx->movementScale;
up = ctx->up;
renderJob = NULL;
wasRendering = false;
cancelled = false;
progress = 0.0f;
framebuffer = ctx->framebuffer->clone();
mode = ctx->renderJob ? EPreview : ctx->mode;
gamma = ctx->gamma;
exposure = ctx->exposure;
clamping = ctx->clamping;
srgb = ctx->srgb;
pathLength = ctx->pathLength;
shadowMapResolution = ctx->shadowMapResolution;
previewMethod = ctx->previewMethod;
toneMappingMethod = ctx->toneMappingMethod;
windowSize = ctx->windowSize;
sizeIncrease = ctx->sizeIncrease;
scrollOffset = ctx->scrollOffset;
reinhardKey = ctx->reinhardKey;
reinhardBurn = ctx->reinhardBurn;
diffuseReceivers = ctx->diffuseReceivers;
diffuseSources = ctx->diffuseSources;
showKDTree = ctx->showKDTree;
shownKDTreeLevel = ctx->shownKDTreeLevel;
selectedShape = ctx->selectedShape;
selectionMode = ctx->selectionMode;
originalSize = ctx->originalSize;
doc = ctx->doc.cloneNode(true).toDocument();
}
SceneContext::~SceneContext() {
if (scene && sceneResID != -1)
Scheduler::getInstance()->unregisterResource(sceneResID);
if (previewBuffer.buffer)
previewBuffer.buffer->decRef();
if (previewBuffer.sync)
previewBuffer.sync->decRef();
}
int SceneContext::detectPathLength() const {
if (!scene)
return 2;
const Integrator *integrator = scene->getIntegrator();
int extraDepth = 0;
while (integrator->getSubIntegrator() != NULL) {
if (integrator->getClass()->getName() == "IrradianceCacheIntegrator")
extraDepth = 1;
integrator = integrator->getSubIntegrator();
}
const Properties &integratorProps = integrator->getProperties();
int maxDepth = -1;
if (integratorProps.hasProperty("maxDepth"))
maxDepth = integratorProps.getInteger("maxDepth");
if (maxDepth == -1) {
if (integratorProps.getPluginName() == "direct")
maxDepth = 2;
else
maxDepth = 5;
}
return std::max(2, std::min(maxDepth + extraDepth, 6));
}
MTS_IMPLEMENT_CLASS(QRenderListener, false, RenderListener)