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