diff --git a/include/mitsuba/core/bitmap.h b/include/mitsuba/core/bitmap.h index 84227114..02cf8fd2 100644 --- a/include/mitsuba/core/bitmap.h +++ b/include/mitsuba/core/bitmap.h @@ -297,6 +297,22 @@ public: EDivision }; + /** + * \brief Describes a sub-layer of a multilayer bitmap (e.g. OpenEXR) + * + * A layer is defined as a named collection of bitmap channels and a pixel format + * + * This data structure is used by \ref Bitmap::getLayers() + */ + struct Layer { + /// Descriptive name of the bitmap layer + std::string name; + /// Pixel format that should be associated with underlying combination of bitmap channels + EPixelFormat format; + /// References to bitmap channels + std::vector channels; + }; + /** * \brief Create a bitmap of the specified type and allocate * the necessary amount of memory @@ -813,9 +829,26 @@ public: */ ref extractChannels(Bitmap::EPixelFormat fmt, const std::vector &channels) const; + /** + * \brief Extracts layer information from the bitmap + * + * This is a convenience function which analyzes the bitmap's + * channel names and extracts groups matching a name + * convention specified by the OpenEXR standard. + * + * A series of channel names referred to as + * 'diffuse.R', 'diffuse.B', and 'diffuse.G' + * will be identified as a single \ref Bitmap::ERGB "layer". + * This works for RGB[A]/XYZ[A], and Luminance[A] images + */ + std::vector getLayers() const; + /** * \brief Split an multi-channel image buffer (e.g. from an OpenEXR image - * with lots of AOVs) into group of RGB[A]/XYZ[A]/Luminance images + * with lots of AOVs) into its constituent layers + * + * This operation internally calls \ref getLayers() to extract bitmap + * layers followed by one or more calls to \ref extractChannels() */ std::map split() const; diff --git a/src/libcore/bitmap.cpp b/src/libcore/bitmap.cpp index 5261e75f..f26c582b 100644 --- a/src/libcore/bitmap.cpp +++ b/src/libcore/bitmap.cpp @@ -1875,32 +1875,34 @@ void Bitmap::tonemapReinhard(Float &logAvgLuminance, Float &maxLuminance, Float } } - -std::map Bitmap::split() const { +std::vector Bitmap::getLayers() const { typedef std::map ChannelMap; - std::map result; if (m_channelNames.empty()) - Log(EError, "Bitmap::split(): color channel names not available!"); + Log(EError, "Bitmap::getLayers(): required color channel names were not available!"); ChannelMap channels; for (size_t i=0; i layers; for (size_t i=0; i= 3 && name[name.length()-2] == '.') { + prefix = name.substr(0, name.length() - 1); + layer.name = layer.name.substr(0, layer.name.length() - 2); + postfix = name[name.length() - 1]; } - std::vector extractChannels; - EPixelFormat extractFormat; - ChannelMap::iterator itR = channels.find(prefix + "r"), itG = channels.find(prefix + "g"), @@ -1915,68 +1917,75 @@ std::map Bitmap::split() const { bool maybeY = postfix == 'y' || postfix == 'a'; if (maybeRGB && itR != channels.end() && itG != channels.end() && itB != channels.end()) { - extractFormat = ERGB; - extractChannels.push_back(itR->second); - extractChannels.push_back(itG->second); - extractChannels.push_back(itB->second); + layer.format = ERGB; + layer.channels.push_back(itR->second); + layer.channels.push_back(itG->second); + layer.channels.push_back(itB->second); if (itA != channels.end()) { - extractFormat = ERGBA; - extractChannels.push_back(itA->second); + layer.format = ERGBA; + layer.channels.push_back(itA->second); channels.erase(prefix + "a"); } channels.erase(prefix + "r"); channels.erase(prefix + "g"); channels.erase(prefix + "b"); } else if (maybeXYZ && itX != channels.end() && itY != channels.end() && itZ != channels.end()) { - extractFormat = EXYZ; - extractChannels.push_back(itX->second); - extractChannels.push_back(itY->second); - extractChannels.push_back(itZ->second); + layer.format = EXYZ; + layer.channels.push_back(itX->second); + layer.channels.push_back(itY->second); + layer.channels.push_back(itZ->second); if (itA != channels.end()) { - extractFormat = EXYZA; - extractChannels.push_back(itA->second); + layer.format = EXYZA; + layer.channels.push_back(itA->second); channels.erase(prefix + "a"); } channels.erase(prefix + "x"); channels.erase(prefix + "y"); channels.erase(prefix + "z"); } else if (maybeY && itY != channels.end()) { - extractFormat = ELuminance; - extractChannels.push_back(itY->second); + layer.format = ELuminance; + layer.channels.push_back(itY->second); if (itA != channels.end()) { - extractFormat = ELuminanceAlpha; - extractChannels.push_back(itA->second); + layer.format = ELuminanceAlpha; + layer.channels.push_back(itA->second); channels.erase(prefix + "a"); } channels.erase(prefix + "y"); } else { - extractFormat = ELuminance; - extractChannels.push_back((int) i); + if (layer.name.empty()) + layer.name = m_channelNames[i]; + layer.format = ELuminance; + layer.channels.push_back((int) i); channels.erase(name); } + layers.push_back(layer); + } + return layers; +} + +std::map Bitmap::split() const { + std::map result; + + std::vector layers = getLayers(); + for (size_t i=0; i channelNames; - for (size_t j=0; j _bitmap = this->extractChannels(extractFormat, extractChannels); + ref _bitmap = this->extractChannels(layer.format, layer.channels); bitmap = _bitmap.get(); bitmap->incRef(); } bitmap->decRef(false); bitmap->setChannelNames(channelNames); - prefix = m_channelNames[i]; - if (!prefix.empty()) - prefix = prefix.substr(0, prefix.length()-1); - if (!prefix.empty() && prefix[prefix.length()-1] == '.') - prefix = prefix.substr(0, prefix.length()-1); - - if (result.find(prefix) != result.end()) - Log(EError, "Internal error -- encountered two sub-images with the same prefix"); - result[prefix] = bitmap; + if (result.find(layer.name) != result.end()) + Log(EError, "Internal error -- encountered two layers with the same name \"%s\"", layer.name.c_str()); + result[layer.name] = bitmap; } return result; } diff --git a/src/libhw/glrenderer.cpp b/src/libhw/glrenderer.cpp index e65e400f..7ad86a35 100644 --- a/src/libhw/glrenderer.cpp +++ b/src/libhw/glrenderer.cpp @@ -771,7 +771,8 @@ void GLRenderer::drawText(const Point2i &_pos, glLoadIdentity(); font->getTexture()->bind(); glEnable(GL_BLEND); - glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); +// glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR); Point2i pos(_pos); int initial = pos.x; diff --git a/src/librender/trimesh.cpp b/src/librender/trimesh.cpp index e28ba68e..8326fc5d 100644 --- a/src/librender/trimesh.cpp +++ b/src/librender/trimesh.cpp @@ -1110,7 +1110,7 @@ void TriMesh::writePLY(const fs::path &path) const { *ptr += (uint8_t) std::max(0.0f, std::min(255.0f, (float) m_colors[i][2] * 255.0f + 0.5f)); } } - Assert(ptr-vertexStorage == vertexStorageSize); + Assert((size_t) (ptr-vertexStorage) == vertexStorageSize); os.write((const char *) vertexStorage, vertexStorageSize); delete[] vertexStorage; @@ -1122,7 +1122,7 @@ void TriMesh::writePLY(const fs::path &path) const { memcpy(ptr, &m_triangles[i], sizeof(Triangle)); ptr += sizeof(Triangle); } - Assert(ptr-faceStorage == faceStorageSize); + Assert((size_t) (ptr-faceStorage) == faceStorageSize); os.write((const char *) faceStorage, faceStorageSize); delete[] faceStorage; diff --git a/src/mtsgui/common.h b/src/mtsgui/common.h index 5f3bf675..47d7371e 100644 --- a/src/mtsgui/common.h +++ b/src/mtsgui/common.h @@ -184,7 +184,9 @@ struct SceneContext { float progress; QString eta, progressName; ref framebuffer; + std::vector > layers; std::set workUnits; + int currentLayer; EMode mode, cancelMode; Float gamma, exposure, clamping; bool srgb; @@ -209,6 +211,7 @@ struct SceneContext { SceneContext() : scene(NULL), sceneResID(-1), renderJob(NULL), wasRendering(false), + currentLayer(0), selectionMode(ENothing), selectedShape(NULL) { } diff --git a/src/mtsgui/glwidget.cpp b/src/mtsgui/glwidget.cpp index 3ecdf0d6..8f2eb9ab 100644 --- a/src/mtsgui/glwidget.cpp +++ b/src/mtsgui/glwidget.cpp @@ -220,6 +220,14 @@ void GLWidget::setScene(SceneContext *context) { m_movementTimer->stop(); m_context = context; + if (context && context->layers.size() > 0) { + m_statusMessage = + formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.", + m_context->layers[m_context->currentLayer].first.c_str(), + m_context->currentLayer+1, m_context->layers.size()); + m_statusTimer->reset(); + } + if (context && context->scene == NULL) context = NULL; @@ -503,7 +511,9 @@ void GLWidget::keyPressEvent(QKeyEvent *event) { emit stopRendering(); } - if (event->isAutoRepeat() || !m_context) + if (!m_context || + (event->isAutoRepeat() && event->key() != Qt::Key_BracketLeft && + event->key() != Qt::Key_BracketRight)) return; switch (event->key()) { @@ -526,7 +536,15 @@ void GLWidget::keyPressEvent(QKeyEvent *event) { case Qt::Key_Down: m_downKeyDown = true; break; case Qt::Key_BracketLeft: - if (m_context->showKDTree) { + if (m_context->layers.size() > 0) { + m_context->currentLayer = std::max(0, m_context->currentLayer-1); + m_context->framebuffer = m_context->layers[m_context->currentLayer].second->convert(Bitmap::ERGBA, Bitmap::EFloat32); + m_statusMessage = + formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.", + m_context->layers[m_context->currentLayer].first.c_str(), + m_context->currentLayer+1, m_context->layers.size()); + m_statusTimer->reset(); + } else if (m_context->showKDTree) { m_context->shownKDTreeLevel = std::max(0, m_context->shownKDTreeLevel - 1); updateGL(); @@ -534,7 +552,15 @@ void GLWidget::keyPressEvent(QKeyEvent *event) { } break; case Qt::Key_BracketRight: - if (m_context->showKDTree) { + if (m_context->layers.size() > 0) { + m_context->currentLayer = std::min((int) m_context->layers.size() - 1, m_context->currentLayer+1); + m_context->framebuffer = m_context->layers[m_context->currentLayer].second->convert(Bitmap::ERGBA, Bitmap::EFloat32); + m_statusMessage = + formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.", + m_context->layers[m_context->currentLayer].first.c_str(), + m_context->currentLayer+1, m_context->layers.size()); + m_statusTimer->reset(); + } else if (m_context->showKDTree) { m_context->shownKDTreeLevel++; updateGL(); return; @@ -1215,11 +1241,17 @@ void GLWidget::paintGL() { if (m_context->mode == EPreview) { m_preview->releaseBuffer(entry); if (m_context->showKDTree) { + std::string message = "kd-tree visualization mode\nPress '[' " + "and ']' to change the shown level"; + Vector2i size = m_font->getSize(message); int pos = m_statusMessage.empty() ? 10 : 30; + m_renderer->setBlendMode(Renderer::EBlendAlpha); + m_renderer->setColor(Spectrum(0.0f), 0.5f); + m_renderer->drawFilledRectangle(Point2i(5, pos-2), Point2i(15, pos+5)+size); + m_renderer->setBlendMode(Renderer::EBlendNone); m_renderer->setColor(Spectrum(1.0f)); - m_renderer->drawText(Point2i(10, pos), m_font, - formatString("kd-tree visualization mode\nPress '[' " - "and ']' to change the shown level")); + m_renderer->setColor(Spectrum(1.0f)); + m_renderer->drawText(Point2i(10, pos), m_font, message); } } @@ -1244,7 +1276,15 @@ void GLWidget::paintGL() { m_statusMessage = "Camera type is incompatible with the OpenGL preview!"; } - if (!m_statusMessage.empty() && m_context->mode == EPreview) { + if (!m_statusMessage.empty() && (m_context->mode == EPreview + || (m_context->mode == ERender && m_context->scene == NULL))) { + m_renderer->setDepthTest(false); + Vector2i size = m_font->getSize(m_statusMessage); + m_renderer->setBlendMode(Renderer::EBlendAlpha); + m_renderer->setColor(Spectrum(0.0f), 0.5f); + m_renderer->drawFilledRectangle(Point2i(5, 7), Point2i(15, 15)+size); + m_renderer->setBlendMode(Renderer::EBlendNone); + m_renderer->setColor(Spectrum(1.0f)); m_renderer->drawText(Point2i(10, 10), m_font, m_statusMessage); if (m_statusTimer->getMilliseconds() > 2000) m_statusMessage = ""; diff --git a/src/mtsgui/mainwindow.cpp b/src/mtsgui/mainwindow.cpp index afcc796f..68bafac6 100644 --- a/src/mtsgui/mainwindow.cpp +++ b/src/mtsgui/mainwindow.cpp @@ -398,7 +398,7 @@ bool MainWindow::initWorkersProcessArgv() { scheduler->registerWorker(new LocalWorker(-1, formatString("wrk%i", 0), m_workerPriority)); } - for (int i=0; istart(); @@ -2157,6 +2157,10 @@ SceneContext::SceneContext(SceneContext *ctx) { selectionMode = ctx->selectionMode; originalSize = ctx->originalSize; doc = ctx->doc.cloneNode(true).toDocument(); + layers = ctx->layers; + for (size_t i=0; iincRef(); + currentLayer = ctx->currentLayer; } SceneContext::~SceneContext() { @@ -2166,6 +2170,8 @@ SceneContext::~SceneContext() { previewBuffer.buffer->decRef(); if (previewBuffer.sync) previewBuffer.sync->decRef(); + for (size_t i=0; idecRef(); } int SceneContext::detectPathLength() const { diff --git a/src/mtsgui/sceneloader.cpp b/src/mtsgui/sceneloader.cpp index 38ae555b..c275028c 100644 --- a/src/mtsgui/sceneloader.cpp +++ b/src/mtsgui/sceneloader.cpp @@ -65,12 +65,25 @@ void SceneLoader::run() { m_result->toneMappingMethod = (EToneMappingMethod) settings.value("preview_toneMappingMethod", EGamma).toInt(); m_result->diffuseSources = settings.value("preview_diffuseSources", true).toBool(); m_result->diffuseReceivers = settings.value("preview_diffuseReceivers", false).toBool(); + m_result->currentLayer = 0; if (suffix == "exr" || suffix == "png" || suffix == "jpg" || suffix == "jpeg" || suffix == "hdr" || suffix == "rgbe" || suffix == "pfm" || suffix == "ppm") { /* This is an image, not a scene */ ref fs = new FileStream(toFsPath(m_filename), FileStream::EReadOnly); - ref bitmap = new Bitmap(Bitmap::EAuto, fs); + ref bitmap = new Bitmap(Bitmap::EAuto, fs, (suffix == "exr") ? "*" : ""); + + if (suffix == "exr" && bitmap->getLayers().size() > 1) { + std::map bitmaps = bitmap->split(); + for (std::map::iterator it = bitmaps.begin(); it != bitmaps.end(); ++it) { + std::string name = it->first; + Bitmap *layer = it->second; + layer->incRef(); + m_result->layers.push_back(std::make_pair(name, layer)); + } + bitmap = m_result->layers[m_result->currentLayer].second; + } + bitmap = bitmap->convert(Bitmap::ERGBA, Bitmap::EFloat32); m_result->mode = ERender;