OglViewerWidget.cpp 10.3 KB
Newer Older
1
#include "..\Header\OglViewerWidget.h"
2
#include "..\Header\OutputDevice.h"
Anakin's avatar
Anakin committed
3
#include "..\Header\MainWindow.h"
4
#include <QMouseEvent>
Anakin's avatar
Anakin committed
5 6
#include <QDropEvent>
#include <QMimeData>
Anakin's avatar
Anakin committed
7

Anakin's avatar
Anakin committed
8
#define DEFAULT_Z_DISTANCE -4.0
9

Anakin's avatar
Anakin committed
10 11

/////////////////////////////////////////////////////////////////////////
12
// constructor/destructor
Anakin's avatar
Anakin committed
13

Anakin's avatar
Anakin committed
14 15 16
OglViewerWidget::OglViewerWidget(QWidget *parent)
	: QOpenGLWidget(parent)
	, m_dataEngine(0)
17
{
18
	setFocus();
Anakin's avatar
Anakin committed
19
	setAcceptDrops(true);
Anakin's avatar
Anakin committed
20

Anakin's avatar
Anakin committed
21
	// settings window
Anakin's avatar
Anakin committed
22 23
	m_settings = new SettingsWindow(m_backgroundColorOff.toVector3D() * 255, m_backgroundColorOn.toVector3D() * 255, m_light.intensities * 255, true, m_light.ambientCoefficient, m_light.attenuationFactor, 1, this);

24 25 26 27 28
	connect(m_settings, &SettingsWindow::updateBGColorOff, this, &OglViewerWidget::setBGColorOff);
	connect(m_settings, &SettingsWindow::updateBGColorOn, this, &OglViewerWidget::setBGColorOn);
	connect(m_settings, &SettingsWindow::updateLightColor, this, &OglViewerWidget::setLightColor);
	connect(m_settings, &SettingsWindow::updateAttFac, this, &OglViewerWidget::setAttFac);
	connect(m_settings, &SettingsWindow::updateAmbCoef, this, &OglViewerWidget::setAmbCoef);
29 30 31 32 33 34 35
}

OglViewerWidget::~OglViewerWidget()
{
	// Make sure the context is current when deleting the texture
	// and the buffers.
	makeCurrent();
Anakin's avatar
Anakin committed
36
	delete m_dataEngine;
37
	doneCurrent();
Anakin's avatar
Anakin committed
38 39

	delete m_settings;
40 41
}

Anakin's avatar
Anakin committed
42 43

/////////////////////////////////////////////////////////////////////////
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
// functions

void OglViewerWidget::initShaders()
{
	// Compile vertex shader
	if (!m_program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vshader.glsl"))
		close();

	// Compile fragment shader
	if (!m_program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fshader.glsl"))
		close();

	// Link shader pipeline
	if (!m_program.link())
		close();

	// Bind shader pipeline for use
	if (!m_program.bind())
		close();
}

void OglViewerWidget::resetView()
{
	m_rotation = QQuaternion();
	m_translation = { 0.0, 0.0, DEFAULT_Z_DISTANCE };
	m_zSpeed = 1;
	update();
}

void OglViewerWidget::updateLightPosition()
{
	QMatrix4x4 rotateBack;
	rotateBack.rotate(m_rotation.inverted());
	QVector3D cameraPosition = rotateBack * (-m_translation);

	m_light.position.setX(cameraPosition.x());
	m_light.position.setY(cameraPosition.y());
	m_light.position.setZ(cameraPosition.z());
}

// OpenGL ///////////////////////////////////////////////////////////////

void OglViewerWidget::initializeGL()
{
	initializeOpenGLFunctions();
	initShaders();

	// Enable depth buffer
	glEnable(GL_DEPTH_TEST);

	//TODO: make this optional
	// Enable back face culling
	//glEnable(GL_CULL_FACE);

	// Enable transparency
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	m_dataEngine = new GeometryEngine(this);
Anakin's avatar
Anakin committed
103 104 105 106 107 108 109 110
	connect(m_dataEngine, &GeometryEngine::requestResetView, this, &OglViewerWidget::resetView);
	connect(m_dataEngine, &GeometryEngine::requestUpdate, this, static_cast<void(OglViewerWidget::*)(void)>(&OglViewerWidget::update));

	//TODO: better solution
	MainWindow* parent = dynamic_cast<MainWindow*>(parentWidget());
	if (parent != NULL)
		connect(parent, &MainWindow::loadFile, [this](QString value) {m_dataEngine->loadFile(value); });

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
}

void OglViewerWidget::resizeGL(int w, int h)
{
	// Calculate aspect ratio
	qreal aspect = qreal(w) / qreal(h ? h : 1);

	// Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
	const qreal zNear = 0.1, zFar = 100.0, fov = 45.0;

	// Reset projection
	m_projection.setToIdentity();

	// Set perspective projection
	m_projection.perspective(fov, aspect, zNear, zFar);
}

void OglViewerWidget::paintGL()
{
	// set background color, last value is dirtybit
	if (m_lightOn && m_backgroundColorOn[3] == 1.0)
	{
		glClearColor(m_backgroundColorOn[0], m_backgroundColorOn[1], m_backgroundColorOn[2], 0.0000f);
		m_backgroundColorOn[3] = 0.0;
	}
	else if (!m_lightOn && m_backgroundColorOff[3] == 1.0)
	{
		glClearColor(m_backgroundColorOff[0], m_backgroundColorOff[1], m_backgroundColorOff[2], 0.0000f);
		m_backgroundColorOff[3] = 0.0;
	}

	// Clear color and depth buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Calculate view transformation
	QMatrix4x4 view;
	view.translate(m_translation);
	view.rotate(m_rotation);

	// Set view-projection matrix
	m_program.setUniformValue("vp_matrix", m_projection * view);

	// Set Light values
	m_program.setUniformValue("b_light", m_lightOn);
	m_program.setUniformValue("light.position", m_light.position);
	m_program.setUniformValue("light.intensities", m_light.intensities);
	m_program.setUniformValue("light.attenuationFactor", m_light.attenuationFactor);
	m_program.setUniformValue("light.ambientCoefficient", m_light.ambientCoefficient);

	// Set camera position
	QMatrix4x4 rotateBack;
	rotateBack.rotate(m_rotation.inverted());
	m_program.setUniformValue("cameraPosition", rotateBack * (-m_translation));

	// Draw cube geometry
	m_dataEngine->drawGeometry(&m_program, m_wireframe);
}

// Inputs ///////////////////////////////////////////////////////////////
Anakin's avatar
Anakin committed
170

171 172 173
void OglViewerWidget::mousePressEvent(QMouseEvent *e)
{
	// Save mouse press position
174 175 176 177 178 179 180
	m_mouse.position = QVector2D(e->localPos());

	// Which button has been pressed?
	if (e->button() == Qt::LeftButton)
		m_mouse.left = true;
	else if (e->button() == Qt::RightButton)
		m_mouse.right = true;
181 182 183 184
}

void OglViewerWidget::mouseReleaseEvent(QMouseEvent *e)
{
185 186 187 188 189
	if (e->button() == Qt::LeftButton)
		m_mouse.left = false;
	else if (e->button() == Qt::RightButton)
		m_mouse.right = false;
}
190

191 192 193 194 195 196
void OglViewerWidget::mouseMoveEvent(QMouseEvent *e)
{
	if (m_mouse.left)
	{
		// get the difference between last press and now
		QVector2D diff = QVector2D(e->localPos()) - m_mouse.position;
197

198 199
		// update the new position
		m_mouse.position = QVector2D(e->localPos());
200

201 202
		// calculate the rotations depending on the active axis
		// XYZ
Anakin's avatar
Anakin committed
203 204 205 206
		if (m_rotDirections.x && m_rotDirections.y && m_rotDirections.z)
		{
			m_rotation = QQuaternion::fromAxisAndAngle(QVector3D(diff.y(), diff.x(), 0.0).normalized(), diff.length() * 0.5) * m_rotation;
		}
207
		// XY
Anakin's avatar
Anakin committed
208 209 210
		else if (m_rotDirections.x && m_rotDirections.y && !m_rotDirections.z)
		{

Anakin's avatar
Anakin committed
211 212
			float pitch, yaw, roll;
			m_rotation.getEulerAngles(&pitch, &yaw, &roll);
Anakin's avatar
Anakin committed
213

Anakin's avatar
Anakin committed
214 215
			pitch += diff.y() * 0.5;
			yaw += diff.x() * 0.5;
Anakin's avatar
Anakin committed
216

Anakin's avatar
Anakin committed
217 218 219 220 221 222
			if (pitch > 89)
				pitch = 89;
			else if (pitch < -89)
				pitch = -89;

			m_rotation = QQuaternion::fromEulerAngles(pitch, yaw, roll);
Anakin's avatar
Anakin committed
223 224

		}
225
		// X
Anakin's avatar
Anakin committed
226 227 228 229
		else if (m_rotDirections.x && !m_rotDirections.y && !m_rotDirections.z)
		{
			m_rotation = QQuaternion::fromAxisAndAngle(QVector3D(0.0, 1.0, 0.0).normalized(), diff.x() * 0.5) * m_rotation;
		}
230
		// Y
Anakin's avatar
Anakin committed
231 232 233 234
		else if (!m_rotDirections.x && m_rotDirections.y && !m_rotDirections.z)
		{
			m_rotation = QQuaternion::fromAxisAndAngle(QVector3D(1.0, 0.0, 0.0).normalized(), diff.y() * 0.5) * m_rotation;
		}
235
		// Z
Anakin's avatar
Anakin committed
236 237 238 239
		else if (!m_rotDirections.x && !m_rotDirections.y && m_rotDirections.z)
		{
			m_rotation = QQuaternion::fromAxisAndAngle(QVector3D(0.0, 0.0, 1.0).normalized(), diff.x() * 0.5) * m_rotation;
		}
240
		// XZ
Anakin's avatar
Anakin committed
241 242 243 244 245 246 247 248 249
		else if (m_rotDirections.x && !m_rotDirections.y && m_rotDirections.z)
		{
			float pitch, yaw, roll;
			m_rotation.getEulerAngles(&pitch, &yaw, &roll);
			roll -= diff.y() * 0.5;
			yaw += diff.x() * 0.5;

			m_rotation = QQuaternion::fromEulerAngles(pitch, yaw, roll);
		}
250
		// YZ
Anakin's avatar
Anakin committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264
		else if (!m_rotDirections.x && m_rotDirections.y && m_rotDirections.z)
		{
			float pitch, yaw, roll;
			m_rotation.getEulerAngles(&pitch, &yaw, &roll);
			pitch += diff.y() * 0.5;
			roll += diff.x() * 0.5;

			if (pitch > 89)
				pitch = 89;
			else if (pitch < -89)
				pitch = -89;

			m_rotation = QQuaternion::fromEulerAngles(pitch, yaw, roll);
		}
Anakin's avatar
Anakin committed
265

Anakin's avatar
Anakin committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279
		// request an update
		update();
	}
	else if (m_mouse.right)
	{
		// get the difference between last press and now
		QVector2D diff = QVector2D(e->localPos()) - m_mouse.position;

		// update the new position
		m_mouse.position = QVector2D(e->localPos());

		// calculate the translation
		m_translation += {(float)(diff.x() * 0.01), (float)(diff.y() * -0.01), 0.0};

280 281 282
		// request an update
		update();
	}
283 284
}

Anakin's avatar
Anakin committed
285 286
void OglViewerWidget::wheelEvent(QWheelEvent *e)
{
Anakin's avatar
Anakin committed
287
	m_translation += {0.0, 0.0, (float)m_zSpeed * e->angleDelta().y() / 240};
Anakin's avatar
Anakin committed
288 289 290
	update();
}

291
void OglViewerWidget::keyPressEvent(QKeyEvent *e)
292
{
293
	if (e->key() == Qt::Key_Space)
Anakin's avatar
Anakin committed
294
	{
Anakin's avatar
Anakin committed
295
		resetView();
Anakin's avatar
Anakin committed
296
	}
Anakin's avatar
Anakin committed
297 298 299 300
	else if (e->key() == Qt::Key_Escape)
	{
		parentWidget()->close();
	}
Anakin's avatar
Anakin committed
301 302 303 304 305
	else if (e->key() == Qt::Key_L)
	{
		updateLightPosition();
		update();
	}
Anakin's avatar
Anakin committed
306 307 308 309
	else if (e->key() == Qt::Key_Minus)
	{
		m_zSpeed -= 0.1;
		m_zSpeed < 0.09 ? m_zSpeed = 0 : NULL;
310
		OutputDevice::getInstance()->print(QString("Zoom speed = %1%").arg(m_zSpeed * 100), 0);
Anakin's avatar
Anakin committed
311 312 313 314
	}
	else if (e->key() == Qt::Key_Plus)
	{
		m_zSpeed += 0.1;
315
		OutputDevice::getInstance()->print(QString("Zoom speed = %1%").arg(m_zSpeed * 100), 0);
Anakin's avatar
Anakin committed
316
	}
317 318
}

319
void OglViewerWidget::dragEnterEvent(QDragEnterEvent *e)
Anakin's avatar
Anakin committed
320
{
321 322 323 324
	if (e->mimeData()->hasUrls())
		if(e->mimeData()->urls().size() == 1)
			if(e->mimeData()->urls().first().toLocalFile().endsWith(".msh"))
				e->acceptProposedAction();
Anakin's avatar
Anakin committed
325

Anakin's avatar
Anakin committed
326 327
}

328
void OglViewerWidget::dropEvent(QDropEvent * e)
Anakin's avatar
Anakin committed
329
{
Anakin's avatar
Anakin committed
330
	m_dataEngine->loadFile(e->mimeData()->urls().first().toLocalFile());
Anakin's avatar
Anakin committed
331
}
Anakin's avatar
Anakin committed
332 333 334 335 336


/////////////////////////////////////////////////////////////////////////
// public slots

337
void OglViewerWidget::toggleAxis(int axis)
Anakin's avatar
Anakin committed
338
{
339
	switch (axis)
Anakin's avatar
Anakin committed
340 341 342 343 344 345 346 347 348 349 350
	{
	case 1:
		m_rotDirections.x = !m_rotDirections.x;
		break;
	case 2:
		m_rotDirections.y = !m_rotDirections.y;
		break;
	case 3:
		m_rotDirections.z = !m_rotDirections.z;
		break;
	}
Anakin's avatar
Anakin committed
351
}
Anakin's avatar
Anakin committed
352 353 354

void OglViewerWidget::toggleWireframe()
{
355
	m_wireframe = !m_wireframe;
Anakin's avatar
Anakin committed
356 357
	update();
}
Anakin's avatar
Anakin committed
358 359 360

void OglViewerWidget::toggleLight()
{
361
	m_lightOn = !m_lightOn;
Anakin's avatar
Anakin committed
362 363 364

	if (m_lightOn)
	{
365
		m_backgroundColorOn[3] = 1.0;
Anakin's avatar
Anakin committed
366 367 368 369
		updateLightPosition();
	}
	else
	{
370
		m_backgroundColorOff[3] = 1.0;
Anakin's avatar
Anakin committed
371
	}
372

Anakin's avatar
Anakin committed
373 374
	update();
}
Anakin's avatar
Anakin committed
375 376 377 378 379

void OglViewerWidget::showSettings()
{
	m_settings->show();
}
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419

void OglViewerWidget::setBGColorOff(QVector3D value)
{
	m_backgroundColorOff = QVector4D(value / 255, 1.0f);

	if (!m_lightOn)
		update();
}

void OglViewerWidget::setBGColorOn(QVector3D value)
{
	m_backgroundColorOn = QVector4D(value / 255, 1.0f);

	if (m_lightOn)
		update();
}

void OglViewerWidget::setLightColor(QVector3D value)
{
	m_light.intensities = value / 255;

	if (m_lightOn)
		update();
}

void OglViewerWidget::setAttFac(double value)
{
	m_light.attenuationFactor = (float)value;

	if (m_lightOn)
		update();
}

void OglViewerWidget::setAmbCoef(double value)
{
	m_light.ambientCoefficient = (float)value;

	if (m_lightOn)
		update();
}