Renamed glRenderer.cpp → glrenderer.cpp

Tue, 14 Feb 2017 14:51:04 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Tue, 14 Feb 2017 14:51:04 +0200
changeset 1144
4f226fd97826
parent 1143
2008959603c9
child 1145
02264bf0108d

Renamed glRenderer.cpp → glrenderer.cpp

CMakeLists.txt file | annotate | diff | comparison | revisions
src/canvas.h file | annotate | diff | comparison | revisions
src/dialogs/externalprogrampathdialog.cpp file | annotate | diff | comparison | revisions
src/documentmanager.cpp file | annotate | diff | comparison | revisions
src/editHistory.cpp file | annotate | diff | comparison | revisions
src/editmodes/drawMode.cpp file | annotate | diff | comparison | revisions
src/glCompiler.h file | annotate | diff | comparison | revisions
src/glRenderer.cpp file | annotate | diff | comparison | revisions
src/glRenderer.h file | annotate | diff | comparison | revisions
src/glrenderer.cpp file | annotate | diff | comparison | revisions
src/glrenderer.h file | annotate | diff | comparison | revisions
src/messageLog.cpp file | annotate | diff | comparison | revisions
src/partdownloader.cpp file | annotate | diff | comparison | revisions
src/toolsets/algorithmtoolset.cpp file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Tue Feb 14 13:37:58 2017 +0200
+++ b/CMakeLists.txt	Tue Feb 14 14:51:04 2017 +0200
@@ -37,7 +37,7 @@
 	src/editHistory.cpp
 	src/glcamera.cpp
 	src/glCompiler.cpp
-	src/glRenderer.cpp
+	src/glrenderer.cpp
 	src/grid.cpp
 	src/guiutilities.cpp
 	src/hierarchyelement.cpp
@@ -99,7 +99,7 @@
 	src/format.h
 	src/glcamera.h
 	src/glCompiler.h
-	src/glRenderer.h
+	src/glrenderer.h
 	src/grid.h
 	src/guiutilities.h
 	src/hierarchyelement.h
--- a/src/canvas.h	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/canvas.h	Tue Feb 14 14:51:04 2017 +0200
@@ -17,7 +17,7 @@
  */
 
 #pragma once
-#include "glRenderer.h"
+#include "glrenderer.h"
 #include "editmodes/abstractEditMode.h"
 
 class Canvas : public GLRenderer
--- a/src/dialogs/externalprogrampathdialog.cpp	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/dialogs/externalprogrampathdialog.cpp	Tue Feb 14 14:51:04 2017 +0200
@@ -21,7 +21,7 @@
 #include "configdialog.h"
 #include "ui_externalprogrampathdialog.h"
 #include "../mainwindow.h"
-#include "../glRenderer.h"
+#include "../glrenderer.h"
 #include "../documentation.h"
 
 /*
--- a/src/documentmanager.cpp	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/documentmanager.cpp	Tue Feb 14 14:51:04 2017 +0200
@@ -23,7 +23,7 @@
 #include "mainwindow.h"
 #include "partdownloader.h"
 #include "documentloader.h"
-#include "glRenderer.h"
+#include "glrenderer.h"
 
 const QStringList DocumentManager::specialSubdirectories {"s", "48", "8"};
 
--- a/src/editHistory.cpp	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/editHistory.cpp	Tue Feb 14 14:51:04 2017 +0200
@@ -21,7 +21,7 @@
 #include "ldDocument.h"
 #include "miscallenous.h"
 #include "mainwindow.h"
-#include "glRenderer.h"
+#include "glrenderer.h"
 
 EditHistory::EditHistory (LDDocument* document) :
 	m_document (document),
--- a/src/editmodes/drawMode.cpp	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/editmodes/drawMode.cpp	Tue Feb 14 14:51:04 2017 +0200
@@ -20,7 +20,7 @@
 #include <QMouseEvent>
 #include "drawMode.h"
 #include "../ldObject.h"
-#include "../glRenderer.h"
+#include "../glrenderer.h"
 #include "../linetypes/edgeline.h"
 
 DrawMode::DrawMode (Canvas* canvas) :
--- a/src/glCompiler.h	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/glCompiler.h	Tue Feb 14 14:51:04 2017 +0200
@@ -18,7 +18,7 @@
 
 #pragma once
 #include "main.h"
-#include "glRenderer.h"
+#include "glrenderer.h"
 #include "glShared.h"
 #include <QMap>
 #include <QSet>
--- 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;
-}
--- a/src/glRenderer.h	Tue Feb 14 13:37:58 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +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/>.
- */
-
-#pragma once
-#include <QGLWidget>
-#include "main.h"
-#include "model.h"
-#include "glShared.h"
-#include "glcamera.h"
-
-class GLCompiler;
-class MessageManager;
-class QDialogButtonBox;
-class RadioGroup;
-class QDoubleSpinBox;
-class QSpinBox;
-class QLineEdit;
-class QTimer;
-class MagicWandMode;
-
-struct CameraInfo
-{
-	CameraInfo(const CameraInfo&) = delete;
-
-	int glrotate[3];
-	Axis localX;
-	Axis localY;
-	bool negatedX;
-	bool negatedY;
-	bool negatedDepth; // is greater depth value closer to camera?
-};
-
-enum class Camera
-{
-	Top,
-	Front,
-	Left,
-	Bottom,
-	Back,
-	Right,
-	Free,
-	_End
-};
-
-struct CameraIcon
-{
-	QPixmap image;
-	QRect sourceRect;
-	QRect targetRect;
-	QRect hitRect;
-	Camera camera;
-};
-
-MAKE_ITERABLE_ENUM(Camera)
-
-// The main renderer object, draws the brick on the screen, manages the camera and selection picking.
-class GLRenderer : public QGLWidget, protected QOpenGLFunctions, public HierarchyElement
-{
-	Q_OBJECT
-
-public:
-	GLRenderer(const Model* model, QWidget* parent = nullptr);
-	~GLRenderer();
-
-	QColor backgroundColor() const;
-	Camera camera() const;
-	QByteArray capturePixels();
-	GLCamera& currentCamera();
-	const GLCamera& currentCamera() const;
-	void drawGLScene();
-	void forgetObject(LDObject* obj);
-	void highlightCursorObject();
-	void initGLData();
-	bool isPicking() const;
-	Qt::KeyboardModifiers keyboardModifiers() const;
-	const Model* model() const;
-	bool mouseHasMoved() const;
-	QPoint const& mousePosition() const;
-	QPointF const& mousePositionF() const;
-	void needZoomToFit();
-	LDObject* objectAtCursor() const;
-	QSet<LDObject*> pick(const QRect& range);
-	LDObject* pick(int mouseX, int mouseY);
-	void refresh();
-	void resetAllAngles();
-	void resetAngles();
-	void setBackground();
-	void setCamera(Camera cam);
-	void setPicking(bool a);
-	QPen textPen() const;
-
-	static const QPen thinBorderPen;
-
-signals:
-	void objectHighlightingChanged(LDObject* object);
-
-protected:
-	void initializeGL();
-	void keyPressEvent(QKeyEvent* event);
-	void keyReleaseEvent(QKeyEvent* event);
-	void leaveEvent(QEvent* event);
-	void mousePressEvent(QMouseEvent* event);
-	void mouseMoveEvent(QMouseEvent* ev);
-	void mouseReleaseEvent(QMouseEvent* ev);
-	void paintEvent(QPaintEvent*);
-	void resizeGL(int w, int h);
-	void wheelEvent(QWheelEvent* ev);
-
-	virtual void overpaint(QPainter& painter);
-	virtual bool freeCameraAllowed() const;
-	bool isDrawingSelectionScene() const;
-	Qt::MouseButtons lastButtons() const;
-	double panning (Axis ax) const;
-	const QGenericMatrix<4, 4, GLfloat>& rotationMatrix() const;
-	double zoom();
-
-	template<typename... Args>
-	QString format (QString fmtstr, Args... args)
-	{
-		return ::format (fmtstr, args...);
-	}
-
-private:
-	const Model* const m_model;
-	GLCompiler* m_compiler;
-	LDObject* m_objectAtCursor = nullptr;
-	CameraIcon m_cameraIcons[7];
-	QTimer* m_toolTipTimer;
-	Qt::MouseButtons m_lastButtons;
-	Qt::KeyboardModifiers m_currentKeyboardModifiers;
-	QGenericMatrix<4, 4, GLfloat> m_rotationMatrix;
-	GLCamera m_cameras[7];
-	bool m_useDarkBackground = false;
-	bool m_takingScreenCapture = false;
-	bool m_panning = false;
-	bool m_initialized = false;
-	bool m_isDrawingSelectionScene = false;
-	bool m_isCameraMoving = false;
-	bool m_needZoomToFit = true;
-	QPoint m_mousePosition;
-	QPoint m_globalpos;
-	QPointF m_mousePositionF;
-	Camera m_camera;
-	GLuint m_axeslist;
-	int m_totalMouseMove;
-	QColor m_backgroundColor;
-	GLuint m_axesVbo;
-	GLuint m_axesColorVbo;
-
-	void calcCameraIcons();
-	void drawVbos (VboClass surface, VboSubclass colors, GLenum type);
-	void zoomToFit();
-	void zoomAllToFit();
-
-private slots:
-	void showCameraIconTooltip();
-	void initializeAxes();
-	void initializeLighting();
-};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/glrenderer.cpp	Tue Feb 14 14:51:04 2017 +0200
@@ -0,0 +1,1007 @@
+/*
+ *  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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/glrenderer.h	Tue Feb 14 14:51:04 2017 +0200
@@ -0,0 +1,174 @@
+/*
+ *  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/>.
+ */
+
+#pragma once
+#include <QGLWidget>
+#include "main.h"
+#include "model.h"
+#include "glShared.h"
+#include "glcamera.h"
+
+class GLCompiler;
+class MessageManager;
+class QDialogButtonBox;
+class RadioGroup;
+class QDoubleSpinBox;
+class QSpinBox;
+class QLineEdit;
+class QTimer;
+class MagicWandMode;
+
+struct CameraInfo
+{
+	CameraInfo(const CameraInfo&) = delete;
+
+	int glrotate[3];
+	Axis localX;
+	Axis localY;
+	bool negatedX;
+	bool negatedY;
+	bool negatedDepth; // is greater depth value closer to camera?
+};
+
+enum class Camera
+{
+	Top,
+	Front,
+	Left,
+	Bottom,
+	Back,
+	Right,
+	Free,
+	_End
+};
+
+struct CameraIcon
+{
+	QPixmap image;
+	QRect sourceRect;
+	QRect targetRect;
+	QRect hitRect;
+	Camera camera;
+};
+
+MAKE_ITERABLE_ENUM(Camera)
+
+// The main renderer object, draws the brick on the screen, manages the camera and selection picking.
+class GLRenderer : public QGLWidget, protected QOpenGLFunctions, public HierarchyElement
+{
+	Q_OBJECT
+
+public:
+	GLRenderer(const Model* model, QWidget* parent = nullptr);
+	~GLRenderer();
+
+	QColor backgroundColor() const;
+	Camera camera() const;
+	QByteArray capturePixels();
+	GLCamera& currentCamera();
+	const GLCamera& currentCamera() const;
+	void drawGLScene();
+	void forgetObject(LDObject* obj);
+	void highlightCursorObject();
+	void initGLData();
+	bool isPicking() const;
+	Qt::KeyboardModifiers keyboardModifiers() const;
+	const Model* model() const;
+	bool mouseHasMoved() const;
+	QPoint const& mousePosition() const;
+	QPointF const& mousePositionF() const;
+	void needZoomToFit();
+	LDObject* objectAtCursor() const;
+	QSet<LDObject*> pick(const QRect& range);
+	LDObject* pick(int mouseX, int mouseY);
+	void refresh();
+	void resetAllAngles();
+	void resetAngles();
+	void setBackground();
+	void setCamera(Camera cam);
+	void setPicking(bool a);
+	QPen textPen() const;
+
+	static const QPen thinBorderPen;
+
+signals:
+	void objectHighlightingChanged(LDObject* object);
+
+protected:
+	void initializeGL();
+	void keyPressEvent(QKeyEvent* event);
+	void keyReleaseEvent(QKeyEvent* event);
+	void leaveEvent(QEvent* event);
+	void mousePressEvent(QMouseEvent* event);
+	void mouseMoveEvent(QMouseEvent* ev);
+	void mouseReleaseEvent(QMouseEvent* ev);
+	void paintEvent(QPaintEvent*);
+	void resizeGL(int w, int h);
+	void wheelEvent(QWheelEvent* ev);
+
+	virtual void overpaint(QPainter& painter);
+	virtual bool freeCameraAllowed() const;
+	bool isDrawingSelectionScene() const;
+	Qt::MouseButtons lastButtons() const;
+	double panning (Axis ax) const;
+	const QGenericMatrix<4, 4, GLfloat>& rotationMatrix() const;
+	double zoom();
+
+	template<typename... Args>
+	QString format (QString fmtstr, Args... args)
+	{
+		return ::format (fmtstr, args...);
+	}
+
+private:
+	const Model* const m_model;
+	GLCompiler* m_compiler;
+	LDObject* m_objectAtCursor = nullptr;
+	CameraIcon m_cameraIcons[7];
+	QTimer* m_toolTipTimer;
+	Qt::MouseButtons m_lastButtons;
+	Qt::KeyboardModifiers m_currentKeyboardModifiers;
+	QGenericMatrix<4, 4, GLfloat> m_rotationMatrix;
+	GLCamera m_cameras[7];
+	bool m_useDarkBackground = false;
+	bool m_takingScreenCapture = false;
+	bool m_panning = false;
+	bool m_initialized = false;
+	bool m_isDrawingSelectionScene = false;
+	bool m_isCameraMoving = false;
+	bool m_needZoomToFit = true;
+	QPoint m_mousePosition;
+	QPoint m_globalpos;
+	QPointF m_mousePositionF;
+	Camera m_camera;
+	GLuint m_axeslist;
+	int m_totalMouseMove;
+	QColor m_backgroundColor;
+	GLuint m_axesVbo;
+	GLuint m_axesColorVbo;
+
+	void calcCameraIcons();
+	void drawVbos (VboClass surface, VboSubclass colors, GLenum type);
+	void zoomToFit();
+	void zoomAllToFit();
+
+private slots:
+	void showCameraIconTooltip();
+	void initializeAxes();
+	void initializeLighting();
+};
--- a/src/messageLog.cpp	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/messageLog.cpp	Tue Feb 14 14:51:04 2017 +0200
@@ -19,7 +19,7 @@
 #include <QTimer>
 #include <QDate>
 #include "messageLog.h"
-#include "glRenderer.h"
+#include "glrenderer.h"
 #include "mainwindow.h"
 
 enum
--- a/src/partdownloader.cpp	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/partdownloader.cpp	Tue Feb 14 14:51:04 2017 +0200
@@ -24,7 +24,7 @@
 #include "ui_partdownloader.h"
 #include "mainwindow.h"
 #include "ldDocument.h"
-#include "glRenderer.h"
+#include "glrenderer.h"
 #include "documentmanager.h"
 
 const char* g_unofficialLibraryURL = "http://ldraw.org/library/unofficial/";
--- a/src/toolsets/algorithmtoolset.cpp	Tue Feb 14 13:37:58 2017 +0200
+++ b/src/toolsets/algorithmtoolset.cpp	Tue Feb 14 14:51:04 2017 +0200
@@ -27,7 +27,7 @@
 #include "../ldDocument.h"
 #include "../miscallenous.h"
 #include "../radioGroup.h"
-#include "../glRenderer.h"
+#include "../glrenderer.h"
 #include "../colors.h"
 #include "../mathfunctions.h"
 #include "../ldobjectiterator.h"

mercurial