OglViewerWidget.cpp 10.4 KB
Newer Older
1
#include "..\Header\OglViewerWidget.h"
2

3
#include <QMouseEvent>
Anakin's avatar
Anakin committed
4 5
#include <QDropEvent>
#include <QMimeData>
Anakin's avatar
Anakin committed
6

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

Anakin's avatar
Anakin committed
9 10

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

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

Anakin's avatar
Anakin committed
20 21
	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);

22 23 24 25 26
	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);
27 28 29 30 31 32 33
}

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

	delete m_settings;
38 39
}

Anakin's avatar
Anakin committed
40 41

/////////////////////////////////////////////////////////////////////////
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 103 104 105 106 107 108 109 110 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 170
// 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::setConnections()
{
	connect(m_dataEngine, &GeometryEngine::requestResetView, this, &OglViewerWidget::resetView);
	connect(parentWidget(), SIGNAL(loadFile(QString)), m_dataEngine, SLOT(loadFile(QString)));
	connect(this, SIGNAL(loadFile(QString)), m_dataEngine, SLOT(loadFile(QString)));
	connect(m_dataEngine, SIGNAL(sendMessage(QString, int)), parentWidget(), SLOT(printMessage(QString, int)));
	connect(m_dataEngine, SIGNAL(requestUpdate()), this, SLOT(update()));
	connect(m_dataEngine, SIGNAL(sendFileInfo(QString, QVector<Material>*, int, int)), parentWidget(), SLOT(setFileInfo(QString, QVector<Material>*, int, int)));
}

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);
	setConnections();
}

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
171

172 173 174
void OglViewerWidget::mousePressEvent(QMouseEvent *e)
{
	// Save mouse press position
175 176 177 178 179 180 181
	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;
182 183 184 185
}

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

192 193 194 195 196 197
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;
198

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

202 203
		// calculate the rotations depending on the active axis
		// XYZ
Anakin's avatar
Anakin committed
204 205 206 207
		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;
		}
208
		// XY
Anakin's avatar
Anakin committed
209 210 211
		else if (m_rotDirections.x && m_rotDirections.y && !m_rotDirections.z)
		{

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

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

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

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

		}
226
		// X
Anakin's avatar
Anakin committed
227 228 229 230
		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;
		}
231
		// Y
Anakin's avatar
Anakin committed
232 233 234 235
		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;
		}
236
		// Z
Anakin's avatar
Anakin committed
237 238 239 240
		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;
		}
241
		// XZ
Anakin's avatar
Anakin committed
242 243 244 245 246 247 248 249 250
		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);
		}
251
		// YZ
Anakin's avatar
Anakin committed
252 253 254 255 256 257 258 259 260 261 262 263 264 265
		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
266

Anakin's avatar
Anakin committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280
		// 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};

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

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

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

320
void OglViewerWidget::dragEnterEvent(QDragEnterEvent *e)
Anakin's avatar
Anakin committed
321
{
322 323 324 325
	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
326

Anakin's avatar
Anakin committed
327 328
}

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


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

338
void OglViewerWidget::toggleAxis(int axis)
Anakin's avatar
Anakin committed
339
{
340
	switch (axis)
Anakin's avatar
Anakin committed
341 342 343 344 345 346 347 348 349 350 351
	{
	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
352
}
Anakin's avatar
Anakin committed
353 354 355

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

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

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

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

void OglViewerWidget::showSettings()
{
	m_settings->show();
}
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 420

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();
}