46 } |
46 } |
47 this->compiler->initialize(); |
47 this->compiler->initialize(); |
48 this->compiler->build(this->model, this->documents); |
48 this->compiler->build(this->model, this->documents); |
49 this->initializeLighting(); |
49 this->initializeLighting(); |
50 this->initialized = true; |
50 this->initialized = true; |
51 this->rotation = QQuaternion::fromAxisAndAngle({1, 0, 0}, 30); |
51 this->modelQuaternion = glm::angleAxis(glm::radians(30.0f), glm::vec3{1, 0, 0}); |
52 this->rotation *= QQuaternion::fromAxisAndAngle({0, 1, 0}, 330); |
52 this->modelQuaternion *= glm::angleAxis(glm::radians(330.0f), glm::vec3{0, 1, 0}); |
|
53 this->updateViewMatrix(); |
53 glLineWidth(2.0); |
54 glLineWidth(2.0); |
54 this->update(); |
55 this->update(); |
55 } |
56 } |
56 |
57 |
57 void PartRenderer::initializeLighting() |
58 void PartRenderer::initializeLighting() |
72 |
73 |
73 void PartRenderer::resizeGL(int width, int height) |
74 void PartRenderer::resizeGL(int width, int height) |
74 { |
75 { |
75 glViewport(0, 0, width, height); |
76 glViewport(0, 0, width, height); |
76 this->projectionMatrix = glm::perspective( |
77 this->projectionMatrix = glm::perspective( |
77 glm::radians(90.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 100.f); |
81 10000.f); |
81 } |
82 } |
82 |
83 |
83 static GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass) |
84 static GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass) |
84 { |
85 { |
85 switch (vboClass) |
86 switch (vboClass) |
125 break; |
126 break; |
126 case gl::RenderStyle::Wireframe: |
127 case gl::RenderStyle::Wireframe: |
127 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
128 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
128 break; |
129 break; |
129 } |
130 } |
130 this->compiler->setUniformMatrix("CameraTransformation", this->projectionMatrix * this->viewMatrix); |
131 this->compiler->setUniformMatrix("CameraTransformation", this->projectionMatrix * this->viewMatrix * glm::mat4_cast(this->modelQuaternion)); |
131 // Lines need to be rendered last so that anti-aliasing does not interfere with polygon rendering. |
132 // Lines need to be rendered last so that anti-aliasing does not interfere with polygon rendering. |
132 renderVao(gl::ArrayClass::Triangles); |
133 renderVao(gl::ArrayClass::Triangles); |
133 renderVao(gl::ArrayClass::Quads); |
134 renderVao(gl::ArrayClass::Quads); |
134 renderVao(gl::ArrayClass::Lines); |
135 renderVao(gl::ArrayClass::Lines); |
135 glDisable(GL_POLYGON_OFFSET_FILL); |
136 glDisable(GL_POLYGON_OFFSET_FILL); |
136 } |
137 } |
137 |
138 |
|
139 void PartRenderer::updateViewMatrix() |
|
140 { |
|
141 // I'm not quite sure why using the exponent function on the zoom factor causes linear zoom behavior |
|
142 const double z = 2 * std::exp(this->zoom) * (1 + this->compiler->modelDistance()); |
|
143 this->viewMatrix = glm::lookAt(glm::vec3{0, 0, z}, {0, 0, 0}, {0, -1, 0}); |
|
144 } |
|
145 |
138 void PartRenderer::renderVao(const gl::ArrayClass arrayClass) |
146 void PartRenderer::renderVao(const gl::ArrayClass arrayClass) |
139 { |
147 { |
140 this->compiler->bindVertexArray(arrayClass); |
148 this->compiler->bindVertexArray(arrayClass); |
141 const std::size_t vertexCount = this->compiler->vboSize(arrayClass) / gl::FLOATS_PER_VERTEX; |
149 const std::size_t vertexCount = this->compiler->vboSize(arrayClass) / gl::FLOATS_PER_VERTEX; |
142 glDrawArrays(getGlTypeForArrayClass(arrayClass), 0, static_cast<GLsizei>(vertexCount)); |
150 glDrawArrays(getGlTypeForArrayClass(arrayClass), 0, static_cast<GLsizei>(vertexCount)); |
166 { |
174 { |
167 const bool left = event->buttons() & Qt::LeftButton; |
175 const bool left = event->buttons() & Qt::LeftButton; |
168 const QPointF move = pointToPointF(event->pos()) - this->lastMousePosition; |
176 const QPointF move = pointToPointF(event->pos()) - this->lastMousePosition; |
169 if (left and not move.isNull()) |
177 if (left and not move.isNull()) |
170 { |
178 { |
171 // FIXME: find a more elegant way to do this |
179 // q_x is the rotation of the brick along the vertical y-axis, because turning the |
172 const QQuaternion versor = QQuaternion::fromAxisAndAngle( |
180 // vertical axis causes horizontal (=x) rotation. Likewise q_y is the rotation of the |
173 QVector3D{static_cast<float>(-move.y()), static_cast<float>(move.x()), 0.0f}, |
181 // brick along the horizontal x-axis, which causes vertical rotation. |
174 0.6f * static_cast<float>(std::hypot(move.x(), move.y())) |
182 const auto scalar = 0.006f; |
175 ); |
183 const float move_x = static_cast<float>(move.x()); |
176 this->rotation = versor * this->rotation; |
184 const float move_y = static_cast<float>(move.y()); |
177 QVector3D cameraPosition = this->rotation.rotatedVector({0, 0, static_cast<float>(2 + 2 * this->compiler->modelDistance())}); |
185 const glm::quat q_x = glm::angleAxis(scalar * move_x, glm::vec3{0, -1, 0}); |
178 glm::vec3 cameraPosition_glm = {cameraPosition.x(), cameraPosition.y(), cameraPosition.z()}; |
186 const glm::quat q_y = glm::angleAxis(scalar * move_y, glm::vec3{-1, 0, 0}); |
179 this->viewMatrix = glm::lookAt(cameraPosition_glm, {0, 0, 0}, {0, -1, 0}); |
187 this->modelQuaternion = q_x * q_y * this->modelQuaternion; |
180 this->update(); |
188 this->update(); |
181 } |
189 } |
182 this->lastMousePosition = pointToPointF(event->pos()); |
190 this->lastMousePosition = pointToPointF(event->pos()); |
|
191 } |
|
192 |
|
193 void PartRenderer::wheelEvent(QWheelEvent* event) |
|
194 { |
|
195 static constexpr double WHEEL_STEP = 1 / 1000.0; |
|
196 const double move = (-event->angleDelta().y()) * WHEEL_STEP; |
|
197 this->zoom = std::clamp(this->zoom + move, MIN_ZOOM, MAX_ZOOM); |
|
198 this->updateViewMatrix(); |
|
199 this->update(); |
183 } |
200 } |
184 |
201 |
185 void PartRenderer::setRenderStyle(const gl::RenderStyle newStyle) |
202 void PartRenderer::setRenderStyle(const gl::RenderStyle newStyle) |
186 { |
203 { |
187 this->renderStyle = newStyle; |
204 this->renderStyle = newStyle; |