Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screenshot Command #98

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
build
./data
gh-pages
*.swp
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ set(CMAKE_BUILD_TYPE "Release" CACHE STRING
)

if(NOT WIN32)
set(CMAKE_CXX_FLAGS "-Wall -std=c++0x" CACHE STRING "Flags used by the compiler during all build types.")
set(CMAKE_CXX_FLAGS "-Wall -Wno-deprecated-register -std=c++0x" CACHE STRING "Flags used by the compiler during all build types.")
endif()

project(displaz)
Expand Down
15 changes: 15 additions & 0 deletions src/gui/PointViewerMainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,15 @@ void PointViewerMainWindow::handleMessage(QByteArray message)
}
m_geometries->unloadFiles(regex);
}
else if (commandTokens[0] == "SAVE_SCREENSHOT")
{
if (commandTokens.size()-1 != 1)
{
tfm::format(std::cerr, "Expected screenshot filename\n");
return;
}
saveScreenShot(commandTokens[1]);
}
else if (commandTokens[0] == "SET_VIEW_POSITION")
{
if (commandTokens.size()-1 != 3)
Expand Down Expand Up @@ -619,6 +628,12 @@ void PointViewerMainWindow::screenShot()
sshot.save(fileName);
}

void PointViewerMainWindow::saveScreenShot(const QString & fileName)
{
QImage sshot = m_pointView->renderScreenShot();
sshot.save(fileName);
g_logger.info("Saved screenshot to file '%s'", fileName.toStdString());
}

void PointViewerMainWindow::aboutDialog()
{
Expand Down
1 change: 1 addition & 0 deletions src/gui/PointViewerMainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class PointViewerMainWindow : public QMainWindow
void handleIpcConnection();

private:
void saveScreenShot(const QString & fileName);
// Gui objects
QProgressBar* m_progressBar;
View3D* m_pointView;
Expand Down
6 changes: 6 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ int main(int argc, char *argv[])

std::string shaderName;
std::string unloadFiles;
std::string saveScreenShot;
bool noServer = false;

bool clearFiles = false;
Expand Down Expand Up @@ -98,6 +99,7 @@ int main(int argc, char *argv[])
"-querycursor", &queryCursor, "Query 3D cursor location from displaz instance",
"-script", &script, "Script mode: enable several settings which are useful when calling displaz from a script:"
" (a) do not wait for displaz GUI to exit before returning,",
"-screenshot %s", &saveScreenShot, "Remote: save current view as image to given file",

"<SEPARATOR>", "\nAdditional information:",
"-version", &printVersion, "Print version number",
Expand Down Expand Up @@ -223,6 +225,10 @@ int main(int argc, char *argv[])
{
channel->sendMessage(QByteArray("UNLOAD_FILES\n") + unloadFiles.c_str());
}
if (!saveScreenShot.empty())
{
channel->sendMessage(QByteArray("SAVE_SCREENSHOT\n") + saveScreenShot.c_str());
}
if (posX != -DBL_MAX)
{
channel->sendMessage("SET_VIEW_POSITION\n" +
Expand Down
78 changes: 65 additions & 13 deletions src/render/View3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <QItemSelectionModel>
#include <QMessageBox>
#include <QGLFormat>
#include <QOpenGLFramebufferObject>

#include "config.h"
#include "fileloader.h"
Expand Down Expand Up @@ -309,8 +310,7 @@ void View3D::initializeGL()
setFocus();
}


void View3D::resizeGL(int w, int h)
void View3D::resizeViewport(int w, int h)
{
//Note: This function receives "device pixel sizes" for correct OpenGL viewport setup
// Under OS X with retina display, this becomes important as it is 2x the width() or height()
Expand All @@ -325,6 +325,14 @@ void View3D::resizeGL(int w, int h)
double dPR = getDevicePixelRatio();

m_camera.setViewport(QRect(0,0,double(w)/dPR,double(h)/dPR));
}

void View3D::resizeGL(int w, int h)
{
if (m_badOpenGL)
return;

resizeViewport(w, h);

m_incrementalFramebuffer = allocIncrementalFramebuffer(w,h);

Expand Down Expand Up @@ -372,6 +380,23 @@ unsigned int View3D::allocIncrementalFramebuffer(int w, int h) const
return fb;
}

void View3D::clearBuffers() const
{
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearColor(m_backgroundColor.redF(), m_backgroundColor.greenF(),
m_backgroundColor.blueF(), 1.0f);
if (!m_incrementalDraw)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

TransformState View3D::makeTransformState(int w, int h) const
{
return TransformState(Imath::V2i(w, h),
m_camera.projectionMatrix(),
m_camera.viewMatrix());
}

void View3D::paintGL()
{
Expand All @@ -397,18 +422,9 @@ void View3D::paintGL()

//--------------------------------------------------
// Draw main scene
TransformState transState(Imath::V2i(w, h),
m_camera.projectionMatrix(),
m_camera.viewMatrix());

glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearColor(m_backgroundColor.redF(), m_backgroundColor.greenF(),
m_backgroundColor.blueF(), 1.0f);
if (!m_incrementalDraw)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
TransformState transState = makeTransformState(w, h);

clearBuffers();
std::vector<const Geometry*> geoms = selectedGeometry();

// Draw bounding boxes
Expand Down Expand Up @@ -498,6 +514,42 @@ void View3D::paintGL()
glCheckError();
}


QImage View3D::renderScreenShot(int width, int height)
{
if (m_badOpenGL)
return QImage();

double dPR = getDevicePixelRatio();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand things, this shouldn't be required, as getDevicePixelRatio() is related to the physical screen, not an offscreen buffer.

Instead, I'd suggest explicitly antialiasing, either by using QOpenGLFramebufferObjectFormat, or possibly more portably by just using a multiple of the actual width and height and downsampling on the CPU side (as you already have below with scaleToWidth())

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is, just keep what we have, but get dPR from a setting or a fixed supersampling (eg, dPR = 2). Probably want to rename it as such.

if (width < 0)
width = this->width();

if (height < 0)
height = this->height();

// Note: the reason for the following two lines and the subsequent downsampling of the
// resulting image is to make the radius of rendered points consistent between the
// gui and screenshot on displas with dPR != 1.
width *= dPR;
height *= dPR;

QOpenGLFramebufferObject framebuffer(QSize(width, height), QOpenGLFramebufferObject::Depth);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is Qt5 only, so we'd be breaking with Qt4 compatibility by using this. I guess let's get it all working, and then I'll need to decide.

(TBH, I'm kinda keen to give up on Qt4 because the number of different OpenGL and OS versions is already enough to be a real drag on limited development resources...)

framebuffer.bind();
resizeViewport(width, height);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ends up calling m_camera.setViewport() which is wired into restartRender() via the projectionChanged() signal, so will probably force an unnecessary GUI redraw. Will need some disentangling - perhaps just copying the required glViewport() stuff in here? Really it's not a resize, so calling resize is a bit confusing anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for scaling up the offscreen buffer by dPR and subsequently downscaling the resulting image by the same factor was just a workaround to make points rendered into the offscreen buffer the same size as points in the gui when rendered on my Retina Macbook (dPR=2). If rendering to the offscreen buffer at the same resolution as the gui window, the points in the screenshot end up being twice as large as those on the screen. I made a short attempt at figuring out exactly why this was happening but had to get on with other things. Any idea why this would happen?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, of course!

The problem here is that in the vertex shader gl_PointSize must be specified in pixels, so the shader needs to know the size of the viewport. This is communicated with the pointPixelScale uniform variable (see View3d::drawPoints()). Just looking at that code, it currently assumes dPR should be multiplied in, and grabs information from the camera directly. This is another thing which should be disentangled to make it possible to cleanly render to an offscreen buffer: everything about the camera and view should be passed along explicitly in transState. Luckily transState already has viewSize, so you can probably just use something like

prog.setUniformValue("pointPixelScale", (GLfloat)(0.5*transState.viewSize.x*transState.projMatrix[0][0]));

clearBuffers();

// Render points only
drawPoints(makeTransformState(width, height),
selectedGeometry(),
DBL_MAX,
false);
glFinish();

QImage img = framebuffer.toImage();
return img.scaledToWidth(width / dPR, Qt::SmoothTransformation);
}


void View3D::drawMeshes(const TransformState& transState,
const std::vector<const Geometry*>& geoms) const
{
Expand Down
7 changes: 7 additions & 0 deletions src/render/View3D.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class View3D : public QGLWidget
QItemSelectionModel* selectionModel() { return m_selectionModel; }
void setSelectionModel(QItemSelectionModel* selectionModel);

// Render pointcloud to offscreen buffer of given size and return as a QImage.
// Negative dimensions are replaced with current gui 3d viewport size.
QImage renderScreenShot(int width = -1, int height = -1);
public slots:
/// Set the backgroud color
void setBackground(QColor col);
Expand Down Expand Up @@ -85,6 +88,8 @@ class View3D : public QGLWidget
void geometryInserted(const QModelIndex&, int firstRow, int lastRow);

private:
void resizeViewport(int w, int h);
void clearBuffers() const;
double getDevicePixelRatio();
unsigned int allocIncrementalFramebuffer(int w, int h) const;

Expand All @@ -101,6 +106,8 @@ class View3D : public QGLWidget
const std::vector<const Geometry*>& geoms,
double quality, bool incrementalDraw);

TransformState makeTransformState(int w, int h) const;

void drawMeshes(const TransformState& transState,
const std::vector<const Geometry*>& geoms) const;

Expand Down