--- a/src/glRenderer.cpp Tue Feb 14 13:37:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1007 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2017 Teemu Piippo - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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/>. - */ - -#define GL_GLEXT_PROTOTYPES -#include <GL/glu.h> -#include <GL/glext.h> -#include <QContextMenuEvent> -#include <QToolTip> -#include <QTimer> -#include <GL/glu.h> -#include "main.h" -#include "ldDocument.h" -#include "glRenderer.h" -#include "colors.h" -#include "mainwindow.h" -#include "miscallenous.h" -#include "editHistory.h" -#include "glCompiler.h" -#include "primitives.h" -#include "documentmanager.h" -#include "grid.h" - -const QPen GLRenderer::thinBorderPen {QColor {0, 0, 0, 208}, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; - -/* - * Constructs a GL renderer. - */ -GLRenderer::GLRenderer(const Model* model, QWidget* parent) : - QGLWidget {parent}, - HierarchyElement {parent}, - m_model {model}, - m_cameras { - {"Top camera", {1, 0, 0, X, Z, false, false, false}}, // top - {"Front camera", {0, 0, 1, X, Y, false, true, false}}, // front - {"Left camera", {0, 1, 0, Z, Y, true, true, false}}, // left - {"Bottom camera", {-1, 0, 0, X, Z, false, true, true}}, // bottom - {"Back camera", {0, 0, -1, X, Y, true, true, true}}, // back - {"Right camera", {0, -1, 0, Z, Y, false, true, true}}, // right - {"Free camera", GLCamera::FreeCamera}, // free - } -{ - m_camera = (Camera) m_config->camera(); - m_compiler = new GLCompiler (this); - m_toolTipTimer = new QTimer (this); - m_toolTipTimer->setSingleShot (true); - setAcceptDrops (true); - connect (m_toolTipTimer, SIGNAL (timeout()), this, SLOT (showCameraIconTooltip())); - resetAllAngles(); - m_needZoomToFit = true; - - // Init camera icons - for (Camera camera : iterateEnum<Camera>()) - { - const char* cameraIconNames[EnumLimits<Camera>::Count] = - { - "camera-top", "camera-front", "camera-left", - "camera-bottom", "camera-back", "camera-right", - "camera-free" - }; - - CameraIcon* info = &m_cameraIcons[static_cast<int>(camera)]; - info->image = GetIcon (cameraIconNames[static_cast<int>(camera)]); - info->camera = camera; - } - - calcCameraIcons(); -} - -/* - * Cleans up the axes VBOs when the renderer is destroyed. - */ -GLRenderer::~GLRenderer() -{ - glDeleteBuffers(1, &m_axesVbo); - glDeleteBuffers(1, &m_axesColorVbo); -} - -/* - * Calculates the camera icon locations. - */ -void GLRenderer::calcCameraIcons() -{ - int i = 0; - const int columns = 3; - const int firstAtLastRow = countof(m_cameras) - (countof(m_cameras) % columns); - - for (CameraIcon& cameraIcon : m_cameraIcons) - { - int row = i / columns; - int column = i % columns; - - // Do right-justifying on the last row. - if (i >= firstAtLastRow) - column += columns - (countof(m_cameras) % columns); - - int x1 = width() - 48 + (column * 16) - 1; - int y1 = (row * 16) + 1; - - cameraIcon.sourceRect = {0, 0, 16, 16}; - cameraIcon.targetRect = {x1, y1, 16, 16}; - cameraIcon.hitRect = { - cameraIcon.targetRect.x(), - cameraIcon.targetRect.y(), - cameraIcon.targetRect.width() + 1, - cameraIcon.targetRect.height() + 1 - }; - - ++i; - } -} - -/* - * Returns the camera currently in use. - */ -GLCamera& GLRenderer::currentCamera() -{ - return m_cameras[static_cast<int>(camera())]; -} - -/* - * Returns the camera currently in use. - */ -const GLCamera& GLRenderer::currentCamera() const -{ - return m_cameras[static_cast<int>(camera())]; -} - -/* - * Prepares the GL context for rendering. - */ -void GLRenderer::initGLData() -{ - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable (GL_POLYGON_OFFSET_FILL); - glPolygonOffset (1.0f, 1.0f); - glEnable (GL_DEPTH_TEST); - glShadeModel (GL_SMOOTH); - glEnable (GL_MULTISAMPLE); - - if (m_config->antiAliasedLines()) - { - glEnable (GL_LINE_SMOOTH); - glEnable (GL_POLYGON_SMOOTH); - glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); - glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST); - } - else - { - glDisable (GL_LINE_SMOOTH); - glDisable (GL_POLYGON_SMOOTH); - } -} - -/* - * Returns the object currently highlighted by the cursor. - */ -LDObject* GLRenderer::objectAtCursor() const -{ - return m_objectAtCursor; -} - -// ============================================================================= -// -void GLRenderer::needZoomToFit() -{ - m_needZoomToFit = true; -} - -// ============================================================================= -// -void GLRenderer::resetAngles() -{ - if (m_initialized) - { - // Why did I even bother trying to compute this by pen and paper? Let GL figure it out... - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glRotatef(30, 1, 0, 0); - glRotatef(330, 0, 1, 0); - glGetFloatv(GL_MODELVIEW_MATRIX, m_rotationMatrix.data()); - glPopMatrix(); - } - currentCamera().setPanning(0, 0); - needZoomToFit(); -} - -// ============================================================================= -// -void GLRenderer::resetAllAngles() -{ - Camera oldcam = camera(); - - for (int i = 0; i < 7; ++i) - { - setCamera ((Camera) i); - resetAngles(); - } - - setCamera (oldcam); -} - -// ============================================================================= -// -void GLRenderer::initializeGL() -{ - initializeOpenGLFunctions(); - setBackground(); - glLineWidth (m_config->lineThickness()); - glLineStipple (1, 0x6666); - setAutoFillBackground (false); - setMouseTracking (true); - setFocusPolicy (Qt::WheelFocus); - m_compiler->initialize(); - initializeAxes(); - initializeLighting(); - m_initialized = true; - // Now that GL is initialized, we can reset angles. - resetAllAngles(); -} - -void GLRenderer::initializeLighting() -{ - GLfloat materialShininess[] = {5.0}; - GLfloat lightPosition[] = {1.0, 1.0, 1.0, 0.0}; - GLfloat ambientLightingLevel[] = {0.8, 0.8, 0.8, 1.0}; - glShadeModel(GL_SMOOTH); - glMaterialfv(GL_FRONT, GL_SHININESS, materialShininess); - glMaterialfv(GL_FRONT, GL_AMBIENT, ambientLightingLevel); - glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_COLOR_MATERIAL); - glEnable(GL_DEPTH_TEST); -} - -// ============================================================================= -// -void GLRenderer::initializeAxes() -{ - // Definitions for visual axes, drawn on the screen - struct - { - QColor color; - Vertex extrema; - } axisInfo[3] = - { - { QColor {192, 96, 96}, Vertex {10000, 0, 0} }, // X - { QColor {48, 192, 48}, Vertex {0, 10000, 0} }, // Y - { QColor {48, 112, 192}, Vertex {0, 0, 10000} }, // Z - }; - - float axisdata[18]; - float colorData[18]; - memset (axisdata, 0, sizeof axisdata); - - for (int i = 0; i < 3; ++i) - { - const auto& data = axisInfo[i]; - - for (Axis axis : axes) - { - axisdata[(i * 6) + axis] = data.extrema[axis]; - axisdata[(i * 6) + 3 + axis] = -data.extrema[axis]; - } - - int offset = i * 6; - colorData[offset + 0] = colorData[offset + 3] = data.color.red(); - colorData[offset + 1] = colorData[offset + 4] = data.color.green(); - colorData[offset + 2] = colorData[offset + 5] = data.color.blue(); - } - - glGenBuffers (1, &m_axesVbo); - glBindBuffer (GL_ARRAY_BUFFER, m_axesVbo); - glBufferData (GL_ARRAY_BUFFER, sizeof axisdata, axisdata, GL_STATIC_DRAW); - glGenBuffers (1, &m_axesColorVbo); - glBindBuffer (GL_ARRAY_BUFFER, m_axesColorVbo); - glBufferData (GL_ARRAY_BUFFER, sizeof colorData, colorData, GL_STATIC_DRAW); - glBindBuffer (GL_ARRAY_BUFFER, 0); -} - -// ============================================================================= -// -void GLRenderer::setBackground() -{ - if (not m_isDrawingSelectionScene) - { - // Otherwise use the background that the user wants. - QColor color = m_config->backgroundColor(); - - if (color.isValid()) - { - color.setAlpha(255); - m_useDarkBackground = luma(color) < 80; - m_backgroundColor = color; - qglClearColor(color); - } - } - else - { - // The picking scene requires a black background. - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - } -} - -QColor GLRenderer::backgroundColor() const -{ - return m_backgroundColor; -} - -// ============================================================================= -// -void GLRenderer::refresh() -{ - update(); - - if (isVisible()) - swapBuffers(); -} - -// ============================================================================= -// -void GLRenderer::resizeGL (int width, int height) -{ - calcCameraIcons(); - glViewport (0, 0, width, height); - glMatrixMode (GL_PROJECTION); - glLoadIdentity(); - gluPerspective (45.0f, (double) width / (double) height, 1.0f, 10000.0f); - glMatrixMode (GL_MODELVIEW); - - // Unfortunately Qt does not provide a resized() signal so we have to manually feed the information. - for (GLCamera& camera : m_cameras) - camera.rendererResized(width, height); -} - -// ============================================================================= -// -void GLRenderer::drawGLScene() -{ - if (m_needZoomToFit) - { - m_needZoomToFit = false; - zoomAllToFit(); - } - - if (m_config->drawWireframe() and not m_isDrawingSelectionScene) - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_TEST); - - if (m_config->lighting()) - glEnable(GL_LIGHTING); - else - glDisable(GL_LIGHTING); - - if (camera() != Camera::Free) - { - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - - glLoadIdentity(); - const QSizeF& virtualSize = currentCamera().virtualSize(); - glOrtho(-virtualSize.width(), virtualSize.width(), -virtualSize.height(), virtualSize.height(), -100.0f, 100.0f); - glTranslatef(panning (X), panning (Y), 0.0f); - - if (camera() != Camera::Front and camera() != Camera::Back) - glRotatef(90.0f, currentCamera().glRotate(X), currentCamera().glRotate(Y), 0); - - // Back camera needs to be handled differently - if (camera() == Camera::Back) - { - glRotatef(180.0f, 1.0f, 0.0f, 0.0f); - glRotatef(180.0f, 0.0f, 0.0f, 1.0f); - } - } - else - { - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glTranslatef(0.0f, 0.0f, -2.0f); - glTranslatef(panning (X), panning (Y), -zoom()); - glMultMatrixf(m_rotationMatrix.constData()); - } - - glEnableClientState (GL_NORMAL_ARRAY); - glEnableClientState (GL_VERTEX_ARRAY); - glEnableClientState (GL_COLOR_ARRAY); - - if (m_isDrawingSelectionScene) - { - drawVbos (VboClass::Triangles, VboSubclass::PickColors, GL_TRIANGLES); - drawVbos (VboClass::Quads, VboSubclass::PickColors, GL_QUADS); - drawVbos (VboClass::Lines, VboSubclass::PickColors, GL_LINES); - drawVbos (VboClass::ConditionalLines, VboSubclass::PickColors, GL_LINES); - } - else - { - if (m_config->bfcRedGreenView()) - { - glEnable (GL_CULL_FACE); - glCullFace (GL_BACK); - drawVbos (VboClass::Triangles, VboSubclass::BfcFrontColors, GL_TRIANGLES); - drawVbos (VboClass::Quads, VboSubclass::BfcFrontColors, GL_QUADS); - glCullFace (GL_FRONT); - drawVbos (VboClass::Triangles, VboSubclass::BfcBackColors, GL_TRIANGLES); - drawVbos (VboClass::Quads, VboSubclass::BfcBackColors, GL_QUADS); - glDisable (GL_CULL_FACE); - } - else - { - VboSubclass colors; - - if (m_config->randomColors()) - colors = VboSubclass::RandomColors; - else - colors = VboSubclass::NormalColors; - - drawVbos (VboClass::Triangles, colors, GL_TRIANGLES); - drawVbos (VboClass::Quads, colors, GL_QUADS); - } - - drawVbos (VboClass::Lines, VboSubclass::NormalColors, GL_LINES); - glEnable (GL_LINE_STIPPLE); - drawVbos (VboClass::ConditionalLines, VboSubclass::NormalColors, GL_LINES); - glDisable (GL_LINE_STIPPLE); - - if (m_config->drawAxes()) - { - glDisableClientState (GL_NORMAL_ARRAY); - glBindBuffer (GL_ARRAY_BUFFER, m_axesVbo); - glVertexPointer (3, GL_FLOAT, 0, NULL); - glBindBuffer (GL_ARRAY_BUFFER, m_axesVbo); - glColorPointer (3, GL_FLOAT, 0, NULL); - glDrawArrays (GL_LINES, 0, 6); - glEnableClientState (GL_NORMAL_ARRAY); - CHECK_GL_ERROR(); - } - } - - glPopMatrix(); - glBindBuffer (GL_ARRAY_BUFFER, 0); - glDisableClientState (GL_VERTEX_ARRAY); - glDisableClientState (GL_COLOR_ARRAY); - glDisableClientState (GL_NORMAL_ARRAY); - CHECK_GL_ERROR(); - glDisable (GL_CULL_FACE); - glMatrixMode (GL_MODELVIEW); - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); -} - -// ============================================================================= -// -void GLRenderer::drawVbos (VboClass surface, VboSubclass colors, GLenum type) -{ - // Filter this through some configuration options - if ((isOneOf (surface, VboClass::Quads, VboClass::Triangles) and m_config->drawSurfaces() == false) - or (surface == VboClass::Lines and m_config->drawEdgeLines() == false) - or (surface == VboClass::ConditionalLines and m_config->drawConditionalLines() == false)) - { - return; - } - - int surfaceVboNumber = m_compiler->vboNumber(surface, VboSubclass::Surfaces); - int colorVboNumber = m_compiler->vboNumber(surface, colors); - int normalVboNumber = m_compiler->vboNumber(surface, VboSubclass::Normals); - m_compiler->prepareVBO(surfaceVboNumber, m_model); - m_compiler->prepareVBO(colorVboNumber, m_model); - m_compiler->prepareVBO(normalVboNumber, m_model); - GLuint surfaceVbo = m_compiler->vbo(surfaceVboNumber); - GLuint colorVbo = m_compiler->vbo(colorVboNumber); - GLuint normalVbo = m_compiler->vbo(normalVboNumber); - GLsizei count = m_compiler->vboSize(surfaceVboNumber) / 3; - - if (count > 0) - { - glBindBuffer(GL_ARRAY_BUFFER, surfaceVbo); - glVertexPointer(3, GL_FLOAT, 0, nullptr); - CHECK_GL_ERROR(); - glBindBuffer(GL_ARRAY_BUFFER, colorVbo); - glColorPointer(4, GL_FLOAT, 0, nullptr); - CHECK_GL_ERROR(); - glBindBuffer(GL_ARRAY_BUFFER, normalVbo); - glNormalPointer(GL_FLOAT, 0, nullptr); - CHECK_GL_ERROR(); - glDrawArrays(type, 0, count); - CHECK_GL_ERROR(); - } -} - -QPen GLRenderer::textPen() const -{ - return {m_useDarkBackground ? Qt::white : Qt::black}; -} - -bool GLRenderer::freeCameraAllowed() const -{ - return true; -} - -void GLRenderer::paintEvent(QPaintEvent*) -{ - makeCurrent(); - initGLData(); - drawGLScene(); - - if (isDrawingSelectionScene()) - return; - - QPainter painter {this}; - painter.setRenderHint(QPainter::Antialiasing); - overpaint(painter); -} - -void GLRenderer::overpaint(QPainter &painter) -{ - // Draw a background for the selected camera - painter.setPen(thinBorderPen); - painter.setBrush(QBrush {QColor {0, 128, 160, 128}}); - painter.drawRect(m_cameraIcons[static_cast<int>(camera())].hitRect); - - // Draw the camera icons - for (const CameraIcon& info : m_cameraIcons) - { - // Don't draw the free camera icon when we can't use the free camera - if (info.camera == Camera::Free and not freeCameraAllowed()) - continue; - - painter.drawPixmap(info.targetRect, info.image, info.sourceRect); - } - - // Draw a label for the current camera in the bottom left corner - { - QFontMetrics metrics {QFont {}}; - int margin = 4; - painter.setPen(textPen()); - painter.drawText(QPoint {margin, height() - margin - metrics.descent()}, currentCamera().name()); - } -} - -// ============================================================================= -// -void GLRenderer::mouseReleaseEvent(QMouseEvent* event) -{ - bool wasLeft = (m_lastButtons & Qt::LeftButton) and not (event->buttons() & Qt::LeftButton); - m_panning = false; - - // Check if we selected a camera icon - if (wasLeft and not mouseHasMoved()) - { - for (CameraIcon& info : m_cameraIcons) - { - if (info.targetRect.contains (event->pos())) - { - setCamera (info.camera); - break; - } - } - } - - update(); - m_totalMouseMove = 0; -} - -// ============================================================================= -// -void GLRenderer::mousePressEvent(QMouseEvent* event) -{ - m_lastButtons = event->buttons(); - m_totalMouseMove = 0; -} - -// ============================================================================= -// -void GLRenderer::mouseMoveEvent(QMouseEvent* event) -{ - int xMove = event->x() - m_mousePosition.x(); - int yMove = event->y() - m_mousePosition.y(); - m_totalMouseMove += qAbs(xMove) + qAbs(yMove); - m_isCameraMoving = false; - - bool left = event->buttons() & Qt::LeftButton; - bool mid = event->buttons() & Qt::MidButton; - bool shift = event->modifiers() & Qt::ShiftModifier; - - if (mid or (left and shift)) - { - currentCamera().pan(xMove, yMove); - m_panning = true; - m_isCameraMoving = true; - } - else if (left and camera() == Camera::Free and (xMove != 0 or yMove != 0)) - { - // Apply current rotation input to the rotation matrix - // ref: https://forums.ldraw.org/thread-22006-post-24426.html#pid24426 - glPushMatrix(); - glLoadIdentity(); - // 0.6 is an arbitrary rotation sensitivity scalar - glRotatef(0.6 * hypot(xMove, yMove), yMove, xMove, 0); - glMultMatrixf(m_rotationMatrix.constData()); - glGetFloatv(GL_MODELVIEW_MATRIX, m_rotationMatrix.data()); - glPopMatrix(); - m_isCameraMoving = true; - } - - // Start the tool tip timer - m_toolTipTimer->start (500); - - // Update 2d position - m_mousePosition = event->pos(); - m_globalpos = event->globalPos(); - m_mousePositionF = event->localPos(); - - highlightCursorObject(); - update(); - event->accept(); -} - -// ============================================================================= -// -void GLRenderer::keyPressEvent(QKeyEvent* event) -{ - m_currentKeyboardModifiers = event->modifiers(); -} - -// ============================================================================= -// -void GLRenderer::keyReleaseEvent(QKeyEvent* event) -{ - m_currentKeyboardModifiers = event->modifiers(); - update(); -} - -// ============================================================================= -// -void GLRenderer::wheelEvent(QWheelEvent* ev) -{ - makeCurrent(); - currentCamera().zoomNotch(ev->delta() > 0); - m_isCameraMoving = true; - update(); - ev->accept(); -} - -// ============================================================================= -// -void GLRenderer::leaveEvent(QEvent*) -{ - m_toolTipTimer->stop(); - update(); -} - -// ============================================================================= -// -void GLRenderer::setCamera(Camera camera) -{ - // The edit mode may forbid the free camera. - if (freeCameraAllowed() or camera != Camera::Free) - { - m_camera = camera; - m_config->setCamera(static_cast<int>(camera)); - } -} - -/* - * Returns the set of objects found in the specified pixel area. - */ -QSet<LDObject*> GLRenderer::pick(const QRect& range) -{ - makeCurrent(); - QSet<LDObject*> newSelection; - - // Paint the picking scene - setPicking(true); - drawGLScene(); - - int x0 = range.left(); - int y0 = range.top(); - int x1 = x0 + range.width(); - int y1 = y0 + range.height(); - - // Clamp the values to ensure they're within bounds - x0 = qMax (0, x0); - y0 = qMax (0, y0); - x1 = qMin (x1, width()); - y1 = qMin (y1, height()); - const int areawidth = (x1 - x0); - const int areaheight = (y1 - y0); - const qint32 numpixels = areawidth * areaheight; - - // Allocate space for the pixel data. - QVector<unsigned char> pixelData; - pixelData.resize(4 * numpixels); - - // Read pixels from the color buffer. - glReadPixels(x0, height() - y1, areawidth, areaheight, GL_RGBA, GL_UNSIGNED_BYTE, pixelData.data()); - - QSet<qint32> indices; - - // Go through each pixel read and add them to the selection. - // Each pixel maps to an LDObject index injectively. - // Note: black is background, those indices are skipped. - for (unsigned char *pixelCursor = pixelData.begin(); pixelCursor < pixelData.end(); pixelCursor += 4) - { - qint32 index = pixelCursor[0] * 0x10000 + pixelCursor[1] * 0x100 + pixelCursor[2] * 0x1; - if (index != 0) - indices.insert(index); - } - - // For each index read, resolve the LDObject behind it and add it to the selection. - for (qint32 index : indices) - { - LDObject* object = LDObject::fromID(index); - - if (object != nullptr) - newSelection.insert(object); - } - - setPicking(false); - repaint(); - return newSelection; -} - -/* - * Simpler version of GLRenderer::pick which simply picks whatever object on the cursor - */ -LDObject* GLRenderer::pick(int mouseX, int mouseY) -{ - unsigned char pixel[4]; - makeCurrent(); - setPicking(true); - drawGLScene(); - glReadPixels(mouseX, height() - mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); - LDObject* object = LDObject::fromID(pixel[0] * 0x10000 + pixel[1] * 0x100 + pixel[2]); - setPicking(false); - repaint(); - return object; -} - -// ============================================================================= -// -void GLRenderer::setPicking(bool value) -{ - m_isDrawingSelectionScene = value; - setBackground(); - - if (m_isDrawingSelectionScene) - { - glDisable(GL_DITHER); - - // Use particularly thick lines while picking ease up selecting lines. - glLineWidth(qMax<double>(m_config->lineThickness(), 6.5)); - } - else - { - glEnable(GL_DITHER); - - // Restore line thickness - glLineWidth(m_config->lineThickness()); - } -} - -// ============================================================================= -// -void GLRenderer::forgetObject(LDObject* obj) -{ - if (m_objectAtCursor == obj) - m_objectAtCursor = nullptr; -} - -// ============================================================================= -// -QByteArray GLRenderer::capturePixels() -{ - QByteArray result; - result.resize (4 * width() * height()); - m_takingScreenCapture = true; - update(); // Smile! - m_takingScreenCapture = false; - glReadPixels(0, 0, width(), height(), GL_RGBA, GL_UNSIGNED_BYTE, result.data()); - return result; -} - -/* - * Show a tooltip if the cursor is currently hovering over a camera icon. - */ -void GLRenderer::showCameraIconTooltip() -{ - for (CameraIcon & icon : m_cameraIcons) - { - if (icon.targetRect.contains (m_mousePosition)) - { - QToolTip::showText(m_globalpos, m_cameras[static_cast<int>(icon.camera)].name()); - update(); - break; - } - } -} - -// ============================================================================= -// -void GLRenderer::zoomToFit() -{ - currentCamera().setZoom(30.0f); - bool lastfilled = false; - bool firstrun = true; - enum { black = 0xFF000000 }; - bool inward = true; - int runaway = 50; - - // Use the pick list while drawing the scene, this way we can tell whether borders - // are background or not. - setPicking (true); - - while (--runaway) - { - if (zoom() > 10000.0 or zoom() < 0.0) - { - // Nothing to draw if we get here. - currentCamera().setZoom(30.0); - break; - } - - currentCamera().zoomNotch(inward); - QVector<unsigned char> capture (4 * width() * height()); - drawGLScene(); - glReadPixels (0, 0, width(), height(), GL_RGBA, GL_UNSIGNED_BYTE, capture.data()); - QImage image (capture.constData(), width(), height(), QImage::Format_ARGB32); - bool filled = false; - - // Check the top and bottom rows - for (int i = 0; i < image.width(); ++i) - { - if (image.pixel (i, 0) != black or image.pixel (i, height() - 1) != black) - { - filled = true; - break; - } - } - - // Left and right edges - if (filled == false) - { - for (int i = 0; i < image.height(); ++i) - { - if (image.pixel (0, i) != black or image.pixel (width() - 1, i) != black) - { - filled = true; - break; - } - } - } - - if (firstrun) - { - // If this is the first run, we don't know enough to determine - // whether the zoom was to fit, so we mark in our knowledge so - // far and start over. - inward = not filled; - firstrun = false; - } - else - { - // If this run filled the screen and the last one did not, the - // last run had ideal zoom - zoom a bit back and we should reach it. - if (filled and not lastfilled) - { - currentCamera().zoomNotch(false); - break; - } - - // If this run did not fill the screen and the last one did, we've - // now reached ideal zoom so we're done here. - if (not filled and lastfilled) - break; - - inward = not filled; - } - - lastfilled = filled; - } - - setPicking (false); -} - -// ============================================================================= -// -void GLRenderer::zoomAllToFit() -{ - zoomToFit(); -} - -// ============================================================================= -// -void GLRenderer::highlightCursorObject() -{ - if (not m_config->highlightObjectBelowCursor() and objectAtCursor() == nullptr) - return; - - LDObject* newObject = nullptr; - LDObject* oldObject = objectAtCursor(); - qint32 newIndex; - - if (m_isCameraMoving or not m_config->highlightObjectBelowCursor()) - { - newIndex = 0; - } - else - { - setPicking (true); - drawGLScene(); - setPicking (false); - - unsigned char pixel[4]; - glReadPixels (m_mousePosition.x(), height() - m_mousePosition.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel[0]); - newIndex = pixel[0] * 0x10000 | pixel[1] * 0x100 | pixel[2]; - } - - if (newIndex != (oldObject ? oldObject->id() : 0)) - { - if (newIndex != 0) - newObject = LDObject::fromID (newIndex); - - m_objectAtCursor = newObject; - - if (oldObject) - emit objectHighlightingChanged(oldObject); - - if (newObject) - emit objectHighlightingChanged(newObject); - } - - update(); -} - -bool GLRenderer::mouseHasMoved() const -{ - return m_totalMouseMove >= 10; -} - -QPoint const& GLRenderer::mousePosition() const -{ - return m_mousePosition; -} - -QPointF const& GLRenderer::mousePositionF() const -{ - return m_mousePositionF; -} - -Qt::KeyboardModifiers GLRenderer::keyboardModifiers() const -{ - return m_currentKeyboardModifiers; -} - -Camera GLRenderer::camera() const -{ - return m_camera; -} - -double GLRenderer::panning (Axis ax) const -{ - return (ax == X) ? currentCamera().panningX() : currentCamera().panningY(); -} - -double GLRenderer::zoom() -{ - return currentCamera().zoom(); -} - -const QGenericMatrix<4, 4, GLfloat>& GLRenderer::rotationMatrix() const -{ - return m_rotationMatrix; -} - -bool GLRenderer::isDrawingSelectionScene() const -{ - return m_isDrawingSelectionScene; -} - -Qt::MouseButtons GLRenderer::lastButtons() const -{ - return m_lastButtons; -} - -const Model* GLRenderer::model() const -{ - return m_model; -}