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