|
1 #include <cstring> |
|
2 #include "gldata.h" |
|
3 #include "ldtypes.h" |
|
4 #include "colors.h" |
|
5 #include "document.h" |
|
6 #include "misc.h" |
|
7 #include "gldraw.h" |
|
8 #include <QDate> |
|
9 |
|
10 #define DEBUG_PRINT(...) fprint (stdout, __VA_ARGS__) |
|
11 |
|
12 extern_cfg (Bool, gl_blackedges); |
|
13 static QList<short> g_warnedColors; |
|
14 VertexCompiler g_vertexCompiler; |
|
15 |
|
16 // ============================================================================= |
|
17 // ----------------------------------------------------------------------------- |
|
18 VertexCompiler::Array::Array() : |
|
19 m_data (null) |
|
20 { |
|
21 clear(); |
|
22 } |
|
23 |
|
24 // ============================================================================= |
|
25 // ----------------------------------------------------------------------------- |
|
26 VertexCompiler::Array::~Array() |
|
27 { |
|
28 delete[] m_data; |
|
29 } |
|
30 |
|
31 // ============================================================================= |
|
32 // ----------------------------------------------------------------------------- |
|
33 void VertexCompiler::Array::clear() |
|
34 { |
|
35 delete[] m_data; |
|
36 |
|
37 m_data = new Vertex[64]; |
|
38 m_size = 64; |
|
39 m_ptr = &m_data[0]; |
|
40 } |
|
41 |
|
42 // ============================================================================= |
|
43 // ----------------------------------------------------------------------------- |
|
44 void VertexCompiler::Array::resizeToFit (Size newSize) |
|
45 { |
|
46 if (allocatedSize() >= newSize) |
|
47 return; |
|
48 |
|
49 int32 cachedWriteSize = writtenSize(); |
|
50 |
|
51 // Add some lee-way space to reduce the amount of resizing. |
|
52 newSize += 256; |
|
53 |
|
54 const Size oldSize = allocatedSize(); |
|
55 |
|
56 // We need to back up the data first |
|
57 Vertex* copy = new Vertex[oldSize]; |
|
58 memcpy (copy, m_data, oldSize); |
|
59 |
|
60 // Re-create the buffer |
|
61 delete[] m_data; |
|
62 m_data = new Vertex[newSize]; |
|
63 m_size = newSize; |
|
64 m_ptr = &m_data[cachedWriteSize / sizeof (Vertex)]; |
|
65 |
|
66 // Copy the data back |
|
67 memcpy (m_data, copy, oldSize); |
|
68 delete[] copy; |
|
69 } |
|
70 |
|
71 // ============================================================================= |
|
72 // ----------------------------------------------------------------------------- |
|
73 const VertexCompiler::Vertex* VertexCompiler::Array::data() const |
|
74 { |
|
75 return m_data; |
|
76 } |
|
77 |
|
78 // ============================================================================= |
|
79 // ----------------------------------------------------------------------------- |
|
80 const VertexCompiler::Array::Size& VertexCompiler::Array::allocatedSize() const |
|
81 { |
|
82 return m_size; |
|
83 } |
|
84 |
|
85 // ============================================================================= |
|
86 // ----------------------------------------------------------------------------- |
|
87 VertexCompiler::Array::Size VertexCompiler::Array::writtenSize() const |
|
88 { |
|
89 return (m_ptr - m_data) * sizeof (Vertex); |
|
90 } |
|
91 |
|
92 // ============================================================================= |
|
93 // ----------------------------------------------------------------------------- |
|
94 void VertexCompiler::Array::write (const Vertex& f) |
|
95 { |
|
96 // Ensure there's enoughspace for the new vertex |
|
97 resizeToFit (writtenSize() + sizeof f); |
|
98 |
|
99 // Write the float in |
|
100 *m_ptr++ = f; |
|
101 } |
|
102 |
|
103 // ============================================================================= |
|
104 // ----------------------------------------------------------------------------- |
|
105 void VertexCompiler::Array::merge (Array* other) |
|
106 { |
|
107 // Ensure there's room for both buffers |
|
108 resizeToFit (writtenSize() + other->writtenSize()); |
|
109 |
|
110 memcpy (m_ptr, other->data(), other->writtenSize()); |
|
111 m_ptr += other->writtenSize() / sizeof (Vertex); |
|
112 } |
|
113 |
|
114 // ============================================================================= |
|
115 // ----------------------------------------------------------------------------- |
|
116 VertexCompiler::VertexCompiler() : |
|
117 m_file (null) |
|
118 { |
|
119 needMerge(); |
|
120 } |
|
121 |
|
122 VertexCompiler::~VertexCompiler() {} |
|
123 |
|
124 // ============================================================================= |
|
125 // Note: we use the top level object's color but the draw object's vertices. |
|
126 // This is so that the index color is generated correctly - it has to reference |
|
127 // the top level object's ID. This is crucial for picking to work. |
|
128 // ----------------------------------------------------------------------------- |
|
129 void VertexCompiler::compilePolygon (LDObject* drawobj, LDObject* trueobj, QList< VertexCompiler::CompiledTriangle >& data) |
|
130 { |
|
131 const QColor pickColor = getObjectColor (trueobj, PickColor); |
|
132 LDObject::Type type = drawobj->getType(); |
|
133 LDObjectList objs; |
|
134 |
|
135 assert (type != LDObject::ESubfile); |
|
136 |
|
137 if (type == LDObject::EQuad) |
|
138 { |
|
139 for (LDTriangle * t : static_cast<LDQuad*> (drawobj)->splitToTriangles()) |
|
140 objs << t; |
|
141 } |
|
142 else |
|
143 objs << drawobj; |
|
144 |
|
145 for (LDObject* obj : objs) |
|
146 { |
|
147 const LDObject::Type objtype = obj->getType(); |
|
148 const bool isline = (objtype == LDObject::ELine || objtype == LDObject::ECondLine); |
|
149 const int verts = isline ? 2 : obj->vertices(); |
|
150 QColor normalColor = getObjectColor (obj, Normal); |
|
151 |
|
152 assert (isline || objtype == LDObject::ETriangle); |
|
153 |
|
154 CompiledTriangle a; |
|
155 a.rgb = normalColor.rgb(); |
|
156 a.pickrgb = pickColor.rgb(); |
|
157 a.numVerts = verts; |
|
158 a.obj = trueobj; |
|
159 a.isCondLine = (objtype == LDObject::ECondLine); |
|
160 |
|
161 for (int i = 0; i < verts; ++i) |
|
162 { |
|
163 a.verts[i] = obj->getVertex (i); |
|
164 a.verts[i].y() = -a.verts[i].y(); |
|
165 a.verts[i].z() = -a.verts[i].z(); |
|
166 } |
|
167 |
|
168 data << a; |
|
169 } |
|
170 } |
|
171 |
|
172 // ============================================================================= |
|
173 // ----------------------------------------------------------------------------- |
|
174 void VertexCompiler::compileObject (LDObject* obj) |
|
175 { |
|
176 initObject (obj); |
|
177 QList<CompiledTriangle> data; |
|
178 QTime t0; |
|
179 |
|
180 for (int i = 0; i < GL::NumArrays; ++i) |
|
181 m_objArrays[obj][i].clear(); |
|
182 |
|
183 t0 = QTime::currentTime(); |
|
184 compileSubObject (obj, obj, data); |
|
185 DEBUG_PRINT ("COMPILATION: %1ms\n", t0.msecsTo (QTime::currentTime())); |
|
186 |
|
187 t0 = QTime::currentTime(); |
|
188 |
|
189 for (int i = 0; i < GL::NumArrays; ++i) |
|
190 { |
|
191 GL::VAOType type = (GL::VAOType) i; |
|
192 const bool islinearray = (type == GL::EdgeArray || type == GL::EdgePickArray); |
|
193 |
|
194 for (const CompiledTriangle & poly : data) |
|
195 { |
|
196 if (poly.isCondLine) |
|
197 { |
|
198 // Conditional lines go to the edge pick array and the array |
|
199 // specifically designated for conditional lines and nowhere else. |
|
200 if (type != GL::EdgePickArray && type != GL::CondEdgeArray) |
|
201 continue; |
|
202 } |
|
203 else |
|
204 { |
|
205 // Lines and only lines go to the line array and only to the line array. |
|
206 if ( (poly.numVerts == 2) ^ islinearray) |
|
207 continue; |
|
208 |
|
209 // Only conditional lines go into the conditional line array |
|
210 if (type == GL::CondEdgeArray) |
|
211 continue; |
|
212 } |
|
213 |
|
214 Array* verts = postprocess (poly, type); |
|
215 m_objArrays[obj][type].merge (verts); |
|
216 delete verts; |
|
217 } |
|
218 } |
|
219 |
|
220 DEBUG_PRINT ("POST-PROCESS: %1ms\n", t0.msecsTo (QTime::currentTime())); |
|
221 |
|
222 needMerge(); |
|
223 } |
|
224 |
|
225 // ============================================================================= |
|
226 // ----------------------------------------------------------------------------- |
|
227 void VertexCompiler::compileSubObject (LDObject* obj, LDObject* topobj, QList< VertexCompiler::CompiledTriangle >& data) |
|
228 { |
|
229 LDObjectList objs; |
|
230 |
|
231 switch (obj->getType()) |
|
232 { |
|
233 case LDObject::ETriangle: |
|
234 case LDObject::ELine: |
|
235 case LDObject::ECondLine: |
|
236 { |
|
237 compilePolygon (obj, topobj, data); |
|
238 } break; |
|
239 |
|
240 case LDObject::EQuad: |
|
241 { |
|
242 for (LDTriangle * triangle : static_cast<LDQuad*> (obj)->splitToTriangles()) |
|
243 compilePolygon (triangle, topobj, data); |
|
244 } break; |
|
245 |
|
246 case LDObject::ESubfile: |
|
247 { |
|
248 QTime t0 = QTime::currentTime(); |
|
249 objs = static_cast<LDSubfile*> (obj)->inlineContents (LDSubfile::RendererInline | LDSubfile::DeepCacheInline); |
|
250 DEBUG_PRINT ("\t- INLINE: %1ms\n", t0.msecsTo (QTime::currentTime())); |
|
251 DEBUG_PRINT ("\t- %1 objects\n", objs.size()); |
|
252 |
|
253 t0 = QTime::currentTime(); |
|
254 |
|
255 for (LDObject* obj : objs) |
|
256 { |
|
257 compileSubObject (obj, topobj, data); |
|
258 obj->deleteSelf(); |
|
259 } |
|
260 |
|
261 DEBUG_PRINT ("\t- SUB-COMPILATION: %1ms\n", t0.msecsTo (QTime::currentTime())); |
|
262 } break; |
|
263 |
|
264 default: |
|
265 {} break; |
|
266 } |
|
267 } |
|
268 |
|
269 // ============================================================================= |
|
270 // ----------------------------------------------------------------------------- |
|
271 void VertexCompiler::compileDocument() |
|
272 { |
|
273 for (LDObject * obj : m_file->getObjects()) |
|
274 compileObject (obj); |
|
275 } |
|
276 |
|
277 // ============================================================================= |
|
278 // ----------------------------------------------------------------------------- |
|
279 void VertexCompiler::forgetObject (LDObject* obj) |
|
280 { |
|
281 auto it = m_objArrays.find (obj); |
|
282 |
|
283 if (it != m_objArrays.end()) |
|
284 delete *it; |
|
285 |
|
286 m_objArrays.remove (obj); |
|
287 } |
|
288 |
|
289 // ============================================================================= |
|
290 // ----------------------------------------------------------------------------- |
|
291 void VertexCompiler::setFile (LDDocument* file) |
|
292 { |
|
293 m_file = file; |
|
294 } |
|
295 |
|
296 // ============================================================================= |
|
297 // ----------------------------------------------------------------------------- |
|
298 const VertexCompiler::Array* VertexCompiler::getMergedBuffer (GL::VAOType type) |
|
299 { |
|
300 // If there are objects staged for compilation, compile them now. |
|
301 if (m_staged.size() > 0) |
|
302 { |
|
303 for (LDObject * obj : m_staged) |
|
304 compileObject (obj); |
|
305 |
|
306 m_staged.clear(); |
|
307 } |
|
308 |
|
309 assert (type < GL::NumArrays); |
|
310 |
|
311 if (m_changed[type]) |
|
312 { |
|
313 m_changed[type] = false; |
|
314 m_mainArrays[type].clear(); |
|
315 |
|
316 for (LDObject* obj : m_file->getObjects()) |
|
317 { |
|
318 if (!obj->isScemantic()) |
|
319 continue; |
|
320 |
|
321 auto it = m_objArrays.find (obj); |
|
322 |
|
323 if (it != m_objArrays.end()) |
|
324 m_mainArrays[type].merge (& (*it) [type]); |
|
325 } |
|
326 |
|
327 DEBUG_PRINT ("merged array %1: %2 bytes\n", (int) type, m_mainArrays[type].writtenSize()); |
|
328 } |
|
329 |
|
330 return &m_mainArrays[type]; |
|
331 } |
|
332 |
|
333 // ============================================================================= |
|
334 // This turns a compiled triangle into usable VAO vertices |
|
335 // ----------------------------------------------------------------------------- |
|
336 VertexCompiler::Array* VertexCompiler::postprocess (const CompiledTriangle& triangle, GL::VAOType type) |
|
337 { |
|
338 Array* va = new Array; |
|
339 QList<Vertex> verts; |
|
340 |
|
341 for (int i = 0; i < triangle.numVerts; ++i) |
|
342 { |
|
343 alias v0 = triangle.verts[i]; |
|
344 Vertex v; |
|
345 v.x = v0.x(); |
|
346 v.y = v0.y(); |
|
347 v.z = v0.z(); |
|
348 |
|
349 switch (type) |
|
350 { |
|
351 case GL::MainArray: |
|
352 case GL::EdgeArray: |
|
353 case GL::CondEdgeArray: |
|
354 { |
|
355 v.color = triangle.rgb; |
|
356 } break; |
|
357 |
|
358 case GL::PickArray: |
|
359 case GL::EdgePickArray: |
|
360 { |
|
361 v.color = triangle.pickrgb; |
|
362 } break; |
|
363 |
|
364 case GL::BFCArray: |
|
365 case GL::NumArrays: |
|
366 break; // handled separately |
|
367 } |
|
368 |
|
369 verts << v; |
|
370 } |
|
371 |
|
372 if (type == GL::BFCArray) |
|
373 { |
|
374 int32 rgb = getObjectColor (triangle.obj, BFCFront).rgb(); |
|
375 |
|
376 for (Vertex v : verts) |
|
377 { |
|
378 v.color = rgb; |
|
379 va->write (v); |
|
380 } |
|
381 |
|
382 rgb = getObjectColor (triangle.obj, BFCBack).rgb(); |
|
383 |
|
384 for (int i = verts.size() - 1; i >= 0; --i) |
|
385 { |
|
386 Vertex v = verts[i]; |
|
387 v.color = rgb; |
|
388 va->write (v); |
|
389 } |
|
390 } |
|
391 else |
|
392 { |
|
393 for (Vertex v : verts) |
|
394 va->write (v); |
|
395 } |
|
396 |
|
397 return va; |
|
398 } |
|
399 |
|
400 // ============================================================================= |
|
401 // ----------------------------------------------------------------------------- |
|
402 uint32 VertexCompiler::getColorRGB (QColor& color) |
|
403 { |
|
404 return |
|
405 (color.red() & 0xFF) << 0x00 | |
|
406 (color.green() & 0xFF) << 0x08 | |
|
407 (color.blue() & 0xFF) << 0x10 | |
|
408 (color.alpha() & 0xFF) << 0x18; |
|
409 } |
|
410 |
|
411 // ============================================================================= |
|
412 // ----------------------------------------------------------------------------- |
|
413 QColor VertexCompiler::getObjectColor (LDObject* obj, ColorType colotype) const |
|
414 { |
|
415 QColor qcol; |
|
416 |
|
417 if (!obj->isColored()) |
|
418 return QColor(); |
|
419 |
|
420 if (colotype == PickColor) |
|
421 { |
|
422 // Make the color by the object's ID if we're picking, so we can make the |
|
423 // ID again from the color we get from the picking results. Be sure to use |
|
424 // the top level parent's index since we want a subfile's children point |
|
425 // to the subfile itself. |
|
426 long i = obj->topLevelParent()->getID(); |
|
427 |
|
428 // Calculate a color based from this index. This method caters for |
|
429 // 16777216 objects. I don't think that'll be exceeded anytime soon. :) |
|
430 // ATM biggest is 53588.dat with 12600 lines. |
|
431 int r = (i / (256 * 256)) % 256, |
|
432 g = (i / 256) % 256, |
|
433 b = i % 256; |
|
434 |
|
435 return QColor (r, g, b); |
|
436 } |
|
437 |
|
438 if ( (colotype == BFCFront || colotype == BFCBack) && |
|
439 obj->getType() != LDObject::ELine && |
|
440 obj->getType() != LDObject::ECondLine |
|
441 ) |
|
442 { |
|
443 if (colotype == BFCFront) |
|
444 qcol = QColor (40, 192, 0); |
|
445 else |
|
446 qcol = QColor (224, 0, 0); |
|
447 } |
|
448 else |
|
449 { |
|
450 if (obj->getColor() == maincolor) |
|
451 qcol = GL::getMainColor(); |
|
452 else |
|
453 { |
|
454 LDColor* col = getColor (obj->getColor()); |
|
455 |
|
456 if (col) |
|
457 qcol = col->faceColor; |
|
458 } |
|
459 |
|
460 if (obj->getColor() == edgecolor) |
|
461 { |
|
462 qcol = QColor (32, 32, 32); // luma (m_bgcolor) < 40 ? QColor (64, 64, 64) : Qt::black; |
|
463 LDColor* col; |
|
464 |
|
465 if (!gl_blackedges && obj->getParent() && (col = getColor (obj->getParent()->getColor()))) |
|
466 qcol = col->edgeColor; |
|
467 } |
|
468 |
|
469 if (qcol.isValid() == false) |
|
470 { |
|
471 // The color was unknown. Use main color to make the object at least |
|
472 // not appear pitch-black. |
|
473 if (obj->getColor() != edgecolor) |
|
474 qcol = GL::getMainColor(); |
|
475 else |
|
476 qcol = Qt::black; |
|
477 |
|
478 // Warn about the unknown color, but only once. |
|
479 for (short i : g_warnedColors) |
|
480 if (obj->getColor() == i) |
|
481 return qcol; |
|
482 |
|
483 log ("%1: Unknown color %2!\n", __func__, obj->getColor()); |
|
484 g_warnedColors << obj->getColor(); |
|
485 return qcol; |
|
486 } |
|
487 } |
|
488 |
|
489 if (obj->topLevelParent()->isSelected()) |
|
490 { |
|
491 // Brighten it up if selected. |
|
492 const int add = 51; |
|
493 |
|
494 qcol.setRed (min (qcol.red() + add, 255)); |
|
495 qcol.setGreen (min (qcol.green() + add, 255)); |
|
496 qcol.setBlue (min (qcol.blue() + add, 255)); |
|
497 } |
|
498 |
|
499 return qcol; |
|
500 } |
|
501 |
|
502 // ============================================================================= |
|
503 // ----------------------------------------------------------------------------- |
|
504 void VertexCompiler::needMerge() |
|
505 { |
|
506 // Set all of m_changed to true |
|
507 memset (m_changed, 0xFF, sizeof m_changed); |
|
508 } |
|
509 |
|
510 // ============================================================================= |
|
511 // ----------------------------------------------------------------------------- |
|
512 void VertexCompiler::initObject (LDObject* obj) |
|
513 { |
|
514 if (m_objArrays.find (obj) == m_objArrays.end()) |
|
515 m_objArrays[obj] = new Array[GL::NumArrays]; |
|
516 } |
|
517 |
|
518 // ============================================================================= |
|
519 // ----------------------------------------------------------------------------- |
|
520 void VertexCompiler::stageForCompilation (LDObject* obj) |
|
521 { |
|
522 m_staged << obj; |
|
523 removeDuplicates (m_staged); |
|
524 } |
|
525 |