44 // ============================================================================= |
45 // ============================================================================= |
45 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
46 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
46 // ============================================================================= |
47 // ============================================================================= |
47 GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent) { |
48 GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent) { |
48 resetAngles (); |
49 resetAngles (); |
49 picking = false; |
50 picking = rangepick = false; |
50 |
51 |
51 qPulseTimer = new QTimer (this); |
52 qPulseTimer = new QTimer (this); |
52 connect (qPulseTimer, SIGNAL (timeout ()), this, SLOT (slot_timerUpdate ())); |
53 connect (qPulseTimer, SIGNAL (timeout ()), this, SLOT (slot_timerUpdate ())); |
53 } |
54 } |
54 |
55 |
236 |
237 |
237 // ============================================================================= |
238 // ============================================================================= |
238 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
239 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
239 // ============================================================================= |
240 // ============================================================================= |
240 void GLRenderer::resizeGL (int w, int h) { |
241 void GLRenderer::resizeGL (int w, int h) { |
|
242 width = w; |
|
243 height = h; |
|
244 |
241 glViewport (0, 0, w, h); |
245 glViewport (0, 0, w, h); |
242 glLoadIdentity (); |
246 glLoadIdentity (); |
243 glMatrixMode (GL_PROJECTION); |
247 glMatrixMode (GL_PROJECTION); |
244 gluPerspective (45.0f, (double)w / (double)h, 0.1f, 100.0f); |
248 gluPerspective (45.0f, (double)w / (double)h, 0.1f, 100.0f); |
245 } |
249 } |
246 |
250 |
|
251 template<class T> using initlist = std::initializer_list<T>; |
|
252 |
247 // ============================================================================= |
253 // ============================================================================= |
248 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
254 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
249 // ============================================================================= |
255 // ============================================================================= |
250 void GLRenderer::paintGL () { |
256 void GLRenderer::paintGL () { |
251 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
257 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
265 glRotatef (rotZ, 0.0f, 0.0f, 1.0f); |
271 glRotatef (rotZ, 0.0f, 0.0f, 1.0f); |
266 |
272 |
267 for (LDObject* obj : g_CurrentFile->objects) |
273 for (LDObject* obj : g_CurrentFile->objects) |
268 glCallList ((picking == false) ? obj->uGLList : obj->uGLPickList); |
274 glCallList ((picking == false) ? obj->uGLList : obj->uGLPickList); |
269 glPopMatrix (); |
275 glPopMatrix (); |
|
276 |
|
277 // If we're range-picking, draw a rectangle encompassing the selection area. |
|
278 if (rangepick && !picking) { |
|
279 const short x0 = rangeStart.x (), |
|
280 y0 = rangeStart.y (), |
|
281 x1 = pos.x (), |
|
282 y1 = pos.y (); |
|
283 |
|
284 glMatrixMode (GL_PROJECTION); |
|
285 |
|
286 glPushMatrix (); |
|
287 glLoadIdentity (); |
|
288 glOrtho (.0, width, height, .0, -1.0, 1.0); |
|
289 |
|
290 for (int x : vector<int> ({GL_QUADS, GL_LINE_LOOP})) { |
|
291 if (x == GL_QUADS) |
|
292 glColor4f (.0, .8, 1.0, .6); |
|
293 else |
|
294 glColor4f (1.0, 1.0, 1.0, 1.0); |
|
295 |
|
296 glBegin (x); |
|
297 glVertex2i (x0, y0); |
|
298 glVertex2i (x1, y0); |
|
299 glVertex2i (x1, y1); |
|
300 glVertex2i (x0, y1); |
|
301 glEnd (); |
|
302 } |
|
303 glPopMatrix (); |
|
304 |
|
305 glMatrixMode (GL_MODELVIEW); |
|
306 } |
270 } |
307 } |
271 |
308 |
272 // ============================================================================= |
309 // ============================================================================= |
273 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
310 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
274 // ============================================================================= |
311 // ============================================================================= |
435 |
472 |
436 // ============================================================================= |
473 // ============================================================================= |
437 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
474 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
438 // ============================================================================= |
475 // ============================================================================= |
439 void GLRenderer::mouseReleaseEvent (QMouseEvent* ev) { |
476 void GLRenderer::mouseReleaseEvent (QMouseEvent* ev) { |
440 if ((qMouseButtons & Qt::LeftButton) && !(ev->buttons() & Qt::LeftButton)) { |
477 if ((lastButtons & Qt::LeftButton) && !(ev->buttons() & Qt::LeftButton)) { |
441 if (ulTotalMouseMove < 10) |
478 if (ulTotalMouseMove < 10 || rangepick) |
442 pick (ev->x(), ev->y(), (qKeyMods & Qt::ControlModifier)); |
479 pick (ev->x (), ev->y (), (qKeyMods & Qt::ControlModifier)); |
443 |
480 |
|
481 rangepick = false; |
444 ulTotalMouseMove = 0; |
482 ulTotalMouseMove = 0; |
445 } |
483 } |
446 } |
484 } |
447 |
485 |
448 // ============================================================================= |
486 // ============================================================================= |
449 void GLRenderer::mousePressEvent (QMouseEvent* ev) { |
487 void GLRenderer::mousePressEvent (QMouseEvent* ev) { |
450 qMouseButtons = ev->buttons(); |
488 if (ev->buttons () & Qt::LeftButton) |
451 if (ev->buttons() & Qt::LeftButton) |
|
452 ulTotalMouseMove = 0; |
489 ulTotalMouseMove = 0; |
|
490 |
|
491 if (ev->modifiers () & Qt::ShiftModifier) { |
|
492 rangepick = true; |
|
493 rangeStart.setX (ev->x ()); |
|
494 rangeStart.setY (ev->y ()); |
|
495 } |
|
496 |
|
497 lastButtons = ev->buttons (); |
453 } |
498 } |
454 |
499 |
455 // ============================================================================= |
500 // ============================================================================= |
456 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
501 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
457 // ============================================================================= |
502 // ============================================================================= |
458 void GLRenderer::mouseMoveEvent (QMouseEvent* ev) { |
503 void GLRenderer::mouseMoveEvent (QMouseEvent* ev) { |
459 int dx = ev->x () - lastPos.x (); |
504 int dx = ev->x () - pos.x (); |
460 int dy = ev->y () - lastPos.y (); |
505 int dy = ev->y () - pos.y (); |
461 ulTotalMouseMove += abs (dx) + abs (dy); |
506 ulTotalMouseMove += abs (dx) + abs (dy); |
462 |
507 |
463 if (ev->buttons () & Qt::LeftButton) { |
508 if (ev->buttons () & Qt::LeftButton && !rangepick) { |
464 rotX = rotX + (dy); |
509 rotX = rotX + (dy); |
465 rotY = rotY + (dx); |
510 rotY = rotY + (dx); |
|
511 |
466 clampAngle (rotX); |
512 clampAngle (rotX); |
467 clampAngle (rotY); |
513 clampAngle (rotY); |
468 } |
|
469 |
|
470 if (ev->buttons () & Qt::RightButton) { |
|
471 rotX = rotX + (dy); |
|
472 rotZ = rotZ + (dx); |
|
473 clampAngle (rotX); |
|
474 clampAngle (rotZ); |
|
475 } |
514 } |
476 |
515 |
477 if (ev->buttons () & Qt::MidButton) { |
516 if (ev->buttons () & Qt::MidButton) { |
478 panX += 0.03f * dx; |
517 panX += 0.03f * dx; |
479 panY -= 0.03f * dy; |
518 panY -= 0.03f * dy; |
480 } |
519 } |
481 |
520 |
482 lastPos = ev->pos (); |
521 pos = ev->pos (); |
483 updateGL (); |
522 updateGL (); |
484 } |
523 } |
485 |
524 |
486 // ============================================================================= |
525 // ============================================================================= |
487 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
526 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
514 } |
553 } |
515 |
554 |
516 // ========================================================================= // |
555 // ========================================================================= // |
517 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // |
556 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // |
518 // ========================================================================= // |
557 // ========================================================================= // |
519 void GLRenderer::pick (uint uMouseX, uint uMouseY, bool bAdd) { |
558 void GLRenderer::pick (uint mx, uint my, bool add) { |
520 if (bAdd == false) { |
559 GLint viewport[4]; |
|
560 |
|
561 if (add == false) { |
521 // Clear the selection if we don't wish to add to it. |
562 // Clear the selection if we don't wish to add to it. |
522 std::vector<LDObject*> paOldSelection = g_ForgeWindow->paSelection; |
563 std::vector<LDObject*> paOldSelection = g_ForgeWindow->paSelection; |
523 g_ForgeWindow->paSelection.clear (); |
564 g_ForgeWindow->paSelection.clear (); |
524 |
565 |
525 // Recompile the prior selection to remove the highlight color |
566 // Recompile the prior selection to remove the highlight color |
526 for (LDObject* obj : paOldSelection) |
567 for (LDObject* obj : paOldSelection) |
527 recompileObject (obj); |
568 recompileObject (obj); |
528 } |
569 } |
529 |
570 |
|
571 picking = true; |
|
572 |
|
573 // Paint the picking scene |
530 glDisable (GL_DITHER); |
574 glDisable (GL_DITHER); |
531 glClearColor (1.0f, 1.0f, 1.0f, 1.0f); |
575 glClearColor (1.0f, 1.0f, 1.0f, 1.0f); |
532 |
|
533 picking = true; |
|
534 |
|
535 paintGL (); |
576 paintGL (); |
536 |
577 |
537 GLubyte ucaPixel[3]; |
578 glGetIntegerv (GL_VIEWPORT, viewport); |
538 GLint daViewport[4]; |
579 |
539 glGetIntegerv (GL_VIEWPORT, daViewport); |
580 short x0 = mx, |
540 glReadPixels (uMouseX, daViewport[3] - uMouseY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, |
581 y0 = my; |
541 reinterpret_cast<GLvoid*> (ucaPixel)); |
582 short x1, y1; |
542 |
583 |
543 // If we hit a white pixel, we selected the background. This no object is selected. |
584 if (rangepick) { |
544 const bool bHasSelection = (ucaPixel[0] != 255 || ucaPixel[1] != 255 || ucaPixel[2] != 255); |
585 x1 = rangeStart.x (); |
545 |
586 y1 = rangeStart.y (); |
546 if (bHasSelection) { |
587 } else { |
547 ulong idx = ucaPixel[0] + (ucaPixel[1] * 256) + (ucaPixel[2] * 256 * 256); |
588 x1 = x0 + 1; |
|
589 y1 = y0 + 1; |
|
590 } |
|
591 |
|
592 if (x0 > x1) |
|
593 dataswap (x0, x1); |
|
594 |
|
595 if (y0 > y1) |
|
596 dataswap (y0, y1); |
|
597 |
|
598 // Clamp the values to ensure they're within bounds |
|
599 x0 = max<short> (0, x0); |
|
600 y0 = max<short> (0, y0); |
|
601 x1 = min<short> (x1, width); |
|
602 y1 = min<short> (y1, height); |
|
603 |
|
604 short areawidth = (x1 - x0); |
|
605 short areaheight = (y1 - y0); |
|
606 |
|
607 const long numpixels = areawidth * areaheight; |
|
608 uchar* const pixeldata = new uchar[4 * numpixels]; |
|
609 uchar* pixelptr = &pixeldata[0]; |
|
610 |
|
611 assert (viewport[3] == height); |
|
612 |
|
613 glReadPixels (x0, viewport[3] - y1, areawidth, areaheight, GL_RGBA, GL_UNSIGNED_BYTE, pixeldata); |
|
614 |
|
615 for (long i = 0; i < numpixels; ++i) { |
|
616 uint32 idx = |
|
617 (*(pixelptr) * 0x10000) + |
|
618 (*(pixelptr + 1) * 0x00100) + |
|
619 (*(pixelptr + 2) * 0x00001); |
|
620 pixelptr += 4; |
|
621 |
|
622 if (idx == 0xFFFFFF) |
|
623 continue; // White is background; skip |
548 |
624 |
549 LDObject* obj = g_CurrentFile->object (idx); |
625 LDObject* obj = g_CurrentFile->object (idx); |
550 g_ForgeWindow->paSelection.push_back (obj); |
626 g_ForgeWindow->paSelection.push_back (obj); |
551 } |
627 } |
552 |
628 |
|
629 delete[] pixeldata; |
|
630 |
|
631 std::vector<LDObject*>& sel = g_ForgeWindow->paSelection; |
|
632 std::sort (sel.begin(), sel.end ()); |
|
633 std::vector<LDObject*>::iterator pos = std::unique (sel.begin (), sel.end ()); |
|
634 sel.resize (std::distance (sel.begin (), pos)); |
|
635 |
553 g_ForgeWindow->buildObjList (); |
636 g_ForgeWindow->buildObjList (); |
554 |
637 |
555 picking = false; |
638 picking = false; |
|
639 rangepick = false; |
556 glEnable (GL_DITHER); |
640 glEnable (GL_DITHER); |
557 |
641 |
558 setBackground (); |
642 setBackground (); |
559 updateSelFlash (); |
643 updateSelFlash (); |
560 |
644 |