src/gldraw.cc

changeset 667
31540c1f22ea
parent 626
f3c01a033e9e
child 668
6a05c4969074
equal deleted inserted replaced
666:c595cfb4791c 667:31540c1f22ea
1 /*
2 * LDForge: LDraw parts authoring CAD
3 * Copyright (C) 2013, 2014 Santeri Piippo
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <QGLWidget>
20 #include <QWheelEvent>
21 #include <QMouseEvent>
22 #include <QContextMenuEvent>
23 #include <QInputDialog>
24 #include <QToolTip>
25 #include <QTimer>
26 #include <GL/glu.h>
27
28 #include "main.h"
29 #include "config.h"
30 #include "document.h"
31 #include "gldraw.h"
32 #include "colors.h"
33 #include "gui.h"
34 #include "misc.h"
35 #include "history.h"
36 #include "dialogs.h"
37 #include "addObjectDialog.h"
38 #include "messagelog.h"
39 #include "gldata.h"
40 #include "primitives.h"
41 #include "misc/ringFinder.h"
42 #include "moc_gldraw.cpp"
43
44 static const LDFixedCameraInfo g_FixedCameras[6] =
45 {
46 {{ 1, 0, 0 }, X, Z, false, false },
47 {{ 0, 0, 0 }, X, Y, false, true },
48 {{ 0, 1, 0 }, Z, Y, true, true },
49 {{ -1, 0, 0 }, X, Z, false, true },
50 {{ 0, 0, 0 }, X, Y, true, true },
51 {{ 0, -1, 0 }, Z, Y, false, true },
52 };
53
54 // Matrix templates for circle drawing. 2 is substituted with
55 // the scale value, 1 is inverted to -1 if needed.
56 static const Matrix g_circleDrawMatrixTemplates[3] =
57 {
58 { 2, 0, 0, 0, 1, 0, 0, 0, 2 },
59 { 2, 0, 0, 0, 0, 2, 0, 1, 0 },
60 { 0, 1, 0, 2, 0, 0, 0, 0, 2 },
61 };
62
63 cfg (String, gl_bgcolor, "#FFFFFF")
64 cfg (String, gl_maincolor, "#A0A0A0")
65 cfg (Float, gl_maincolor_alpha, 1.0)
66 cfg (String, gl_selectcolor, "#0080FF")
67 cfg (Int, gl_linethickness, 2)
68 cfg (Bool, gl_colorbfc, false)
69 cfg (Int, gl_camera, GLRenderer::EFreeCamera)
70 cfg (Bool, gl_blackedges, false)
71 cfg (Bool, gl_axes, false)
72 cfg (Bool, gl_wireframe, false)
73 cfg (Bool, gl_logostuds, false)
74 cfg (Bool, gl_aa, true)
75 cfg (Bool, gl_linelengths, true)
76 cfg (Bool, gl_drawangles, false)
77
78 // argh
79 const char* g_CameraNames[7] =
80 {
81 QT_TRANSLATE_NOOP ("GLRenderer", "Top"),
82 QT_TRANSLATE_NOOP ("GLRenderer", "Front"),
83 QT_TRANSLATE_NOOP ("GLRenderer", "Left"),
84 QT_TRANSLATE_NOOP ("GLRenderer", "Bottom"),
85 QT_TRANSLATE_NOOP ("GLRenderer", "Back"),
86 QT_TRANSLATE_NOOP ("GLRenderer", "Right"),
87 QT_TRANSLATE_NOOP ("GLRenderer", "Free")
88 };
89
90 const GL::EFixedCamera g_Cameras[7] =
91 {
92 GL::ETopCamera,
93 GL::EFrontCamera,
94 GL::ELeftCamera,
95 GL::EBottomCamera,
96 GL::EBackCamera,
97 GL::ERightCamera,
98 GL::EFreeCamera
99 };
100
101 // Definitions for visual axes, drawn on the screen
102 const struct LDGLAxis
103 {
104 const QColor col;
105 const Vertex vert;
106 } g_GLAxes[3] =
107 {
108 { QColor (255, 0, 0), Vertex (10000, 0, 0) }, // X
109 { QColor (80, 192, 0), Vertex (0, 10000, 0) }, // Y
110 { QColor (0, 160, 192), Vertex (0, 0, 10000) }, // Z
111 };
112
113 // =============================================================================
114 //
115 GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent)
116 {
117 m_Picking = m_rangepick = false;
118 m_camera = (GL::EFixedCamera) gl_camera;
119 m_drawToolTip = false;
120 m_EditMode = ESelectMode;
121 m_rectdraw = false;
122 m_panning = false;
123 setFile (null);
124 setDrawOnly (false);
125 setMessageLog (null);
126 m_width = m_height = -1;
127 m_hoverpos = g_origin;
128
129 m_toolTipTimer = new QTimer (this);
130 m_toolTipTimer->setSingleShot (true);
131 connect (m_toolTipTimer, SIGNAL (timeout()), this, SLOT (slot_toolTipTimer()));
132
133 m_thickBorderPen = QPen (QColor (0, 0, 0, 208), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
134 m_thinBorderPen = m_thickBorderPen;
135 m_thinBorderPen.setWidth (1);
136
137 // Init camera icons
138 for (const GL::EFixedCamera cam : g_Cameras)
139 {
140 QString iconname = fmt ("camera-%1", tr (g_CameraNames[cam]).toLower());
141
142 CameraIcon* info = &m_cameraIcons[cam];
143 info->img = new QPixmap (getIcon (iconname));
144 info->cam = cam;
145 }
146
147 calcCameraIcons();
148 }
149
150 // =============================================================================
151 //
152 GLRenderer::~GLRenderer()
153 {
154 for (int i = 0; i < 6; ++i)
155 delete currentDocumentData().overlays[i].img;
156
157 for (CameraIcon& info : m_cameraIcons)
158 delete info.img;
159 }
160
161 // =============================================================================
162 // Calculates the "hitboxes" of the camera icons so that we can tell when the
163 // cursor is pointing at the camera icon.
164 //
165 void GLRenderer::calcCameraIcons()
166 {
167 int i = 0;
168
169 for (CameraIcon& info : m_cameraIcons)
170 {
171 // MATH
172 const long x1 = (m_width - (info.cam != EFreeCamera ? 48 : 16)) + ((i % 3) * 16) - 1,
173 y1 = ((i / 3) * 16) + 1;
174
175 info.srcRect = QRect (0, 0, 16, 16);
176 info.destRect = QRect (x1, y1, 16, 16);
177 info.selRect = QRect (
178 info.destRect.x(),
179 info.destRect.y(),
180 info.destRect.width() + 1,
181 info.destRect.height() + 1
182 );
183
184 ++i;
185 }
186 }
187
188 // =============================================================================
189 //
190 void GLRenderer::initGLData()
191 {
192 glEnable (GL_BLEND);
193 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
194 glEnable (GL_POLYGON_OFFSET_FILL);
195 glPolygonOffset (1.0f, 1.0f);
196
197 glEnable (GL_DEPTH_TEST);
198 glShadeModel (GL_SMOOTH);
199 glEnable (GL_MULTISAMPLE);
200
201 if (gl_aa)
202 {
203 glEnable (GL_LINE_SMOOTH);
204 glEnable (GL_POLYGON_SMOOTH);
205 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
206 glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
207 } else
208 {
209 glDisable (GL_LINE_SMOOTH);
210 glDisable (GL_POLYGON_SMOOTH);
211 }
212 }
213
214 // =============================================================================
215 //
216 void GLRenderer::resetAngles()
217 {
218 rot (X) = 30.0f;
219 rot (Y) = 325.f;
220 pan (X) = pan (Y) = rot (Z) = 0.0f;
221 zoomToFit();
222 }
223
224 // =============================================================================
225 //
226 void GLRenderer::resetAllAngles()
227 {
228 EFixedCamera oldcam = camera();
229
230 for (int i = 0; i < 7; ++i)
231 {
232 setCamera ((EFixedCamera) i);
233 resetAngles();
234 }
235
236 setCamera (oldcam);
237 }
238
239 // =============================================================================
240 //
241 void GLRenderer::initializeGL()
242 {
243 setBackground();
244
245 glLineWidth (gl_linethickness);
246 glLineStipple (1, 0x6666);
247
248 setAutoFillBackground (false);
249 setMouseTracking (true);
250 setFocusPolicy (Qt::WheelFocus);
251
252 g_vertexCompiler.compileFile();
253 }
254
255 // =============================================================================
256 //
257 QColor GLRenderer::getMainColor()
258 {
259 QColor col (gl_maincolor);
260
261 if (!col.isValid())
262 return QColor (0, 0, 0);
263
264 col.setAlpha (gl_maincolor_alpha * 255.f);
265 return col;
266 }
267
268 // =============================================================================
269 //
270 void GLRenderer::setBackground()
271 {
272 QColor col (gl_bgcolor);
273
274 if (!col.isValid())
275 return;
276
277 col.setAlpha (255);
278
279 m_darkbg = luma (col) < 80;
280 m_bgcolor = col;
281 qglClearColor (col);
282 }
283
284 // =============================================================================
285 // -----------------------------------------------------------------------------
286 void GLRenderer::refresh()
287 {
288 update();
289 swapBuffers();
290 }
291
292 // =============================================================================
293 // -----------------------------------------------------------------------------
294 void GLRenderer::hardRefresh()
295 {
296 g_vertexCompiler.compileFile();
297 refresh();
298
299 glLineWidth (gl_linethickness);
300 }
301
302 // =============================================================================
303 //
304 void GLRenderer::resizeGL (int w, int h)
305 {
306 m_width = w;
307 m_height = h;
308
309 calcCameraIcons();
310
311 glViewport (0, 0, w, h);
312 glMatrixMode (GL_PROJECTION);
313 glLoadIdentity();
314 gluPerspective (45.0f, (double) w / (double) h, 1.0f, 10000.0f);
315 glMatrixMode (GL_MODELVIEW);
316 }
317
318 // =============================================================================
319 //
320 void GLRenderer::drawGLScene()
321 {
322 if (getFile() == null)
323 return;
324
325 if (gl_wireframe && !isPicking())
326 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
327
328 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
329 glEnable (GL_DEPTH_TEST);
330
331 if (m_camera != EFreeCamera)
332 {
333 glMatrixMode (GL_PROJECTION);
334 glPushMatrix();
335
336 glLoadIdentity();
337 glOrtho (-m_virtWidth, m_virtWidth, -m_virtHeight, m_virtHeight, -100.0f, 100.0f);
338 glTranslatef (pan (X), pan (Y), 0.0f);
339
340 if (m_camera != EFrontCamera && m_camera != EBackCamera)
341 {
342 glRotatef (90.0f, g_FixedCameras[camera()].glrotate[0],
343 g_FixedCameras[camera()].glrotate[1],
344 g_FixedCameras[camera()].glrotate[2]);
345 }
346
347 // Back camera needs to be handled differently
348 if (m_camera == GLRenderer::EBackCamera)
349 {
350 glRotatef (180.0f, 1.0f, 0.0f, 0.0f);
351 glRotatef (180.0f, 0.0f, 0.0f, 1.0f);
352 }
353 }
354 else
355 {
356 glMatrixMode (GL_MODELVIEW);
357 glPushMatrix();
358 glLoadIdentity();
359
360 glTranslatef (0.0f, 0.0f, -2.0f);
361 glTranslatef (pan (X), pan (Y), -zoom());
362 glRotatef (rot (X), 1.0f, 0.0f, 0.0f);
363 glRotatef (rot (Y), 0.0f, 1.0f, 0.0f);
364 glRotatef (rot (Z), 0.0f, 0.0f, 1.0f);
365 }
366
367 // Draw the VAOs now
368 glEnableClientState (GL_VERTEX_ARRAY);
369 glEnableClientState (GL_COLOR_ARRAY);
370 glDisableClientState (GL_NORMAL_ARRAY);
371
372 if (gl_colorbfc) {
373 glEnable (GL_CULL_FACE);
374 glCullFace (GL_CCW);
375 } else
376 glDisable (GL_CULL_FACE);
377
378 drawVAOs ((isPicking() ? PickArray : gl_colorbfc ? BFCArray : MainArray), GL_TRIANGLES);
379 drawVAOs ((isPicking() ? EdgePickArray : EdgeArray), GL_LINES);
380
381 // Draw conditional lines. Note that conditional lines are drawn into
382 // EdgePickArray in the picking scene, so when picking, don't do anything
383 // here.
384 if (!isPicking())
385 {
386 glEnable (GL_LINE_STIPPLE);
387 drawVAOs (CondEdgeArray, GL_LINES);
388 glDisable (GL_LINE_STIPPLE);
389 }
390
391 glPopMatrix();
392 glMatrixMode (GL_MODELVIEW);
393 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
394 }
395
396 // =============================================================================
397 //
398 void GLRenderer::drawVAOs (VAOType arrayType, GLenum type)
399 {
400 const VertexCompiler::Array* array = g_vertexCompiler.getMergedBuffer (arrayType);
401 glVertexPointer (3, GL_FLOAT, sizeof (VertexCompiler::Vertex), &array->data()[0].x);
402 glColorPointer (4, GL_UNSIGNED_BYTE, sizeof (VertexCompiler::Vertex), &array->data()[0].color);
403 glDrawArrays (type, 0, array->writtenSize() / sizeof (VertexCompiler::Vertex));
404 }
405
406 // =============================================================================
407 // This converts a 2D point on the screen to a 3D point in the model. If 'snap'
408 // is true, the 3D point will snap to the current grid.
409 //
410 Vertex GLRenderer::coordconv2_3 (const QPoint& pos2d, bool snap) const
411 {
412 assert (camera() != EFreeCamera);
413
414 Vertex pos3d;
415 const LDFixedCameraInfo* cam = &g_FixedCameras[m_camera];
416 const Axis axisX = cam->axisX;
417 const Axis axisY = cam->axisY;
418 const int negXFac = cam->negX ? -1 : 1,
419 negYFac = cam->negY ? -1 : 1;
420
421 // Calculate cx and cy - these are the LDraw unit coords the cursor is at.
422 double cx = (-m_virtWidth + ((2 * pos2d.x() * m_virtWidth) / m_width) - pan (X));
423 double cy = (m_virtHeight - ((2 * pos2d.y() * m_virtHeight) / m_height) - pan (Y));
424
425 if (snap)
426 {
427 cx = Grid::snap (cx, (Grid::Config) axisX);
428 cy = Grid::snap (cy, (Grid::Config) axisY);
429 }
430
431 cx *= negXFac;
432 cy *= negYFac;
433
434 roundToDecimals (cx, 4);
435 roundToDecimals (cy, 4);
436
437 // Create the vertex from the coordinates
438 pos3d[axisX] = cx;
439 pos3d[axisY] = cy;
440 pos3d[3 - axisX - axisY] = getDepthValue();
441 return pos3d;
442 }
443
444 // =============================================================================
445 //
446 // Inverse operation for the above - convert a 3D position to a 2D screen
447 // position. Don't ask me how this code manages to work, I don't even know.
448 //
449 QPoint GLRenderer::coordconv3_2 (const Vertex& pos3d) const
450 {
451 GLfloat m[16];
452 const LDFixedCameraInfo* cam = &g_FixedCameras[m_camera];
453 const Axis axisX = cam->axisX;
454 const Axis axisY = cam->axisY;
455 const int negXFac = cam->negX ? -1 : 1,
456 negYFac = cam->negY ? -1 : 1;
457
458 glGetFloatv (GL_MODELVIEW_MATRIX, m);
459
460 const double x = pos3d.x();
461 const double y = pos3d.y();
462 const double z = pos3d.z();
463
464 Vertex transformed;
465 transformed[X] = (m[0] * x) + (m[1] * y) + (m[2] * z) + m[3];
466 transformed[Y] = (m[4] * x) + (m[5] * y) + (m[6] * z) + m[7];
467 transformed[Z] = (m[8] * x) + (m[9] * y) + (m[10] * z) + m[11];
468
469 double rx = (((transformed[axisX] * negXFac) + m_virtWidth + pan (X)) * m_width) / (2 * m_virtWidth);
470 double ry = (((transformed[axisY] * negYFac) - m_virtHeight + pan (Y)) * m_height) / (2 * m_virtHeight);
471
472 return QPoint (rx, -ry);
473 }
474
475 // =============================================================================
476 //
477 void GLRenderer::paintEvent (QPaintEvent* ev)
478 {
479 Q_UNUSED (ev)
480
481 makeCurrent();
482 m_virtWidth = zoom();
483 m_virtHeight = (m_height * m_virtWidth) / m_width;
484
485 initGLData();
486 drawGLScene();
487
488 const QPen textpen (m_darkbg ? Qt::white : Qt::black);
489 const QBrush polybrush (QColor (64, 192, 0, 128));
490 QPainter paint (this);
491 QFontMetrics metrics = QFontMetrics (QFont());
492 paint.setRenderHint (QPainter::HighQualityAntialiasing);
493
494 // If we wish to only draw the brick, stop here
495 if (isDrawOnly())
496 return;
497
498 if (m_camera != EFreeCamera && !isPicking())
499 {
500 // Paint the overlay image if we have one
501 const LDGLOverlay& overlay = currentDocumentData().overlays[m_camera];
502
503 if (overlay.img != null)
504 {
505 QPoint v0 = coordconv3_2 (currentDocumentData().overlays[m_camera].v0),
506 v1 = coordconv3_2 (currentDocumentData().overlays[m_camera].v1);
507
508 QRect targRect (v0.x(), v0.y(), abs (v1.x() - v0.x()), abs (v1.y() - v0.y())),
509 srcRect (0, 0, overlay.img->width(), overlay.img->height());
510 paint.drawImage (targRect, *overlay.img, srcRect);
511 }
512
513 // Paint the coordinates onto the screen.
514 QString text = fmt (tr ("X: %1, Y: %2, Z: %3"), m_hoverpos[X], m_hoverpos[Y], m_hoverpos[Z]);
515 QFontMetrics metrics = QFontMetrics (font());
516 QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text);
517 paint.setPen (textpen);
518 paint.drawText (m_width - textSize.width(), m_height - 16, textSize.width(),
519 textSize.height(), Qt::AlignCenter, text);
520
521 QPen linepen = m_thinBorderPen;
522 linepen.setWidth (2);
523 linepen.setColor (luma (m_bgcolor) < 40 ? Qt::white : Qt::black);
524
525 // Mode-specific rendering
526 if (getEditMode() == EDrawMode)
527 {
528 QPoint poly[4];
529 Vertex poly3d[4];
530 int numverts = 4;
531
532 // Calculate polygon data
533 if (!m_rectdraw)
534 {
535 numverts = m_drawedVerts.size() + 1;
536 int i = 0;
537
538 for (Vertex& vert : m_drawedVerts)
539 poly3d[i++] = vert;
540
541 // Draw the cursor vertex as the last one in the list.
542 if (numverts <= 4)
543 poly3d[i] = m_hoverpos;
544 else
545 numverts = 4;
546 }
547 else
548 {
549 // Get vertex information from m_rectverts
550 if (m_drawedVerts.size() > 0)
551 for (int i = 0; i < numverts; ++i)
552 poly3d[i] = m_rectverts[i];
553 else
554 poly3d[0] = m_hoverpos;
555 }
556
557 // Convert to 2D
558 for (int i = 0; i < numverts; ++i)
559 poly[i] = coordconv3_2 (poly3d[i]);
560
561 if (numverts > 0)
562 {
563 // Draw the polygon-to-be
564 paint.setBrush (polybrush);
565 paint.drawPolygon (poly, numverts);
566
567 // Draw vertex blips
568 for (int i = 0; i < numverts; ++i)
569 {
570 QPoint& blip = poly[i];
571 paint.setPen (linepen);
572 drawBlip (paint, blip);
573
574 // Draw their coordinates
575 paint.setPen (textpen);
576 paint.drawText (blip.x(), blip.y() - 8, poly3d[i].toString (true));
577 }
578
579 // Draw line lenghts and angle info if appropriate
580 if (numverts >= 2)
581 {
582 int numlines = (m_drawedVerts.size() == 1) ? 1 : m_drawedVerts.size() + 1;
583 paint.setPen (textpen);
584
585 for (int i = 0; i < numlines; ++i)
586 {
587 const int j = (i + 1 < numverts) ? i + 1 : 0;
588 const int h = (i - 1 >= 0) ? i - 1 : numverts - 1;
589
590 if (gl_linelengths)
591 {
592 const QString label = QString::number (poly3d[i].distanceTo (poly3d[j]));
593 QPoint origin = QLineF (poly[i], poly[j]).pointAt (0.5).toPoint();
594 paint.drawText (origin, label);
595 }
596
597 if (gl_drawangles)
598 {
599 QLineF l0 (poly[h], poly[i]),
600 l1 (poly[i], poly[j]);
601
602 double angle = 180 - l0.angleTo (l1);
603
604 if (angle < 0)
605 angle = 180 - l1.angleTo (l0);
606
607 QString label = QString::number (angle) + QString::fromUtf8 (QByteArray ("\302\260"));
608 QPoint pos = poly[i];
609 pos.setY (pos.y() + metrics.height());
610
611 paint.drawText (pos, label);
612 }
613 }
614 }
615 }
616 }
617 elif (getEditMode() == ECircleMode)
618 {
619 // If we have not specified the center point of the circle yet, preview it on the screen.
620 if (m_drawedVerts.isEmpty())
621 drawBlip (paint, coordconv3_2 (m_hoverpos));
622 else
623 {
624 QVector<Vertex> verts, verts2;
625 const double dist0 = getCircleDrawDist (0),
626 dist1 = (m_drawedVerts.size() >= 2) ? getCircleDrawDist (1) : -1;
627 const int segs = lores;
628 const double angleUnit = (2 * pi) / segs;
629 Axis relX, relY;
630 QVector<QPoint> ringpoints, circlepoints, circle2points;
631
632 getRelativeAxes (relX, relY);
633
634 // Calculate the preview positions of vertices
635 for (int i = 0; i < segs; ++i)
636 {
637 Vertex v = g_origin;
638 v[relX] = m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist0);
639 v[relY] = m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist0);
640 verts << v;
641
642 if (dist1 != -1)
643 {
644 v[relX] = m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist1);
645 v[relY] = m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist1);
646 verts2 << v;
647 }
648 }
649
650 int i = 0;
651 for (const Vertex& v : verts + verts2)
652 {
653 // Calculate the 2D point of the vertex
654 QPoint point = coordconv3_2 (v);
655
656 // Draw a green blip at where it is
657 drawBlip (paint, point);
658
659 // Add it to the list of points for the green ring fill.
660 ringpoints << point;
661
662 // Also add the circle points to separate lists
663 if (i < verts.size())
664 circlepoints << point;
665 else
666 circle2points << point;
667
668 ++i;
669 }
670
671 // Insert the first point as the seventeenth one so that
672 // the ring polygon is closed properly.
673 if (ringpoints.size() >= 16)
674 ringpoints.insert (16, ringpoints[0]);
675
676 // Same for the outer ring. Note that the indices are offset by 1
677 // because of the insertion done above bumps the values.
678 if (ringpoints.size() >= 33)
679 ringpoints.insert (33, ringpoints[17]);
680
681 // Draw the ring
682 paint.setBrush ((m_drawedVerts.size() >= 2) ? polybrush : Qt::NoBrush);
683 paint.setPen (Qt::NoPen);
684 paint.drawPolygon (QPolygon (ringpoints));
685
686 // Draw the circles
687 paint.setBrush (Qt::NoBrush);
688 paint.setPen (linepen);
689 paint.drawPolygon (QPolygon (circlepoints));
690 paint.drawPolygon (QPolygon (circle2points));
691
692 { // Draw the current radius in the middle of the circle.
693 QPoint origin = coordconv3_2 (m_drawedVerts[0]);
694 QString label = QString::number (dist0);
695 paint.setPen (textpen);
696 paint.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label);
697
698 if (m_drawedVerts.size() >= 2)
699 {
700 label = QString::number (dist1);
701 paint.drawText (origin.x() - (metrics.width (label) / 2), origin.y() + metrics.height(), label);
702 }
703 }
704 }
705 }
706 }
707
708 // Camera icons
709 if (!isPicking())
710 {
711 // Draw a background for the selected camera
712 paint.setPen (m_thinBorderPen);
713 paint.setBrush (QBrush (QColor (0, 128, 160, 128)));
714 paint.drawRect (m_cameraIcons[camera()].selRect);
715
716 // Draw the actual icons
717 for (CameraIcon& info : m_cameraIcons)
718 {
719 // Don't draw the free camera icon when in draw mode
720 if (&info == &m_cameraIcons[GL::EFreeCamera] && getEditMode() != ESelectMode)
721 continue;
722
723 paint.drawPixmap (info.destRect, *info.img, info.srcRect);
724 }
725
726 QString fmtstr = tr ("%1 Camera");
727
728 // Draw a label for the current camera in the bottom left corner
729 {
730 const int margin = 4;
731
732 QString label;
733 label = fmt (fmtstr, tr (g_CameraNames[camera()]));
734 paint.setPen (textpen);
735 paint.drawText (QPoint (margin, height() - (margin + metrics.descent())), label);
736 }
737
738 // Tool tips
739 if (m_drawToolTip)
740 {
741 if (m_cameraIcons[m_toolTipCamera].destRect.contains (m_pos) == false)
742 m_drawToolTip = false;
743 else
744 {
745 QString label = fmt (fmtstr, tr (g_CameraNames[m_toolTipCamera]));
746 QToolTip::showText (m_globalpos, label);
747 }
748 }
749 }
750
751 // Message log
752 if (getMessageLog())
753 {
754 int y = 0;
755 const int margin = 2;
756 QColor penColor = textpen.color();
757
758 for (const MessageManager::Line& line : getMessageLog()->getLines())
759 {
760 penColor.setAlphaF (line.alpha);
761 paint.setPen (penColor);
762 paint.drawText (QPoint (margin, y + margin + metrics.ascent()), line.text);
763 y += metrics.height();
764 }
765 }
766
767 // If we're range-picking, draw a rectangle encompassing the selection area.
768 if (m_rangepick && !isPicking() && m_totalmove >= 10)
769 {
770 int x0 = m_rangeStart.x(),
771 y0 = m_rangeStart.y(),
772 x1 = m_pos.x(),
773 y1 = m_pos.y();
774
775 QRect rect (x0, y0, x1 - x0, y1 - y0);
776 QColor fillColor = (m_addpick ? "#40FF00" : "#00CCFF");
777 fillColor.setAlphaF (0.2f);
778
779 paint.setPen (m_thickBorderPen);
780 paint.setBrush (QBrush (fillColor));
781 paint.drawRect (rect);
782 }
783 }
784
785 // =============================================================================
786 //
787 void GLRenderer::drawBlip (QPainter& paint, QPoint pos) const
788 {
789 QPen pen = m_thinBorderPen;
790 const int blipsize = 8;
791 pen.setWidth (1);
792 paint.setPen (pen);
793 paint.setBrush (QColor (64, 192, 0));
794 paint.drawEllipse (pos.x() - blipsize / 2, pos.y() - blipsize / 2, blipsize, blipsize);
795 }
796
797 // =============================================================================
798 //
799 void GLRenderer::compileAllObjects()
800 {
801 g_vertexCompiler.compileFile();
802 }
803
804 // =============================================================================
805 //
806 void GLRenderer::clampAngle (double& angle) const
807 {
808 while (angle < 0)
809 angle += 360.0;
810
811 while (angle > 360.0)
812 angle -= 360.0;
813 }
814
815 // =============================================================================
816 //
817 void GLRenderer::addDrawnVertex (Vertex pos)
818 {
819 // If we picked an already-existing vertex, stop drawing
820 if (getEditMode() == EDrawMode)
821 {
822 for (Vertex& vert : m_drawedVerts)
823 {
824 if (vert == pos)
825 {
826 endDraw (true);
827 return;
828 }
829 }
830 }
831
832 m_drawedVerts << pos;
833 }
834
835 // =============================================================================
836 //
837 void GLRenderer::mouseReleaseEvent (QMouseEvent* ev)
838 {
839 const bool wasLeft = (m_lastButtons & Qt::LeftButton) && ! (ev->buttons() & Qt::LeftButton),
840 wasRight = (m_lastButtons & Qt::RightButton) && ! (ev->buttons() & Qt::RightButton),
841 wasMid = (m_lastButtons & Qt::MidButton) && ! (ev->buttons() & Qt::MidButton);
842
843 if (m_panning)
844 m_panning = false;
845
846 if (wasLeft)
847 {
848 // Check if we selected a camera icon
849 if (!m_rangepick)
850 {
851 for (CameraIcon & info : m_cameraIcons)
852 {
853 if (info.destRect.contains (ev->pos()))
854 {
855 setCamera (info.cam);
856 goto end;
857 }
858 }
859 }
860
861 switch (getEditMode())
862 {
863 case EDrawMode:
864 {
865 if (m_rectdraw)
866 {
867 if (m_drawedVerts.size() == 2)
868 {
869 endDraw (true);
870 return;
871 }
872 } else
873 {
874 // If we have 4 verts, stop drawing.
875 if (m_drawedVerts.size() >= 4)
876 {
877 endDraw (true);
878 return;
879 }
880
881 if (m_drawedVerts.isEmpty() && ev->modifiers() & Qt::ShiftModifier)
882 {
883 m_rectdraw = true;
884 updateRectVerts();
885 }
886 }
887
888 addDrawnVertex (m_hoverpos);
889 } break;
890
891 case ECircleMode:
892 {
893 if (m_drawedVerts.size() == 3)
894 {
895 endDraw (true);
896 return;
897 }
898
899 addDrawnVertex (m_hoverpos);
900 } break;
901
902 case ESelectMode:
903 {
904 if (!isDrawOnly())
905 {
906 if (m_totalmove < 10)
907 m_rangepick = false;
908
909 if (!m_rangepick)
910 m_addpick = (m_keymods & Qt::ControlModifier);
911
912 if (m_totalmove < 10 || m_rangepick)
913 pick (ev->x(), ev->y());
914 }
915 } break;
916 }
917
918 m_rangepick = false;
919 }
920
921 if (wasMid && getEditMode() != ESelectMode && m_drawedVerts.size() < 4 && m_totalmove < 10)
922 {
923 // Find the closest vertex to our cursor
924 double mindist = 1024.0f;
925 Vertex closest;
926 bool valid = false;
927
928 QPoint curspos = coordconv3_2 (m_hoverpos);
929
930 for (const Vertex& pos3d: m_knownVerts)
931 {
932 QPoint pos2d = coordconv3_2 (pos3d);
933
934 // Measure squared distance
935 const double dx = abs (pos2d.x() - curspos.x()),
936 dy = abs (pos2d.y() - curspos.y()),
937 distsq = (dx * dx) + (dy * dy);
938
939 if (distsq >= 1024.0f) // 32.0f ** 2
940 continue; // too far away
941
942 if (distsq < mindist)
943 {
944 mindist = distsq;
945 closest = pos3d;
946 valid = true;
947
948 // If it's only 4 pixels away, I think we found our vertex now.
949 if (distsq <= 16.0f) // 4.0f ** 2
950 break;
951 }
952 }
953
954 if (valid)
955 addDrawnVertex (closest);
956 }
957
958 if (wasRight && !m_drawedVerts.isEmpty())
959 {
960 // Remove the last vertex
961 m_drawedVerts.removeLast();
962
963 if (m_drawedVerts.isEmpty())
964 m_rectdraw = false;
965 }
966
967 end:
968 update();
969 m_totalmove = 0;
970 }
971
972 // =============================================================================
973 //
974 void GLRenderer::mousePressEvent (QMouseEvent* ev)
975 {
976 m_totalmove = 0;
977
978 if (ev->modifiers() & Qt::ControlModifier)
979 {
980 m_rangepick = true;
981 m_rangeStart.setX (ev->x());
982 m_rangeStart.setY (ev->y());
983 m_addpick = (m_keymods & Qt::AltModifier);
984 ev->accept();
985 }
986
987 m_lastButtons = ev->buttons();
988 }
989
990 // =============================================================================
991 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
992 // =============================================================================
993 void GLRenderer::mouseMoveEvent (QMouseEvent* ev)
994 {
995 int dx = ev->x() - m_pos.x();
996 int dy = ev->y() - m_pos.y();
997 m_totalmove += abs (dx) + abs (dy);
998
999 const bool left = ev->buttons() & Qt::LeftButton,
1000 mid = ev->buttons() & Qt::MidButton,
1001 shift = ev->modifiers() & Qt::ShiftModifier;
1002
1003 if (mid || (left && shift))
1004 {
1005 pan (X) += 0.03f * dx * (zoom() / 7.5f);
1006 pan (Y) -= 0.03f * dy * (zoom() / 7.5f);
1007 m_panning = true;
1008 } elif (left && !m_rangepick && camera() == EFreeCamera)
1009 {
1010 rot (X) = rot (X) + dy;
1011 rot (Y) = rot (Y) + dx;
1012
1013 clampAngle (rot (X));
1014 clampAngle (rot (Y));
1015 }
1016
1017 // Start the tool tip timer
1018 if (!m_drawToolTip)
1019 m_toolTipTimer->start (500);
1020
1021 // Update 2d position
1022 m_pos = ev->pos();
1023 m_globalpos = ev->globalPos();
1024
1025 // Calculate 3d position of the cursor
1026 m_hoverpos = (camera() != EFreeCamera) ? coordconv2_3 (m_pos, true) : g_origin;
1027
1028 // Update rect vertices since m_hoverpos may have changed
1029 updateRectVerts();
1030
1031 update();
1032 }
1033
1034 // =============================================================================
1035 //
1036 void GLRenderer::keyPressEvent (QKeyEvent* ev)
1037 {
1038 m_keymods = ev->modifiers();
1039 }
1040
1041 // =============================================================================
1042 //
1043 void GLRenderer::keyReleaseEvent (QKeyEvent* ev)
1044 {
1045 m_keymods = ev->modifiers();
1046 }
1047
1048 // =============================================================================
1049 //
1050 void GLRenderer::wheelEvent (QWheelEvent* ev)
1051 {
1052 makeCurrent();
1053
1054 zoomNotch (ev->delta() > 0);
1055 zoom() = clamp (zoom(), 0.01, 10000.0);
1056
1057 update();
1058 ev->accept();
1059 }
1060
1061 // =============================================================================
1062 //
1063 void GLRenderer::leaveEvent (QEvent* ev)
1064 {
1065 (void) ev;
1066 m_drawToolTip = false;
1067 m_toolTipTimer->stop();
1068 update();
1069 }
1070
1071 // =============================================================================
1072 //
1073 void GLRenderer::contextMenuEvent (QContextMenuEvent* ev)
1074 {
1075 g_win->spawnContextMenu (ev->globalPos());
1076 }
1077
1078 // =============================================================================
1079 //
1080 void GLRenderer::setCamera (const GLRenderer::EFixedCamera cam)
1081 {
1082 m_camera = cam;
1083 gl_camera = (int) cam;
1084 g_win->updateEditModeActions();
1085 }
1086
1087 // =============================================================================
1088 //
1089 void GLRenderer::pick (int mouseX, int mouseY)
1090 {
1091 makeCurrent();
1092
1093 // Use particularly thick lines while picking ease up selecting lines.
1094 glLineWidth (max<double> (gl_linethickness, 6.5f));
1095
1096 // Clear the selection if we do not wish to add to it.
1097 if (!m_addpick)
1098 {
1099 LDObjectList oldsel = selection();
1100 getCurrentDocument()->clearSelection();
1101
1102 for (LDObject* obj : oldsel)
1103 compileObject (obj);
1104 }
1105
1106 setPicking (true);
1107
1108 // Paint the picking scene
1109 glDisable (GL_DITHER);
1110 glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
1111 drawGLScene();
1112
1113 int x0 = mouseX,
1114 y0 = mouseY;
1115 int x1, y1;
1116
1117 // Determine how big an area to read - with range picking, we pick by
1118 // the area given, with single pixel picking, we use an 1 x 1 area.
1119 if (m_rangepick)
1120 {
1121 x1 = m_rangeStart.x();
1122 y1 = m_rangeStart.y();
1123 }
1124 else
1125 {
1126 x1 = x0 + 1;
1127 y1 = y0 + 1;
1128 }
1129
1130 // x0 and y0 must be less than x1 and y1, respectively.
1131 if (x0 > x1)
1132 qSwap (x0, x1);
1133
1134 if (y0 > y1)
1135 qSwap (y0, y1);
1136
1137 // Clamp the values to ensure they're within bounds
1138 x0 = max (0, x0);
1139 y0 = max (0, y0);
1140 x1 = min (x1, m_width);
1141 y1 = min (y1, m_height);
1142 const int areawidth = (x1 - x0);
1143 const int areaheight = (y1 - y0);
1144 const qint32 numpixels = areawidth * areaheight;
1145
1146 // Allocate space for the pixel data.
1147 uchar* const pixeldata = new uchar[4 * numpixels];
1148 uchar* pixelptr = &pixeldata[0];
1149
1150 // Read pixels from the color buffer.
1151 glReadPixels (x0, m_height - y1, areawidth, areaheight, GL_RGBA, GL_UNSIGNED_BYTE, pixeldata);
1152
1153 LDObject* removedObj = null;
1154
1155 // Go through each pixel read and add them to the selection.
1156 for (qint32 i = 0; i < numpixels; ++i)
1157 {
1158 qint32 idx =
1159 (*(pixelptr + 0) * 0x10000) +
1160 (*(pixelptr + 1) * 0x00100) +
1161 (*(pixelptr + 2) * 0x00001);
1162 pixelptr += 4;
1163
1164 if (idx == 0xFFFFFF)
1165 continue; // White is background; skip
1166
1167 LDObject* obj = LDObject::fromID (idx);
1168 assert (obj != null);
1169
1170 // If this is an additive single pick and the object is currently selected,
1171 // we remove it from selection instead.
1172 if (!m_rangepick && m_addpick)
1173 {
1174 if (obj->isSelected())
1175 {
1176 obj->unselect();
1177 removedObj = obj;
1178 break;
1179 }
1180 }
1181
1182 obj->select();
1183 }
1184
1185 delete[] pixeldata;
1186
1187 // Update everything now.
1188 g_win->updateSelection();
1189
1190 // Recompile the objects now to update their color
1191 for (LDObject* obj : selection())
1192 compileObject (obj);
1193
1194 if (removedObj)
1195 compileObject (removedObj);
1196
1197 // Restore line thickness
1198 glLineWidth (gl_linethickness);
1199
1200 setPicking (false);
1201 m_rangepick = false;
1202 glEnable (GL_DITHER);
1203
1204 setBackground();
1205 repaint();
1206 }
1207
1208 // =============================================================================
1209 //
1210 void GLRenderer::setEditMode (EditMode const& a)
1211 {
1212 m_EditMode = a;
1213
1214 switch (a)
1215 {
1216 case ESelectMode:
1217 {
1218 unsetCursor();
1219 setContextMenuPolicy (Qt::DefaultContextMenu);
1220 } break;
1221
1222 case EDrawMode:
1223 case ECircleMode:
1224 {
1225 // Cannot draw into the free camera - use top instead.
1226 if (m_camera == EFreeCamera)
1227 setCamera (ETopCamera);
1228
1229 // Disable the context menu - we need the right mouse button
1230 // for removing vertices.
1231 setContextMenuPolicy (Qt::NoContextMenu);
1232
1233 // Use the crosshair cursor when drawing.
1234 setCursor (Qt::CrossCursor);
1235
1236 // Clear the selection when beginning to draw.
1237 LDObjectList priorsel = selection();
1238 getCurrentDocument()->clearSelection();
1239
1240 for (LDObject* obj : priorsel)
1241 compileObject (obj);
1242
1243 g_win->updateSelection();
1244 m_drawedVerts.clear();
1245 } break;
1246 }
1247
1248 g_win->updateEditModeActions();
1249 update();
1250 }
1251
1252 // =============================================================================
1253 //
1254 void GLRenderer::setFile (LDDocument* const& a)
1255 {
1256 m_File = a;
1257 g_vertexCompiler.setFile (a);
1258
1259 if (a != null)
1260 {
1261 initOverlaysFromObjects();
1262
1263 if (currentDocumentData().init == false)
1264 {
1265 resetAllAngles();
1266 currentDocumentData().init = true;
1267 }
1268 }
1269 }
1270
1271 // =============================================================================
1272 //
1273 Matrix GLRenderer::getCircleDrawMatrix (double scale)
1274 {
1275 Matrix transform = g_circleDrawMatrixTemplates[camera() % 3];
1276
1277 for (int i = 0; i < 9; ++i)
1278 {
1279 if (transform[i] == 2)
1280 transform[i] = scale;
1281 elif (transform[i] == 1 && camera() >= 3)
1282 transform[i] = -1;
1283 }
1284
1285 return transform;
1286 }
1287
1288 // =============================================================================
1289 //
1290 void GLRenderer::endDraw (bool accept)
1291 {
1292 (void) accept;
1293
1294 // Clean the selection and create the object
1295 QList<Vertex>& verts = m_drawedVerts;
1296 LDObjectList objs;
1297
1298 switch (getEditMode())
1299 {
1300 case EDrawMode:
1301 {
1302 if (m_rectdraw)
1303 {
1304 LDQuad* quad = new LDQuad;
1305
1306 // Copy the vertices from m_rectverts
1307 updateRectVerts();
1308
1309 for (int i = 0; i < quad->vertices(); ++i)
1310 quad->setVertex (i, m_rectverts[i]);
1311
1312 quad->setColor (maincolor);
1313 objs << quad;
1314 }
1315 else
1316 {
1317 switch (verts.size())
1318 {
1319 case 1:
1320 {
1321 // 1 vertex - add a vertex object
1322 LDVertex* obj = new LDVertex;
1323 obj->pos = verts[0];
1324 obj->setColor (maincolor);
1325 objs << obj;
1326 } break;
1327
1328 case 2:
1329 {
1330 // 2 verts - make a line
1331 LDLine* obj = new LDLine (verts[0], verts[1]);
1332 obj->setColor (edgecolor);
1333 objs << obj;
1334 } break;
1335
1336 case 3:
1337 case 4:
1338 {
1339 LDObject* obj = (verts.size() == 3) ?
1340 static_cast<LDObject*> (new LDTriangle) :
1341 static_cast<LDObject*> (new LDQuad);
1342
1343 obj->setColor (maincolor);
1344
1345 for (int i = 0; i < obj->vertices(); ++i)
1346 obj->setVertex (i, verts[i]);
1347
1348 objs << obj;
1349 } break;
1350 }
1351 }
1352 } break;
1353
1354 case ECircleMode:
1355 {
1356 const int segs = lores, divs = lores; // TODO: make customizable
1357 double dist0 = getCircleDrawDist (0),
1358 dist1 = getCircleDrawDist (1);
1359 LDDocument* refFile = null;
1360 Matrix transform;
1361 bool circleOrDisc = false;
1362
1363 if (dist1 < dist0)
1364 std::swap<double> (dist0, dist1);
1365
1366 if (dist0 == dist1)
1367 {
1368 // If the radii are the same, there's no ring space to fill. Use a circle.
1369 refFile = ::getDocument ("4-4edge.dat");
1370 transform = getCircleDrawMatrix (dist0);
1371 circleOrDisc = true;
1372 }
1373 elif (dist0 == 0 || dist1 == 0)
1374 {
1375 // If either radii is 0, use a disc.
1376 refFile = ::getDocument ("4-4disc.dat");
1377 transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1);
1378 circleOrDisc = true;
1379 }
1380 elif (g_RingFinder (dist0, dist1))
1381 {
1382 // The ring finder found a solution, use that. Add the component rings to the file.
1383 for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents())
1384 {
1385 // Get a ref file for this primitive. If we cannot find it in the
1386 // LDraw library, generate it.
1387 if ((refFile = ::getDocument (radialFileName (::Ring, lores, lores, cmp.num))) == null)
1388 {
1389 refFile = generatePrimitive (::Ring, lores, lores, cmp.num);
1390 refFile->setImplicit (false);
1391 }
1392
1393 LDSubfile* ref = new LDSubfile;
1394 ref->setFileInfo (refFile);
1395 ref->setTransform (getCircleDrawMatrix (cmp.scale));
1396 ref->setPosition (m_drawedVerts[0]);
1397 ref->setColor (maincolor);
1398 objs << ref;
1399 }
1400 }
1401 else
1402 {
1403 // Ring finder failed, last resort: draw the ring with quads
1404 QList<QLineF> c0, c1;
1405 Axis relX, relY, relZ;
1406 getRelativeAxes (relX, relY);
1407 relZ = (Axis) (3 - relX - relY);
1408 double x0 = m_drawedVerts[0][relX],
1409 y0 = m_drawedVerts[0][relY];
1410
1411 Vertex templ;
1412 templ[relX] = x0;
1413 templ[relY] = y0;
1414 templ[relZ] = getDepthValue();
1415
1416 // Calculate circle coords
1417 makeCircle (segs, divs, dist0, c0);
1418 makeCircle (segs, divs, dist1, c1);
1419
1420 for (int i = 0; i < segs; ++i)
1421 {
1422 Vertex v0, v1, v2, v3;
1423 v0 = v1 = v2 = v3 = templ;
1424 v0[relX] += c0[i].x1();
1425 v0[relY] += c0[i].y1();
1426 v1[relX] += c0[i].x2();
1427 v1[relY] += c0[i].y2();
1428 v2[relX] += c1[i].x2();
1429 v2[relY] += c1[i].y2();
1430 v3[relX] += c1[i].x1();
1431 v3[relY] += c1[i].y1();
1432
1433 LDQuad* q = new LDQuad (v0, v1, v2, v3);
1434 q->setColor (maincolor);
1435
1436 // Ensure the quads always are BFC-front towards the camera
1437 if (camera() % 3 <= 0)
1438 q->invert();
1439
1440 objs << q;
1441 }
1442 }
1443
1444 if (circleOrDisc)
1445 {
1446 LDSubfile* ref = new LDSubfile;
1447 ref->setFileInfo (refFile);
1448 ref->setTransform (transform);
1449 ref->setPosition (m_drawedVerts[0]);
1450 ref->setColor (maincolor);
1451 objs << ref;
1452 }
1453 } break;
1454
1455 case ESelectMode:
1456 {
1457 // this shouldn't happen
1458 assert (false);
1459 return;
1460 } break;
1461 }
1462
1463 if (objs.size() > 0)
1464 {
1465 for (LDObject* obj : objs)
1466 {
1467 getFile()->addObject (obj);
1468 compileObject (obj);
1469 }
1470
1471 g_win->refresh();
1472 g_win->endAction();
1473 }
1474
1475 m_drawedVerts.clear();
1476 m_rectdraw = false;
1477 }
1478
1479 // =============================================================================
1480 //
1481 double GLRenderer::getCircleDrawDist (int pos) const
1482 {
1483 assert (m_drawedVerts.size() >= pos + 1);
1484 const Vertex& v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : m_hoverpos;
1485 Axis relX, relY;
1486 getRelativeAxes (relX, relY);
1487
1488 const double dx = m_drawedVerts[0][relX] - v1[relX];
1489 const double dy = m_drawedVerts[0][relY] - v1[relY];
1490 return sqrt ((dx * dx) + (dy * dy));
1491 }
1492
1493 // =============================================================================
1494 //
1495 void GLRenderer::getRelativeAxes (Axis& relX, Axis& relY) const
1496 {
1497 const LDFixedCameraInfo* cam = &g_FixedCameras[m_camera];
1498 relX = cam->axisX;
1499 relY = cam->axisY;
1500 }
1501
1502 // =============================================================================
1503 //
1504 static QList<Vertex> getVertices (LDObject* obj)
1505 {
1506 QList<Vertex> verts;
1507
1508 if (obj->vertices() >= 2)
1509 {
1510 for (int i = 0; i < obj->vertices(); ++i)
1511 verts << obj->getVertex (i);
1512 } elif (obj->getType() == LDObject::ESubfile)
1513 {
1514 LDSubfile* ref = static_cast<LDSubfile*> (obj);
1515 LDObjectList objs = ref->inlineContents (LDSubfile::DeepCacheInline);
1516
1517 for (LDObject* obj : objs)
1518 {
1519 verts << getVertices (obj);
1520 obj->deleteSelf();
1521 }
1522 }
1523
1524 return verts;
1525 }
1526
1527 // =============================================================================
1528 // -----------------------------------------------------------------------------
1529 void GLRenderer::compileObject (LDObject* obj)
1530 {
1531 g_vertexCompiler.stageForCompilation (obj);
1532
1533 // Mark in known vertices of this object
1534 QList<Vertex> verts = getVertices (obj);
1535 m_knownVerts << verts;
1536 removeDuplicates (m_knownVerts);
1537
1538 obj->setGLInit (true);
1539 }
1540
1541 // =============================================================================
1542 //
1543 uchar* GLRenderer::getScreencap (int& w, int& h)
1544 {
1545 w = m_width;
1546 h = m_height;
1547 uchar* cap = new uchar[4 * w * h];
1548
1549 m_screencap = true;
1550 update();
1551 m_screencap = false;
1552
1553 // Capture the pixels
1554 glReadPixels (0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, cap);
1555
1556 return cap;
1557 }
1558
1559 // =============================================================================
1560 //
1561 void GLRenderer::slot_toolTipTimer()
1562 {
1563 // We come here if the cursor has stayed in one place for longer than a
1564 // a second. Check if we're holding it over a camera icon - if so, draw
1565 // a tooltip.
1566 for (CameraIcon & icon : m_cameraIcons)
1567 {
1568 if (icon.destRect.contains (m_pos))
1569 {
1570 m_toolTipCamera = icon.cam;
1571 m_drawToolTip = true;
1572 update();
1573 break;
1574 }
1575 }
1576 }
1577
1578 // =============================================================================
1579 //
1580 void GLRenderer::deleteLists (LDObject* obj)
1581 {
1582 // Delete the lists but only if they have been initialized
1583 if (!obj->isGLInit())
1584 return;
1585
1586 for (const GL::ListType listType : g_glListTypes)
1587 glDeleteLists (obj->glLists[listType], 1);
1588
1589 obj->setGLInit (false);
1590 }
1591
1592 // =============================================================================
1593 //
1594 Axis GLRenderer::getCameraAxis (bool y, GLRenderer::EFixedCamera camid)
1595 {
1596 if (camid == (GL::EFixedCamera) - 1)
1597 camid = m_camera;
1598
1599 const LDFixedCameraInfo* cam = &g_FixedCameras[camid];
1600 return (y) ? cam->axisY : cam->axisX;
1601 }
1602
1603 // =============================================================================
1604 //
1605 bool GLRenderer::setupOverlay (EFixedCamera cam, QString file, int x, int y, int w, int h)
1606 {
1607 QImage* img = new QImage (QImage (file).convertToFormat (QImage::Format_ARGB32));
1608 LDGLOverlay& info = getOverlay (cam);
1609
1610 if (img->isNull())
1611 {
1612 critical (tr ("Failed to load overlay image!"));
1613 delete img;
1614 return false;
1615 }
1616
1617 delete info.img; // delete the old image
1618
1619 info.fname = file;
1620 info.lw = w;
1621 info.lh = h;
1622 info.ox = x;
1623 info.oy = y;
1624 info.img = img;
1625
1626 if (info.lw == 0)
1627 info.lw = (info.lh * img->width()) / img->height();
1628 elif (info.lh == 0)
1629 info.lh = (info.lw * img->height()) / img->width();
1630
1631 const Axis x2d = getCameraAxis (false, cam),
1632 y2d = getCameraAxis (true, cam);
1633 const double negXFac = g_FixedCameras[cam].negX ? -1 : 1,
1634 negYFac = g_FixedCameras[cam].negY ? -1 : 1;
1635
1636 info.v0 = info.v1 = g_origin;
1637 info.v0[x2d] = - (info.ox * info.lw * negXFac) / img->width();
1638 info.v0[y2d] = (info.oy * info.lh * negYFac) / img->height();
1639 info.v1[x2d] = info.v0[x2d] + info.lw;
1640 info.v1[y2d] = info.v0[y2d] + info.lh;
1641
1642 // Set alpha of all pixels to 0.5
1643 for (long i = 0; i < img->width(); ++i)
1644 for (long j = 0; j < img->height(); ++j)
1645 {
1646 uint32 pixel = img->pixel (i, j);
1647 img->setPixel (i, j, 0x80000000 | (pixel & 0x00FFFFFF));
1648 }
1649
1650 updateOverlayObjects();
1651 return true;
1652 }
1653
1654 // =============================================================================
1655 //
1656 void GLRenderer::clearOverlay()
1657 {
1658 if (camera() == EFreeCamera)
1659 return;
1660
1661 LDGLOverlay& info = currentDocumentData().overlays[camera()];
1662 delete info.img;
1663 info.img = null;
1664
1665 updateOverlayObjects();
1666 }
1667
1668 // =============================================================================
1669 //
1670 void GLRenderer::setDepthValue (double depth)
1671 {
1672 assert (camera() < EFreeCamera);
1673 currentDocumentData().depthValues[camera()] = depth;
1674 }
1675
1676 // =============================================================================
1677 //
1678 double GLRenderer::getDepthValue() const
1679 {
1680 assert (camera() < EFreeCamera);
1681 return currentDocumentData().depthValues[camera()];
1682 }
1683
1684 // =============================================================================
1685 //
1686 const char* GLRenderer::getCameraName() const
1687 {
1688 return g_CameraNames[camera()];
1689 }
1690
1691 // =============================================================================
1692 //
1693 LDGLOverlay& GLRenderer::getOverlay (int newcam)
1694 {
1695 return currentDocumentData().overlays[newcam];
1696 }
1697
1698 // =============================================================================
1699 //
1700 void GLRenderer::zoomNotch (bool inward)
1701 {
1702 if (zoom() > 15)
1703 zoom() *= inward ? 0.833f : 1.2f;
1704 else
1705 zoom() += inward ? -1.2f : 1.2f;
1706 }
1707
1708 // =============================================================================
1709 //
1710 void GLRenderer::zoomToFit()
1711 {
1712 if (getFile() == null || m_width == -1 || m_height == -1)
1713 {
1714 zoom() = 30.0f;
1715 return;
1716 }
1717
1718 bool lastfilled = false;
1719 bool firstrun = true;
1720 const uint32 white = 0xFFFFFFFF;
1721 bool inward = true;
1722 const int w = m_width, h = m_height;
1723 int runaway = 50;
1724
1725 glClearColor (1.0, 1.0, 1.0, 1.0);
1726 glDisable (GL_DITHER);
1727
1728 // Use the pick list while drawing the scene, this way we can tell whether borders
1729 // are background or not.
1730 setPicking (true);
1731
1732 while (--runaway)
1733 {
1734 if (zoom() > 10000.0 || zoom() < 0.0)
1735 {
1736 // Obviously, there's nothing to draw if we get here.
1737 // Default to 30.0f and break out.
1738 zoom() = 30.0;
1739 break;
1740 }
1741
1742 zoomNotch (inward);
1743
1744 uchar* cap = new uchar[4 * w * h];
1745 drawGLScene();
1746 glReadPixels (0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, cap);
1747 uint32* imgdata = reinterpret_cast<uint32*> (cap);
1748 bool filled = false;
1749
1750 // Check the top and bottom rows
1751 for (int i = 0; i < w; ++i)
1752 {
1753 if (imgdata[i] != white || imgdata[((h - 1) * w) + i] != white)
1754 {
1755 filled = true;
1756 goto endOfLoop;
1757 }
1758 }
1759
1760 // Left and right edges
1761 for (int i = 0; i < h; ++i)
1762 {
1763 if (imgdata[i * w] != white || imgdata[(i * w) + w - 1] != white)
1764 {
1765 filled = true;
1766 goto endOfLoop;
1767 }
1768 }
1769
1770 endOfLoop:
1771
1772 delete[] cap;
1773
1774 if (firstrun)
1775 {
1776 // If this is the first run, we don't know enough to determine
1777 // whether the zoom was to fit, so we mark in our knowledge so
1778 // far and start over.
1779 inward = !filled;
1780 firstrun = false;
1781 }
1782 else
1783 {
1784 // If this run filled the screen and the last one did not, the
1785 // last run had ideal zoom - zoom a bit back and we should reach it.
1786 if (filled && !lastfilled)
1787 {
1788 zoomNotch (false);
1789 break;
1790 }
1791
1792 // If this run did not fill the screen and the last one did, we've
1793 // now reached ideal zoom so we're done here.
1794 if (!filled && lastfilled)
1795 break;
1796
1797 inward = !filled;
1798 }
1799
1800 lastfilled = filled;
1801 }
1802
1803 setBackground();
1804 setPicking (false);
1805 }
1806
1807 // =============================================================================
1808 //
1809 void GLRenderer::zoomAllToFit()
1810 {
1811 EFixedCamera oldcam = camera();
1812
1813 for (int i = 0; i < 7; ++i)
1814 {
1815 setCamera ((EFixedCamera) i);
1816 zoomToFit();
1817 }
1818
1819 setCamera (oldcam);
1820 }
1821
1822 // =============================================================================
1823 //
1824 void GLRenderer::updateRectVerts()
1825 {
1826 if (!m_rectdraw)
1827 return;
1828
1829 if (m_drawedVerts.isEmpty())
1830 {
1831 for (int i = 0; i < 4; ++i)
1832 m_rectverts[i] = m_hoverpos;
1833
1834 return;
1835 }
1836
1837 Vertex v0 = m_drawedVerts[0],
1838 v1 = (m_drawedVerts.size() >= 2) ? m_drawedVerts[1] : m_hoverpos;
1839
1840 const Axis ax = getCameraAxis (false),
1841 ay = getCameraAxis (true),
1842 az = (Axis) (3 - ax - ay);
1843
1844 for (int i = 0; i < 4; ++i)
1845 m_rectverts[i][az] = getDepthValue();
1846
1847 m_rectverts[0][ax] = v0[ax];
1848 m_rectverts[0][ay] = v0[ay];
1849 m_rectverts[1][ax] = v1[ax];
1850 m_rectverts[1][ay] = v0[ay];
1851 m_rectverts[2][ax] = v1[ax];
1852 m_rectverts[2][ay] = v1[ay];
1853 m_rectverts[3][ax] = v0[ax];
1854 m_rectverts[3][ay] = v1[ay];
1855 }
1856
1857 // =============================================================================
1858 //
1859 void GLRenderer::mouseDoubleClickEvent (QMouseEvent* ev)
1860 {
1861 if (!(ev->buttons() & Qt::LeftButton) || getEditMode() != ESelectMode)
1862 return;
1863
1864 pick (ev->x(), ev->y());
1865
1866 if (selection().isEmpty())
1867 return;
1868
1869 LDObject* obj = selection().first();
1870 AddObjectDialog::staticDialog (obj->getType(), obj);
1871 g_win->endAction();
1872 ev->accept();
1873 }
1874
1875 // =============================================================================
1876 //
1877 LDOverlay* GLRenderer::findOverlayObject (EFixedCamera cam)
1878 {
1879 LDOverlay* ovlobj = null;
1880
1881 for (LDObject* obj : getFile()->getObjects())
1882 {
1883 if (obj->getType() == LDObject::EOverlay && static_cast<LDOverlay*> (obj)->getCamera() == cam)
1884 {
1885 ovlobj = static_cast<LDOverlay*> (obj);
1886 break;
1887 }
1888 }
1889
1890 return ovlobj;
1891 }
1892
1893 // =============================================================================
1894 //
1895 // Read in overlays from the current file and update overlay info accordingly.
1896 //
1897 void GLRenderer::initOverlaysFromObjects()
1898 {
1899 for (EFixedCamera cam : g_Cameras)
1900 {
1901 if (cam == EFreeCamera)
1902 continue;
1903
1904 LDGLOverlay& meta = currentDocumentData().overlays[cam];
1905 LDOverlay* ovlobj = findOverlayObject (cam);
1906
1907 if (!ovlobj && meta.img)
1908 {
1909 delete meta.img;
1910 meta.img = null;
1911 } elif (ovlobj && (!meta.img || meta.fname != ovlobj->getFileName()))
1912 setupOverlay (cam, ovlobj->getFileName(), ovlobj->getX(),
1913 ovlobj->getY(), ovlobj->getWidth(), ovlobj->getHeight());
1914 }
1915 }
1916
1917 // =============================================================================
1918 //
1919 void GLRenderer::updateOverlayObjects()
1920 {
1921 for (EFixedCamera cam : g_Cameras)
1922 {
1923 if (cam == EFreeCamera)
1924 continue;
1925
1926 LDGLOverlay& meta = currentDocumentData().overlays[cam];
1927 LDOverlay* ovlobj = findOverlayObject (cam);
1928
1929 if (!meta.img && ovlobj)
1930 {
1931 // If this is the last overlay image, we need to remove the empty space after it as well.
1932 LDObject* nextobj = ovlobj->next();
1933
1934 if (nextobj && nextobj->getType() == LDObject::EEmpty)
1935 nextobj->deleteSelf();
1936
1937 // If the overlay object was there and the overlay itself is
1938 // not, remove the object.
1939 ovlobj->deleteSelf();
1940 } elif (meta.img && !ovlobj)
1941 {
1942 // Inverse case: image is there but the overlay object is
1943 // not, thus create the object.
1944 ovlobj = new LDOverlay;
1945
1946 // Find a suitable position to place this object. We want to place
1947 // this into the header, which is everything up to the first scemantic
1948 // object. If we find another overlay object, place this object after
1949 // the last one found. Otherwise, place it before the first schemantic
1950 // object and put an empty object after it (though don't do this if
1951 // there was no schemantic elements at all)
1952 int i, lastOverlay = -1;
1953 bool found = false;
1954
1955 for (i = 0; i < getFile()->getObjectCount(); ++i)
1956 {
1957 LDObject* obj = getFile()->getObject (i);
1958
1959 if (obj->isScemantic())
1960 {
1961 found = true;
1962 break;
1963 }
1964
1965 if (obj->getType() == LDObject::EOverlay)
1966 lastOverlay = i;
1967 }
1968
1969 if (lastOverlay != -1)
1970 getFile()->insertObj (lastOverlay + 1, ovlobj);
1971 else
1972 {
1973 getFile()->insertObj (i, ovlobj);
1974
1975 if (found)
1976 getFile()->insertObj (i + 1, new LDEmpty);
1977 }
1978 }
1979
1980 if (meta.img && ovlobj)
1981 {
1982 ovlobj->setCamera (cam);
1983 ovlobj->setFileName (meta.fname);
1984 ovlobj->setX (meta.ox);
1985 ovlobj->setY (meta.oy);
1986 ovlobj->setWidth (meta.lw);
1987 ovlobj->setHeight (meta.lh);
1988 }
1989 }
1990
1991 if (g_win->R() == this)
1992 g_win->refresh();
1993 }

mercurial