(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("If you proceed, Mitsuba will create symbolic links in /usr/bin and /Library/Python/{2.6,2.7}/site-packages, 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.
"
"Create links?
"),
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?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::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::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(sender());
Sensor *sensor = static_cast(action->data().value());
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;
try {
if (type == EDirectConnection) {
stream = new SocketStream(hostName.toStdString(), port);
} else {
std::vector 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 "
"passwordless authentication 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::getThread();
ref oldResolver = thread->getFileResolver();
ref newResolver = oldResolver->clone();
newResolver->prependPath(fs::absolute(ctx->scene->getSourceFile()).parent_path());
thread->setFileResolver(newResolver);
scene = new Scene(ctx->scene);
ref pluginMgr = PluginManager::getInstance();
ref oldSensor = static_cast(ctx->scene->getSensor());
ref newSensor = static_cast
(pluginMgr->createObject(MTS_CLASS(Sensor), oldSensor->getProperties()));
ref sampler = static_cast
(pluginMgr->createObject(MTS_CLASS(Sampler), ctx->scene->getSampler()->getProperties()));
ref film = static_cast
(pluginMgr->createObject(MTS_CLASS(Film), oldSensor->getFilm()->getProperties()));
const Integrator *oldIntegrator = ctx->scene->getIntegrator();
ref currentIntegrator;
int depth = 0;
std::vector integratorList;
while (oldIntegrator != NULL) {
ref integrator = static_cast (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 rfilter = static_cast
(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)