--- a/src/gldraw.cpp Fri Dec 13 00:39:49 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1995 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 Santeri 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/>. - */ - -#include <QGLWidget> -#include <QWheelEvent> -#include <QMouseEvent> -#include <QContextMenuEvent> -#include <QInputDialog> -#include <QToolTip> -#include <QTimer> -#include <GL/glu.h> - -#include "main.h" -#include "config.h" -#include "document.h" -#include "gldraw.h" -#include "colors.h" -#include "gui.h" -#include "misc.h" -#include "history.h" -#include "dialogs.h" -#include "addObjectDialog.h" -#include "messagelog.h" -#include "primitives.h" -#include "moc_gldraw.cpp" - -static const LDFixedCameraInfo g_FixedCameras[6] = -{ {{ 1, 0, 0 }, X, Z, false, false }, - {{ 0, 0, 0 }, X, Y, false, true }, - {{ 0, 1, 0 }, Z, Y, true, true }, - {{ -1, 0, 0 }, X, Z, false, true }, - {{ 0, 0, 0 }, X, Y, true, true }, - {{ 0, -1, 0 }, Z, Y, false, true }, -}; - -static const matrix g_circleDrawTransforms[3] = -{ { 2, 0, 0, 0, 1, 0, 0, 0, 2 }, - { 2, 0, 0, 0, 0, 2, 0, 1, 0 }, - { 0, 1, 0, 2, 0, 0, 0, 0, 2 }, -}; - -cfg (String, gl_bgcolor, "#CCCCD9"); -cfg (String, gl_maincolor, "#707078"); -cfg (Float, gl_maincolor_alpha, 1.0); -cfg (Int, gl_linethickness, 2); -cfg (Bool, gl_colorbfc, false); -cfg (Int, gl_camera, GLRenderer::EFreeCamera); -cfg (Bool, gl_blackedges, false); -cfg (Bool, gl_axes, false); -cfg (Bool, gl_wireframe, false); -cfg (Bool, gl_logostuds, false); -cfg (Bool, gl_aa, 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") -}; - -const GL::EFixedCamera g_Cameras[7] = -{ GL::ETopCamera, - GL::EFrontCamera, - GL::ELeftCamera, - GL::EBottomCamera, - GL::EBackCamera, - GL::ERightCamera, - GL::EFreeCamera -}; - -const struct LDGLAxis -{ const QColor col; - const vertex vert; -} g_GLAxes[3] = -{ { QColor (255, 0, 0), vertex (10000, 0, 0) }, - { QColor (80, 192, 0), vertex (0, 10000, 0) }, - { QColor (0, 160, 192), vertex (0, 0, 10000) }, -}; - -static bool g_glInvert = false; -static QList<int> g_warnedColors; - -// ============================================================================= -// ----------------------------------------------------------------------------- -GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent) -{ m_Picking = m_rangepick = false; - m_camera = (GL::EFixedCamera) gl_camera.value; - m_drawToolTip = false; - m_EditMode = ESelectMode; - m_rectdraw = false; - m_panning = false; - setFile (null); - setDrawOnly (false); - setMessageLog (null); - m_width = m_height = -1; - m_hoverpos = g_origin; - - m_toolTipTimer = new QTimer (this); - m_toolTipTimer->setSingleShot (true); - connect (m_toolTipTimer, SIGNAL (timeout()), this, SLOT (slot_toolTipTimer())); - - m_thickBorderPen = QPen (QColor (0, 0, 0, 208), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); - m_thinBorderPen = m_thickBorderPen; - m_thinBorderPen.setWidth (1); - - // Init camera icons - for (const GL::EFixedCamera cam : g_Cameras) - { str iconname = fmt ("camera-%1", tr (g_CameraNames[cam]).toLower()); - - CameraIcon* info = &m_cameraIcons[cam]; - info->img = new QPixmap (getIcon (iconname)); - info->cam = cam; - } - - for (int i = 0; i < 6; ++i) - { m_overlays[i].img = null; - m_depthValues[i] = 0.0f; - } - - calcCameraIcons(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -GLRenderer::~GLRenderer() -{ for (int i = 0; i < 6; ++i) - delete m_overlays[i].img; - - for (CameraIcon& info : m_cameraIcons) - delete info.img; -} - -// ============================================================================= -// 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 (gl_aa) - { 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::resetAngles() -{ rot (X) = 30.0f; - rot (Y) = 325.f; - pan (X) = pan (Y) = rot (Z) = 0.0f; - zoomToFit(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::resetAllAngles() -{ EFixedCamera oldcam = camera(); - - for (int i = 0; i < 7; ++i) - { setCamera ((EFixedCamera) i); - resetAngles(); - } - - setCamera (oldcam); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::initializeGL() -{ setBackground(); - - glLineWidth (gl_linethickness); - - setAutoFillBackground (false); - setMouseTracking (true); - setFocusPolicy (Qt::WheelFocus); - compileAllObjects(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -QColor GLRenderer::getMainColor() -{ QColor col (gl_maincolor); - - if (!col.isValid()) - return QColor (0, 0, 0); - - col.setAlpha (gl_maincolor_alpha * 255.f); - return col; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::setBackground() -{ QColor col (gl_bgcolor); - - if (!col.isValid()) - return; - - col.setAlpha (255); - - m_darkbg = luma (col) < 80; - m_bgcolor = col; - qglClearColor (col); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::setObjectColor (LDObject* obj, const ListType list) -{ QColor qcol; - - if (!obj->isColored()) - return; - - if (list == GL::PickList) - { // Make the color by the object's ID if we're picking, so we can make the - // ID again from the color we get from the picking results. Be sure to use - // the top level parent's index since we want a subfile's children point - // to the subfile itself. - long i = obj->topLevelParent()->getID(); - - // Calculate a color based from this index. This method caters for - // 16777216 objects. I don't think that'll be exceeded anytime soon. :) - // ATM biggest is 53588.dat with 12600 lines. - double r = (i / (256 * 256)) % 256, - g = (i / 256) % 256, - b = i % 256; - - qglColor (QColor (r, g, b)); - return; - } - - if ((list == BFCFrontList || list == BFCBackList) && - obj->getType() != LDObject::Line && - obj->getType() != LDObject::CondLine) - { if (list == GL::BFCFrontList) - qcol = QColor (40, 192, 0); - else - qcol = QColor (224, 0, 0); - } - else - { if (obj->getColor() == maincolor) - qcol = getMainColor(); - else - { LDColor* col = getColor (obj->getColor()); - - if (col) - qcol = col->faceColor; - } - - if (obj->getColor() == edgecolor) - { qcol = luma (m_bgcolor) < 40 ? QColor (64, 64, 64) : Qt::black; - LDColor* col; - - if (!gl_blackedges && obj->getParent() && (col = getColor (obj->getParent()->getColor()))) - qcol = col->edgeColor; - } - - if (qcol.isValid() == false) - { // The color was unknown. Use main color to make the object at least - // not appear pitch-black. - if (obj->getColor() != edgecolor) - qcol = getMainColor(); - - // Warn about the unknown colors, but only once. - for (int i : g_warnedColors) - if (obj->getColor() == i) - return; - - log ("%1: Unknown color %2!\n", __func__, obj->getColor()); - g_warnedColors << obj->getColor(); - return; - } - } - - long r = qcol.red(), - g = qcol.green(), - b = qcol.blue(), - a = qcol.alpha(); - - if (obj->topLevelParent()->isSelected()) - { // Brighten it up for the select list. - const uchar add = 51; - - r = min (r + add, 255l); - g = min (g + add, 255l); - b = min (b + add, 255l); - } - - glColor4f ( - ((double) r) / 255.0f, - ((double) g) / 255.0f, - ((double) b) / 255.0f, - ((double) a) / 255.0f); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::refresh() -{ update(); - swapBuffers(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::hardRefresh() -{ compileAllObjects(); - refresh(); - - glLineWidth (gl_linethickness); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -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 (getFile() == null) - return; - - if (gl_wireframe && !isPicking()) - glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable (GL_DEPTH_TEST); - - if (m_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 (m_camera != EFrontCamera && m_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 (m_camera == GLRenderer::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); - } - - const GL::ListType list = (!isDrawOnly() && isPicking()) ? PickList : NormalList; - - if (gl_colorbfc && !isPicking() && !isDrawOnly()) - { glEnable (GL_CULL_FACE); - - for (LDObject* obj : getFile()->getObjects()) - { if (obj->isHidden()) - continue; - - glCullFace (GL_BACK); - glCallList (obj->glLists[BFCFrontList]); - - glCullFace (GL_FRONT); - glCallList (obj->glLists[BFCBackList]); - } - - glDisable (GL_CULL_FACE); - } - else - { for (LDObject* obj : getFile()->getObjects()) - { if (obj->isHidden()) - continue; - - glCallList (obj->glLists[list]); - } - } - - if (gl_axes && !isPicking() && !isDrawOnly()) - glCallList (m_axeslist); - - glPopMatrix(); - glMatrixMode (GL_MODELVIEW); - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -// 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 LDFixedCameraInfo* cam = &g_FixedCameras[m_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::Config) axisX); - cy = Grid::snap (cy, (Grid::Config) axisY); - } - - cx *= negXFac; - cy *= negYFac; - - str tmp; - // Create the vertex from the coordinates - pos3d[axisX] = tmp.sprintf ("%.3f", cx).toDouble(); - pos3d[axisY] = tmp.sprintf ("%.3f", cy).toDouble(); - pos3d[3 - axisX - axisY] = getDepthValue(); - return pos3d; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -// Inverse operation for the above - convert a 3D position to a 2D screen -// position -// ----------------------------------------------------------------------------- -QPoint GLRenderer::coordconv3_2 (const vertex& pos3d) const -{ GLfloat m[16]; - const LDFixedCameraInfo* cam = &g_FixedCameras[m_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[X] = (m[0] * x) + (m[1] * y) + (m[2] * z) + m[3]; - transformed[Y] = (m[4] * x) + (m[5] * y) + (m[6] * z) + m[7]; - transformed[Z] = (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); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::paintEvent (QPaintEvent* ev) -{ Q_UNUSED (ev) - - makeCurrent(); - m_virtWidth = zoom(); - m_virtHeight = (m_height * m_virtWidth) / m_width; - - initGLData(); - drawGLScene(); - - const QPen textpen = getTextPen(); - const QBrush polybrush (QColor (64, 192, 0, 128)); - QPainter paint (this); - QFontMetrics metrics = QFontMetrics (QFont()); - paint.setRenderHint (QPainter::HighQualityAntialiasing); - - // If we wish to only draw the brick, stop here - if (isDrawOnly()) - return; - - if (m_camera != EFreeCamera && !isPicking()) - { // Paint the overlay image if we have one - const LDGLOverlay& overlay = m_overlays[m_camera]; - - if (overlay.img != null) - { QPoint v0 = coordconv3_2 (m_overlays[m_camera].v0), - v1 = coordconv3_2 (m_overlays[m_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. - str text = fmt (tr ("X: %1, Y: %2, Z: %3"), m_hoverpos[X], m_hoverpos[Y], m_hoverpos[Z]); - - QFontMetrics metrics = QFontMetrics (font()); - QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text); - - paint.setPen (getTextPen()); - paint.drawText (m_width - textSize.width(), m_height - 16, textSize.width(), - textSize.height(), Qt::AlignCenter, text); - - QPen linepen = m_thinBorderPen; - linepen.setWidth (2); - linepen.setColor (luma (m_bgcolor) < 40 ? Qt::white : Qt::black); - - // If we're drawing, draw the vertices onto the screen. - if (getEditMode() == EDrawMode) - { int numverts = 4; - - if (!m_rectdraw) - numverts = m_drawedVerts.size() + 1; - - if (numverts > 0) - { QPoint poly[4]; - vertex polyverts[4]; - - if (!m_rectdraw) - { uchar i = 0; - - for (vertex& vert : m_drawedVerts) - { poly[i] = coordconv3_2 (vert); - polyverts[i] = vert; - ++i; - } - - // Draw the cursor vertex as the last one in the list. - if (numverts <= 4) - { poly[i] = coordconv3_2 (m_hoverpos); - polyverts[i] = m_hoverpos; - } - else - { numverts = 4; - } - } - else - { if (m_drawedVerts.size() > 0) - { // Get vertex information from m_rectverts - for (int i = 0; i < numverts; ++i) - { polyverts[i] = m_rectverts[i]; - poly[i] = coordconv3_2 (polyverts[i]); - } - } - else - { poly[0] = coordconv3_2 (m_hoverpos); - polyverts[0] = m_hoverpos; - } - } - - // Draw the polygon-to-be - paint.setPen (linepen); - paint.setBrush (polybrush); - paint.drawPolygon (poly, numverts); - - // Draw vertex blips - for (int i = 0; i < numverts; ++i) - { QPoint& blip = poly[i]; - drawBlip (paint, blip); - - // Draw their coordinates - paint.drawText (blip.x(), blip.y() - 8, polyverts[i].stringRep (true)); - } - } - } - elif (getEditMode() == ECircleMode) - { // If we have not specified the center point of the circle yet, preview it on the screen. - if (m_drawedVerts.isEmpty()) - drawBlip (paint, coordconv3_2 (m_hoverpos)); - else - { QVector<vertex> verts, verts2; - const double dist0 = getCircleDrawDist (0), - dist1 = (m_drawedVerts.size() >= 2) ? getCircleDrawDist (1) : -1; - const int segs = lores; - const double angleUnit = (2 * pi) / segs; - Axis relX, relY; - QVector<QPoint> ringpoints, circlepoints, circle2points; - - getRelativeAxes (relX, relY); - - // Calculate the preview positions of vertices - for (int i = 0; i < segs; ++i) - { vertex v = g_origin; - v[relX] = m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist0); - v[relY] = m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist0); - verts << v; - - if (dist1 != -1) - { v[relX] = m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist1); - v[relY] = m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist1); - verts2 << v; - } - } - - int i = 0; - for (const vertex& v : verts + verts2) - { // Calculate the 2D point of the vertex - QPoint point = coordconv3_2 (v); - - // Draw a green blip at where it is - drawBlip (paint, point); - - // Add it to the list of points for the green ring fill. - ringpoints << point; - - // Also add the circle points to separate lists - if (i < verts.size()) - circlepoints << point; - else - circle2points << point; - - ++i; - } - - // Insert the first point as the seventeenth one so that - // the ring polygon is closed properly. - if (ringpoints.size() >= 16) - ringpoints.insert (16, ringpoints[0]); - - // Same for the outer ring. Note that the indices are offset by 1 - // because of the insertion done above bumps the values. - if (ringpoints.size() >= 33) - ringpoints.insert (33, ringpoints[17]); - - // Draw the ring - paint.setBrush ((m_drawedVerts.size() >= 2) ? polybrush : Qt::NoBrush); - paint.setPen (Qt::NoPen); - paint.drawPolygon (QPolygon (ringpoints)); - - // Draw the circles - paint.setBrush (Qt::NoBrush); - paint.setPen (linepen); - paint.drawPolygon (QPolygon (circlepoints)); - paint.drawPolygon (QPolygon (circle2points)); - - { // Draw the current radius in the middle of the circle. - QPoint origin = coordconv3_2 (m_drawedVerts[0]); - str label = str::number (dist0); - paint.setPen (textpen); - paint.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label); - - if (m_drawedVerts.size() >= 2) - { label = str::number (dist1); - paint.drawText (origin.x() - (metrics.width (label) / 2), origin.y() + metrics.height(), label); - } - } - } - } - } - - // Camera icons - if (!isPicking()) - { // 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 actual icons - for (CameraIcon& info : m_cameraIcons) - { // Don't draw the free camera icon when in draw mode - if (&info == &m_cameraIcons[GL::EFreeCamera] && getEditMode() != ESelectMode) - continue; - - paint.drawPixmap (info.destRect, *info.img, info.srcRect); - } - - str fmtstr = tr ("%1 Camera"); - - // Draw a label for the current camera in the bottom left corner - { const int margin = 4; - - str label; - label = fmt (fmtstr, tr (g_CameraNames[camera()])); - paint.setPen (textpen); - paint.drawText (QPoint (margin, height() - (margin + metrics.descent())), label); - } - - // Tool tips - if (m_drawToolTip) - { if (m_cameraIcons[m_toolTipCamera].destRect.contains (m_pos) == false) - m_drawToolTip = false; - else - { str label = fmt (fmtstr, tr (g_CameraNames[m_toolTipCamera])); - QToolTip::showText (m_globalpos, label); - } - } - } - - // Message log - if (getMessageLog()) - { int y = 0; - const int margin = 2; - QColor penColor = getTextPen(); - - for (const MessageManager::Line& line : getMessageLog()->getLines()) - { penColor.setAlphaF (line.alpha); - paint.setPen (penColor); - paint.drawText (QPoint (margin, y + margin + metrics.ascent()), line.text); - y += metrics.height(); - } - } - - // If we're range-picking, draw a rectangle encompassing the selection area. - if (m_rangepick && !isPicking() && m_totalmove >= 10) - { int x0 = m_rangeStart.x(), - y0 = m_rangeStart.y(), - x1 = m_pos.x(), - y1 = m_pos.y(); - - QRect rect (x0, y0, x1 - x0, y1 - y0); - QColor fillColor = (m_addpick ? "#40FF00" : "#00CCFF"); - fillColor.setAlphaF (0.2f); - - paint.setPen (m_thickBorderPen); - paint.setBrush (QBrush (fillColor)); - paint.drawRect (rect); - } -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::drawBlip (QPainter& paint, QPoint 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); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -QColor GLRenderer::getTextPen () const -{ return m_darkbg ? Qt::white : Qt::black; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::compileAllObjects() -{ if (!getFile()) - return; - - // Compiling all is a big job, use a busy cursor - setCursor (Qt::BusyCursor); - - m_knownVerts.clear(); - - for (LDObject* obj : getFile()->getObjects()) - compileObject (obj); - - // Compile axes - glDeleteLists (m_axeslist, 1); - m_axeslist = glGenLists (1); - glNewList (m_axeslist, GL_COMPILE); - glBegin (GL_LINES); - - for (const LDGLAxis& ax : g_GLAxes) - { qglColor (ax.col); - compileVertex (ax.vert); - compileVertex (-ax.vert); - } - - glEnd(); - glEndList(); - - setCursor (Qt::ArrowCursor); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::compileSubObject (LDObject* obj, const GLenum gltype) -{ glBegin (gltype); - - const int numverts = (obj->getType() != LDObject::CondLine) ? obj->vertices() : 2; - - if (g_glInvert == false) - for (int i = 0; i < numverts; ++i) - compileVertex (obj->getVertex (i)); - else - for (int i = numverts - 1; i >= 0; --i) - compileVertex (obj->getVertex (i)); - - glEnd(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::compileList (LDObject* obj, const GLRenderer::ListType list) -{ setObjectColor (obj, list); - - switch (obj->getType()) - { case LDObject::Line: - compileSubObject (obj, GL_LINES); - break; - - case LDObject::CondLine: - - // Draw conditional lines with a dash pattern - however, use a full - // line when drawing a pick list to make selecting them easier. - if (list != GL::PickList) - { glLineStipple (1, 0x6666); - glEnable (GL_LINE_STIPPLE); - } - - compileSubObject (obj, GL_LINES); - - glDisable (GL_LINE_STIPPLE); - break; - - case LDObject::Triangle: - compileSubObject (obj, GL_TRIANGLES); - break; - - case LDObject::Quad: - compileSubObject (obj, GL_QUADS); - break; - - case LDObject::Subfile: - { LDSubfile* ref = static_cast<LDSubfile*> (obj); - QList<LDObject*> objs; - - objs = ref->inlineContents ( - LDSubfile::DeepInline | - LDSubfile::CacheInline | - LDSubfile::RendererInline); - bool oldinvert = g_glInvert; - - if (ref->getTransform().getDeterminant() < 0) - g_glInvert = !g_glInvert; - - LDObject* prev = ref->prev(); - - if (prev && prev->getType() == LDObject::BFC && static_cast<LDBFC*> (prev)->type == LDBFC::InvertNext) - g_glInvert = !g_glInvert; - - for (LDObject * obj : objs) - { compileList (obj, list); - delete obj; - } - - g_glInvert = oldinvert; - } - break; - - default: - break; - } -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::compileVertex (const vertex& vrt) -{ glVertex3d (vrt[X], -vrt[Y], -vrt[Z]); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::clampAngle (double& angle) const -{ while (angle < 0) - angle += 360.0; - - while (angle > 360.0) - angle -= 360.0; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::addDrawnVertex (vertex pos) -{ // If we picked an already-existing vertex, stop drawing - if (getEditMode() != ECircleMode) - { for (vertex& vert : m_drawedVerts) - { if (vert == pos) - { endDraw (true); - return; - } - } - } - - m_drawedVerts << pos; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::mouseReleaseEvent (QMouseEvent* ev) -{ const bool wasLeft = (m_lastButtons & Qt::LeftButton) && ! (ev->buttons() & Qt::LeftButton), - wasRight = (m_lastButtons & Qt::RightButton) && ! (ev->buttons() & Qt::RightButton), - wasMid = (m_lastButtons & Qt::MidButton) && ! (ev->buttons() & Qt::MidButton); - - if (m_panning) - m_panning = false; - - if (wasLeft) - { // Check if we selected a camera icon - if (!m_rangepick) - { for (CameraIcon & info : m_cameraIcons) - { if (info.destRect.contains (ev->pos())) - { setCamera (info.cam); - goto end; - } - } - } - - switch (getEditMode()) - { case EDrawMode: - { if (m_rectdraw) - { if (m_drawedVerts.size() == 2) - { endDraw (true); - return; - } - } else - { // If we have 4 verts, stop drawing. - if (m_drawedVerts.size() >= 4) - { endDraw (true); - return; - } - - if (m_drawedVerts.isEmpty() && ev->modifiers() & Qt::ShiftModifier) - { m_rectdraw = true; - updateRectVerts(); - } - } - - addDrawnVertex (m_hoverpos); - } break; - - case ECircleMode: - { if (m_drawedVerts.size() == 3) - { endDraw (true); - return; - } - - addDrawnVertex (m_hoverpos); - } break; - - case ESelectMode: - { if (!isDrawOnly()) - { if (m_totalmove < 10) - m_rangepick = false; - - if (!m_rangepick) - m_addpick = (m_keymods & Qt::ControlModifier); - - if (m_totalmove < 10 || m_rangepick) - pick (ev->x(), ev->y()); - } - } break; - } - - m_rangepick = false; - } - - if (wasMid && getEditMode() != ESelectMode && m_drawedVerts.size() < 4 && m_totalmove < 10) - { // Find the closest vertex to our cursor - double mindist = 1024.0f; - vertex closest; - bool valid = false; - - QPoint curspos = coordconv3_2 (m_hoverpos); - - for (const vertex& pos3d: m_knownVerts) - { QPoint pos2d = coordconv3_2 (pos3d); - - // Measure squared distance - const double dx = abs (pos2d.x() - curspos.x()), - dy = abs (pos2d.y() - curspos.y()), - distsq = (dx * dx) + (dy * dy); - - if (distsq >= 1024.0f) // 32.0f ** 2 - continue; // too far away - - if (distsq < mindist) - { mindist = distsq; - closest = pos3d; - valid = true; - - // If it's only 4 pixels away, I think we found our vertex now. - if (distsq <= 16.0f) // 4.0f ** 2 - break; - } - } - - if (valid) - addDrawnVertex (closest); - } - - if (wasRight && !m_drawedVerts.isEmpty()) - { // Remove the last vertex - m_drawedVerts.removeLast(); - - if (m_drawedVerts.isEmpty()) - m_rectdraw = false; - } - -end: - update(); - m_totalmove = 0; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::mousePressEvent (QMouseEvent* ev) -{ m_totalmove = 0; - - if (ev->modifiers() & Qt::ControlModifier) - { m_rangepick = true; - m_rangeStart.setX (ev->x()); - m_rangeStart.setY (ev->y()); - m_addpick = (m_keymods & Qt::AltModifier); - ev->accept(); - } - - m_lastButtons = ev->buttons(); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::mouseMoveEvent (QMouseEvent* ev) -{ int dx = ev->x() - m_pos.x(); - int dy = ev->y() - m_pos.y(); - m_totalmove += abs (dx) + abs (dy); - - const bool left = ev->buttons() & Qt::LeftButton, - mid = ev->buttons() & Qt::MidButton, - shift = ev->modifiers() & Qt::ShiftModifier; - - if (mid || (left && shift)) - { pan (X) += 0.03f * dx * (zoom() / 7.5f); - pan (Y) -= 0.03f * dy * (zoom() / 7.5f); - m_panning = true; - } elif (left && !m_rangepick && camera() == EFreeCamera) - { rot (X) = rot (X) + dy; - rot (Y) = rot (Y) + dx; - - clampAngle (rot (X)); - clampAngle (rot (Y)); - } - - // Start the tool tip timer - if (!m_drawToolTip) - m_toolTipTimer->start (500); - - // Update 2d position - m_pos = ev->pos(); - m_globalpos = ev->globalPos(); - - // Calculate 3d position of the cursor - m_hoverpos = (camera() != EFreeCamera) ? coordconv2_3 (m_pos, true) : g_origin; - - // Update rect vertices since m_hoverpos may have changed - updateRectVerts(); - - update(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::keyPressEvent (QKeyEvent* ev) -{ m_keymods = ev->modifiers(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::keyReleaseEvent (QKeyEvent* ev) -{ m_keymods = ev->modifiers(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::wheelEvent (QWheelEvent* ev) -{ makeCurrent(); - - zoomNotch (ev->delta() > 0); - zoom() = clamp (zoom(), 0.01, 10000.0); - - 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 GLRenderer::EFixedCamera cam) -{ m_camera = cam; - gl_camera = (int) cam; - g_win->updateEditModeActions(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::pick (int mouseX, int mouseY) -{ GLint viewport[4]; - makeCurrent(); - - // Use particularly thick lines while picking ease up selecting lines. - glLineWidth (max<double> (gl_linethickness, 6.5f)); - - // Clear the selection if we do not wish to add to it. - if (!m_addpick) - { QList<LDObject*> oldsel = selection(); - getCurrentDocument()->clearSelection(); - - for (LDObject* obj : oldsel) - compileObject (obj); - } - - setPicking (true); - - // Paint the picking scene - glDisable (GL_DITHER); - glClearColor (1.0f, 1.0f, 1.0f, 1.0f); - - drawGLScene(); - - glGetIntegerv (GL_VIEWPORT, viewport); - - int x0 = mouseX, - y0 = mouseY; - int x1, y1; - - // Determine how big an area to read - with range picking, we pick by - // the area given, with single pixel picking, we use an 1 x 1 area. - if (m_rangepick) - { x1 = m_rangeStart.x(); - y1 = m_rangeStart.y(); - } - else - { x1 = x0 + 1; - y1 = y0 + 1; - } - - // x0 and y0 must be less than x1 and y1, respectively. - if (x0 > x1) - qSwap (x0, x1); - - if (y0 > y1) - qSwap (y0, y1); - - // 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]; - - assert (viewport[3] == m_height); - - // Read pixels from the color buffer. - glReadPixels (x0, viewport[3] - y1, areawidth, areaheight, GL_RGBA, GL_UNSIGNED_BYTE, pixeldata); - - LDObject* removedObj = null; - - // Go through each pixel read and add them to the selection. - for (qint32 i = 0; i < numpixels; ++i) - { qint32 idx = - (*(pixelptr + 0) * 0x10000) + - (*(pixelptr + 1) * 0x00100) + - (*(pixelptr + 2) * 0x00001); - pixelptr += 4; - - if (idx == 0xFFFFFF) - continue; // White is background; skip - - LDObject* obj = LDObject::fromID (idx); - - // If this is an additive single pick and the object is currently selected, - // we remove it from selection instead. - if (!m_rangepick && m_addpick) - { if (obj->isSelected()) - { obj->unselect(); - removedObj = obj; - break; - } - } - - obj->select(); - } - - delete[] pixeldata; - - // Update everything now. - g_win->updateSelection(); - - // Recompile the objects now to update their color - for (LDObject* obj : selection()) - compileObject (obj); - - if (removedObj) - compileObject (removedObj); - - // Restore line thickness - glLineWidth (gl_linethickness); - - setPicking (false); - m_rangepick = false; - glEnable (GL_DITHER); - - setBackground(); - repaint(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::setEditMode (EditMode const& a) -{ m_EditMode = a; - - switch (a) - { case ESelectMode: - { unsetCursor(); - setContextMenuPolicy (Qt::DefaultContextMenu); - } break; - - case EDrawMode: - case ECircleMode: - { // Cannot draw into the free camera - use top instead. - if (m_camera == EFreeCamera) - setCamera (ETopCamera); - - // Disable the context menu - we need the right mouse button - // for removing vertices. - setContextMenuPolicy (Qt::NoContextMenu); - - // Use the crosshair cursor when drawing. - setCursor (Qt::CrossCursor); - - // Clear the selection when beginning to draw. - QList<LDObject*> priorsel = selection(); - getCurrentDocument()->clearSelection(); - - for (LDObject* obj : priorsel) - compileObject (obj); - - g_win->updateSelection(); - m_drawedVerts.clear(); - } break; - } - - g_win->updateEditModeActions(); - update(); -} - -void GLRenderer::setFile (LDDocument* const& a) -{ m_File = a; - - if (a != null) - initOverlaysFromObjects(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -matrix GLRenderer::getCircleDrawMatrix (double scale) -{ matrix transform = g_circleDrawTransforms[camera() % 3]; - - for (int i = 0; i < 9; ++i) - { if (transform[i] == 2) - transform[i] = scale; - elif (transform[i] == 1 && camera() >= 3) - transform[i] = -1; - } - - return transform; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::endDraw (bool accept) -{ (void) accept; - - // Clean the selection and create the object - QList<vertex>& verts = m_drawedVerts; - QList<LDObject*> objs; - - switch (getEditMode()) - { case EDrawMode: - { if (m_rectdraw) - { LDQuad* quad = new LDQuad; - - // Copy the vertices from m_rectverts - updateRectVerts(); - - for (int i = 0; i < quad->vertices(); ++i) - quad->setVertex (i, m_rectverts[i]); - - quad->setColor (maincolor); - objs << quad; - } - else - { switch (verts.size()) - { case 1: - { // 1 vertex - add a vertex object - LDVertex* obj = new LDVertex; - obj->pos = verts[0]; - obj->setColor (maincolor); - objs << obj; - } break; - - case 2: - { // 2 verts - make a line - LDLine* obj = new LDLine (verts[0], verts[1]); - obj->setColor (edgecolor); - objs << obj; - } break; - - case 3: - case 4: - { LDObject* obj = (verts.size() == 3) ? - static_cast<LDObject*> (new LDTriangle) : - static_cast<LDObject*> (new LDQuad); - - obj->setColor (maincolor); - - for (int i = 0; i < obj->vertices(); ++i) - obj->setVertex (i, verts[i]); - - objs << obj; - } break; - } - } - } break; - - case ECircleMode: - { const int segs = lores, divs = lores; // TODO: make customizable - double dist0 = getCircleDrawDist (0), - dist1 = getCircleDrawDist (1); - LDDocument* refFile = null; - matrix transform; - bool circleOrDisc = false; - - if (dist1 < dist0) - std::swap<double> (dist0, dist1); - - if (dist0 == dist1) - { // If the radii are the same, there's no ring space to fill. Use a circle. - refFile = ::getDocument ("4-4edge.dat"); - transform = getCircleDrawMatrix (dist0); - circleOrDisc = true; - } - elif (dist0 == 0 || dist1 == 0) - { // If either radii is 0, use a disc. - refFile = ::getDocument ("4-4disc.dat"); - transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1); - circleOrDisc = true; - } - elif (g_RingFinder (dist0, dist1)) - { // The ring finder found a solution, use that. Add the component rings to the file. - for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents()) - { // Get a ref file for this primitive. If we cannot find it in the - // LDraw library, generate it. - if ((refFile = ::getDocument (radialFileName (::Ring, lores, lores, cmp.num))) == null) - { refFile = generatePrimitive (::Ring, lores, lores, cmp.num); - refFile->setImplicit (false); - } - - LDSubfile* ref = new LDSubfile; - ref->setFileInfo (refFile); - ref->setTransform (getCircleDrawMatrix (cmp.scale)); - ref->setPosition (m_drawedVerts[0]); - ref->setColor (maincolor); - objs << ref; - } - } - else - { // Ring finder failed, last resort: draw the ring with quads - QList<QLineF> c0, c1; - Axis relX, relY, relZ; - getRelativeAxes (relX, relY); - relZ = (Axis) (3 - relX - relY); - double x0 = m_drawedVerts[0][relX], - y0 = m_drawedVerts[0][relY]; - - vertex templ; - templ[relX] = x0; - templ[relY] = y0; - templ[relZ] = getDepthValue(); - - // Calculate circle coords - makeCircle (segs, divs, dist0, c0); - makeCircle (segs, divs, dist1, c1); - - for (int i = 0; i < segs; ++i) - { vertex v0, v1, v2, v3; - v0 = v1 = v2 = v3 = templ; - v0[relX] += c0[i].x1(); - v0[relY] += c0[i].y1(); - v1[relX] += c0[i].x2(); - v1[relY] += c0[i].y2(); - v2[relX] += c1[i].x2(); - v2[relY] += c1[i].y2(); - v3[relX] += c1[i].x1(); - v3[relY] += c1[i].y1(); - - LDQuad* q = new LDQuad (v0, v1, v2, v3); - q->setColor (maincolor); - - // Ensure the quads always are BFC-front towards the camera - if (camera() % 3 <= 0) - q->invert(); - - objs << q; - } - } - - if (circleOrDisc) - { LDSubfile* ref = new LDSubfile; - ref->setFileInfo (refFile); - ref->setTransform (transform); - ref->setPosition (m_drawedVerts[0]); - ref->setColor (maincolor); - objs << ref; - } - } break; - - case ESelectMode: - { // this shouldn't happen - assert (false); - return; - } break; - } - - if (objs.size() > 0) - { g_win->beginAction (null); - - for (LDObject* obj : objs) - { getFile()->addObject (obj); - compileObject (obj); - } - - g_win->refresh(); - g_win->endAction(); - } - - m_drawedVerts.clear(); - m_rectdraw = false; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -double GLRenderer::getCircleDrawDist (int pos) const -{ assert (m_drawedVerts.size() >= pos + 1); - const vertex& v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : m_hoverpos; - Axis relX, relY; - getRelativeAxes (relX, relY); - - const double dx = m_drawedVerts[0][relX] - v1[relX]; - const double dy = m_drawedVerts[0][relY] - v1[relY]; - return sqrt ((dx * dx) + (dy * dy)); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::getRelativeAxes (Axis& relX, Axis& relY) const -{ const LDFixedCameraInfo* cam = &g_FixedCameras[m_camera]; - relX = cam->axisX; - relY = cam->axisY; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -static QList<vertex> getVertices (LDObject* obj) -{ QList<vertex> verts; - - if (obj->vertices() >= 2) - { for (int i = 0; i < obj->vertices(); ++i) - verts << obj->getVertex (i); - } elif (obj->getType() == LDObject::Subfile) - { LDSubfile* ref = static_cast<LDSubfile*> (obj); - QList<LDObject*> objs = ref->inlineContents (LDSubfile::DeepCacheInline); - - for (LDObject* obj : objs) - { verts << getVertices (obj); - delete obj; - } - } - - return verts; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::compileObject (LDObject* obj) -{ deleteLists (obj); - - for (const GL::ListType listType : g_glListTypes) - { if (isDrawOnly() && listType != GL::NormalList) - continue; - - GLuint list = glGenLists (1); - glNewList (list, GL_COMPILE); - - obj->glLists[listType] = list; - compileList (obj, listType); - - glEndList(); - } - - // Mark in known vertices of this object - QList<vertex> verts = getVertices (obj); - m_knownVerts << verts; - removeDuplicates (m_knownVerts); - - obj->m_glinit = true; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -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_pos)) - { m_toolTipCamera = icon.cam; - m_drawToolTip = true; - update(); - break; - } - } -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::deleteLists (LDObject* obj) -{ // Delete the lists but only if they have been initialized - if (!obj->m_glinit) - return; - -for (const GL::ListType listType : g_glListTypes) - glDeleteLists (obj->glLists[listType], 1); - - obj->m_glinit = false; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -Axis GLRenderer::getCameraAxis (bool y, GLRenderer::EFixedCamera camid) -{ if (camid == (GL::EFixedCamera) - 1) - camid = m_camera; - - const LDFixedCameraInfo* cam = &g_FixedCameras[camid]; - return (y) ? cam->axisY : cam->axisX; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -bool GLRenderer::setupOverlay (EFixedCamera cam, str file, int x, int y, int w, int h) -{ QImage* img = new QImage (file); - LDGLOverlay& info = getOverlay (cam); - - if (img->isNull()) - { critical (tr ("Failed to load overlay image!")); - 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; - - 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 = g_origin; - info.v0[x2d] = - (info.ox * info.lw * negXFac) / img->width(); - info.v0[y2d] = (info.oy * info.lh * negYFac) / img->height(); - info.v1[x2d] = info.v0[x2d] + info.lw; - info.v1[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 = m_overlays[camera()]; - delete info.img; - info.img = null; - - updateOverlayObjects(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::setDepthValue (double depth) -{ assert (camera() < EFreeCamera); - m_depthValues[camera()] = depth; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -double GLRenderer::getDepthValue() const -{ assert (camera() < EFreeCamera); - return m_depthValues[camera()]; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -const char* GLRenderer::getCameraName() const -{ return g_CameraNames[camera()]; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -LDGLOverlay& GLRenderer::getOverlay (int newcam) -{ return m_overlays[newcam]; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::zoomNotch (bool inward) -{ if (zoom() > 15) - zoom() *= inward ? 0.833f : 1.2f; - else - zoom() += inward ? -1.2f : 1.2f; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::zoomToFit() -{ if (getFile() == null || m_width == -1 || m_height == -1) - { zoom() = 30.0f; - return; - } - - bool lastfilled = false; - bool firstrun = true; - const uint32 white = 0xFFFFFFFF; - bool inward = true; - const int w = m_width, h = m_height; - - glClearColor (1.0, 1.0, 1.0, 1.0); - glDisable (GL_DITHER); - - // Use the pick list while drawing the scene, this way we can tell whether borders - // are background or not. - setPicking (true); - - for (;;) - { if (zoom() > 10000.0 || zoom() < 0.0) - { // Obviously, there's nothing to draw if we get here. - // Default to 30.0f and break out. - zoom() = 30.0; - break; - } - - zoomNotch (inward); - - uchar* cap = new uchar[4 * w * h]; - drawGLScene(); - glReadPixels (0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, cap); - uint32* imgdata = reinterpret_cast<uint32*> (cap); - bool filled = false; - - // Check the top and bottom rows - for (int i = 0; i < w && !filled; ++i) - if (imgdata[i] != white || imgdata[((h - 1) * w) + i] != white) - filled = true; - - // Left and right edges - for (int i = 0; i < h && !filled; ++i) - if (imgdata[i * w] != white || imgdata[(i * w) + w - 1] != white) - filled = true; - - delete[] cap; - - 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 = !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 && !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 (!filled && lastfilled) - break; - - inward = !filled; - } - - lastfilled = filled; - } - - setBackground(); - setPicking (false); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::zoomAllToFit() -{ EFixedCamera oldcam = camera(); - - for (int i = 0; i < 7; ++i) - { setCamera ((EFixedCamera) i); - zoomToFit(); - } - - setCamera (oldcam); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::updateRectVerts() -{ if (!m_rectdraw) - return; - - if (m_drawedVerts.isEmpty()) - { for (int i = 0; i < 4; ++i) - m_rectverts[i] = m_hoverpos; - - return; - } - - vertex v0 = m_drawedVerts[0], - v1 = (m_drawedVerts.size() >= 2) ? m_drawedVerts[1] : m_hoverpos; - - const Axis ax = getCameraAxis (false), - ay = getCameraAxis (true), - az = (Axis) (3 - ax - ay); - - for (int i = 0; i < 4; ++i) - m_rectverts[i][az] = getDepthValue(); - - m_rectverts[0][ax] = v0[ax]; - m_rectverts[0][ay] = v0[ay]; - m_rectverts[1][ax] = v1[ax]; - m_rectverts[1][ay] = v0[ay]; - m_rectverts[2][ax] = v1[ax]; - m_rectverts[2][ay] = v1[ay]; - m_rectverts[3][ax] = v0[ax]; - m_rectverts[3][ay] = v1[ay]; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::mouseDoubleClickEvent (QMouseEvent* ev) -{ if (!(ev->buttons() & Qt::LeftButton) || getEditMode() != ESelectMode) - return; - - pick (ev->x(), ev->y()); - - if (selection().isEmpty()) - return; - - g_win->beginAction (null); - LDObject* obj = selection().first(); - AddObjectDialog::staticDialog (obj->getType(), obj); - g_win->endAction(); - ev->accept(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -LDOverlay* GLRenderer::findOverlayObject (EFixedCamera cam) -{ LDOverlay* ovlobj = null; - - for (LDObject * obj : getFile()->getObjects()) - { if (obj->getType() == LDObject::Overlay && static_cast<LDOverlay*> (obj)->getCamera() == cam) - { ovlobj = static_cast<LDOverlay*> (obj); - break; - } - } - - return ovlobj; -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -// Read in overlays from the current file and update overlay info accordingly. -// ----------------------------------------------------------------------------- -void GLRenderer::initOverlaysFromObjects() -{ for (EFixedCamera cam : g_Cameras) - { if (cam == EFreeCamera) - continue; - - LDGLOverlay& meta = m_overlays[cam]; - LDOverlay* ovlobj = findOverlayObject (cam); - - if (!ovlobj && meta.img) - { delete meta.img; - meta.img = null; - } elif (ovlobj && (!meta.img || meta.fname != ovlobj->getFileName())) - setupOverlay (cam, ovlobj->getFileName(), ovlobj->getX(), - ovlobj->getY(), ovlobj->getWidth(), ovlobj->getHeight()); - } -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void GLRenderer::updateOverlayObjects() -{ for (EFixedCamera cam : g_Cameras) - { if (cam == EFreeCamera) - continue; - - LDGLOverlay& meta = m_overlays[cam]; - LDOverlay* ovlobj = findOverlayObject (cam); - - if (!meta.img && ovlobj) - { // If this is the last overlay image, we need to remove the empty space after it as well. - LDObject* nextobj = ovlobj->next(); - - if (nextobj && nextobj->getType() == LDObject::Empty) - { getFile()->forgetObject (nextobj); - delete nextobj; - } - - // If the overlay object was there and the overlay itself is - // not, remove the object. - getFile()->forgetObject (ovlobj); - delete ovlobj; - } elif (meta.img && !ovlobj) - { // Inverse case: image is there but the overlay object is - // not, thus create the object. - ovlobj = new LDOverlay; - - // 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 < getFile()->getObjectCount(); ++i) - { LDObject* obj = getFile()->getObject (i); - - if (obj->isScemantic()) - { found = true; - break; - } - - if (obj->getType() == LDObject::Overlay) - lastOverlay = i; - } - - if (lastOverlay != -1) - getFile()->insertObj (lastOverlay + 1, ovlobj); - else - { getFile()->insertObj (i, ovlobj); - - if (found) - getFile()->insertObj (i + 1, new LDEmpty); - } - } - - if (meta.img && ovlobj) - { 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(); -}