1 /* |
|
2 * LDForge: LDraw parts authoring CAD |
|
3 * Copyright (C) 2013, 2014 Santeri 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 "LDObject.h" |
|
24 #include "Colors.h" |
|
25 #include "Document.h" |
|
26 #include "Misc.h" |
|
27 #include "GLRenderer.h" |
|
28 #include "Dialogs.h" |
|
29 |
|
30 cfg (String, gl_selectcolor, "#0080FF") |
|
31 |
|
32 struct GLErrorInfo |
|
33 { |
|
34 GLenum value; |
|
35 QString text; |
|
36 }; |
|
37 |
|
38 static const GLErrorInfo g_GLErrors[] = |
|
39 { |
|
40 { GL_NO_ERROR, "No error" }, |
|
41 { GL_INVALID_ENUM, "Unacceptable enumerator passed" }, |
|
42 { GL_INVALID_VALUE, "Numeric argument out of range" }, |
|
43 { GL_INVALID_OPERATION, "The operation is not allowed to be done in this state" }, |
|
44 { GL_INVALID_FRAMEBUFFER_OPERATION, "Framebuffer object is not complete"}, |
|
45 { GL_OUT_OF_MEMORY, "Out of memory" }, |
|
46 { GL_STACK_UNDERFLOW, "The operation would have caused an underflow" }, |
|
47 { GL_STACK_OVERFLOW, "The operation would have caused an overflow" }, |
|
48 }; |
|
49 |
|
50 #include <QTime> |
|
51 |
|
52 #define CLOCK_INIT QTime t0; |
|
53 |
|
54 #define CLOCK_START \ |
|
55 { \ |
|
56 t0 = QTime::currentTime(); \ |
|
57 } |
|
58 |
|
59 #define CLOCK_TIME(A) \ |
|
60 { \ |
|
61 fprint (stderr, A ": %1ms\n", t0.msecsTo (QTime::currentTime())); \ |
|
62 } |
|
63 |
|
64 #define DEBUG_PRINT(...) fprint (stdout, __VA_ARGS__) |
|
65 |
|
66 extern_cfg (Bool, gl_blackedges); |
|
67 extern_cfg (String, gl_bgcolor); |
|
68 static QList<short> g_warnedColors; |
|
69 static const QColor g_BFCFrontColor (40, 192, 40); |
|
70 static const QColor g_BFCBackColor (224, 40, 40); |
|
71 |
|
72 // ============================================================================= |
|
73 // |
|
74 void checkGLError_private (const char* file, int line) |
|
75 { |
|
76 QString errmsg; |
|
77 GLenum errnum = glGetError(); |
|
78 |
|
79 if (errnum == GL_NO_ERROR) |
|
80 return; |
|
81 |
|
82 for (const GLErrorInfo& err : g_GLErrors) |
|
83 { |
|
84 if (err.value == errnum) |
|
85 { |
|
86 errmsg = err.text; |
|
87 break; |
|
88 } |
|
89 } |
|
90 |
|
91 print ("OpenGL ERROR: at %1:%2: %3", basename (QString (file)), line, errmsg); |
|
92 } |
|
93 |
|
94 // ============================================================================= |
|
95 // |
|
96 GLCompiler::GLCompiler() |
|
97 { |
|
98 needMerge(); |
|
99 memset (m_vboSizes, 0, sizeof m_vboSizes); |
|
100 } |
|
101 |
|
102 // ============================================================================= |
|
103 // |
|
104 void GLCompiler::initialize() |
|
105 { |
|
106 glGenBuffers (g_numVBOs, &m_vbo[0]); |
|
107 checkGLError(); |
|
108 } |
|
109 |
|
110 // ============================================================================= |
|
111 // |
|
112 GLCompiler::~GLCompiler() |
|
113 { |
|
114 glDeleteBuffers (g_numVBOs, &m_vbo[0]); |
|
115 checkGLError(); |
|
116 } |
|
117 |
|
118 // ============================================================================= |
|
119 // |
|
120 uint32 GLCompiler::colorToRGB (const QColor& color) |
|
121 { |
|
122 return |
|
123 (color.red() & 0xFF) << 0x00 | |
|
124 (color.green() & 0xFF) << 0x08 | |
|
125 (color.blue() & 0xFF) << 0x10 | |
|
126 (color.alpha() & 0xFF) << 0x18; |
|
127 } |
|
128 |
|
129 // ============================================================================= |
|
130 // |
|
131 QColor GLCompiler::indexColorForID (int id) const |
|
132 { |
|
133 // Calculate a color based from this index. This method caters for |
|
134 // 16777216 objects. I don't think that will be exceeded anytime soon. :) |
|
135 int r = (id / 0x10000) % 0x100, |
|
136 g = (id / 0x100) % 0x100, |
|
137 b = id % 0x100; |
|
138 |
|
139 return QColor (r, g, b); |
|
140 } |
|
141 |
|
142 // ============================================================================= |
|
143 // |
|
144 QColor GLCompiler::polygonColor (LDPolygon& poly, LDObject* topobj) const |
|
145 { |
|
146 QColor qcol; |
|
147 |
|
148 if (poly.color == maincolor) |
|
149 { |
|
150 if (topobj->color() == maincolor) |
|
151 qcol = GLRenderer::getMainColor(); |
|
152 else |
|
153 qcol = getColor (topobj->color())->faceColor; |
|
154 } |
|
155 elif (poly.color == edgecolor) |
|
156 { |
|
157 qcol = luma (QColor (gl_bgcolor)) > 40 ? Qt::black : Qt::white; |
|
158 } |
|
159 else |
|
160 { |
|
161 LDColor* col = getColor (poly.color); |
|
162 |
|
163 if (col) |
|
164 qcol = col->faceColor; |
|
165 } |
|
166 |
|
167 if (qcol.isValid() == false) |
|
168 { |
|
169 // The color was unknown. Use main color to make the poly.object at least |
|
170 // not appear pitch-black. |
|
171 if (poly.num != 2 && poly.num != 5) |
|
172 qcol = GLRenderer::getMainColor(); |
|
173 else |
|
174 qcol = Qt::black; |
|
175 |
|
176 // Warn about the unknown color, but only once. |
|
177 if (g_warnedColors.contains (poly.color) == false) |
|
178 { |
|
179 print ("Unknown color %1!\n", poly.color); |
|
180 g_warnedColors << poly.color; |
|
181 } |
|
182 |
|
183 return qcol; |
|
184 } |
|
185 |
|
186 if (topobj->isSelected()) |
|
187 { |
|
188 // Brighten it up for the select list. |
|
189 QColor selcolor (gl_selectcolor); |
|
190 qcol.setRed ((qcol.red() + selcolor.red()) / 2); |
|
191 qcol.setGreen ((qcol.green() + selcolor.green()) / 2); |
|
192 qcol.setBlue ((qcol.blue() + selcolor.blue()) / 2); |
|
193 } |
|
194 |
|
195 return qcol; |
|
196 } |
|
197 |
|
198 // ============================================================================= |
|
199 // |
|
200 void GLCompiler::needMerge() |
|
201 { |
|
202 for (int i = 0; i < countof (m_vboChanged); ++i) |
|
203 m_vboChanged[i] = true; |
|
204 } |
|
205 |
|
206 // ============================================================================= |
|
207 // |
|
208 void GLCompiler::stageForCompilation (LDObject* obj) |
|
209 { |
|
210 m_staged << obj; |
|
211 } |
|
212 |
|
213 // ============================================================================= |
|
214 // |
|
215 void GLCompiler::compileDocument (LDDocument* doc) |
|
216 { |
|
217 if (doc == null) |
|
218 return; |
|
219 |
|
220 for (LDObject* obj : doc->objects()) |
|
221 compileObject (obj); |
|
222 } |
|
223 |
|
224 // ============================================================================= |
|
225 // |
|
226 void GLCompiler::compileStaged() |
|
227 { |
|
228 removeDuplicates (m_staged); |
|
229 |
|
230 for (LDObject* obj : m_staged) |
|
231 compileObject (obj); |
|
232 |
|
233 m_staged.clear(); |
|
234 } |
|
235 |
|
236 // ============================================================================= |
|
237 // |
|
238 void GLCompiler::prepareVBO (int vbonum) |
|
239 { |
|
240 // Compile anything that still awaits it |
|
241 compileStaged(); |
|
242 |
|
243 if (m_vboChanged[vbonum] == false) |
|
244 return; |
|
245 |
|
246 QVector<GLfloat> vbodata; |
|
247 |
|
248 for (auto it = m_objectInfo.begin(); it != m_objectInfo.end(); ++it) |
|
249 { |
|
250 if (it.key()->document() == getCurrentDocument()) |
|
251 vbodata += it->data[vbonum]; |
|
252 } |
|
253 |
|
254 glBindBuffer (GL_ARRAY_BUFFER, m_vbo[vbonum]); |
|
255 glBufferData (GL_ARRAY_BUFFER, vbodata.size() * sizeof(GLfloat), vbodata.constData(), GL_DYNAMIC_DRAW); |
|
256 glBindBuffer (GL_ARRAY_BUFFER, 0); |
|
257 checkGLError(); |
|
258 m_vboChanged[vbonum] = false; |
|
259 m_vboSizes[vbonum] = vbodata.size(); |
|
260 } |
|
261 |
|
262 // ============================================================================= |
|
263 // |
|
264 void GLCompiler::dropObject (LDObject* obj) |
|
265 { |
|
266 auto it = m_objectInfo.find (obj); |
|
267 |
|
268 if (it != m_objectInfo.end()) |
|
269 { |
|
270 m_objectInfo.erase (it); |
|
271 needMerge(); |
|
272 } |
|
273 } |
|
274 |
|
275 // ============================================================================= |
|
276 // |
|
277 void GLCompiler::compileObject (LDObject* obj) |
|
278 { |
|
279 print ("compiling #%1 (%2, %3)\n", obj->id(), obj->typeName(), obj->origin()); |
|
280 ObjectVBOInfo info; |
|
281 dropObject (obj); |
|
282 compileSubObject (obj, obj, &info); |
|
283 m_objectInfo[obj] = info; |
|
284 needMerge(); |
|
285 print ("#%1 compiled.\n", obj->id()); |
|
286 } |
|
287 |
|
288 // ============================================================================= |
|
289 // |
|
290 void GLCompiler::compilePolygon (LDPolygon& poly, LDObject* topobj, ObjectVBOInfo* objinfo) |
|
291 { |
|
292 EVBOSurface surface; |
|
293 int numverts; |
|
294 |
|
295 switch (poly.num) |
|
296 { |
|
297 case 3: surface = VBOSF_Triangles; numverts = 3; break; |
|
298 case 4: surface = VBOSF_Quads; numverts = 4; break; |
|
299 case 2: surface = VBOSF_Lines; numverts = 2; break; |
|
300 case 5: surface = VBOSF_CondLines; numverts = 2; break; |
|
301 |
|
302 default: |
|
303 print ("OMGWTFBBQ weird polygon with number %1 (topobj: #%2, %3), origin: %4", |
|
304 (int) poly.num, topobj->id(), topobj->typeName(), poly.origin); |
|
305 assert (false); |
|
306 } |
|
307 |
|
308 for (int complement = 0; complement < VBOCM_NumComplements; ++complement) |
|
309 { |
|
310 const int vbonum = vboNumber (surface, (EVBOComplement) complement); |
|
311 QVector<GLfloat>& vbodata = objinfo->data[vbonum]; |
|
312 const QColor normalColor = polygonColor (poly, topobj); |
|
313 const QColor pickColor = indexColorForID (topobj->id()); |
|
314 |
|
315 for (int vert = 0; vert < numverts; ++vert) |
|
316 { |
|
317 switch ((EVBOComplement) complement) |
|
318 { |
|
319 case VBOCM_Surfaces: |
|
320 { |
|
321 // Write coordinates. Apparently Z must be flipped too? |
|
322 vbodata << poly.vertices[vert].x() |
|
323 << -poly.vertices[vert].y() |
|
324 << -poly.vertices[vert].z(); |
|
325 break; |
|
326 } |
|
327 |
|
328 case VBOCM_NormalColors: |
|
329 { |
|
330 writeColor (vbodata, normalColor); |
|
331 break; |
|
332 } |
|
333 |
|
334 case VBOCM_PickColors: |
|
335 { |
|
336 writeColor (vbodata, pickColor); |
|
337 break; |
|
338 } |
|
339 |
|
340 case VBOCM_BFCFrontColors: |
|
341 { |
|
342 writeColor (vbodata, g_BFCFrontColor); |
|
343 break; |
|
344 } |
|
345 |
|
346 case VBOCM_BFCBackColors: |
|
347 { |
|
348 writeColor (vbodata, g_BFCBackColor); |
|
349 break; |
|
350 } |
|
351 |
|
352 case VBOCM_NumComplements: |
|
353 break; |
|
354 } |
|
355 } |
|
356 } |
|
357 } |
|
358 |
|
359 // ============================================================================= |
|
360 // |
|
361 void GLCompiler::compileSubObject (LDObject* obj, LDObject* topobj, ObjectVBOInfo* objinfo) |
|
362 { |
|
363 switch (obj->type()) |
|
364 { |
|
365 // Note: We cannot split quads into triangles here, it would mess up the |
|
366 // wireframe view. Quads must go into separate vbos. |
|
367 case LDObject::ETriangle: |
|
368 case LDObject::EQuad: |
|
369 case LDObject::ELine: |
|
370 case LDObject::ECondLine: |
|
371 { |
|
372 LDPolygon* poly = obj->getPolygon(); |
|
373 poly->id = topobj->id(); |
|
374 compilePolygon (*poly, topobj, objinfo); |
|
375 delete poly; |
|
376 break; |
|
377 } |
|
378 |
|
379 case LDObject::ESubfile: |
|
380 { |
|
381 LDSubfile* ref = static_cast<LDSubfile*> (obj); |
|
382 auto data = ref->inlinePolygons(); |
|
383 |
|
384 for (LDPolygon& poly : data) |
|
385 { |
|
386 poly.id = topobj->id(); |
|
387 compilePolygon (poly, topobj, objinfo); |
|
388 } |
|
389 break; |
|
390 } |
|
391 |
|
392 default: |
|
393 break; |
|
394 } |
|
395 } |
|
396 |
|
397 // ============================================================================= |
|
398 // |
|
399 void GLCompiler::writeColor (QVector<GLfloat>& array, const QColor& color) |
|
400 { |
|
401 array << ((float) color.red()) / 255.0f |
|
402 << ((float) color.green()) / 255.0f |
|
403 << ((float) color.blue()) / 255.0f |
|
404 << ((float) color.alpha()) / 255.0f; |
|
405 } |
|