-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmainview.cpp
330 lines (286 loc) · 10.4 KB
/
mainview.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#include "mainview.h"
#include <math.h>
#include <QPainter>
#include <QLoggingCategory>
#include <QOpenGLVersionFunctionsFactory>
/**
* @brief MainView::MainView
* @param Parent
*/
MainView::MainView(QWidget* Parent) : QOpenGLWidget(Parent), scale(1.0f) {}
/**
* @brief MainView::~MainView Deconstructs the main view.
*/
MainView::~MainView() {
debugLogger.stopLogging();
makeCurrent();
}
/**
* @brief MainView::initializeGL Initializes the opengl functions and settings,
* initialises the renderers and sets up the debugger.
*/
void MainView::initializeGL() {
initializeOpenGLFunctions();
qDebug() << ":: OpenGL initialized";
connect(&debugLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this,
SLOT(onMessageLogged(QOpenGLDebugMessage)), Qt::DirectConnection);
if (debugLogger.initialize()) {
QLoggingCategory::setFilterRules(
"qt.*=false\n"
"qt.text.font.*=false");
qDebug() << ":: Logging initialized";
debugLogger.startLogging(QOpenGLDebugLogger::SynchronousLogging);
debugLogger.enableMessages();
}
QString glVersion;
glVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
qDebug() << ":: Using OpenGL" << qPrintable(glVersion);
makeCurrent();
// Enable depth buffer
glEnable(GL_DEPTH_TEST);
// Default is GL_LESS
glDepthFunc(GL_LEQUAL);
// grab the opengl context
QOpenGLFunctions_4_1_Core* functions =
QOpenGLVersionFunctionsFactory::get<QOpenGLFunctions_4_1_Core>(
this->context());
// initialize renderers here with the current context
meshRenderer.init(functions, &settings);
}
/**
* @brief MainView::resizeGL Handles window resizing.
* @param newWidth The new width of the window in pixels.
* @param newHeight The new height of the window in pixels.
*/
void MainView::resizeGL(int newWidth, int newHeight) {
qDebug() << ".. resizeGL";
settings.dispRatio = float(newWidth) / float(newHeight);
settings.projectionMatrix.setToIdentity();
settings.projectionMatrix.perspective(settings.FoV, settings.dispRatio, 0.1f,
40.0f);
updateMatrices();
}
/**
* @brief MainView::updateMatrices Updates the matrices used for the model
* transforms.
*/
void MainView::updateMatrices() {
settings.modelViewMatrix.setToIdentity();
settings.modelViewMatrix.translate(QVector3D(0.0, 0.0, -3.0));
settings.modelViewMatrix.scale(scale);
settings.modelViewMatrix.rotate(rotationQuaternion);
settings.normalMatrix = settings.modelViewMatrix.normalMatrix();
settings.uniformUpdateRequired = true;
update();
}
/**
* @brief MainView::updateBuffers Updates the buffers of the renderers.
* @param mesh The mesh used to update the buffer content with.
*/
void MainView::updateBuffers(Mesh& mesh) {
mesh.extractAttributes();
meshRenderer.updateBuffers(mesh);
update();
}
/**
* @brief MainView::paintGL Draw call.
*/
void MainView::paintGL() {
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (settings.wireframeMode) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
if (settings.modelLoaded) {
meshRenderer.draw();
}
if (settings.selectedVertex == -1){
meshRenderer.draw();
}
}
/**
* @brief MainView::toNormalizedScreenCoordinates Normalizes the mouse
* coordinates to screen coordinates.
* @param x X coordinate.
* @param y Y coordinate.
* @return A vector containing the normalized x and y screen coordinates.
*/
QVector2D MainView::toNormalizedScreenCoordinates(float x, float y) {
float xRatio = x / float(width());
float yRatio = y / float(height());
// By default, the drawing canvas is the square [-1,1]^2:
float xScene = (1 - xRatio) * -1 + xRatio * 1;
// Note that the origin of the canvas is in the top left corner (not the lower
// left).
float yScene = yRatio * -1 + (1 - yRatio) * 1;
return {xScene, yScene};
}
/**
* @brief MainView::mouseMoveEvent Handles the dragging and rotating of the mesh
* by looking at the mouse movement.
* @param event Mouse event.
*/
void MainView::mouseMoveEvent(QMouseEvent* event) {
// Only works when vertex selection is not checked
if (!settings.renderVertexSelection){
if (event->buttons() == Qt::LeftButton) {
QVector2D sPos = toNormalizedScreenCoordinates(event->position().x(),
event->position().y());
QVector3D newVec = QVector3D(sPos.x(), sPos.y(), 0.0);
// project onto sphere
float sqrZ = 1.0f - QVector3D::dotProduct(newVec, newVec);
if (sqrZ > 0) {
newVec.setZ(sqrt(sqrZ));
} else {
newVec.normalize();
}
QVector3D v2 = newVec.normalized();
// reset if we are starting a drag
if (!dragging) {
dragging = true;
oldVec = newVec;
return;
}
// calculate axis and angle
QVector3D v1 = oldVec.normalized();
QVector3D N = QVector3D::crossProduct(v1, v2).normalized();
if (N.length() == 0.0f) {
oldVec = newVec;
return;
}
float angle = 180.0f / M_PI * acos(QVector3D::dotProduct(v1, v2));
rotationQuaternion =
QQuaternion::fromAxisAndAngle(N, angle) * rotationQuaternion;
updateMatrices();
// for next iteration
oldVec = newVec;
} else {
// to reset drag
dragging = false;
oldVec = QVector3D();
}
}
}
/**
* @brief MainView::mousePressEvent Handles presses by the mouse. Currently does
* nothing except setting focus.
* @param event Mouse event.
*/
void MainView::mousePressEvent(QMouseEvent* event) {
setFocus();
// Only works when vertex selection is checked
if (settings.renderVertexSelection){
if (event->buttons() == Qt::LeftButton) {
// Get screen space coordinates
int mouse_x = event->position().x();
int mouse_y = event->position().y();
GLfloat depth;
glReadPixels(mouse_x, height() - 1 - mouse_y,1, 1,
GL_LEQUAL, GL_FLOAT, &depth);
// Get NDC
QVector3D ray_nds = toNormalizedDeviceCoordinates(mouse_x,mouse_y);
// Clipping
QVector4D ray_clip = QVector4D(ray_nds.x(),ray_nds.y(), 1.0 , 1.0);
// Viewport transformation
QVector4D ray_eye = settings.projectionMatrix.inverted() * ray_clip;
float w = 2;
QVector4D ray_eye_view = QVector4D(ray_eye.x()*w,ray_eye.y()*w,ray_eye.z()*w,1.0);
// World
QVector4D ray_eye_inverted = (settings.modelViewMatrix.inverted() * ray_eye_view);
QVector3D ray_wor = QVector3D(ray_eye_inverted.x(),ray_eye_inverted.y(),ray_eye_inverted.z());
// Find closest point in the current mesh
int indexPos = findClosest(ray_wor,0.4f);
// Update index of the point
settings.selectedVertex = indexPos;
updateMatrices();
update();
}
}
}
/**
* @brief MainView::wheelEvent Handles zooming of the view.
* @param event Mouse event.
*/
void MainView::wheelEvent(QWheelEvent* event) {
// Only works when vertex selection is not checked
if (!settings.renderVertexSelection){
// Delta is usually 120
float phi = 1.0f + (event->angleDelta().y() / 2000.0f);
scale = fmin(fmax(phi * scale, 0.01f), 100.0f);
updateMatrices();
}
}
/**
* @brief MainView::keyPressEvent Handles keyboard shortcuts. Currently support
* 'Z' for wireframe mode and 'R' to reset orientation.
* @param event Mouse event.
*/
void MainView::keyPressEvent(QKeyEvent* event) {
// Only works when vertex selection is not checked
if (!settings.renderVertexSelection){
switch (event->key()) {
case 'Z':
settings.wireframeMode = !settings.wireframeMode;
update();
break;
case 'R':
scale = 1.0f;
rotationQuaternion = QQuaternion();
updateMatrices();
update();
break;
}
}
}
/**
* @brief MainView::onMessageLogged Helper function for logging messages.
* @param message The message to log.
*/
void MainView::onMessageLogged(QOpenGLDebugMessage Message) {
qDebug() << " → Log:" << Message;
}
/**
* @brief MainView::toNormalizedDeviceCoordinates Transforms the screen corrdinates to
* 3D normalised device coordinates.
* @param mouse_x X coordinates of screen space.
* @param mouse_y Y coordinates of screen space.
*/
QVector3D MainView::toNormalizedDeviceCoordinates(int mouse_x, int mouse_y){
QVector3D ray_nds;
// Scale the range of x and y [-1:1] and reverse the direction of y.
float x = (2.0f * mouse_x) / width() - 1.0f;
float y = 1.0f - (2.0f * mouse_y) / height();
float z = 0.0f;
ray_nds = QVector3D(x, y, z);
return ray_nds;
}
/**
* @brief MainView::findClosest Finds the index of the closest vertex in
* themesh to the provided vertex.
* @param p The point to find the closest point to.
* @param maxDist The maximum distance a point and the provided point can have.
* Is a value between 0 and 1.
* @return The index of the closest point to the provided point. Returns -1 if
* no point was found within the maximum distance.
*/
int MainView::findClosest(const QVector3D& p, const float maxDist) {
int ptIndex = -1;
float currentDist, minDist = 4;
QVector<QVector3D> vertices = currentVertices;
for (int k = 0; k < currentVertices.size(); k++) {
currentDist = vertices[k].distanceToPoint(p);
if (currentDist < minDist) {
minDist = currentDist;
ptIndex = k;
}
}
if (minDist >= maxDist) {
return -1;
}
return ptIndex;
}
void MainView::updateCurrentMesh(const QVector<QVector3D> newVertice){
currentVertices = newVertice;
}