diff -r 66e8453e1768 -r 85080f7a1c20 src/glRenderer.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/glRenderer.cpp Tue Mar 03 16:55:36 2015 +0200
@@ -0,0 +1,1642 @@
+/*
+ * LDForge: LDraw parts authoring CAD
+ * Copyright (C) 2013 - 2015 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 .
+ */
+
+#define GL_GLEXT_PROTOTYPES
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "main.h"
+#include "configuration.h"
+#include "ldDocument.h"
+#include "glRenderer.h"
+#include "colors.h"
+#include "mainWindow.h"
+#include "miscallenous.h"
+#include "editHistory.h"
+#include "dialogs.h"
+#include "addObjectDialog.h"
+#include "messageLog.h"
+#include "glCompiler.h"
+#include "primitives.h"
+
+const LDFixedCamera g_FixedCameras[6] =
+{
+ {{ 1, 0, 0 }, X, Z, false, false, false }, // top
+ {{ 0, 0, 0 }, X, Y, false, true, false }, // front
+ {{ 0, 1, 0 }, Z, Y, true, true, false }, // left
+ {{ -1, 0, 0 }, X, Z, false, true, true }, // bottom
+ {{ 0, 0, 0 }, X, Y, true, true, true }, // back
+ {{ 0, -1, 0 }, Z, Y, false, true, true }, // right
+};
+
+CFGENTRY (String, BackgroundColor, "#FFFFFF")
+CFGENTRY (String, MainColor, "#A0A0A0")
+CFGENTRY (Float, MainColorAlpha, 1.0)
+CFGENTRY (Int, LineThickness, 2)
+CFGENTRY (Bool, BFCRedGreenView, false)
+CFGENTRY (Int, Camera, EFreeCamera)
+CFGENTRY (Bool, BlackEdges, false)
+CFGENTRY (Bool, DrawAxes, false)
+CFGENTRY (Bool, DrawWireframe, false)
+CFGENTRY (Bool, UseLogoStuds, false)
+CFGENTRY (Bool, AntiAliasedLines, true)
+CFGENTRY (Bool, RandomColors, false)
+CFGENTRY (Bool, HighlightObjectBelowCursor, true)
+CFGENTRY (Bool, DrawSurfaces, true)
+CFGENTRY (Bool, DrawEdgeLines, true)
+CFGENTRY (Bool, DrawConditionalLines, true)
+
+// argh
+const char* g_CameraNames[7] =
+{
+ QT_TRANSLATE_NOOP ("GLRenderer", "Top"),
+ QT_TRANSLATE_NOOP ("GLRenderer", "Front"),
+ QT_TRANSLATE_NOOP ("GLRenderer", "Left"),
+ QT_TRANSLATE_NOOP ("GLRenderer", "Bottom"),
+ QT_TRANSLATE_NOOP ("GLRenderer", "Back"),
+ QT_TRANSLATE_NOOP ("GLRenderer", "Right"),
+ QT_TRANSLATE_NOOP ("GLRenderer", "Free")
+};
+
+struct LDGLAxis
+{
+ const QColor col;
+ const Vertex vert;
+};
+
+// Definitions for visual axes, drawn on the screen
+static const LDGLAxis g_GLAxes[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
+};
+
+static bool RendererInitialized (false);
+
+// =============================================================================
+//
+GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent)
+{
+ m_isPicking = false;
+ m_camera = (ECamera) cfg::Camera;
+ m_drawToolTip = false;
+ m_editmode = AbstractEditMode::createByType (this, EditModeType::Select);
+ m_panning = false;
+ m_compiler = new GLCompiler (this);
+ setDrawOnly (false);
+ setMessageLog (null);
+ m_width = m_height = -1;
+ m_position3D = Origin;
+ m_toolTipTimer = new QTimer (this);
+ m_toolTipTimer->setSingleShot (true);
+ m_isCameraMoving = false;
+ m_thinBorderPen = QPen (QColor (0, 0, 0, 208), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
+ m_thinBorderPen.setWidth (1);
+ setAcceptDrops (true);
+ connect (m_toolTipTimer, SIGNAL (timeout()), this, SLOT (slot_toolTipTimer()));
+
+ // Init camera icons
+ for (ECamera cam = EFirstCamera; cam < ENumCameras; ++cam)
+ {
+ QString iconname = format ("camera-%1", tr (g_CameraNames[cam]).toLower());
+ CameraIcon* info = &m_cameraIcons[cam];
+ info->img = new QPixmap (GetIcon (iconname));
+ info->cam = cam;
+ }
+
+ calcCameraIcons();
+}
+
+// =============================================================================
+//
+GLRenderer::~GLRenderer()
+{
+ for (int i = 0; i < 6; ++i)
+ delete currentDocumentData().overlays[i].img;
+
+ for (CameraIcon& info : m_cameraIcons)
+ delete info.img;
+
+ if (messageLog())
+ messageLog()->setRenderer (null);
+
+ m_compiler->setRenderer (null);
+ delete m_compiler;
+ delete m_editmode;
+
+ glDeleteBuffers (1, &m_axesVBO);
+ glDeleteBuffers (1, &m_axesColorVBO);
+}
+
+// =============================================================================
+// Calculates the "hitboxes" of the camera icons so that we can tell when the
+// cursor is pointing at the camera icon.
+//
+void GLRenderer::calcCameraIcons()
+{
+ int i = 0;
+
+ for (CameraIcon& info : m_cameraIcons)
+ {
+ // MATH
+ const long x1 = (m_width - (info.cam != EFreeCamera ? 48 : 16)) + ((i % 3) * 16) - 1,
+ y1 = ((i / 3) * 16) + 1;
+
+ info.srcRect = QRect (0, 0, 16, 16);
+ info.destRect = QRect (x1, y1, 16, 16);
+ info.selRect = QRect (
+ info.destRect.x(),
+ info.destRect.y(),
+ info.destRect.width() + 1,
+ info.destRect.height() + 1
+ );
+
+ ++i;
+ }
+}
+
+// =============================================================================
+//
+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 (cfg::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);
+ }
+}
+
+// =============================================================================
+//
+void GLRenderer::needZoomToFit()
+{
+ if (document() != null)
+ currentDocumentData().needZoomToFit = true;
+}
+
+// =============================================================================
+//
+void GLRenderer::resetAngles()
+{
+ rot (X) = 30.0f;
+ rot (Y) = 325.f;
+ pan (X) = pan (Y) = rot (Z) = 0.0f;
+ needZoomToFit();
+}
+
+// =============================================================================
+//
+void GLRenderer::resetAllAngles()
+{
+ ECamera oldcam = camera();
+
+ for (int i = 0; i < 7; ++i)
+ {
+ setCamera ((ECamera) i);
+ resetAngles();
+ }
+
+ setCamera (oldcam);
+}
+
+// =============================================================================
+//
+void GLRenderer::initializeGL()
+{
+#ifdef USE_QT5
+ initializeOpenGLFunctions();
+#endif
+ setBackground();
+ glLineWidth (cfg::LineThickness);
+ glLineStipple (1, 0x6666);
+ setAutoFillBackground (false);
+ setMouseTracking (true);
+ setFocusPolicy (Qt::WheelFocus);
+ compiler()->initialize();
+ initializeAxes();
+ RendererInitialized = true;
+}
+
+// =============================================================================
+//
+void GLRenderer::initializeAxes()
+{
+ float axesdata[18];
+ float colordata[18];
+ memset (axesdata, 0, sizeof axesdata);
+
+ for (int i = 0; i < 3; ++i)
+ {
+ for_axes (ax)
+ {
+ axesdata[(i * 6) + ax] = g_GLAxes[i].vert[ax];
+ axesdata[(i * 6) + 3 + ax] = -g_GLAxes[i].vert[ax];
+ }
+
+ for (int j = 0; j < 2; ++j)
+ {
+ colordata[(i * 6) + (j * 3) + 0] = g_GLAxes[i].col.red();
+ colordata[(i * 6) + (j * 3) + 1] = g_GLAxes[i].col.green();
+ colordata[(i * 6) + (j * 3) + 2] = g_GLAxes[i].col.blue();
+ }
+ }
+
+ glGenBuffers (1, &m_axesVBO);
+ glBindBuffer (GL_ARRAY_BUFFER, m_axesVBO);
+ glBufferData (GL_ARRAY_BUFFER, sizeof axesdata, axesdata, 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);
+}
+
+// =============================================================================
+//
+QColor GLRenderer::getMainColor()
+{
+ QColor col (cfg::MainColor);
+
+ if (not col.isValid())
+ return QColor (0, 0, 0);
+
+ col.setAlpha (cfg::MainColorAlpha * 255.f);
+ return col;
+}
+
+// =============================================================================
+//
+void GLRenderer::setBackground()
+{
+ if (isPicking())
+ {
+ glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
+ return;
+ }
+
+ QColor col (cfg::BackgroundColor);
+
+ if (not col.isValid())
+ return;
+
+ col.setAlpha (255);
+
+ m_darkbg = Luma (col) < 80;
+ m_bgcolor = col;
+ qglClearColor (col);
+}
+
+// =============================================================================
+//
+void GLRenderer::refresh()
+{
+ update();
+
+ if (isVisible())
+ swapBuffers();
+}
+
+// =============================================================================
+//
+void GLRenderer::hardRefresh()
+{
+ if (not RendererInitialized)
+ return;
+
+ compiler()->compileDocument (CurrentDocument());
+ refresh();
+}
+
+// =============================================================================
+//
+void GLRenderer::resizeGL (int w, int h)
+{
+ m_width = w;
+ m_height = h;
+
+ calcCameraIcons();
+
+ glViewport (0, 0, w, h);
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective (45.0f, (double) w / (double) h, 1.0f, 10000.0f);
+ glMatrixMode (GL_MODELVIEW);
+}
+
+// =============================================================================
+//
+void GLRenderer::drawGLScene()
+{
+ if (document() == null)
+ return;
+
+ if (currentDocumentData().needZoomToFit)
+ {
+ currentDocumentData().needZoomToFit = false;
+ zoomAllToFit();
+ }
+
+ if (cfg::DrawWireframe and not isPicking())
+ glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable (GL_DEPTH_TEST);
+
+ if (camera() != EFreeCamera)
+ {
+ glMatrixMode (GL_PROJECTION);
+ glPushMatrix();
+
+ glLoadIdentity();
+ glOrtho (-m_virtWidth, m_virtWidth, -m_virtHeight, m_virtHeight, -100.0f, 100.0f);
+ glTranslatef (pan (X), pan (Y), 0.0f);
+
+ if (camera() != EFrontCamera and camera() != EBackCamera)
+ {
+ glRotatef (90.0f, g_FixedCameras[camera()].glrotate[0],
+ g_FixedCameras[camera()].glrotate[1],
+ g_FixedCameras[camera()].glrotate[2]);
+ }
+
+ // Back camera needs to be handled differently
+ if (camera() == EBackCamera)
+ {
+ 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 (pan (X), pan (Y), -zoom());
+ glRotatef (rot (X), 1.0f, 0.0f, 0.0f);
+ glRotatef (rot (Y), 0.0f, 1.0f, 0.0f);
+ glRotatef (rot (Z), 0.0f, 0.0f, 1.0f);
+ }
+
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glEnableClientState (GL_COLOR_ARRAY);
+
+ if (isPicking())
+ {
+ drawVBOs (VBOSF_Triangles, VBOCM_PickColors, GL_TRIANGLES);
+ drawVBOs (VBOSF_Quads, VBOCM_PickColors, GL_QUADS);
+ drawVBOs (VBOSF_Lines, VBOCM_PickColors, GL_LINES);
+ drawVBOs (VBOSF_CondLines, VBOCM_PickColors, GL_LINES);
+ }
+ else
+ {
+ if (cfg::BFCRedGreenView)
+ {
+ glEnable (GL_CULL_FACE);
+ glCullFace (GL_BACK);
+ drawVBOs (VBOSF_Triangles, VBOCM_BFCFrontColors, GL_TRIANGLES);
+ drawVBOs (VBOSF_Quads, VBOCM_BFCFrontColors, GL_QUADS);
+ glCullFace (GL_FRONT);
+ drawVBOs (VBOSF_Triangles, VBOCM_BFCBackColors, GL_TRIANGLES);
+ drawVBOs (VBOSF_Quads, VBOCM_BFCBackColors, GL_QUADS);
+ glDisable (GL_CULL_FACE);
+ }
+ else
+ {
+ if (cfg::RandomColors)
+ {
+ drawVBOs (VBOSF_Triangles, VBOCM_RandomColors, GL_TRIANGLES);
+ drawVBOs (VBOSF_Quads, VBOCM_RandomColors, GL_QUADS);
+ }
+ else
+ {
+ drawVBOs (VBOSF_Triangles, VBOCM_NormalColors, GL_TRIANGLES);
+ drawVBOs (VBOSF_Quads, VBOCM_NormalColors, GL_QUADS);
+ }
+ }
+
+ drawVBOs (VBOSF_Lines, VBOCM_NormalColors, GL_LINES);
+ glEnable (GL_LINE_STIPPLE);
+ drawVBOs (VBOSF_CondLines, VBOCM_NormalColors, GL_LINES);
+ glDisable (GL_LINE_STIPPLE);
+
+ if (cfg::DrawAxes)
+ {
+ 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);
+ CHECK_GL_ERROR();
+ }
+ }
+
+ glPopMatrix();
+ glBindBuffer (GL_ARRAY_BUFFER, 0);
+ glDisableClientState (GL_VERTEX_ARRAY);
+ glDisableClientState (GL_COLOR_ARRAY);
+ CHECK_GL_ERROR();
+ glDisable (GL_CULL_FACE);
+ glMatrixMode (GL_MODELVIEW);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+}
+
+// =============================================================================
+//
+void GLRenderer::drawVBOs (EVBOSurface surface, EVBOComplement colors, GLenum type)
+{
+ // Filter this through some configuration options
+ if ((Eq (surface, VBOSF_Quads, VBOSF_Triangles) and cfg::DrawSurfaces == false) or
+ (surface == VBOSF_Lines and cfg::DrawEdgeLines == false) or
+ (surface == VBOSF_CondLines and cfg::DrawConditionalLines == false))
+ {
+ return;
+ }
+
+ int surfacenum = m_compiler->vboNumber (surface, VBOCM_Surfaces);
+ int colornum = m_compiler->vboNumber (surface, colors);
+ m_compiler->prepareVBO (surfacenum);
+ m_compiler->prepareVBO (colornum);
+ GLuint surfacevbo = m_compiler->vbo (surfacenum);
+ GLuint colorvbo = m_compiler->vbo (colornum);
+ GLsizei count = m_compiler->vboSize (surfacenum) / 3;
+
+ if (count > 0)
+ {
+ glBindBuffer (GL_ARRAY_BUFFER, surfacevbo);
+ glVertexPointer (3, GL_FLOAT, 0, null);
+ CHECK_GL_ERROR();
+ glBindBuffer (GL_ARRAY_BUFFER, colorvbo);
+ glColorPointer (4, GL_FLOAT, 0, null);
+ CHECK_GL_ERROR();
+ glDrawArrays (type, 0, count);
+ CHECK_GL_ERROR();
+ }
+}
+
+// =============================================================================
+// This converts a 2D point on the screen to a 3D point in the model. If 'snap'
+// is true, the 3D point will snap to the current grid.
+//
+Vertex GLRenderer::coordconv2_3 (const QPoint& pos2d, bool snap) const
+{
+ assert (camera() != EFreeCamera);
+
+ Vertex pos3d;
+ const LDFixedCamera* cam = &g_FixedCameras[camera()];
+ const Axis axisX = cam->axisX;
+ const Axis axisY = cam->axisY;
+ const int negXFac = cam->negX ? -1 : 1,
+ negYFac = cam->negY ? -1 : 1;
+
+ // Calculate cx and cy - these are the LDraw unit coords the cursor is at.
+ double cx = (-m_virtWidth + ((2 * pos2d.x() * m_virtWidth) / m_width) - pan (X));
+ double cy = (m_virtHeight - ((2 * pos2d.y() * m_virtHeight) / m_height) - pan (Y));
+
+ if (snap)
+ {
+ cx = Grid::Snap (cx, Grid::Coordinate);
+ cy = Grid::Snap (cy, Grid::Coordinate);
+ }
+
+ cx *= negXFac;
+ cy *= negYFac;
+
+ RoundToDecimals (cx, 4);
+ RoundToDecimals (cy, 4);
+
+ // Create the vertex from the coordinates
+ pos3d.setCoordinate (axisX, cx);
+ pos3d.setCoordinate (axisY, cy);
+ pos3d.setCoordinate ((Axis) (3 - axisX - axisY), getDepthValue());
+ return pos3d;
+}
+
+// =============================================================================
+//
+// Inverse operation for the above - convert a 3D position to a 2D screen
+// position. Don't ask me how this code manages to work, I don't even know.
+//
+QPoint GLRenderer::coordconv3_2 (const Vertex& pos3d)
+{
+ GLfloat m[16];
+ const LDFixedCamera* cam = &g_FixedCameras[camera()];
+ const Axis axisX = cam->axisX;
+ const Axis axisY = cam->axisY;
+ const int negXFac = cam->negX ? -1 : 1,
+ negYFac = cam->negY ? -1 : 1;
+
+ glGetFloatv (GL_MODELVIEW_MATRIX, m);
+
+ const double x = pos3d.x();
+ const double y = pos3d.y();
+ const double z = pos3d.z();
+
+ Vertex transformed;
+ transformed.setX ((m[0] * x) + (m[1] * y) + (m[2] * z) + m[3]);
+ transformed.setY ((m[4] * x) + (m[5] * y) + (m[6] * z) + m[7]);
+ transformed.setZ ((m[8] * x) + (m[9] * y) + (m[10] * z) + m[11]);
+
+ double rx = (((transformed[axisX] * negXFac) + m_virtWidth + pan (X)) * m_width) / (2 * m_virtWidth);
+ double ry = (((transformed[axisY] * negYFac) - m_virtHeight + pan (Y)) * m_height) / (2 * m_virtHeight);
+
+ return QPoint (rx, -ry);
+}
+
+QPen GLRenderer::textPen() const
+{
+ return QPen (m_darkbg ? Qt::white : Qt::black);
+}
+
+QPen GLRenderer::linePen() const
+{
+ QPen linepen (m_thinBorderPen);
+ linepen.setWidth (2);
+ linepen.setColor (Luma (m_bgcolor) < 40 ? Qt::white : Qt::black);
+ return linepen;
+}
+
+// =============================================================================
+//
+void GLRenderer::paintEvent (QPaintEvent*)
+{
+ doMakeCurrent();
+ m_virtWidth = zoom();
+ m_virtHeight = (m_height * m_virtWidth) / m_width;
+ initGLData();
+ drawGLScene();
+
+ QPainter paint (this);
+ QFontMetrics metrics = QFontMetrics (QFont());
+ paint.setRenderHint (QPainter::HighQualityAntialiasing);
+
+ // If we wish to only draw the brick, stop here
+ if (isDrawOnly())
+ return;
+
+#ifndef RELEASE
+ if (not isPicking())
+ {
+ QString text = format ("Rotation: (%1, %2, %3)\nPanning: (%4, %5), Zoom: %6",
+ rot(X), rot(Y), rot(Z), pan(X), pan(Y), zoom());
+ QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text);
+ paint.setPen (textPen());
+ paint.drawText ((width() - textSize.width()) / 2, height() - textSize.height(), textSize.width(),
+ textSize.height(), Qt::AlignCenter, text);
+ }
+#endif
+
+ if (camera() != EFreeCamera and not isPicking())
+ {
+ // Paint the overlay image if we have one
+ const LDGLOverlay& overlay = currentDocumentData().overlays[camera()];
+
+ if (overlay.img != null)
+ {
+ QPoint v0 = coordconv3_2 (currentDocumentData().overlays[camera()].v0),
+ v1 = coordconv3_2 (currentDocumentData().overlays[camera()].v1);
+
+ QRect targRect (v0.x(), v0.y(), Abs (v1.x() - v0.x()), Abs (v1.y() - v0.y())),
+ srcRect (0, 0, overlay.img->width(), overlay.img->height());
+ paint.drawImage (targRect, *overlay.img, srcRect);
+ }
+
+ // Paint the coordinates onto the screen.
+ QString text = format (tr ("X: %1, Y: %2, Z: %3"), m_position3D[X], m_position3D[Y], m_position3D[Z]);
+ QFontMetrics metrics = QFontMetrics (font());
+ QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text);
+ paint.setPen (textPen());
+ paint.drawText (m_width - textSize.width(), m_height - 16, textSize.width(),
+ textSize.height(), Qt::AlignCenter, text);
+ }
+
+ if (not isPicking())
+ {
+ // Draw edit mode HUD
+ m_editmode->render (paint);
+
+ // Draw a background for the selected camera
+ paint.setPen (m_thinBorderPen);
+ paint.setBrush (QBrush (QColor (0, 128, 160, 128)));
+ paint.drawRect (m_cameraIcons[camera()].selRect);
+
+ // Draw the camera icons
+ for (CameraIcon& info : m_cameraIcons)
+ {
+ // Don't draw the free camera icon when in draw mode
+ if (&info == &m_cameraIcons[EFreeCamera] and not m_editmode->allowFreeCamera())
+ continue;
+
+ paint.drawPixmap (info.destRect, *info.img, info.srcRect);
+ }
+
+ QString formatstr = tr ("%1 Camera");
+
+ // Draw a label for the current camera in the bottom left corner
+ {
+ const int margin = 4;
+
+ QString label;
+ label = format (formatstr, tr (g_CameraNames[camera()]));
+ paint.setPen (textPen());
+ paint.drawText (QPoint (margin, height() - (margin + metrics.descent())), label);
+ }
+
+ // Tool tips
+ if (m_drawToolTip)
+ {
+ if (not m_cameraIcons[m_toolTipCamera].destRect.contains (m_mousePosition))
+ m_drawToolTip = false;
+ else
+ {
+ QString label = format (formatstr, tr (g_CameraNames[m_toolTipCamera]));
+ QToolTip::showText (m_globalpos, label);
+ }
+ }
+ }
+
+ // Message log
+ if (messageLog())
+ {
+ int y = 0;
+ const int margin = 2;
+ QColor penColor = textPen().color();
+
+ for (const MessageManager::Line& line : messageLog()->getLines())
+ {
+ penColor.setAlphaF (line.alpha);
+ paint.setPen (penColor);
+ paint.drawText (QPoint (margin, y + margin + metrics.ascent()), line.text);
+ y += metrics.height();
+ }
+ }
+}
+
+// =============================================================================
+//
+void GLRenderer::drawBlip (QPainter& paint, QPointF pos) const
+{
+ QPen pen = m_thinBorderPen;
+ const int blipsize = 8;
+ pen.setWidth (1);
+ paint.setPen (pen);
+ paint.setBrush (QColor (64, 192, 0));
+ paint.drawEllipse (pos.x() - blipsize / 2, pos.y() - blipsize / 2, blipsize, blipsize);
+}
+
+// =============================================================================
+//
+void GLRenderer::clampAngle (double& angle) const
+{
+ while (angle < 0)
+ angle += 360.0;
+
+ while (angle > 360.0)
+ angle -= 360.0;
+}
+
+// =============================================================================
+//
+void GLRenderer::mouseReleaseEvent (QMouseEvent* ev)
+{
+ const bool wasLeft = (m_lastButtons & Qt::LeftButton) and not (ev->buttons() & Qt::LeftButton);
+
+ Qt::MouseButtons releasedbuttons = m_lastButtons & ~ev->buttons();
+
+ if (m_panning)
+ m_panning = false;
+
+ if (wasLeft)
+ {
+ // Check if we selected a camera icon
+ if (not mouseHasMoved())
+ {
+ for (CameraIcon & info : m_cameraIcons)
+ {
+ if (info.destRect.contains (ev->pos()))
+ {
+ setCamera (info.cam);
+ goto end;
+ }
+ }
+ }
+ }
+
+ if (not isDrawOnly())
+ {
+ AbstractEditMode::MouseEventData data;
+ data.ev = ev;
+ data.mouseMoved = mouseHasMoved();
+ data.keymods = m_keymods;
+ data.releasedButtons = releasedbuttons;
+
+ if (m_editmode->mouseReleased (data))
+ goto end;
+ }
+
+end:
+ update();
+ m_totalmove = 0;
+}
+
+// =============================================================================
+//
+void GLRenderer::mousePressEvent (QMouseEvent* ev)
+{
+ m_totalmove = 0;
+ m_lastButtons = ev->buttons();
+
+ if (m_editmode->mousePressed (ev))
+ ev->accept();
+}
+
+// =============================================================================
+//
+void GLRenderer::mouseMoveEvent (QMouseEvent* ev)
+{
+ int dx = ev->x() - m_mousePosition.x();
+ int dy = ev->y() - m_mousePosition.y();
+ m_totalmove += Abs (dx) + Abs (dy);
+ setCameraMoving (false);
+
+ if (not m_editmode->mouseMoved (ev))
+ {
+ const bool left = ev->buttons() & Qt::LeftButton,
+ mid = ev->buttons() & Qt::MidButton,
+ shift = ev->modifiers() & Qt::ShiftModifier;
+
+ if (mid or (left and shift))
+ {
+ pan (X) += 0.03f * dx * (zoom() / 7.5f);
+ pan (Y) -= 0.03f * dy * (zoom() / 7.5f);
+ m_panning = true;
+ setCameraMoving (true);
+ }
+ elif (left and camera() == EFreeCamera)
+ {
+ rot (X) = rot (X) + dy;
+ rot (Y) = rot (Y) + dx;
+
+ clampAngle (rot (X));
+ clampAngle (rot (Y));
+ setCameraMoving (true);
+ }
+ }
+
+ // Start the tool tip timer
+ if (not m_drawToolTip)
+ m_toolTipTimer->start (500);
+
+ // Update 2d position
+ m_mousePosition = ev->pos();
+ m_globalpos = ev->globalPos();
+
+#ifndef USE_QT5
+ m_mousePositionF = ev->posF();
+#else
+ m_mousePositionF = ev->localPos();
+#endif
+
+ // Calculate 3d position of the cursor
+ m_position3D = (camera() != EFreeCamera) ? coordconv2_3 (m_mousePosition, true) : Origin;
+
+ highlightCursorObject();
+ update();
+ ev->accept();
+}
+
+// =============================================================================
+//
+void GLRenderer::keyPressEvent (QKeyEvent* ev)
+{
+ m_keymods = ev->modifiers();
+}
+
+// =============================================================================
+//
+void GLRenderer::keyReleaseEvent (QKeyEvent* ev)
+{
+ m_keymods = ev->modifiers();
+ m_editmode->keyReleased (ev);
+ update();
+}
+
+// =============================================================================
+//
+void GLRenderer::wheelEvent (QWheelEvent* ev)
+{
+ doMakeCurrent();
+
+ zoomNotch (ev->delta() > 0);
+ zoom() = Clamp (zoom(), 0.01, 10000.0);
+ setCameraMoving (true);
+ update();
+ ev->accept();
+}
+
+// =============================================================================
+//
+void GLRenderer::leaveEvent (QEvent* ev)
+{
+ (void) ev;
+ m_drawToolTip = false;
+ m_toolTipTimer->stop();
+ update();
+}
+
+// =============================================================================
+//
+void GLRenderer::contextMenuEvent (QContextMenuEvent* ev)
+{
+ g_win->spawnContextMenu (ev->globalPos());
+}
+
+// =============================================================================
+//
+void GLRenderer::setCamera (const ECamera cam)
+{
+ // The edit mode may forbid the free camera.
+ if (cam == EFreeCamera and not m_editmode->allowFreeCamera())
+ return;
+
+ m_camera = cam;
+ cfg::Camera = (int) cam;
+ g_win->updateEditModeActions();
+}
+
+// =============================================================================
+//
+void GLRenderer::pick (int mouseX, int mouseY, bool additive)
+{
+ pick (QRect (mouseX, mouseY, mouseX + 1, mouseY + 1), additive);
+}
+
+// =============================================================================
+//
+void GLRenderer::pick (QRect const& range, bool additive)
+{
+ doMakeCurrent();
+
+ // Clear the selection if we do not wish to add to it.
+ if (not additive)
+ {
+ LDObjectList oldsel = Selection();
+ CurrentDocument()->clearSelection();
+
+ for (LDObjectPtr obj : oldsel)
+ compileObject (obj);
+ }
+
+ // 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 = Max (0, x0);
+ y0 = Max (0, y0);
+ x1 = Min (x1, m_width);
+ y1 = Min (y1, m_height);
+ const int areawidth = (x1 - x0);
+ const int areaheight = (y1 - y0);
+ const qint32 numpixels = areawidth * areaheight;
+
+ // Allocate space for the pixel data.
+ uchar* const pixeldata = new uchar[4 * numpixels];
+ uchar* pixelptr = &pixeldata[0];
+
+ // Read pixels from the color buffer.
+ glReadPixels (x0, m_height - y1, areawidth, areaheight, GL_RGBA, GL_UNSIGNED_BYTE, pixeldata);
+
+ LDObjectPtr removedObj;
+ QList indices;
+
+ // Go through each pixel read and add them to the selection.
+ // Note: black is background, those indices are skipped.
+ for (qint32 i = 0; i < numpixels; ++i)
+ {
+ qint32 idx =
+ (*(pixelptr + 0) * 0x10000) +
+ (*(pixelptr + 1) * 0x100) +
+ *(pixelptr + 2);
+ pixelptr += 4;
+
+ if (idx != 0)
+ indices << idx;
+ }
+
+ RemoveDuplicates (indices);
+
+ for (qint32 idx : indices)
+ {
+ LDObjectPtr obj = LDObject::fromID (idx);
+ assert (obj != null);
+
+ // If this is an additive single pick and the object is currently selected,
+ // we remove it from selection instead.
+ if (additive)
+ {
+ if (obj->isSelected())
+ {
+ obj->deselect();
+ removedObj = obj;
+ break;
+ }
+ }
+
+ obj->select();
+ }
+
+ delete[] pixeldata;
+
+ // Update everything now.
+ g_win->updateSelection();
+
+ // Recompile the objects now to update their color
+ for (LDObjectPtr obj : Selection())
+ compileObject (obj);
+
+ if (removedObj)
+ compileObject (removedObj);
+
+ setPicking (false);
+ repaint();
+}
+
+//
+// Simpler version of GLRenderer::pick which simply picks whatever object on the screen
+//
+LDObjectPtr GLRenderer::pickOneObject (int mouseX, int mouseY)
+{
+ uchar pixel[4];
+ doMakeCurrent();
+ setPicking (true);
+ drawGLScene();
+ glReadPixels (mouseX, m_height - mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+ LDObjectPtr obj = LDObject::fromID ((pixel[0] * 0x10000) + (pixel[1] * 0x100) + pixel[2]);
+ setPicking (false);
+ repaint();
+ return obj;
+}
+
+// =============================================================================
+//
+void GLRenderer::setEditMode (EditModeType a)
+{
+ if (m_editmode != null and m_editmode->type() == a)
+ return;
+
+ delete m_editmode;
+ m_editmode = AbstractEditMode::createByType (this, a);
+
+ // If we cannot use the free camera, use the top one instead.
+ if (camera() == EFreeCamera and not m_editmode->allowFreeCamera())
+ setCamera (ETopCamera);
+
+ g_win->updateEditModeActions();
+ update();
+}
+
+// =============================================================================
+//
+EditModeType GLRenderer::currentEditModeType() const
+{
+ return m_editmode->type();
+}
+
+// =============================================================================
+//
+void GLRenderer::setDocument (LDDocumentPtr const& a)
+{
+ m_document = a;
+
+ if (a != null)
+ {
+ initOverlaysFromObjects();
+
+ if (not currentDocumentData().init)
+ {
+ resetAllAngles();
+ currentDocumentData().init = true;
+ }
+
+ currentDocumentData().needZoomToFit = true;
+ }
+}
+
+// =============================================================================
+//
+void GLRenderer::setPicking (const bool& a)
+{
+ m_isPicking = a;
+ setBackground();
+
+ if (isPicking())
+ {
+ glDisable (GL_DITHER);
+
+ // Use particularly thick lines while picking ease up selecting lines.
+ glLineWidth (Max (cfg::LineThickness, 6.5));
+ }
+ else
+ {
+ glEnable (GL_DITHER);
+
+ // Restore line thickness
+ glLineWidth (cfg::LineThickness);
+ }
+}
+
+// =============================================================================
+//
+void GLRenderer::getRelativeAxes (Axis& relX, Axis& relY) const
+{
+ const LDFixedCamera* cam = &g_FixedCameras[camera()];
+ relX = cam->axisX;
+ relY = cam->axisY;
+}
+
+// =============================================================================
+//
+Axis GLRenderer::getRelativeZ() const
+{
+ const LDFixedCamera* cam = &g_FixedCameras[camera()];
+ return (Axis) (3 - cam->axisX - cam->axisY);
+}
+
+// =============================================================================
+//
+static QList GetVerticesOf (LDObjectPtr obj)
+{
+ QList verts;
+
+ if (obj->numVertices() >= 2)
+ {
+ for (int i = 0; i < obj->numVertices(); ++i)
+ verts << obj->vertex (i);
+ }
+ elif (obj->type() == OBJ_Subfile)
+ {
+ LDSubfilePtr ref = obj.staticCast();
+ LDObjectList objs = ref->inlineContents (true, false);
+
+ for (LDObjectPtr obj : objs)
+ {
+ verts << GetVerticesOf (obj);
+ obj->destroy();
+ }
+ }
+
+ return verts;
+}
+
+// =============================================================================
+//
+void GLRenderer::compileObject (LDObjectPtr obj)
+{
+ compiler()->stageForCompilation (obj);
+}
+
+// =============================================================================
+//
+void GLRenderer::forgetObject (LDObjectPtr obj)
+{
+ if (compiler() != null)
+ compiler()->dropObject (obj);
+}
+
+// =============================================================================
+//
+uchar* GLRenderer::getScreencap (int& w, int& h)
+{
+ w = m_width;
+ h = m_height;
+ uchar* cap = new uchar[4 * w * h];
+
+ m_screencap = true;
+ update();
+ m_screencap = false;
+
+ // Capture the pixels
+ glReadPixels (0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, cap);
+
+ return cap;
+}
+
+// =============================================================================
+//
+void GLRenderer::slot_toolTipTimer()
+{
+ // We come here if the cursor has stayed in one place for longer than a
+ // a second. Check if we're holding it over a camera icon - if so, draw
+ // a tooltip.
+ for (CameraIcon & icon : m_cameraIcons)
+ {
+ if (icon.destRect.contains (m_mousePosition))
+ {
+ m_toolTipCamera = icon.cam;
+ m_drawToolTip = true;
+ update();
+ break;
+ }
+ }
+}
+
+// =============================================================================
+//
+Axis GLRenderer::getCameraAxis (bool y, ECamera camid)
+{
+ if (camid == (ECamera) -1)
+ camid = camera();
+
+ const LDFixedCamera* cam = &g_FixedCameras[camid];
+ return (y) ? cam->axisY : cam->axisX;
+}
+
+// =============================================================================
+//
+bool GLRenderer::setupOverlay (ECamera cam, QString file, int x, int y, int w, int h)
+{
+ QImage* img = new QImage (QImage (file).convertToFormat (QImage::Format_ARGB32));
+ LDGLOverlay& info = getOverlay (cam);
+
+ if (img->isNull())
+ {
+ Critical (tr ("Failed to load overlay image!"));
+ currentDocumentData().overlays[cam].invalid = true;
+ delete img;
+ return false;
+ }
+
+ delete info.img; // delete the old image
+
+ info.fname = file;
+ info.lw = w;
+ info.lh = h;
+ info.ox = x;
+ info.oy = y;
+ info.img = img;
+ info.invalid = false;
+
+ if (info.lw == 0)
+ info.lw = (info.lh * img->width()) / img->height();
+ elif (info.lh == 0)
+ info.lh = (info.lw * img->height()) / img->width();
+
+ const Axis x2d = getCameraAxis (false, cam),
+ y2d = getCameraAxis (true, cam);
+ const double negXFac = g_FixedCameras[cam].negX ? -1 : 1,
+ negYFac = g_FixedCameras[cam].negY ? -1 : 1;
+
+ info.v0 = info.v1 = Origin;
+ info.v0.setCoordinate (x2d, -(info.ox * info.lw * negXFac) / img->width());
+ info.v0.setCoordinate (y2d, (info.oy * info.lh * negYFac) / img->height());
+ info.v1.setCoordinate (x2d, info.v0[x2d] + info.lw);
+ info.v1.setCoordinate (y2d, info.v0[y2d] + info.lh);
+
+ // Set alpha of all pixels to 0.5
+ for (long i = 0; i < img->width(); ++i)
+ for (long j = 0; j < img->height(); ++j)
+ {
+ uint32 pixel = img->pixel (i, j);
+ img->setPixel (i, j, 0x80000000 | (pixel & 0x00FFFFFF));
+ }
+
+ updateOverlayObjects();
+ return true;
+}
+
+// =============================================================================
+//
+void GLRenderer::clearOverlay()
+{
+ if (camera() == EFreeCamera)
+ return;
+
+ LDGLOverlay& info = currentDocumentData().overlays[camera()];
+ delete info.img;
+ info.img = null;
+
+ updateOverlayObjects();
+}
+
+// =============================================================================
+//
+void GLRenderer::setDepthValue (double depth)
+{
+ assert (camera() < EFreeCamera);
+ currentDocumentData().depthValues[camera()] = depth;
+}
+
+// =============================================================================
+//
+double GLRenderer::getDepthValue() const
+{
+ assert (camera() < EFreeCamera);
+ return currentDocumentData().depthValues[camera()];
+}
+
+// =============================================================================
+//
+const char* GLRenderer::getCameraName() const
+{
+ return g_CameraNames[camera()];
+}
+
+// =============================================================================
+//
+LDGLOverlay& GLRenderer::getOverlay (int newcam)
+{
+ return currentDocumentData().overlays[newcam];
+}
+
+// =============================================================================
+//
+void GLRenderer::zoomNotch (bool inward)
+{
+ zoom() *= inward ? 0.833f : 1.2f;
+}
+
+// =============================================================================
+//
+void GLRenderer::zoomToFit()
+{
+ zoom() = 30.0f;
+
+ if (document() == null or m_width == -1 or m_height == -1)
+ return;
+
+ 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.
+ zoom() = 30.0;
+ break;
+ }
+
+ zoomNotch (inward);
+ QVector capture (4 * m_width * m_height);
+ drawGLScene();
+ glReadPixels (0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, capture.data());
+ QImage image (capture.constData(), m_width, m_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, m_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 (m_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)
+ {
+ 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::mouseDoubleClickEvent (QMouseEvent* ev)
+{
+ if (m_editmode->mouseDoubleClicked (ev))
+ ev->accept();
+}
+
+// =============================================================================
+//
+LDOverlayPtr GLRenderer::findOverlayObject (ECamera cam)
+{
+ for (LDObjectPtr obj : document()->objects())
+ {
+ LDOverlayPtr ovlobj = obj.dynamicCast();
+
+ if (ovlobj != null and obj.staticCast()->camera() == cam)
+ return ovlobj;
+ }
+
+ return LDOverlayPtr();
+}
+
+// =============================================================================
+//
+// Read in overlays from the current file and update overlay info accordingly.
+//
+void GLRenderer::initOverlaysFromObjects()
+{
+ for (ECamera cam = EFirstCamera; cam < ENumCameras; ++cam)
+ {
+ if (cam == EFreeCamera)
+ continue;
+
+ LDGLOverlay& meta = currentDocumentData().overlays[cam];
+ LDOverlayPtr ovlobj = findOverlayObject (cam);
+
+ if (ovlobj == null and meta.img != null)
+ {
+ delete meta.img;
+ meta.img = null;
+ }
+ elif (ovlobj != null and
+ (meta.img == null or meta.fname != ovlobj->fileName()) and
+ not meta.invalid)
+ {
+ setupOverlay (cam, ovlobj->fileName(), ovlobj->x(),
+ ovlobj->y(), ovlobj->width(), ovlobj->height());
+ }
+ }
+}
+
+// =============================================================================
+//
+void GLRenderer::updateOverlayObjects()
+{
+ for (ECamera cam = EFirstCamera; cam < ENumCameras; ++cam)
+ {
+ if (cam == EFreeCamera)
+ continue;
+
+ LDGLOverlay& meta = currentDocumentData().overlays[cam];
+ LDOverlayPtr ovlobj = findOverlayObject (cam);
+
+ if (meta.img == null and ovlobj != null)
+ {
+ // If this is the last overlay image, we need to remove the empty space after it as well.
+ LDObjectPtr nextobj = ovlobj->next();
+
+ if (nextobj and nextobj->type() == OBJ_Empty)
+ nextobj->destroy();
+
+ // If the overlay object was there and the overlay itself is
+ // not, remove the object.
+ ovlobj->destroy();
+ }
+ elif (meta.img != null and ovlobj == null)
+ {
+ // Inverse case: image is there but the overlay object is
+ // not, thus create the object.
+ ovlobj = LDSpawn();
+
+ // Find a suitable position to place this object. We want to place
+ // this into the header, which is everything up to the first scemantic
+ // object. If we find another overlay object, place this object after
+ // the last one found. Otherwise, place it before the first schemantic
+ // object and put an empty object after it (though don't do this if
+ // there was no schemantic elements at all)
+ int i, lastOverlay = -1;
+ bool found = false;
+
+ for (i = 0; i < document()->getObjectCount(); ++i)
+ {
+ LDObjectPtr obj = document()->getObject (i);
+
+ if (obj->isScemantic())
+ {
+ found = true;
+ break;
+ }
+
+ if (obj->type() == OBJ_Overlay)
+ lastOverlay = i;
+ }
+
+ if (lastOverlay != -1)
+ document()->insertObj (lastOverlay + 1, ovlobj);
+ else
+ {
+ document()->insertObj (i, ovlobj);
+
+ if (found)
+ document()->insertObj (i + 1, LDSpawn());
+ }
+ }
+
+ if (meta.img != null and ovlobj != null)
+ {
+ ovlobj->setCamera (cam);
+ ovlobj->setFileName (meta.fname);
+ ovlobj->setX (meta.ox);
+ ovlobj->setY (meta.oy);
+ ovlobj->setWidth (meta.lw);
+ ovlobj->setHeight (meta.lh);
+ }
+ }
+
+ if (g_win->R() == this)
+ g_win->refresh();
+}
+
+// =============================================================================
+//
+void GLRenderer::highlightCursorObject()
+{
+ if (not cfg::HighlightObjectBelowCursor and objectAtCursor() == null)
+ return;
+
+ LDObjectWeakPtr newObject;
+ LDObjectWeakPtr oldObject = objectAtCursor();
+ qint32 newIndex;
+
+ if (isCameraMoving() or not cfg::HighlightObjectBelowCursor)
+ {
+ newIndex = 0;
+ }
+ else
+ {
+ setPicking (true);
+ drawGLScene();
+ setPicking (false);
+
+ unsigned char pixel[4];
+ glReadPixels (m_mousePosition.x(), m_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 != null ? oldObject.toStrongRef()->id() : 0))
+ {
+ if (newIndex != 0)
+ newObject = LDObject::fromID (newIndex);
+
+ setObjectAtCursor (newObject);
+
+ if (oldObject != null)
+ compileObject (oldObject);
+
+ if (newObject != null)
+ compileObject (newObject);
+ }
+
+ update();
+}
+
+void GLRenderer::dragEnterEvent (QDragEnterEvent* ev)
+{
+ if (g_win != null and ev->source() == g_win->getPrimitivesTree() and g_win->getPrimitivesTree()->currentItem() != null)
+ ev->acceptProposedAction();
+}
+
+void GLRenderer::dropEvent (QDropEvent* ev)
+{
+ if (g_win != null and ev->source() == g_win->getPrimitivesTree())
+ {
+ QString primName = static_cast (g_win->getPrimitivesTree()->currentItem())->primitive()->name;
+ LDSubfilePtr ref = LDSpawn();
+ ref->setColor (MainColor());
+ ref->setFileInfo (GetDocument (primName));
+ ref->setPosition (Origin);
+ ref->setTransform (IdentityMatrix);
+ LDDocument::current()->insertObj (g_win->getInsertionPoint(), ref);
+ ref->select();
+ g_win->buildObjList();
+ g_win->R()->refresh();
+ ev->acceptProposedAction();
+ }
+}
+
+Vertex const& GLRenderer::position3D() const
+{
+ return m_position3D;
+}
+
+LDFixedCamera const& GLRenderer::getFixedCamera (ECamera cam) const
+{
+ return g_FixedCameras[cam];
+}
+
+bool GLRenderer::mouseHasMoved() const
+{
+ return m_totalmove >= 10;
+}
+
+QPoint const& GLRenderer::mousePosition() const
+{
+ return m_mousePosition;
+}
+
+QPointF const& GLRenderer::mousePositionF() const
+{
+ return m_mousePositionF;
+}
+
+void GLRenderer::doMakeCurrent()
+{
+ makeCurrent();
+ initializeOpenGLFunctions();
+}
+
+int GLRenderer::depthNegateFactor() const
+{
+ return g_FixedCameras[camera()].negatedDepth ? -1 : 1;
+}
+
+Qt::KeyboardModifiers GLRenderer::keyboardModifiers() const
+{
+ return m_keymods;
+}
+
+LDFixedCamera const& GetFixedCamera (ECamera cam)
+{
+ assert (cam != EFreeCamera);
+ return g_FixedCameras[cam];
+}