14 * |
14 * |
15 * You should have received a copy of the GNU General Public License |
15 * You should have received a copy of the GNU General Public License |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 */ |
17 */ |
18 |
18 |
|
19 #include <GL/glut.h> |
19 #include <QMouseEvent> |
20 #include <QMouseEvent> |
20 #include <GL/glut.h> |
21 #include <QMessageBox> |
21 #include "partrenderer.h" |
22 #include "partrenderer.h" |
22 |
23 |
23 PartRenderer::PartRenderer(Model* model, DocumentManager* documents, QWidget* parent) : |
24 |
|
25 static const char* vertexShaderSource = R"( |
|
26 #version 330 core |
|
27 |
|
28 layout(location=0) in vec3 position; |
|
29 layout(location=1) in vec4 color; |
|
30 out vec4 vColor; |
|
31 uniform mat4 CameraTransformation; |
|
32 |
|
33 void main() |
|
34 { |
|
35 vColor = color; |
|
36 gl_Position = CameraTransformation * vec4(position, 1.0); |
|
37 } |
|
38 )"; |
|
39 |
|
40 static const char* fragmentShaderSource = R"( |
|
41 #version 330 core |
|
42 |
|
43 in vec4 vColor; |
|
44 out vec4 fColor; |
|
45 |
|
46 void main() |
|
47 { |
|
48 fColor = vColor; |
|
49 } |
|
50 )"; |
|
51 |
|
52 PartRenderer::PartRenderer(Model* model, DocumentManager* documents, const ColorTable& colorTable, QWidget* parent) : |
24 QOpenGLWidget{parent}, |
53 QOpenGLWidget{parent}, |
25 model{model}, |
54 model{model}, |
26 documents{documents}, |
55 documents{documents}, |
27 compiler{new gl::Compiler{this}} |
56 colorTable{colorTable}, |
|
57 compiler{new gl::Compiler{this->colorTable, this}} |
28 { |
58 { |
29 this->setMouseTracking(true); |
59 this->setMouseTracking(true); |
|
60 } |
|
61 |
|
62 PartRenderer::~PartRenderer() |
|
63 { |
|
64 delete this->objects.program; |
30 } |
65 } |
31 |
66 |
32 void PartRenderer::initializeGL() |
67 void PartRenderer::initializeGL() |
33 { |
68 { |
34 this->initializeOpenGLFunctions(); |
69 this->initializeOpenGLFunctions(); |
35 if (glGetError() != GL_NO_ERROR) |
70 if (glGetError() != GL_NO_ERROR) |
36 { |
71 { |
37 abort(); |
72 abort(); |
38 } |
73 } |
|
74 glEnableClientState(GL_NORMAL_ARRAY); |
|
75 glEnableClientState(GL_VERTEX_ARRAY); |
|
76 //this->compiler->initialize(); |
|
77 //this->compiler->build(this->model, this->documents); |
39 this->initializeLighting(); |
78 this->initializeLighting(); |
40 this->initialized = true; |
79 this->initialized = true; |
41 this->rotation = QQuaternion::fromAxisAndAngle({1, 0, 0}, 30); |
80 this->rotation = QQuaternion::fromAxisAndAngle({1, 0, 0}, 30); |
42 this->rotation *= QQuaternion::fromAxisAndAngle({0, 1, 0}, 330); |
81 this->rotation *= QQuaternion::fromAxisAndAngle({0, 1, 0}, 330); |
43 this->compiler->build(this->model, this->documents); |
|
44 glLineWidth(2.0); |
82 glLineWidth(2.0); |
|
83 this->objects.program = new QOpenGLShaderProgram; |
|
84 this->objects.program->create(); |
|
85 this->checkForGLErrors(); |
|
86 this->objects.program->addShaderFromSourceCode(QOpenGLShader::Vertex, ::vertexShaderSource); |
|
87 this->checkForGLErrors(); |
|
88 this->objects.program->addShaderFromSourceCode(QOpenGLShader::Fragment, ::fragmentShaderSource); |
|
89 this->checkForGLErrors(); |
|
90 this->objects.program->link(); |
|
91 this->checkForGLErrors(); |
|
92 this->objects.program->bind(); |
|
93 this->checkForGLErrors(); |
|
94 this->objects.buffer.create(); |
|
95 this->checkForGLErrors(); |
|
96 this->objects.buffer.bind(); |
|
97 this->checkForGLErrors(); |
|
98 this->objects.buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); |
|
99 this->checkForGLErrors(); |
|
100 /* |
|
101 GLfloat data[] = { |
|
102 20.0f, 20.0f, 6.0f, 1.0f, 0.0f, 0.0f, 1.0f, |
|
103 30.0f, 20.0f, 6.0f, 0.0f, 1.0f, 0.0f, 1.0f, |
|
104 30.0f, 30.0f, 6.0f, 0.0f, 0.0f, 1.0f, 1.0f, |
|
105 }; |
|
106 */ |
|
107 GLfloat data[] = { |
|
108 0.00f, 0.75f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, |
|
109 -0.75f, -0.75f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, |
|
110 0.75f, -0.75f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f |
|
111 }; |
|
112 this->objects.buffer.allocate(data, sizeof data); |
|
113 this->checkForGLErrors(); |
|
114 this->objects.vertexArray.create(); |
|
115 this->checkForGLErrors(); |
|
116 this->objects.vertexArray.bind(); |
|
117 this->checkForGLErrors(); |
|
118 this->objects.program->enableAttributeArray(0); |
|
119 this->checkForGLErrors(); |
|
120 this->objects.program->enableAttributeArray(1); |
|
121 this->checkForGLErrors(); |
|
122 this->objects.program->setAttributeBuffer(0, GL_FLOAT, 0, 3); |
|
123 this->checkForGLErrors(); |
|
124 this->objects.program->setAttributeBuffer(1, GL_FLOAT, 3, 4); |
|
125 this->checkForGLErrors(); |
|
126 this->objects.vertexArray.release(); |
|
127 this->checkForGLErrors(); |
|
128 this->objects.buffer.release(); |
|
129 this->checkForGLErrors(); |
|
130 this->objects.program->release(); |
|
131 this->checkForGLErrors(); |
45 } |
132 } |
46 |
133 |
47 /* |
134 /* |
48 * Pads a 3×3 matrix into a 4×4 one by adding cells from the identity matrix. |
135 * Pads a 3×3 matrix into a 4×4 one by adding cells from the identity matrix. |
49 */ |
136 */ |
83 glLoadIdentity(); |
170 glLoadIdentity(); |
84 gluPerspective(45.0, static_cast<double>(width) / static_cast<double>(height), near, far); |
171 gluPerspective(45.0, static_cast<double>(width) / static_cast<double>(height), near, far); |
85 glMatrixMode(GL_MODELVIEW); |
172 glMatrixMode(GL_MODELVIEW); |
86 } |
173 } |
87 |
174 |
88 static GLenum getGlTypeForVboClass(const gl::VboClass vboClass) |
175 static GLenum getGlTypeForArrayClass(const gl::ArrayClass vboClass) |
89 { |
176 { |
90 switch (vboClass) |
177 switch (vboClass) |
91 { |
178 { |
92 case gl::VboClass::Lines: |
179 case gl::ArrayClass::Lines: |
93 case gl::VboClass::ConditionalLines: |
180 case gl::ArrayClass::ConditionalLines: |
94 return GL_LINES; |
181 return GL_LINES; |
95 case gl::VboClass::Triangles: |
182 case gl::ArrayClass::Triangles: |
96 return GL_TRIANGLES; |
183 return GL_TRIANGLES; |
97 case gl::VboClass::Quads: |
184 case gl::ArrayClass::Quads: |
98 return GL_QUADS; |
185 return GL_QUADS; |
99 } |
186 } |
100 throw std::runtime_error{"Bad vbo class passed to getGlTypeForVboClass"}; |
187 throw std::runtime_error{"Bad vbo class passed to getGlTypeForVboClass"}; |
101 } |
188 } |
102 |
189 |
103 // https://www.codemiles.com/c-opengl-examples/drawing-teapot-using-opengl-t9010.html?mobile=on |
|
104 #include <QMessageBox> |
|
105 void PartRenderer::paintGL() |
190 void PartRenderer::paintGL() |
106 { |
191 { |
|
192 /* |
107 glEnable (GL_BLEND); |
193 glEnable (GL_BLEND); |
108 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
194 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
109 glEnable (GL_POLYGON_OFFSET_FILL); |
195 */ |
110 glPolygonOffset (1.0f, 1.0f); |
|
111 glEnable (GL_DEPTH_TEST); |
196 glEnable (GL_DEPTH_TEST); |
112 glShadeModel (GL_SMOOTH); |
197 glShadeModel (GL_SMOOTH); |
113 glEnable (GL_MULTISAMPLE); |
198 glEnable (GL_MULTISAMPLE); |
114 glEnable (GL_LINE_SMOOTH); |
199 glEnable (GL_LINE_SMOOTH); |
115 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); |
200 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); |
116 this->renderScene(); |
201 this->renderScene(); |
117 } |
202 } |
118 |
203 |
119 void PartRenderer::renderScene() |
204 void PartRenderer::renderScene() |
120 { |
205 { |
|
206 glClearColor(0.8f, 0.8f, 0.8f, 1.0f); |
|
207 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|
208 glMatrixMode(GL_MODELVIEW); |
|
209 glEnable(GL_DEPTH_TEST); |
|
210 glEnable(GL_LIGHTING); |
|
211 glLoadIdentity(); |
|
212 //glTranslated(0.0, 0.0, -4.5 * this->compiler->modelDistance()); |
|
213 //glTranslated(0.0, 0.0, -4.5); |
|
214 //glMultMatrixf(padMatrix(this->rotation.toRotationMatrix()).constData()); |
|
215 //xyz(glTranslatef, -this->compiler->modelCenter()); |
|
216 auto rotationMatrix = padMatrix(this->rotation.toRotationMatrix()); |
|
217 rotationMatrix(2, 3) = 0; |
|
218 glEnable(GL_POLYGON_OFFSET_FILL); |
|
219 glPolygonOffset(1.0f, 1.0f); |
121 switch (this->renderStyle) |
220 switch (this->renderStyle) |
122 { |
221 { |
123 case gl::RenderStyle::Normal: |
222 case gl::RenderStyle::Normal: |
124 case gl::RenderStyle::BfcRedGreen: |
223 case gl::RenderStyle::BfcRedGreen: |
125 case gl::RenderStyle::RandomColors: |
224 case gl::RenderStyle::RandomColors: |
126 break; |
225 break; |
127 case gl::RenderStyle::Wireframe: |
226 case gl::RenderStyle::Wireframe: |
128 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
227 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); |
129 break; |
228 break; |
130 } |
229 } |
131 glMatrixMode(GL_MODELVIEW); |
230 this->objects.program->bind(); |
132 glClearColor(1.0f, 1.0f, 1.0f, 1.0f); |
231 this->checkForGLErrors(); |
133 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
232 const int cameraTransformationUniform = glGetUniformLocation(this->objects.program->programId(), "CameraTransformation"); |
134 glEnable(GL_DEPTH_TEST); |
233 this->checkForGLErrors(); |
135 glEnable(GL_LIGHTING); |
234 this->objects.program->setUniformValue(cameraTransformationUniform, rotationMatrix); |
136 glLoadIdentity(); |
235 this->checkForGLErrors(); |
137 glTranslatef(0.0, 0.0, -4.5 * this->compiler->modelDistance()); |
236 this->objects.vertexArray.bind(); |
138 glMultMatrixf(padMatrix(this->rotation.toRotationMatrix()).constData()); |
237 this->checkForGLErrors(); |
139 xyz(glTranslatef, -this->compiler->modelCenter()); |
238 glDrawArrays(GL_TRIANGLES, 0, 3); |
140 glEnableClientState(GL_NORMAL_ARRAY); |
239 this->checkForGLErrors(); |
141 glEnableClientState(GL_VERTEX_ARRAY); |
240 this->objects.vertexArray.release(); |
142 glEnableClientState(GL_COLOR_ARRAY); |
241 this->checkForGLErrors(); |
143 for (const gl::VboClass vboClass : {gl::VboClass::Lines, gl::VboClass::Triangles, gl::VboClass::Quads}) |
242 this->objects.program->release(); |
144 { |
243 this->checkForGLErrors(); |
145 const GLuint vboSurfaces = this->compiler->vbo({vboClass, gl::VboSubclass::Surfaces}); |
244 #if 0 |
146 const GLuint vboColors = this->compiler->vbo({vboClass, gl::VboSubclass::RegularColors}); |
245 // Lines need to be rendered last so that anti-aliasing does not interfere with polygon rendering. |
147 const GLuint vboNormals = this->compiler->vbo({vboClass, gl::VboSubclass::Normals}); |
246 renderVao(gl::ArrayClass::Triangles); |
148 const std::size_t count = this->compiler->vboSize({vboClass, gl::VboSubclass::Surfaces}) / 3_z; |
247 renderVao(gl::ArrayClass::Quads); |
149 glBindBuffer(GL_ARRAY_BUFFER, vboSurfaces); |
248 renderVao(gl::ArrayClass::Lines); |
150 glVertexPointer(3, GL_FLOAT, 0, nullptr); |
249 #endif |
151 glBindBuffer(GL_ARRAY_BUFFER, vboColors); |
250 glDisable(GL_POLYGON_OFFSET_FILL); |
152 glColorPointer(4, GL_FLOAT, 0, nullptr); |
251 } |
153 glBindBuffer(GL_ARRAY_BUFFER, vboNormals); |
252 |
154 glNormalPointer(GL_FLOAT, 0, nullptr); |
253 void PartRenderer::renderVao(const gl::ArrayClass /*arrayClass*/) |
155 glDrawArrays(getGlTypeForVboClass(vboClass), 0, static_cast<GLsizei>(count)); |
254 { |
156 } |
255 /* |
157 glBindBuffer(GL_ARRAY_BUFFER, 0); |
256 this->compiler->bindVertexArray(arrayClass); |
158 glDisableClientState(GL_NORMAL_ARRAY); |
257 const std::size_t vertexCount = this->compiler->vboSize({arrayClass, gl::VboSubclass::VertexData}) / gl::FLOATS_PER_VERTEX; |
159 glDisableClientState(GL_VERTEX_ARRAY); |
258 glDrawArrays(getGlTypeForArrayClass(arrayClass), 0, static_cast<GLsizei>(vertexCount)); |
160 glDisableClientState(GL_COLOR_ARRAY); |
259 this->compiler->releaseVertexArray(arrayClass); |
161 const GLenum glError = this->glGetError(); |
260 this->checkForGLErrors(); |
162 if (glError != GL_NO_ERROR) |
261 */ |
|
262 } |
|
263 |
|
264 void PartRenderer::checkForGLErrors() |
|
265 { |
|
266 GLenum glError; |
|
267 QStringList errors; |
|
268 while ((glError = glGetError()) != GL_NO_ERROR) |
163 { |
269 { |
164 const QString glErrorString = QString::fromLatin1(reinterpret_cast<const char*>(::gluErrorString(glError))); |
270 const QString glErrorString = QString::fromLatin1(reinterpret_cast<const char*>(::gluErrorString(glError))); |
|
271 errors.append(glErrorString); |
|
272 } |
|
273 if (not errors.isEmpty()) |
|
274 { |
165 QMessageBox::critical( |
275 QMessageBox::critical( |
166 this, |
276 this, |
167 tr("Rendering error"), |
277 tr("Rendering error"), |
168 QString{"Failed to render: %1"}.arg(glErrorString)); |
278 QString{"Failed to render.\n%1"}.arg(errors.join("\n"))); |
169 } |
279 } |
170 glDisable(GL_CULL_FACE); |
280 } |
171 } |
|
172 |
|
173 static QPointF pointToPointF(const QPoint& point) |
|
174 { |
|
175 return {static_cast<qreal>(point.x()), static_cast<qreal>(point.y())}; |
|
176 } |
|
177 |
|
178 /* |
|
179 static QPoint pointFToPoint(const QPointF& point) |
|
180 { |
|
181 return {static_cast<int>(std::round(point.x())), static_cast<int>(std::round(point.y()))}; |
|
182 } |
|
183 */ |
|
184 |
281 |
185 void PartRenderer::mouseMoveEvent(QMouseEvent* event) |
282 void PartRenderer::mouseMoveEvent(QMouseEvent* event) |
186 { |
283 { |
187 const bool left = event->buttons() & Qt::LeftButton; |
284 const bool left = event->buttons() & Qt::LeftButton; |
188 const QPointF move = pointToPointF(event->pos()) - this->lastMousePosition; |
285 const QPointF move = pointToPointF(event->pos()) - this->lastMousePosition; |