|     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 	 |