--- a/gldraw.cpp Wed May 08 14:57:48 2013 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1214 +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 <QtGui> -#include <QGLWidget> -#include <GL/glu.h> -#include "common.h" -#include "config.h" -#include "file.h" -#include "gldraw.h" -#include "bbox.h" -#include "colors.h" -#include "gui.h" -#include "misc.h" -#include "history.h" - -static double g_objOffset[3]; - -static short g_pulseTick = 0; -static const short g_numPulseTicks = 8; -static const short g_pulseInterval = 65; - -static const struct staticCameraMeta { - const char glrotate[3]; - const Axis axisX, axisY; - const bool negX, negY; -} g_staticCameras[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 }, -}; - -cfg (str, gl_bgcolor, "#CCCCD9"); -cfg (str, gl_maincolor, "#707078"); -cfg (float, gl_maincolor_alpha, 1.0); -cfg (int, gl_linethickness, 2); -cfg (bool, gl_colorbfc, true); -cfg (bool, gl_selflash, false); -cfg (int, gl_camera, GLRenderer::Free); -cfg (bool, gl_blackedges, true); -cfg (bool, gl_axes, false); - -// CameraIcon::img is a heap-allocated QPixmap because otherwise it gets -// initialized before program gets to main() and constructs a QApplication -// and Qt doesn't like that. -struct CameraIcon { - QPixmap* img; - QRect srcRect, destRect, selRect; - GLRenderer::Camera cam; -} g_CameraIcons[7]; - -const char* g_CameraNames[7] = { "Top", "Front", "Left", "Bottom", "Back", "Right", "Free" }; - -const GLRenderer::Camera g_Cameras[7] = { - GLRenderer::Top, - GLRenderer::Front, - GLRenderer::Left, - GLRenderer::Bottom, - GLRenderer::Back, - GLRenderer::Right, - GLRenderer::Free -}; - -const struct GLAxis { - const QColor col; - const vertex vert; -} g_GLAxes[3] = { - { QColor (255, 0, 0), vertex (10000, 0, 0) }, - { QColor (128, 192, 0), vertex (0, 10000, 0) }, - { QColor (0, 160, 192), vertex (0, 0, 10000) }, -}; - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent) { - resetAngles (); - m_picking = m_rangepick = false; - m_camera = (GLRenderer::Camera) gl_camera.value; - m_drawToolTip = false; - m_planeDraw = false; - - m_pulseTimer = new QTimer (this); - connect (m_pulseTimer, SIGNAL (timeout ()), this, SLOT (slot_timerUpdate ())); - - 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 GLRenderer::Camera cam : g_Cameras) { - str iconname; - iconname.format ("camera-%s", str (g_CameraNames[cam]).tolower ().chars ()); - - CameraIcon* info = &g_CameraIcons[cam]; - info->img = new QPixmap (getIcon (iconname)); - info->cam = cam; - } - - calcCameraIconRects (); -} - -// ============================================================================= -GLRenderer::~GLRenderer() { - for (CameraIcon& info : g_CameraIcons) - delete info.img; -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::calcCameraIconRects () { - ushort i = 0; - - for (CameraIcon& info : g_CameraIcons) { - const long x1 = (m_width - (info.cam != Free ? 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::resetAngles () { - m_rotX = 30.0f; - m_rotY = 325.f; - m_panX = m_panY = m_rotZ = 0.0f; - - // Set the default zoom based on the bounding box - m_zoom = g_BBox.size () * 6; -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::initializeGL () { - setBackground (); - - glEnable (GL_POLYGON_OFFSET_FILL); - glPolygonOffset (1.0f, 1.0f); - - glEnable (GL_DEPTH_TEST); - glShadeModel (GL_SMOOTH); - glEnable (GL_MULTISAMPLE); - - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glEnable (GL_LINE_SMOOTH); - glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); - - glLineWidth (gl_linethickness); - - setAutoFillBackground (false); - setMouseTracking (true); - setFocusPolicy (Qt::WheelFocus); - compileObjects (); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -QColor GLRenderer::getMainColor () { - QColor col (gl_maincolor.value.chars()); - - if (!col.isValid ()) - return QColor (0, 0, 0); - - col.setAlpha (gl_maincolor_alpha * 255.f); - return col; -} - -// ----------------------------------------------------------------------------- -void GLRenderer::setBackground () { - QColor col (gl_bgcolor.value.chars()); - - if (!col.isValid ()) - return; - - m_darkbg = luma (col) < 80; - - col.setAlpha (255); - qglClearColor (col); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -static vector<short> g_daWarnedColors; -void GLRenderer::setObjectColor (LDObject* obj) { - QColor qcol; - - if (!obj->isColored()) - return; - - if (m_picking) { - // Make the color by the object's index color if we're picking, so we can - // make the index from the color we get from the picking results. - long i = obj->getIndex (g_curfile); - - // If we couldn't find the index, this object must not be from this file, - // therefore it must be an object inlined from a subfile reference or - // decomposed from a radial. Find the top level parent object and use - // its index. - if (i == -1) - i = obj->topLevelParent ()->getIndex (g_curfile); - - // We should have the index now. - assert (i != -1); - - // 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, 255)); - return; - } - -#if 0 - if (gl_colorbfc && - obj->getType () != LDObject::Line && - obj->getType () != LDObject::CondLine) - { - if (bBackSide) - glColor4f (0.9f, 0.0f, 0.0f, 1.0f); - else - glColor4f (0.0f, 0.8f, 0.0f, 1.0f); - return; - } -#endif - - if (obj->dColor == maincolor) - qcol = getMainColor (); - else { - color* col = getColor (obj->dColor); - qcol = col->qColor; - } - - if (obj->dColor == edgecolor) { - qcol = Qt::black; - color* col; - - if (!gl_blackedges && obj->parent != null && (col = getColor (obj->parent->dColor)) != null) - qcol = col->qEdge; - } - - if (qcol.isValid () == false) { - // The color was unknown. Use main color to make the object at least - // not appear pitch-black. - if (obj->dColor != edgecolor) - qcol = getMainColor (); - - // Warn about the unknown colors, but only once. - for (short i : g_daWarnedColors) - if (obj->dColor == i) - return; - - printf ("%s: Unknown color %d!\n", __func__, obj->dColor); - g_daWarnedColors.push_back (obj->dColor); - return; - } - - long r = qcol.red (), - g = qcol.green (), - b = qcol.blue (), - a = qcol.alpha (); - - // If it's selected, brighten it up, also pulse flash it if desired. - if (g_win->isSelected (obj)) { - short tick, numTicks; - - if (gl_selflash) { - tick = (g_pulseTick < (g_numPulseTicks / 2)) ? g_pulseTick : (g_numPulseTicks - g_pulseTick); - numTicks = g_numPulseTicks; - } else { - tick = 2; - numTicks = 5; - } - - const long add = ((tick * 128) / numTicks); - r = min (r + add, 255l); - g = min (g + add, 255l); - b = min (b + add, 255l); - - // a = 255; - } - - 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 () { - compileObjects (); - refresh (); - - glLineWidth (gl_linethickness); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::resizeGL (int w, int h) { - m_width = w; - m_height = h; - - calcCameraIconRects (); - - 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 () const { - if (g_curfile == null) - return; - - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable (GL_DEPTH_TEST); - - if (m_camera != GLRenderer::Free) { - glMatrixMode (GL_PROJECTION); - glPushMatrix (); - - glLoadIdentity (); - glOrtho (-m_virtWidth, m_virtWidth, -m_virtHeight, m_virtHeight, -100.0f, 100.0f); - glTranslatef (m_panX, m_panY, 0.0f); - glRotatef (90.0f, g_staticCameras[m_camera].glrotate[0], - g_staticCameras[m_camera].glrotate[1], - g_staticCameras[m_camera].glrotate[2]); - - // Back camera needs to be handled differently - if (m_camera == GLRenderer::Back) { - glRotatef (180.0f, 1.0f, 0.0f, 0.0f); - glRotatef (180.0f, 0.0f, 0.0f, 1.0f); - } - } else { - glMatrixMode (GL_MODELVIEW); - glPushMatrix (); - glLoadIdentity (); - - glTranslatef (0.0f, 0.0f, -2.0f); - glTranslatef (m_panX, m_panY, -m_zoom); - glRotatef (m_rotX, 1.0f, 0.0f, 0.0f); - glRotatef (m_rotY, 0.0f, 1.0f, 0.0f); - glRotatef (m_rotZ, 0.0f, 0.0f, 1.0f); - } - - for (LDObject* obj : g_curfile->m_objs) { - if (obj->hidden ()) - continue; // Don't draw hidden objects - - glCallList (m_picking == false ? obj->uGLList : obj->uGLPickList); - } - - if (gl_axes && !m_picking) - glCallList (m_axeslist); - - glPopMatrix (); - glMatrixMode (GL_MODELVIEW); -} - -// ============================================================================= -vertex GLRenderer::coord_2to3 (const QPoint& pos2d, const bool snap) const { - vertex pos3d; - const staticCameraMeta* cam = &g_staticCameras[m_camera]; - const Axis axisX = cam->axisX; - const Axis axisY = cam->axisY; - const short 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) - m_panX) - (negXFac * g_objOffset[axisX]); - double cy = (m_virtHeight - ((2 * pos2d.y () * m_virtHeight) / m_height) - m_panY) - (negYFac * g_objOffset[axisY]); - - if (snap) { - cx = Grid::snap (cx, (Grid::Config) axisX); - cy = Grid::snap (cy, (Grid::Config) axisY); - } - - cx *= negXFac; - cy *= negYFac; - - pos3d = g_origin; - pos3d[axisX] = cx; - pos3d[axisY] = cy; - return pos3d; -} - -// ============================================================================= -QPoint GLRenderer::coord_3to2 (const vertex& pos3d) const { - /* - cx = (-m_virtWidth + ((2 * pos2d.x () * m_virtWidth) / m_width) - m_panX) - (negXFac * g_objOffset[axisX]); - - cx = (-vw + ((2 * x * vw) / w) - panx) - (neg * ofs) - cx + (neg * ofs) = (-vw + ((2 * x * vw) / w) - panx) - cx + (neg * ofs) = ((2 * x * vw) / w) - vw - panx - (cx + (neg * ofs)) + vw + panx = (2 * x * vw) / w - ((cx + (neg * ofs)) + vw + panx) * w = 2 * vw * x - - x = (((cx + (neg * ofs)) + vw + panx) * w) / (2 * vw) - */ - - QPoint pos2d; - const staticCameraMeta* cam = &g_staticCameras[m_camera]; - const Axis axisX = cam->axisX; - const Axis axisY = cam->axisY; - const short negXFac = cam->negX ? -1 : 1, - negYFac = cam->negY ? -1 : 1; - - short x1 = (((pos3d[axisX] + (negXFac * g_objOffset[axisX])) + - m_virtWidth + m_panX) * m_width) / (2 * m_virtWidth); - short y1 = -(((pos3d[axisY] + (negYFac * g_objOffset[axisY])) - - m_virtHeight + m_panY) * m_height) / (2 * m_virtHeight); - - x1 *= negXFac; - y1 *= negYFac; - - pos2d = QPoint (x1, y1); - return pos2d; -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::paintEvent (QPaintEvent* ev) { - Q_UNUSED (ev) - m_virtWidth = m_zoom; - m_virtHeight = (m_height * m_virtWidth) / m_width; - drawGLScene (); - - QPainter paint (this); - QFontMetrics metrics = QFontMetrics (QFont ()); - paint.setRenderHint (QPainter::Antialiasing); - - m_hoverpos = g_origin; - - if (m_camera != Free) { - // Calculate 3d position of the cursor - m_hoverpos = coord_2to3 (m_pos, true); - - // Paint the coordinates onto the screen. - str text; - text.format ("X: %s, Y: %s, Z: %s", ftoa (m_hoverpos[X]).chars (), - ftoa (m_hoverpos[Y]).chars (), ftoa (m_hoverpos[Z]).chars ()); - - QFontMetrics metrics = QFontMetrics (font ()); - QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text); - - paint.drawText (m_width - textSize.width (), m_height - 16, textSize.width (), - textSize.height (), Qt::AlignCenter, text); - - // If we're plane drawing, draw the vertices onto the screen. - if (m_planeDraw) { - ushort numverts = m_planeDrawVerts.size () + 1; - const short blipsize = 8; - - if (numverts > 0) { - QPoint* poly = new QPoint[numverts]; - - uchar i = 0; - for (vertex& vert : m_planeDrawVerts) { - poly[i] = coord_3to2 (vert); - ++i; - } - - // Draw the cursor vertex as the last one in the list. - if (numverts < 5) - poly[i] = coord_3to2 (m_hoverpos); - else - numverts = 4; - - paint.setPen (m_thinBorderPen); - paint.setBrush (QColor (128, 192, 0)); - - // Draw vertex blips - for (ushort i = 0; i < numverts; ++i) { - QPoint& blip = poly[i]; - paint.drawEllipse (blip.x () - blipsize / 2, blip.y () - blipsize / 2, - blipsize, blipsize); - } - - paint.setPen (m_thickBorderPen); - paint.setBrush (QColor (128, 192, 0, 128)); - paint.drawPolygon (poly, numverts); - - delete[] poly; - } - } - } - - // Camera icons - if (!m_picking && m_planeDraw == false) { - // Draw a background for the selected camera - paint.setPen (m_thinBorderPen); - paint.setBrush (QBrush (QColor (0, 128, 160, 128))); - paint.drawRect (g_CameraIcons[camera ()].selRect); - - // Draw the actual icons - for (CameraIcon& info : g_CameraIcons) - paint.drawPixmap (info.destRect, *info.img, info.srcRect); - - // Draw a label for the current camera in the top left corner - { - const ushort margin = 4; - - str label; - label.format ("%s Camera", g_CameraNames[camera ()]); - paint.setBrush (Qt::black); - paint.drawText (QPoint (margin, margin + metrics.ascent ()), label); - } - - // Tool tips - if (m_drawToolTip) { - if (g_CameraIcons[m_toolTipCamera].destRect.contains (m_pos) == false) - m_drawToolTip = false; - else { - QPen bord = m_thinBorderPen; - bord.setBrush (Qt::black); - - const ushort margin = 2; - ushort x0 = m_pos.x (), - y0 = m_pos.y (); - - str label; - label.format ("%s Camera", g_CameraNames[m_toolTipCamera]); - - const ushort textWidth = metrics.width (label), - textHeight = metrics.height (), - fullWidth = textWidth + (2 * margin), - fullHeight = textHeight + (2 * margin); - - QRect area (m_pos.x (), m_pos.y (), fullWidth, fullHeight); - - if (x0 + fullWidth > m_width) - x0 -= fullWidth; - - if (y0 + fullHeight > m_height) - y0 -= fullHeight; - - paint.setBrush (QColor (0, 128, 255, 208)); - paint.setPen (bord); - paint.drawRect (x0, y0, fullWidth, fullHeight); - - paint.setBrush (Qt::black); - paint.drawText (QPoint (x0 + margin, y0 + margin + metrics.ascent ()), label); - } - } - } - - // If we're range-picking, draw a rectangle encompassing the selection area. - if (m_rangepick && !m_picking) { - const short 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 ? "#80FF00" : "#00CCFF"); - fillColor.setAlphaF (0.2f); - - paint.setPen (m_thickBorderPen); - paint.setBrush (QBrush (fillColor)); - paint.drawRect (rect); - } -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::compileObjects () { - if (g_BBox.empty () == false) { - g_objOffset[X] = -(g_BBox.v0 ()[X] + g_BBox.v1 ()[X]) / 2; - g_objOffset[Y] = -(g_BBox.v0 ()[Y] + g_BBox.v1 ()[Y]) / 2; - g_objOffset[Z] = -(g_BBox.v0 ()[Z] + g_BBox.v1 ()[Z]) / 2; - } else { - // use a default bbox if we need - g_objOffset[X] = g_objOffset[Y] = g_objOffset[Z] = 0; - } - - if (!g_curfile) { - printf ("renderer: no files loaded, cannot compile anything\n"); - return; - } - - for (LDObject* obj : g_curfile->m_objs) { - GLuint* upaLists[2] = { - &obj->uGLList, - &obj->uGLPickList - }; - - for (GLuint* upMemberList : upaLists) { - GLuint uList = glGenLists (1); - glNewList (uList, GL_COMPILE); - - m_picking = (upMemberList == &obj->uGLPickList); - compileOneObject (obj); - m_picking = false; - - glEndList (); - *upMemberList = uList; - } - } - - // Compile axes - m_axeslist = glGenLists (1); - glNewList (m_axeslist, GL_COMPILE); - glBegin (GL_LINES); - for (const GLAxis& ax : g_GLAxes) { - qglColor (ax.col); - compileVertex (ax.vert); - compileVertex (-ax.vert); - } - glEnd (); - glEndList (); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::compileSubObject (LDObject* obj, const GLenum gltype) { - glBegin (gltype); - - const short numverts = (obj->getType () != LDObject::CondLine) ? obj->vertices () : 2; - - for (short i = 0; i < numverts; ++i) - compileVertex (obj->vaCoords[i]); - - glEnd (); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::compileOneObject (LDObject* obj) { - setObjectColor (obj); - - switch (obj->getType ()) { - case LDObject::Line: - compileSubObject (obj, GL_LINES); - break; - - case LDObject::CondLine: - 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); - vector<LDObject*> objs = ref->inlineContents (true, true); - - for (LDObject* obj : objs) { - compileOneObject (obj); - delete obj; - } - } - break; - - case LDObject::Radial: - { - LDRadial* pRadial = static_cast<LDRadial*> (obj); - std::vector<LDObject*> objs = pRadial->decompose (true); - - for (LDObject* obj : objs) { - compileOneObject (obj); - delete obj; - } - } - break; - -#if 0 - TODO: find a proper way to draw vertices without having them be affected by zoom. - case LDObject::Vertex: - { - LDVertex* pVert = static_cast<LDVertex*> (obj); - LDTriangle* pPoly; - vertex* vPos = &(pVert->vPosition); - const double fPolyScale = max (fZoom, 1.0); - -#define BIPYRAMID_COORD(N) ((((i + N) % 4) >= 2 ? 1 : -1) * 0.3f * fPolyScale) - - for (int i = 0; i < 8; ++i) { - pPoly = new LDTriangle; - pPoly->vaCoords[0] = {vPos->x, vPos->y + ((i >= 4 ? 1 : -1) * 0.4f * fPolyScale), vPos->z}; - pPoly->vaCoords[1] = { - vPos->x + BIPYRAMID_COORD (0), - vPos->y, - vPos->z + BIPYRAMID_COORD (1) - }; - - pPoly->vaCoords[2] = { - vPos->x + BIPYRAMID_COORD (1), - vPos->y, - vPos->z + BIPYRAMID_COORD (2) - }; - - pPoly->dColor = pVert->dColor; - compileOneObject (pPoly); - delete pPoly; - } - } - break; -#endif // 0 - - default: - break; - } -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::compileVertex (const vertex& vrt) { - glVertex3d ( - (vrt[X] + g_objOffset[0]), - -(vrt[Y] + g_objOffset[1]), - -(vrt[Z] + g_objOffset[2])); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::clampAngle (double& angle) { - 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) && !(ev->buttons() & Qt::LeftButton); - const bool wasRight = (m_lastButtons & Qt::RightButton) && !(ev->buttons() & Qt::RightButton); - - if (wasLeft) { - if (m_planeDraw) { - // If we picked an already-existing vertex, stop drawing - for (vertex& vert : m_planeDrawVerts) { - if (vert == m_hoverpos) { - endPlaneDraw (true); - return; - } - } - - // Also, if have 4 verts, also stop drawing. - if (m_planeDrawVerts.size () >= 4) - endPlaneDraw (true); - - m_planeDrawVerts.push_back (m_hoverpos); - - update (); - return; - } else { - if (!m_rangepick) - m_addpick = (m_keymods & Qt::ControlModifier); - - if (m_totalmove < 10 || m_rangepick) - pick (ev->x (), ev->y ()); - } - - m_rangepick = false; - m_totalmove = 0; - return; - } - - if (wasRight && m_planeDraw) { - if (m_planeDrawVerts.size () > 0) { - // Remove the last vertex - m_planeDrawVerts.erase (m_planeDrawVerts.end () - 1); - } else { - endPlaneDraw (false); - return; - } - - update (); - } -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::mousePressEvent (QMouseEvent* ev) { - if (ev->buttons () & Qt::LeftButton) - m_totalmove = 0; - - if (ev->modifiers () & Qt::ShiftModifier) { - m_rangepick = true; - m_rangeStart.setX (ev->x ()); - m_rangeStart.setY (ev->y ()); - - m_addpick = (m_keymods & Qt::ControlModifier); - } - - 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); - - if (ev->buttons () & Qt::LeftButton && !m_rangepick) { - m_rotX = m_rotX + (dy); - m_rotY = m_rotY + (dx); - - clampAngle (m_rotX); - clampAngle (m_rotY); - } - - if (ev->buttons () & Qt::MidButton) { - m_panX += 0.03f * dx * (m_zoom / 7.5f); - m_panY -= 0.03f * dy * (m_zoom / 7.5f); - } - - // Start the tool tip timer - if (!m_drawToolTip) - m_toolTipTimer->start (1000); - - m_pos = ev->pos (); - update (); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::keyPressEvent (QKeyEvent* ev) { - m_keymods = ev->modifiers (); -} - -void GLRenderer::keyReleaseEvent (QKeyEvent* ev) { - m_keymods = ev->modifiers (); -} - -// ============================================================================= -void GLRenderer::wheelEvent (QWheelEvent* ev) { - m_zoom *= (ev->delta () < 0) ? 1.2f : (1.0f / 1.2f); - m_zoom = clamp (m_zoom, 0.01, 10000.0); - - update (); - ev->accept (); -} - -// ============================================================================= -void GLRenderer::leaveEvent (QEvent* ev) { - Q_UNUSED (ev); - m_drawToolTip = false; - m_toolTipTimer->stop (); - update (); -} - -// ============================================================================= -void GLRenderer::contextMenuEvent (QContextMenuEvent* ev) { - g_win->spawnContextMenu (ev->globalPos ()); -} - -// ============================================================================= -void GLRenderer::setCamera (const GLRenderer::Camera cam) { - m_camera = cam; - gl_camera = (int) cam; -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::updateSelFlash () { - if (gl_selflash && g_win->sel ().size() > 0) { - m_pulseTimer->start (g_pulseInterval); - g_pulseTick = 0; - } else - m_pulseTimer->stop (); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::pick (uint mouseX, uint mouseY) { - // Check if we selected a camera icon - if (!m_rangepick) { - QPoint pos (mouseX, mouseY); - - for (CameraIcon& info : g_CameraIcons) { - if (info.destRect.contains (pos)) { - setCamera (info.cam); - update (); - return; - } - } - } - - GLint viewport[4]; - LDObject* removedObject = null; - - // Clear the selection if we do not wish to add to it. - if (!m_addpick) { - std::vector<LDObject*> oldsel = g_win->sel (); - g_win->sel ().clear (); - - // Recompile the prior selection to remove the highlight color - for (LDObject* obj : oldsel) - recompileObject (obj); - } - - m_picking = true; - - // Paint the picking scene - glDisable (GL_DITHER); - glClearColor (1.0f, 1.0f, 1.0f, 1.0f); - drawGLScene (); - - glGetIntegerv (GL_VIEWPORT, viewport); - - short x0 = mouseX, - y0 = mouseY; - short 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) - dataswap (x0, x1); - - if (y0 > y1) - dataswap (y0, y1); - - // Clamp the values to ensure they're within bounds - x0 = max<short> (0, x0); - y0 = max<short> (0, y0); - x1 = min<short> (x1, m_width); - y1 = min<short> (y1, m_height); - - const short areawidth = (x1 - x0); - const short areaheight = (y1 - y0); - const long 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); - - // Go through each pixel read and add them to the selection. - for (long i = 0; i < numpixels; ++i) { - uint32 idx = - (*(pixelptr) * 0x10000) + - (*(pixelptr + 1) * 0x00100) + - (*(pixelptr + 2) * 0x00001); - pixelptr += 4; - - if (idx == 0xFFFFFF) - continue; // White is background; skip - - LDObject* obj = g_curfile->object (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) { - bool removed = false; - - for (ulong i = 0; i < g_win->sel ().size(); ++i) { - if (g_win->sel ()[i] == obj) { - g_win->sel ().erase (g_win->sel ().begin () + i); - removedObject = obj; - removed = true; - } - } - - if (removed) - break; - } - - g_win->sel ().push_back (obj); - } - - delete[] pixeldata; - - // Remove duplicate entries. For this to be effective, the vector must be - // sorted first. - std::vector<LDObject*>& sel = g_win->sel (); - std::sort (sel.begin(), sel.end ()); - std::vector<LDObject*>::iterator pos = std::unique (sel.begin (), sel.end ()); - sel.resize (std::distance (sel.begin (), pos)); - - // Update everything now. - g_win->buildObjList (); - - m_picking = false; - m_rangepick = false; - glEnable (GL_DITHER); - - setBackground (); - updateSelFlash (); - - for (LDObject* obj : g_win->sel ()) - recompileObject (obj); - - if (removedObject != null) - recompileObject (removedObject); - - drawGLScene (); - swapBuffers (); - update (); -} - -// ============================================================================= -void GLRenderer::beginPlaneDraw () { - if (m_camera == Free) - return; // Cannot draw with the free camera - - m_planeDraw = true; - - // Disable the context menu - we need the right mouse button - // for removing vertices. - setContextMenuPolicy (Qt::NoContextMenu); - - // Use the crosshair cursor when plane drawing. - setCursor (Qt::CrossCursor); - - // Clear the selection when beginning to draw onto a plane. - // FIXME: make the selection clearing stuff in ::pick a method and use it - // here! This code doesn't update the GL lists. - g_win->sel ().clear (); - g_win->updateSelection (); - update (); -} - -// ============================================================================= -void GLRenderer::endPlaneDraw (bool accept) { - // If we accepted, clean the selection and create the object - if (accept) { - vector<vertex>& verts = m_planeDrawVerts; - LDObject* obj = null; - - switch (verts.size ()) { - case 1: - { - // 1 vertex - add a vertex object - obj = new LDVertex; - static_cast<LDVertex*> (obj)->vPosition = verts[0]; - obj->dColor = maincolor; - } - break; - - case 2: - { - // 2 verts - make a line - obj = new LDLine; - obj->dColor = edgecolor; - for (ushort i = 0; i < 2; ++i) - obj->vaCoords[i] = verts[i]; - } - break; - - case 3: - case 4: - { - obj = (verts.size () == 3) ? - static_cast<LDObject*> (new LDTriangle) : - static_cast<LDObject*> (new LDQuad); - - obj->dColor = maincolor; - for (ushort i = 0; i < obj->vertices (); ++i) - obj->vaCoords[i] = verts[i]; - } - break; - } - - if (obj) { - g_curfile->addObject (obj); - recompileObject (obj); - g_win->refresh (); - - History::addEntry (new AddHistory ({(ulong) obj->getIndex (g_curfile)}, {obj->clone ()})); - } - } - - m_planeDraw = false; - m_planeDrawVerts.clear (); - - unsetCursor (); - - // Restore the context menu - setContextMenuPolicy (Qt::DefaultContextMenu); - - update (); -} - -// ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= -void GLRenderer::recompileObject (LDObject* obj) { - // Replace the old list with the new one. - GLuint uList = glGenLists (1); - glNewList (uList, GL_COMPILE); - - compileOneObject (obj); - - glEndList (); - obj->uGLList = uList; -} - -// ============================================================================= -uchar* GLRenderer::screencap (ushort& w, ushort& 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); - - // Restore the background - setBackground (); - - return cap; -} - -// ============================================================================= -void GLRenderer::slot_timerUpdate () { - ++g_pulseTick %= g_numPulseTicks; - - for (LDObject* obj : g_win->sel ()) - recompileObject (obj); - - update (); -} - -// ============================================================================= -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 : g_CameraIcons) { - if (icon.destRect.contains (m_pos)) { - m_toolTipCamera = icon.cam; - m_drawToolTip = true; - update (); - break; - } - } -} \ No newline at end of file