26 #include "partrenderer.h" |
26 #include "partrenderer.h" |
27 #include "model.h" |
27 #include "model.h" |
28 |
28 |
29 static constexpr double MIN_ZOOM = -3.0; |
29 static constexpr double MIN_ZOOM = -3.0; |
30 static constexpr double MAX_ZOOM = 3.0; |
30 static constexpr double MAX_ZOOM = 3.0; |
|
31 QOpenGLFunctions glfunc; |
31 |
32 |
32 PartRenderer::PartRenderer( |
33 PartRenderer::PartRenderer( |
33 Model* model, |
34 Model* model, |
34 DocumentManager* documents, |
35 DocumentManager* documents, |
35 const ldraw::ColorTable& colorTable, |
36 const ldraw::ColorTable& colorTable, |
36 QWidget* parent) : |
37 QWidget* parent) : |
37 QOpenGLWidget{parent}, |
38 QOpenGLWidget{parent}, |
38 model{model}, |
39 model{model}, |
39 documents{documents}, |
40 documents{documents}, |
40 colorTable{colorTable}, |
41 colorTable{colorTable} |
41 compiler{new gl::Compiler{model, this->colorTable, this}} |
|
42 { |
42 { |
43 this->setMouseTracking(true); |
43 this->setMouseTracking(true); |
44 connect(model, &Model::rowsInserted, [&]{ |
44 connect(model, &Model::rowsInserted, [&]{ |
45 this->needBuild = true; |
45 this->needBuild = true; |
46 }); |
46 }); |
49 |
49 |
50 PartRenderer::~PartRenderer() |
50 PartRenderer::~PartRenderer() |
51 { |
51 { |
52 } |
52 } |
53 |
53 |
54 static QVector3D vec3FromQColor(const QColor& color) |
54 static QVector3D calcQVector3DFromQColor(const QColor& color) |
55 { |
55 { |
56 return { |
56 return { |
57 toFloat(color.redF()), |
57 toFloat(color.redF()), |
58 toFloat(color.greenF()), |
58 toFloat(color.greenF()), |
59 toFloat(color.blueF()), |
59 toFloat(color.blueF()), |
60 }; |
60 }; |
61 } |
61 } |
62 |
62 |
63 void PartRenderer::initializeGL() |
63 void PartRenderer::initializeGL() |
64 { |
64 { |
65 this->initializeOpenGLFunctions(); |
65 ::glfunc.initializeOpenGLFunctions(); |
66 if (glGetError() != GL_NO_ERROR) |
66 if (glGetError() != GL_NO_ERROR) |
67 { |
67 { |
68 abort(); |
68 abort(); |
69 } |
69 } |
70 this->compiler->initialize(); |
70 gl::initializeModelShaders(&this->shaders); |
71 connect(this->model, &Model::dataChanged, this, &PartRenderer::build); |
71 connect(this->model, &Model::dataChanged, this, &PartRenderer::build); |
72 this->initialized = true; |
72 this->initialized = true; |
73 this->modelQuaternion = glm::angleAxis(glm::radians(30.0f), glm::vec3{-1, 0, 0}); |
73 this->modelQuaternion = glm::angleAxis(glm::radians(30.0f), glm::vec3{-1, 0, 0}); |
74 this->modelQuaternion *= glm::angleAxis(glm::radians(225.0f), glm::vec3{-0, 1, 0}); |
74 this->modelQuaternion *= glm::angleAxis(glm::radians(225.0f), glm::vec3{-0, 1, 0}); |
75 this->setupBackgroundColor(); |
|
76 this->updateModelMatrix(); |
75 this->updateModelMatrix(); |
77 this->updateViewMatrix(); |
76 this->updateViewMatrix(); |
78 this->update(); |
77 this->update(); |
79 } |
78 } |
80 |
79 |
115 |
114 |
116 void PartRenderer::renderScene() |
115 void PartRenderer::renderScene() |
117 { |
116 { |
118 if (this->needBuild) |
117 if (this->needBuild) |
119 { |
118 { |
120 this->compiler->build(this->documents, this->renderPreferences); |
119 gl::build(&this->shaders, this->model, this->colorTable, this->documents, this->renderPreferences); |
|
120 this->boundingBox = gl::boundingBoxForModel(this->model, this->documents); |
121 this->needBuild = false; |
121 this->needBuild = false; |
122 } |
122 } |
123 this->checkForGLErrors(); |
123 this->checkForGLErrors(); |
124 if (this->renderPreferences.lineAntiAliasing && this->renderPreferences.style != gl::RenderStyle::PickScene) |
124 if (true |
125 { |
125 and this->renderPreferences.lineAntiAliasing |
|
126 and this->renderPreferences.style != gl::RenderStyle::PickScene |
|
127 ) { |
126 glEnable(GL_LINE_SMOOTH); |
128 glEnable(GL_LINE_SMOOTH); |
127 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); |
129 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); |
128 } |
130 } |
129 else { |
131 else { |
130 glDisable(GL_LINE_SMOOTH); |
132 glDisable(GL_LINE_SMOOTH); |
135 glClearColor( |
137 glClearColor( |
136 static_cast<float>(backgroundColor.redF()), |
138 static_cast<float>(backgroundColor.redF()), |
137 static_cast<float>(backgroundColor.greenF()), |
139 static_cast<float>(backgroundColor.greenF()), |
138 static_cast<float>(backgroundColor.blueF()), |
140 static_cast<float>(backgroundColor.blueF()), |
139 1.0f); |
141 1.0f); |
140 this->compiler->setUniform("useLighting", GL_TRUE); |
142 gl::setShaderUniform(&this->shaders, "useLighting", GL_TRUE); |
141 } |
143 } |
142 else |
144 else |
143 { |
145 { |
144 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
146 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
145 this->compiler->setUniform("useLighting", GL_FALSE); |
147 gl::setShaderUniform(&this->shaders, "useLighting", GL_FALSE); |
146 } |
148 } |
147 this->checkForGLErrors(); |
149 this->checkForGLErrors(); |
148 this->compiler->setUniform("selectedColor", vec3FromQColor(this->renderPreferences.selectedColor)); |
150 const QVector3D color = calcQVector3DFromQColor(this->renderPreferences.selectedColor); |
149 this->compiler->setUniform("highlighted", this->highlighted.value); |
151 gl::setShaderUniform(&this->shaders, "selectedColor", color); |
|
152 gl::setShaderUniform(&this->shaders, "highlighted", this->highlighted.value); |
150 this->checkForGLErrors(); |
153 this->checkForGLErrors(); |
151 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
154 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
152 glEnable(GL_DEPTH_TEST); |
155 glEnable(GL_DEPTH_TEST); |
153 glEnable(GL_POLYGON_OFFSET_FILL); |
156 glEnable(GL_POLYGON_OFFSET_FILL); |
154 glPolygonOffset(1.0f, 1.0f); |
157 glPolygonOffset(1.0f, 1.0f); |
155 glLineWidth(this->renderPreferences.lineThickness); |
158 glLineWidth(this->renderPreferences.lineThickness); |
|
159 const auto renderAllArrays = [this](){ |
|
160 // Lines need to be rendered last so that anti-aliasing does not interfere with polygon rendering. |
|
161 this->renderVao(gl::ArrayClass::Triangles); |
|
162 this->renderVao(gl::ArrayClass::Quads); |
|
163 this->renderVao(gl::ArrayClass::Lines); |
|
164 }; |
156 switch (this->renderPreferences.style) |
165 switch (this->renderPreferences.style) |
157 { |
166 { |
158 case gl::RenderStyle::Normal: |
167 case gl::RenderStyle::Normal: |
159 this->setFragmentStyle(gl::FragmentStyle::Normal); |
168 this->setFragmentStyle(gl::FragmentStyle::Normal); |
160 this->renderAllArrays(); |
169 renderAllArrays(); |
161 break; |
170 break; |
162 case gl::RenderStyle::BfcRedGreen: |
171 case gl::RenderStyle::BfcRedGreen: |
163 glEnable(GL_CULL_FACE); |
172 glEnable(GL_CULL_FACE); |
164 glCullFace(GL_BACK); |
173 glCullFace(GL_BACK); |
165 this->setFragmentStyle(gl::FragmentStyle::BfcGreen); |
174 this->setFragmentStyle(gl::FragmentStyle::BfcGreen); |
170 renderVao(gl::ArrayClass::Triangles); |
179 renderVao(gl::ArrayClass::Triangles); |
171 renderVao(gl::ArrayClass::Quads); |
180 renderVao(gl::ArrayClass::Quads); |
172 glDisable(GL_CULL_FACE); |
181 glDisable(GL_CULL_FACE); |
173 this->setFragmentStyle(gl::FragmentStyle::Normal); |
182 this->setFragmentStyle(gl::FragmentStyle::Normal); |
174 renderVao(gl::ArrayClass::Lines); |
183 renderVao(gl::ArrayClass::Lines); |
|
184 break; |
175 case gl::RenderStyle::RandomColors: |
185 case gl::RenderStyle::RandomColors: |
176 this->setFragmentStyle(gl::FragmentStyle::RandomColors); |
186 this->setFragmentStyle(gl::FragmentStyle::RandomColors); |
177 this->renderAllArrays(); |
187 renderAllArrays(); |
178 break; |
188 break; |
179 case gl::RenderStyle::PickScene: |
189 case gl::RenderStyle::PickScene: |
180 glLineWidth(3.0f); |
190 glLineWidth(3.0f); |
181 this->setFragmentStyle(gl::FragmentStyle::Id); |
191 this->setFragmentStyle(gl::FragmentStyle::Id); |
182 this->renderAllArrays(); |
192 renderAllArrays(); |
183 break; |
193 break; |
184 case gl::RenderStyle::VertexPickScene: |
194 case gl::RenderStyle::VertexPickScene: |
185 glLineWidth(1.0f); |
195 glLineWidth(1.0f); |
186 this->setFragmentStyle(gl::FragmentStyle::Black); |
196 this->setFragmentStyle(gl::FragmentStyle::Black); |
187 this->renderAllArrays(); |
197 renderAllArrays(); |
188 break; |
198 break; |
189 case gl::RenderStyle::Wireframe: |
199 case gl::RenderStyle::Wireframe: |
190 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
200 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
191 this->setFragmentStyle(gl::FragmentStyle::Normal); |
201 this->setFragmentStyle(gl::FragmentStyle::Normal); |
192 this->renderAllArrays(); |
202 renderAllArrays(); |
193 break; |
203 break; |
194 } |
204 } |
195 glDisable(GL_POLYGON_OFFSET_FILL); |
205 glDisable(GL_POLYGON_OFFSET_FILL); |
196 } |
206 } |
197 |
207 |
198 void PartRenderer::renderAllArrays() |
|
199 { |
|
200 // Lines need to be rendered last so that anti-aliasing does not interfere with polygon rendering. |
|
201 renderVao(gl::ArrayClass::Triangles); |
|
202 renderVao(gl::ArrayClass::Quads); |
|
203 renderVao(gl::ArrayClass::Lines); |
|
204 } |
|
205 |
|
206 |
208 |
207 void PartRenderer::updateViewMatrix() |
209 void PartRenderer::updateViewMatrix() |
208 { |
210 { |
209 // I'm not quite sure why using the exponent function on the zoom factor causes linear zoom behavior |
211 // I'm not quite sure why using the exponent function on the zoom factor causes linear zoom behavior |
210 const double z = 2 * std::exp(this->zoom) * (1 + this->compiler->modelDistance()); |
212 const float modelDistance = longestMeasure(this->boundingBox); |
|
213 const double z = 2.0 * std::exp(this->zoom) * (1 + static_cast<double>(modelDistance)); |
211 this->viewMatrix = glm::lookAt(glm::vec3{0, 0, z}, {0, 0, 0}, {0, -1, 0}); |
214 this->viewMatrix = glm::lookAt(glm::vec3{0, 0, z}, {0, 0, 0}, {0, -1, 0}); |
212 this->compiler->setUniformMatrix("viewMatrix", this->viewMatrix); |
215 gl::setShaderUniformMatrix(&this->shaders, "viewMatrix", this->viewMatrix); |
213 Q_EMIT this->viewMatrixChanged(this->viewMatrix); |
216 Q_EMIT this->viewMatrixChanged(this->viewMatrix); |
214 } |
217 } |
215 |
218 |
216 void PartRenderer::updateModelMatrix() |
219 void PartRenderer::updateModelMatrix() |
217 { |
220 { |
218 this->modelMatrix = glm::mat4_cast(this->modelQuaternion); |
221 this->modelMatrix = glm::mat4_cast(this->modelQuaternion); |
219 this->compiler->setUniformMatrix("modelMatrix", modelMatrix); |
222 gl::setShaderUniformMatrix(&this->shaders, "modelMatrix", modelMatrix); |
220 Q_EMIT this->modelMatrixChanged(this->modelMatrix); |
223 Q_EMIT this->modelMatrixChanged(this->modelMatrix); |
221 this->update(); |
224 this->update(); |
222 } |
225 } |
223 |
226 |
224 void PartRenderer::setupBackgroundColor() |
|
225 { |
|
226 } |
|
227 |
|
228 void PartRenderer::build() |
227 void PartRenderer::build() |
229 { |
228 { |
230 this->needBuild = true; |
229 this->needBuild = true; |
231 } |
230 } |
232 |
231 |
233 void PartRenderer::renderVao(const gl::ArrayClass arrayClass) |
232 void PartRenderer::renderVao(const gl::ArrayClass arrayClass) |
234 { |
233 { |
235 this->compiler->bindVertexArray(arrayClass); |
234 gl::bindModelShaderVertexArray(&this->shaders, arrayClass); |
236 const std::size_t vertexCount = this->compiler->vertexCount(arrayClass); |
235 const std::size_t vertexCount = gl::vertexCount(&this->shaders, arrayClass); |
237 this->checkForGLErrors(); |
236 this->checkForGLErrors(); |
238 glDrawArrays(getGlTypeForArrayClass(arrayClass), 0, static_cast<GLsizei>(vertexCount)); |
237 glDrawArrays(getGlTypeForArrayClass(arrayClass), 0, static_cast<GLsizei>(vertexCount)); |
239 this->checkForGLErrors(); |
238 this->checkForGLErrors(); |
240 this->compiler->releaseVertexArray(arrayClass); |
239 gl::releaseModelShaderVertexArray(&this->shaders, arrayClass); |
241 this->checkForGLErrors(); |
240 this->checkForGLErrors(); |
242 } |
241 } |
243 |
242 |
244 void PartRenderer::checkForGLErrors() |
243 void PartRenderer::checkForGLErrors() |
245 { |
244 { |
363 this->checkForGLErrors(); |
362 this->checkForGLErrors(); |
364 glReadPixels(where.x(), this->height() - where.y(), 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &data[0]); |
363 glReadPixels(where.x(), this->height() - where.y(), 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &data[0]); |
365 this->checkForGLErrors(); |
364 this->checkForGLErrors(); |
366 this->renderPreferences.style = oldRenderStyle; |
365 this->renderPreferences.style = oldRenderStyle; |
367 this->update(); |
366 this->update(); |
368 return gl::Compiler::idFromColor(data); |
367 return gl::idFromColor(data); |
369 } |
368 } |
370 |
369 |
371 /** |
370 /** |
372 * @brief Changes the color of rendered fragments |
371 * @brief Changes the color of rendered fragments |
373 * @param newFragmentStyle new fragment style to use |
372 * @param newFragmentStyle new fragment style to use |
374 */ |
373 */ |
375 void PartRenderer::setFragmentStyle(gl::FragmentStyle newFragmentStyle) |
374 void PartRenderer::setFragmentStyle(gl::FragmentStyle newFragmentStyle) |
376 { |
375 { |
377 this->compiler->setUniform("fragmentStyle", static_cast<int>(newFragmentStyle)); |
376 gl::setShaderUniform(&this->shaders, "fragmentStyle", static_cast<int>(newFragmentStyle)); |
378 } |
377 } |
379 |
378 |
380 /** |
379 /** |
381 * @brief Changes the way the scene is rendered |
380 * @brief Changes the way the scene is rendered |
382 * @param newStyle new render style to use |
381 * @param newStyle new render style to use |