|
1 /* |
|
2 * LDForge: LDraw parts authoring CAD |
|
3 * Copyright (C) 2013 - 2017 Teemu Piippo |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU General Public License as published by |
|
7 * the Free Software Foundation, either version 3 of the License, or |
|
8 * (at your option) any later version. |
|
9 * |
|
10 * This program is distributed in the hope that it will be useful, |
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 * GNU General Public License for more details. |
|
14 * |
|
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/>. |
|
17 */ |
|
18 |
|
19 #define GL_GLEXT_PROTOTYPES |
|
20 #include <GL/glu.h> |
|
21 #include <GL/glext.h> |
|
22 #include "glcompiler.h" |
|
23 #include "miscallenous.h" |
|
24 #include "guiutilities.h" |
|
25 #include "documentmanager.h" |
|
26 #include "grid.h" |
|
27 |
|
28 struct GLErrorInfo |
|
29 { |
|
30 GLenum value; |
|
31 QString text; |
|
32 }; |
|
33 |
|
34 static const GLErrorInfo g_GLErrors[] = |
|
35 { |
|
36 { GL_NO_ERROR, "No error" }, |
|
37 { GL_INVALID_ENUM, "Unacceptable enumerator passed" }, |
|
38 { GL_INVALID_VALUE, "Numeric argument out of range" }, |
|
39 { GL_INVALID_OPERATION, "The operation is not allowed to be done in this state" }, |
|
40 { GL_INVALID_FRAMEBUFFER_OPERATION, "Framebuffer object is not complete"}, |
|
41 { GL_OUT_OF_MEMORY, "Out of memory" }, |
|
42 { GL_STACK_UNDERFLOW, "The operation would have caused an underflow" }, |
|
43 { GL_STACK_OVERFLOW, "The operation would have caused an overflow" }, |
|
44 }; |
|
45 |
|
46 void CheckGLErrorImpl (const char* file, int line) |
|
47 { |
|
48 QString errmsg; |
|
49 GLenum errnum = glGetError(); |
|
50 |
|
51 if (errnum == GL_NO_ERROR) |
|
52 return; |
|
53 |
|
54 for (const GLErrorInfo& err : g_GLErrors) |
|
55 { |
|
56 if (err.value == errnum) |
|
57 { |
|
58 errmsg = err.text; |
|
59 break; |
|
60 } |
|
61 } |
|
62 |
|
63 print ("OpenGL ERROR: at %1:%2: %3", Basename (QString (file)), line, errmsg); |
|
64 } |
|
65 |
|
66 |
|
67 GLCompiler::GLCompiler (GLRenderer* renderer) : |
|
68 HierarchyElement (renderer), |
|
69 m_renderer (renderer) |
|
70 { |
|
71 connect(renderer->model(), SIGNAL(objectAdded(LDObject*)), this, SLOT(compileObject(LDObject*))); |
|
72 connect(renderer->model(), SIGNAL(objectModified(LDObject*)), this, SLOT(compileObject(LDObject*))); |
|
73 connect(renderer->model(), SIGNAL(aboutToRemoveObject(LDObject*)), this, SLOT(forgetObject(LDObject*)), Qt::DirectConnection); |
|
74 connect(renderer, SIGNAL(objectHighlightingChanged(LDObject*)), this, SLOT(compileObject(LDObject*))); |
|
75 connect(m_window, SIGNAL(gridChanged()), this, SLOT(recompile())); |
|
76 |
|
77 for (LDObject* object : renderer->model()->objects()) |
|
78 stageForCompilation(object); |
|
79 } |
|
80 |
|
81 |
|
82 void GLCompiler::initialize() |
|
83 { |
|
84 initializeOpenGLFunctions(); |
|
85 glGenBuffers (NumVbos, &m_vbo[0]); |
|
86 CHECK_GL_ERROR(); |
|
87 } |
|
88 |
|
89 |
|
90 GLCompiler::~GLCompiler() |
|
91 { |
|
92 glDeleteBuffers (NumVbos, &m_vbo[0]); |
|
93 CHECK_GL_ERROR(); |
|
94 } |
|
95 |
|
96 |
|
97 QColor GLCompiler::indexColorForID (int id) const |
|
98 { |
|
99 // Calculate a color based from this index. This method caters for |
|
100 // 16777216 objects. I don't think that will be exceeded anytime soon. :) |
|
101 int r = (id / 0x10000) % 0x100; |
|
102 int g = (id / 0x100) % 0x100; |
|
103 int b = id % 0x100; |
|
104 return {r, g, b}; |
|
105 } |
|
106 |
|
107 /* |
|
108 * Returns the suitable color for the polygon. |
|
109 * - polygon is the polygon to colorise. |
|
110 * - polygonOwner is the LDObject from which the polygon originated. |
|
111 * - subclass provides context for the polygon. |
|
112 */ |
|
113 QColor GLCompiler::getColorForPolygon(LDPolygon& polygon, LDObject* polygonOwner, VboSubclass subclass) const |
|
114 { |
|
115 QColor color; |
|
116 |
|
117 switch (subclass) |
|
118 { |
|
119 case VboSubclass::Surfaces: |
|
120 case VboSubclass::Normals: |
|
121 case VboSubclass::_End: |
|
122 // Surface and normal VBOs contain vertex data, not colors. So we can't return anything meaningful. |
|
123 return {}; |
|
124 |
|
125 case VboSubclass::BfcFrontColors: |
|
126 // Use the constant green color for BFC front colors |
|
127 return {64, 192, 80}; |
|
128 |
|
129 case VboSubclass::BfcBackColors: |
|
130 // Use the constant red color for BFC back colors |
|
131 return {208, 64, 64}; |
|
132 |
|
133 case VboSubclass::PickColors: |
|
134 // For the picking scene, determine the color from the owner's ID. |
|
135 return indexColorForID(polygonOwner->id()); |
|
136 |
|
137 case VboSubclass::RandomColors: |
|
138 // For the random color scene, the owner object has rolled up a random color. Use that. |
|
139 color = polygonOwner->randomColor(); |
|
140 break; |
|
141 |
|
142 case VboSubclass::NormalColors: |
|
143 // For normal colors, use the polygon's color. |
|
144 if (polygon.color == MainColor) |
|
145 { |
|
146 // If it's the main color, use the polygon owner's color. |
|
147 if (polygonOwner->color() == MainColor) |
|
148 { |
|
149 // If that also is the main color, then we whatever the user has configured the main color to look like. |
|
150 color = guiUtilities()->mainColorRepresentation(); |
|
151 } |
|
152 else |
|
153 { |
|
154 color = polygonOwner->color().faceColor(); |
|
155 } |
|
156 } |
|
157 else if (polygon.color == EdgeColor) |
|
158 { |
|
159 // Edge color is black, unless we have a dark background, in which case lines need to be bright. |
|
160 color = luma(m_config->backgroundColor()) > 40 ? Qt::black : Qt::white; |
|
161 } |
|
162 else |
|
163 { |
|
164 // Not main or edge color, use the polygon's color as is. |
|
165 color = LDColor {polygon.color}.faceColor(); |
|
166 } |
|
167 break; |
|
168 } |
|
169 |
|
170 if (color.isValid()) |
|
171 { |
|
172 // We may wish to apply blending on the color to indicate selection or highlight. |
|
173 double blendAlpha = 0.0; |
|
174 |
|
175 if (polygonOwner->isSelected()) |
|
176 blendAlpha = 1.0; |
|
177 else if (polygonOwner == m_renderer->objectAtCursor()) |
|
178 blendAlpha = 0.5; |
|
179 |
|
180 if (blendAlpha != 0.0) |
|
181 { |
|
182 QColor selectedColor = m_config->selectColorBlend(); |
|
183 double denominator = blendAlpha + 1.0; |
|
184 color.setRed((color.red() + (selectedColor.red() * blendAlpha)) / denominator); |
|
185 color.setGreen((color.green() + (selectedColor.green() * blendAlpha)) / denominator); |
|
186 color.setBlue((color.blue() + (selectedColor.blue() * blendAlpha)) / denominator); |
|
187 } |
|
188 } |
|
189 else |
|
190 { |
|
191 // The color was unknown. Use main color to make the polygon at least not appear pitch-black. |
|
192 if (polygon.num != 2 and polygon.num != 5) |
|
193 color = guiUtilities()->mainColorRepresentation(); |
|
194 else |
|
195 color = Qt::black; |
|
196 |
|
197 // Warn about the unknown color, but only once. |
|
198 static QSet<int> warnedColors; |
|
199 if (not warnedColors.contains(polygon.color)) |
|
200 { |
|
201 print("Unknown color %1!\n", polygon.color); |
|
202 warnedColors.insert(polygon.color); |
|
203 } |
|
204 } |
|
205 |
|
206 return color; |
|
207 } |
|
208 |
|
209 |
|
210 void GLCompiler::needMerge() |
|
211 { |
|
212 for (int i = 0; i < countof (m_vboChanged); ++i) |
|
213 m_vboChanged[i] = true; |
|
214 } |
|
215 |
|
216 |
|
217 void GLCompiler::stageForCompilation (LDObject* obj) |
|
218 { |
|
219 m_staged << obj; |
|
220 } |
|
221 |
|
222 |
|
223 void GLCompiler::unstage (LDObject* obj) |
|
224 { |
|
225 m_staged.remove (obj); |
|
226 } |
|
227 |
|
228 |
|
229 void GLCompiler::compileStaged() |
|
230 { |
|
231 for (LDObject* object : m_staged) |
|
232 compileObject(object); |
|
233 |
|
234 m_staged.clear(); |
|
235 } |
|
236 |
|
237 |
|
238 void GLCompiler::prepareVBO (int vbonum, const Model* model) |
|
239 { |
|
240 // Compile anything that still awaits it |
|
241 compileStaged(); |
|
242 |
|
243 if (not m_vboChanged[vbonum]) |
|
244 return; |
|
245 |
|
246 QVector<GLfloat> vbodata; |
|
247 |
|
248 for (auto it = m_objectInfo.begin(); it != m_objectInfo.end();) |
|
249 { |
|
250 if (it.key() == nullptr) |
|
251 { |
|
252 it = m_objectInfo.erase (it); |
|
253 continue; |
|
254 } |
|
255 |
|
256 if (it.key()->model() == model and not it.key()->isHidden()) |
|
257 vbodata += it->data[vbonum]; |
|
258 |
|
259 ++it; |
|
260 } |
|
261 |
|
262 glBindBuffer (GL_ARRAY_BUFFER, m_vbo[vbonum]); |
|
263 glBufferData (GL_ARRAY_BUFFER, countof(vbodata) * sizeof(GLfloat), vbodata.constData(), GL_STATIC_DRAW); |
|
264 glBindBuffer (GL_ARRAY_BUFFER, 0); |
|
265 CHECK_GL_ERROR(); |
|
266 m_vboChanged[vbonum] = false; |
|
267 m_vboSizes[vbonum] = countof(vbodata); |
|
268 } |
|
269 |
|
270 |
|
271 void GLCompiler::dropObjectInfo(LDObject* object) |
|
272 { |
|
273 if (m_objectInfo.contains(object)) |
|
274 { |
|
275 m_objectInfo.remove(object); |
|
276 needMerge(); |
|
277 } |
|
278 } |
|
279 |
|
280 void GLCompiler::forgetObject(LDObject* object) |
|
281 { |
|
282 dropObjectInfo(object); |
|
283 m_staged.remove(object); |
|
284 } |
|
285 |
|
286 |
|
287 void GLCompiler::compileObject (LDObject* obj) |
|
288 { |
|
289 if (obj == nullptr) |
|
290 return; |
|
291 |
|
292 ObjectVBOInfo info; |
|
293 info.isChanged = true; |
|
294 dropObjectInfo (obj); |
|
295 |
|
296 switch (obj->type()) |
|
297 { |
|
298 // Note: We cannot split quads into triangles here, it would mess up the wireframe view. |
|
299 // Quads must go into separate vbos. |
|
300 case LDObjectType::Triangle: |
|
301 case LDObjectType::Quadrilateral: |
|
302 case LDObjectType::EdgeLine: |
|
303 case LDObjectType::ConditionalEdge: |
|
304 { |
|
305 LDPolygon* poly = obj->getPolygon(); |
|
306 poly->id = obj->id(); |
|
307 compilePolygon (*poly, obj, &info); |
|
308 delete poly; |
|
309 break; |
|
310 } |
|
311 |
|
312 case LDObjectType::SubfileReference: |
|
313 { |
|
314 LDSubfileReference* ref = static_cast<LDSubfileReference*> (obj); |
|
315 auto data = ref->inlinePolygons(); |
|
316 |
|
317 for (LDPolygon& poly : data) |
|
318 { |
|
319 poly.id = obj->id(); |
|
320 compilePolygon (poly, obj, &info); |
|
321 } |
|
322 break; |
|
323 } |
|
324 |
|
325 case LDObjectType::BezierCurve: |
|
326 { |
|
327 LDBezierCurve* curve = static_cast<LDBezierCurve*> (obj); |
|
328 for (LDPolygon& polygon : curve->rasterizePolygons(grid()->bezierCurveSegments())) |
|
329 { |
|
330 polygon.id = obj->id(); |
|
331 compilePolygon (polygon, obj, &info); |
|
332 } |
|
333 } |
|
334 break; |
|
335 |
|
336 default: |
|
337 break; |
|
338 } |
|
339 |
|
340 m_objectInfo[obj] = info; |
|
341 needMerge(); |
|
342 } |
|
343 |
|
344 |
|
345 void GLCompiler::compilePolygon (LDPolygon& poly, LDObject* topobj, ObjectVBOInfo* objinfo) |
|
346 { |
|
347 VboClass surface; |
|
348 int vertexCount; |
|
349 |
|
350 switch (poly.num) |
|
351 { |
|
352 case 2: surface = VboClass::Lines; vertexCount = 2; break; |
|
353 case 3: surface = VboClass::Triangles; vertexCount = 3; break; |
|
354 case 4: surface = VboClass::Quads; vertexCount = 4; break; |
|
355 case 5: surface = VboClass::ConditionalLines; vertexCount = 2; break; |
|
356 default: return; |
|
357 } |
|
358 |
|
359 // Determine the normals for the polygon. |
|
360 Vertex normals[4]; |
|
361 auto vertexRing = ring(poly.vertices, vertexCount); |
|
362 |
|
363 for (int i = 0; i < vertexCount; ++i) |
|
364 { |
|
365 const Vertex& v1 = vertexRing[i - 1]; |
|
366 const Vertex& v2 = vertexRing[i]; |
|
367 const Vertex& v3 = vertexRing[i + 1]; |
|
368 normals[i] = Vertex::crossProduct(v3 - v2, v1 - v2).normalized(); |
|
369 } |
|
370 |
|
371 for (VboSubclass complement : iterateEnum<VboSubclass>()) |
|
372 { |
|
373 const int vbonum = vboNumber (surface, complement); |
|
374 QVector<GLfloat>& vbodata = objinfo->data[vbonum]; |
|
375 const QColor color = getColorForPolygon (poly, topobj, complement); |
|
376 |
|
377 for (int vert = 0; vert < vertexCount; ++vert) |
|
378 { |
|
379 if (complement == VboSubclass::Surfaces) |
|
380 { |
|
381 // Write coordinates. Apparently Z must be flipped too? |
|
382 vbodata << poly.vertices[vert].x() |
|
383 << -poly.vertices[vert].y() |
|
384 << -poly.vertices[vert].z(); |
|
385 } |
|
386 else if (complement == VboSubclass::Normals) |
|
387 { |
|
388 vbodata << normals[vert].x() |
|
389 << -normals[vert].y() |
|
390 << -normals[vert].z(); |
|
391 } |
|
392 else |
|
393 { |
|
394 vbodata << ((GLfloat) color.red()) / 255.0f |
|
395 << ((GLfloat) color.green()) / 255.0f |
|
396 << ((GLfloat) color.blue()) / 255.0f |
|
397 << ((GLfloat) color.alpha()) / 255.0f; |
|
398 } |
|
399 } |
|
400 } |
|
401 } |
|
402 |
|
403 |
|
404 void GLCompiler::setRenderer (GLRenderer* renderer) |
|
405 { |
|
406 m_renderer = renderer; |
|
407 } |
|
408 |
|
409 |
|
410 int GLCompiler::vboNumber (VboClass surface, VboSubclass complement) |
|
411 { |
|
412 return (static_cast<int>(surface) * EnumLimits<VboSubclass>::Count) + static_cast<int>(complement); |
|
413 } |
|
414 |
|
415 |
|
416 GLuint GLCompiler::vbo (int vbonum) const |
|
417 { |
|
418 return m_vbo[vbonum]; |
|
419 } |
|
420 |
|
421 |
|
422 int GLCompiler::vboSize (int vbonum) const |
|
423 { |
|
424 return m_vboSizes[vbonum]; |
|
425 } |
|
426 |
|
427 |
|
428 void GLCompiler::recompile() |
|
429 { |
|
430 for (LDObject* object : m_renderer->model()->objects()) |
|
431 compileObject(object); |
|
432 } |