src/glcompiler.cpp

changeset 1146
bb728c124d47
parent 1139
51303023d651
child 1159
6ad8cdcd88d9
equal deleted inserted replaced
1145:02264bf0108d 1146:bb728c124d47
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 }

mercurial