| 32 QWidget* parent) : |
32 QWidget* parent) : |
| 33 QOpenGLWidget{parent}, |
33 QOpenGLWidget{parent}, |
| 34 model{model}, |
34 model{model}, |
| 35 documents{documents}, |
35 documents{documents}, |
| 36 colorTable{colorTable}, |
36 colorTable{colorTable}, |
| 37 compiler{new gl::Compiler{this->colorTable, this}}, |
37 compiler{new gl::Compiler{this->colorTable, this}} |
| 38 gridProgram{this} |
|
| 39 { |
38 { |
| 40 this->setMouseTracking(true); |
39 this->setMouseTracking(true); |
| 41 } |
40 } |
| 42 |
41 |
| 43 PartRenderer::~PartRenderer() |
42 PartRenderer::~PartRenderer() |
| 44 { |
43 { |
| 45 } |
44 } |
| 46 |
45 |
| 47 static QVector3D vec3FromQColor(const QColor& color) |
46 static QVector3D vec3FromQColor(const QColor& color) |
| 48 { |
47 { |
| 49 return {(float)color.redF(), (float)color.greenF(), (float)color.blueF()}; |
48 return { |
| |
49 toFloat(color.redF()), |
| |
50 toFloat(color.greenF()), |
| |
51 toFloat(color.blueF()), |
| |
52 }; |
| 50 } |
53 } |
| 51 |
54 |
| 52 void PartRenderer::initializeGL() |
55 void PartRenderer::initializeGL() |
| 53 { |
56 { |
| 54 this->initializeOpenGLFunctions(); |
57 this->initializeOpenGLFunctions(); |
| 55 if (glGetError() != GL_NO_ERROR) |
58 if (glGetError() != GL_NO_ERROR) |
| 56 { |
59 { |
| 57 abort(); |
60 abort(); |
| 58 } |
61 } |
| 59 this->gridProgram.emplace(this); |
|
| 60 this->gridProgram->initialize(); |
|
| 61 this->compiler->initialize(); |
62 this->compiler->initialize(); |
| 62 this->compiler->build(this->model, this->documents, this->renderPreferences); |
63 this->compiler->build(this->model, this->documents, this->renderPreferences); |
| 63 this->initialized = true; |
64 this->initialized = true; |
| 64 this->modelQuaternion = glm::angleAxis(glm::radians(30.0f), glm::vec3{-1, 0, 0}); |
65 this->modelQuaternion = glm::angleAxis(glm::radians(30.0f), glm::vec3{-1, 0, 0}); |
| 65 this->modelQuaternion *= glm::angleAxis(glm::radians(225.0f), glm::vec3{-0, 1, 0}); |
66 this->modelQuaternion *= glm::angleAxis(glm::radians(225.0f), glm::vec3{-0, 1, 0}); |
| 77 glm::radians(45.0f), |
78 glm::radians(45.0f), |
| 78 static_cast<float>(width) / static_cast<float>(height), |
79 static_cast<float>(width) / static_cast<float>(height), |
| 79 0.1f, |
80 0.1f, |
| 80 10000.f); |
81 10000.f); |
| 81 this->compiler->setUniformMatrix("projectionMatrix", this->projectionMatrix); |
82 this->compiler->setUniformMatrix("projectionMatrix", this->projectionMatrix); |
| 82 this->gridProgram->setProjectionMatrix(this->projectionMatrix); |
83 emit projectionMatrixChanged(); |
| 83 } |
84 } |
| 84 |
85 |
| 85 static GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass) |
86 static GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass) |
| 86 { |
87 { |
| 87 switch (vboClass) |
88 switch (vboClass) |
| 171 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
172 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
| 172 this->setFragmentStyle(gl::FragmentStyle::Normal); |
173 this->setFragmentStyle(gl::FragmentStyle::Normal); |
| 173 this->renderAllArrays(); |
174 this->renderAllArrays(); |
| 174 break; |
175 break; |
| 175 } |
176 } |
| 176 glEnable(GL_BLEND); |
|
| 177 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
|
| 178 this->gridProgram->draw(); |
|
| 179 glDisable(GL_BLEND); |
|
| 180 glDisable(GL_POLYGON_OFFSET_FILL); |
177 glDisable(GL_POLYGON_OFFSET_FILL); |
| 181 } |
178 } |
| 182 |
179 |
| 183 void PartRenderer::renderAllArrays() |
180 void PartRenderer::renderAllArrays() |
| 184 { |
181 { |
| 193 { |
190 { |
| 194 // I'm not quite sure why using the exponent function on the zoom factor causes linear zoom behavior |
191 // I'm not quite sure why using the exponent function on the zoom factor causes linear zoom behavior |
| 195 const double z = 2 * std::exp(this->zoom) * (1 + this->compiler->modelDistance()); |
192 const double z = 2 * std::exp(this->zoom) * (1 + this->compiler->modelDistance()); |
| 196 this->viewMatrix = glm::lookAt(glm::vec3{0, 0, z}, {0, 0, 0}, {0, -1, 0}); |
193 this->viewMatrix = glm::lookAt(glm::vec3{0, 0, z}, {0, 0, 0}, {0, -1, 0}); |
| 197 this->compiler->setUniformMatrix("viewMatrix", this->viewMatrix); |
194 this->compiler->setUniformMatrix("viewMatrix", this->viewMatrix); |
| 198 this->gridProgram->setViewMatrix(this->viewMatrix); |
195 emit this->viewMatrixChanged(); |
| 199 } |
196 } |
| 200 |
197 |
| 201 void PartRenderer::updateModelMatrix() |
198 void PartRenderer::updateModelMatrix() |
| 202 { |
199 { |
| 203 const glm::mat4 modelMatrix = glm::mat4_cast(this->modelQuaternion); |
200 this->modelMatrix = glm::mat4_cast(this->modelQuaternion); |
| 204 this->compiler->setUniformMatrix("modelMatrix", modelMatrix); |
201 this->compiler->setUniformMatrix("modelMatrix", modelMatrix); |
| 205 this->gridProgram->setModelMatrix(modelMatrix); |
202 emit this->modelMatrixChanged(); |
| 206 this->update(); |
203 this->update(); |
| 207 } |
204 } |
| 208 |
205 |
| 209 void PartRenderer::setupBackgroundColor() |
206 void PartRenderer::setupBackgroundColor() |
| 210 { |
207 { |
| 211 if (this->gridProgram.has_value()) |
|
| 212 { |
|
| 213 const bool isDark = luma(this->renderPreferences.backgroundColor) < 0.25; |
|
| 214 this->gridProgram->setGridColor(isDark ? Qt::white : Qt::black); |
|
| 215 } |
|
| 216 } |
208 } |
| 217 |
209 |
| 218 void PartRenderer::renderVao(const gl::ArrayClass arrayClass) |
210 void PartRenderer::renderVao(const gl::ArrayClass arrayClass) |
| 219 { |
211 { |
| 220 this->compiler->bindVertexArray(arrayClass); |
212 this->compiler->bindVertexArray(arrayClass); |
| 253 } |
245 } |
| 254 |
246 |
| 255 void PartRenderer::mouseMoveEvent(QMouseEvent* event) |
247 void PartRenderer::mouseMoveEvent(QMouseEvent* event) |
| 256 { |
248 { |
| 257 const bool left = event->buttons() & Qt::LeftButton; |
249 const bool left = event->buttons() & Qt::LeftButton; |
| 258 const QPointF move = pointToPointF(event->pos()) - this->lastMousePosition; |
250 const QPoint move = event->pos() - this->lastMousePosition; |
| 259 if (left and not move.isNull()) |
251 if (left and not move.isNull()) |
| 260 { |
252 { |
| 261 // q_x is the rotation of the brick along the vertical y-axis, because turning the |
253 // q_x is the rotation of the brick along the vertical y-axis, because turning the |
| 262 // vertical axis causes horizontal (=x) rotation. Likewise q_y is the rotation of the |
254 // vertical axis causes horizontal (=x) rotation. Likewise q_y is the rotation of the |
| 263 // brick along the horizontal x-axis, which causes vertical rotation. |
255 // brick along the horizontal x-axis, which causes vertical rotation. |
| 267 const glm::quat q_x = glm::angleAxis(scalar * move_x, glm::vec3{0, -1, 0}); |
259 const glm::quat q_x = glm::angleAxis(scalar * move_x, glm::vec3{0, -1, 0}); |
| 268 const glm::quat q_y = glm::angleAxis(scalar * move_y, glm::vec3{-1, 0, 0}); |
260 const glm::quat q_y = glm::angleAxis(scalar * move_y, glm::vec3{-1, 0, 0}); |
| 269 this->modelQuaternion = q_x * q_y * this->modelQuaternion; |
261 this->modelQuaternion = q_x * q_y * this->modelQuaternion; |
| 270 this->updateModelMatrix(); |
262 this->updateModelMatrix(); |
| 271 } |
263 } |
| 272 this->lastMousePosition = pointToPointF(event->pos()); |
264 this->lastMousePosition = event->pos(); |
| 273 } |
265 } |
| 274 |
266 |
| 275 void PartRenderer::wheelEvent(QWheelEvent* event) |
267 void PartRenderer::wheelEvent(QWheelEvent* event) |
| 276 { |
268 { |
| 277 static constexpr double WHEEL_STEP = 1 / 1000.0; |
269 static constexpr double WHEEL_STEP = 1 / 1000.0; |
| 278 const double move = (-event->angleDelta().y()) * WHEEL_STEP; |
270 const double move = (-event->angleDelta().y()) * WHEEL_STEP; |
| 279 this->zoom = std::clamp(this->zoom + move, MIN_ZOOM, MAX_ZOOM); |
271 this->zoom = std::clamp(this->zoom + move, MIN_ZOOM, MAX_ZOOM); |
| 280 this->updateViewMatrix(); |
272 this->updateViewMatrix(); |
| 281 this->update(); |
273 this->update(); |
| 282 } |
|
| 283 |
|
| 284 glm::vec3 PartRenderer::viewport(const glm::vec3& point) |
|
| 285 { |
|
| 286 return viewport(point, this->width(), this->height()); |
|
| 287 } |
274 } |
| 288 |
275 |
| 289 /** |
276 /** |
| 290 * @brief Converts the specified on the screen into the 3D world. The point is unprojected twice into 3D and the |
277 * @brief Converts the specified on the screen into the 3D world. The point is unprojected twice into 3D and the |
| 291 * intersection of the resulting line with the specified plane is returned. If the intersection point lies behind |
278 * intersection of the resulting line with the specified plane is returned. If the intersection point lies behind |
| 294 * @param plane Plane to raycast against |
281 * @param plane Plane to raycast against |
| 295 * @return world co-ordinates, or no value if the point is behind the camera. |
282 * @return world co-ordinates, or no value if the point is behind the camera. |
| 296 */ |
283 */ |
| 297 std::optional<glm::vec3> PartRenderer::screenToModelCoordinates(const QPoint& point, const geom::Plane& plane) |
284 std::optional<glm::vec3> PartRenderer::screenToModelCoordinates(const QPoint& point, const geom::Plane& plane) |
| 298 { |
285 { |
| 299 auto p1 = this->unproject({point.x(), point.y(), 0}); |
286 const glm::vec3 p1 = this->unproject({point.x(), point.y(), 0}); |
| 300 auto p2 = this->unproject({point.x(), point.y(), 1}); |
287 const glm::vec3 p2 = this->unproject({point.x(), point.y(), 1}); |
| 301 geom::Line line = geom::lineFromPoints(p1, p2); |
288 const geom::Line line = geom::lineFromPoints(p1, p2); |
| 302 std::optional<glm::vec3> result; |
289 std::optional<glm::vec3> result; |
| 303 result = geom::linePlaneIntersection(line, plane, 0.01f); |
290 result = geom::linePlaneIntersection(line, plane, 0.01f); |
| 304 // If the point lies behind the camera, do not return a result. |
291 // If the point lies behind the camera, do not return a result. |
| 305 if (result.has_value() and glm::dot(line.direction, *result - p1) < 0) |
292 if (result.has_value() and glm::dot(line.direction, *result - p1) < 0) |
| 306 { |
293 { |
| 374 if (mainColorChanged or backgroundColorChanged) |
361 if (mainColorChanged or backgroundColorChanged) |
| 375 { |
362 { |
| 376 this->compiler->build(this->model, this->documents, this->renderPreferences); |
363 this->compiler->build(this->model, this->documents, this->renderPreferences); |
| 377 this->setupBackgroundColor(); |
364 this->setupBackgroundColor(); |
| 378 } |
365 } |
| 379 this->update(); |
366 emit this->renderPreferencesChanged(); |
| 380 } |
367 this->update(); |
| 381 |
368 } |
| 382 glm::vec3 PartRenderer::viewport(const glm::vec3& point, float width, float height) |
|
| 383 { |
|
| 384 return { |
|
| 385 width * 0.5 * (point.x + 1), |
|
| 386 height * 0.5 * (-point.y + 1), |
|
| 387 0 |
|
| 388 }; |
|
| 389 } |
|