1 #include "triangle_renderer.hpp"
2 #include "canvas_gl.hpp"
3 #include "gl_util.hpp"
4 #include <glm/gtc/type_ptr.hpp>
5 #include "bitmap_font_util.hpp"
6 #include <cmath> // for std::isnan()
7
8 namespace horizon {
9
create_vao(GLuint program,GLuint & vbo_out,GLuint & ebo_out)10 static GLuint create_vao(GLuint program, GLuint &vbo_out, GLuint &ebo_out)
11 {
12 GLuint p0_index = 0;
13 GLuint p1_index = 1;
14 GLuint p2_index = 2;
15 GLuint color_index = 3;
16 GLuint lod_index = 4;
17 GLuint color2_index = 5;
18 GL_CHECK_ERROR;
19 GLuint vao, buffer, ebuffer;
20
21 glGenBuffers(1, &ebuffer);
22 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebuffer);
23
24 /* we need to create a VAO to store the other buffers */
25 glGenVertexArrays(1, &vao);
26 glBindVertexArray(vao);
27
28 /* this is the VBO that holds the vertex data */
29 glGenBuffers(1, &buffer);
30 glBindBuffer(GL_ARRAY_BUFFER, buffer);
31 // data is buffered lateron
32
33 GLfloat vertices[] = {// Position
34 0, 0, 7500000, 5000000, 2500000, -2500000, 1, 0, 1};
35 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
36
37 /* enable and set the position attribute */
38 glEnableVertexAttribArray(p0_index);
39 glVertexAttribPointer(p0_index, 2, GL_FLOAT, GL_FALSE, sizeof(Triangle), (void *)offsetof(Triangle, x0));
40 glEnableVertexAttribArray(p1_index);
41 glVertexAttribPointer(p1_index, 2, GL_FLOAT, GL_FALSE, sizeof(Triangle), (void *)offsetof(Triangle, x1));
42 glEnableVertexAttribArray(p2_index);
43 glVertexAttribPointer(p2_index, 2, GL_FLOAT, GL_FALSE, sizeof(Triangle), (void *)offsetof(Triangle, x2));
44 glEnableVertexAttribArray(color_index);
45 glVertexAttribIPointer(color_index, 1, GL_UNSIGNED_BYTE, sizeof(Triangle), (void *)offsetof(Triangle, color));
46 glEnableVertexAttribArray(lod_index);
47 glVertexAttribIPointer(lod_index, 1, GL_UNSIGNED_BYTE, sizeof(Triangle), (void *)offsetof(Triangle, lod));
48 glEnableVertexAttribArray(color2_index);
49 glVertexAttribIPointer(color2_index, 1, GL_UNSIGNED_BYTE, sizeof(Triangle), (void *)offsetof(Triangle, color2));
50
51 GL_CHECK_ERROR;
52
53 /* enable and set the color attribute */
54 /* reset the state; we will re-enable the VAO when needed */
55 glBindBuffer(GL_ARRAY_BUFFER, 0);
56 glBindVertexArray(0);
57
58 // glDeleteBuffers (1, &buffer);
59 vbo_out = buffer;
60 ebo_out = ebuffer;
61
62 return vao;
63 }
64
TriangleRenderer(const CanvasGL & c,const std::map<int,vector_pair<Triangle,TriangleInfo>> & tris)65 TriangleRenderer::TriangleRenderer(const CanvasGL &c, const std::map<int, vector_pair<Triangle, TriangleInfo>> &tris)
66 : ca(c), triangles(tris)
67 {
68 }
69
70
realize()71 void TriangleRenderer::realize()
72 {
73
74 glGenTextures(1, &texture_glyph);
75 glActiveTexture(GL_TEXTURE0);
76 glBindTexture(GL_TEXTURE_2D, texture_glyph);
77 bitmap_font::load_texture();
78 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
79 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
80
81 program_triangle = gl_create_program_from_resource("/org/horizon-eda/horizon/canvas/shaders/triangle-vertex.glsl",
82 "/org/horizon-eda/horizon/canvas/shaders/"
83 "triangle-triangle-fragment.glsl",
84 "/org/horizon-eda/horizon/canvas/shaders/"
85 "triangle-triangle-geometry.glsl");
86 program_line0 = gl_create_program_from_resource("/org/horizon-eda/horizon/canvas/shaders/triangle-vertex.glsl",
87 "/org/horizon-eda/horizon/canvas/shaders/"
88 "triangle-line0-fragment.glsl",
89 "/org/horizon-eda/horizon/canvas/shaders/"
90 "triangle-line0-geometry.glsl");
91 program_line_butt = gl_create_program_from_resource("/org/horizon-eda/horizon/canvas/shaders/triangle-vertex.glsl",
92 "/org/horizon-eda/horizon/canvas/shaders/"
93 "triangle-line-butt-fragment.glsl",
94 "/org/horizon-eda/horizon/canvas/shaders/"
95 "triangle-line-butt-geometry.glsl");
96 program_line = gl_create_program_from_resource("/org/horizon-eda/horizon/canvas/shaders/triangle-vertex.glsl",
97 "/org/horizon-eda/horizon/canvas/shaders/"
98 "triangle-line-fragment.glsl",
99 "/org/horizon-eda/horizon/canvas/shaders/"
100 "triangle-line-geometry.glsl");
101 program_glyph = gl_create_program_from_resource("/org/horizon-eda/horizon/canvas/shaders/triangle-vertex.glsl",
102 "/org/horizon-eda/horizon/canvas/shaders/"
103 "triangle-glyph-fragment.glsl",
104 "/org/horizon-eda/horizon/canvas/shaders/"
105 "triangle-glyph-geometry.glsl");
106 program_circle = gl_create_program_from_resource("/org/horizon-eda/horizon/canvas/shaders/triangle-vertex.glsl",
107 "/org/horizon-eda/horizon/canvas/shaders/"
108 "triangle-circle-fragment.glsl",
109 "/org/horizon-eda/horizon/canvas/shaders/"
110 "triangle-circle-geometry.glsl");
111 program_arc0 = gl_create_program_from_resource("/org/horizon-eda/horizon/canvas/shaders/triangle-vertex.glsl",
112 "/org/horizon-eda/horizon/canvas/shaders/"
113 "triangle-arc0-fragment.glsl",
114 "/org/horizon-eda/horizon/canvas/shaders/"
115 "triangle-arc0-geometry.glsl");
116 program_arc = gl_create_program_from_resource("/org/horizon-eda/horizon/canvas/shaders/triangle-vertex.glsl",
117 "/org/horizon-eda/horizon/canvas/shaders/"
118 "triangle-arc-fragment.glsl",
119 "/org/horizon-eda/horizon/canvas/shaders/"
120 "triangle-arc-geometry.glsl");
121 GL_CHECK_ERROR;
122 glGenBuffers(1, &ubo);
123 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
124 float testd[3] = {1, 1, 1};
125 glBufferData(GL_UNIFORM_BUFFER, sizeof(testd), &testd, GL_DYNAMIC_DRAW);
126 glBindBuffer(GL_UNIFORM_BUFFER, 0);
127 GL_CHECK_ERROR;
128 unsigned int block_index = glGetUniformBlockIndex(program_line, "layer_setup");
129 GLuint binding_point_index = 0;
130 glBindBufferBase(GL_UNIFORM_BUFFER, binding_point_index, ubo);
131 glUniformBlockBinding(program_triangle, block_index, binding_point_index);
132 glUniformBlockBinding(program_line0, block_index, binding_point_index);
133 glUniformBlockBinding(program_line_butt, block_index, binding_point_index);
134 glUniformBlockBinding(program_line, block_index, binding_point_index);
135 glUniformBlockBinding(program_glyph, block_index, binding_point_index);
136 glUniformBlockBinding(program_circle, block_index, binding_point_index);
137 glUniformBlockBinding(program_arc, block_index, binding_point_index);
138 glUniformBlockBinding(program_arc0, block_index, binding_point_index);
139 GL_CHECK_ERROR;
140 vao = create_vao(program_line0, vbo, ebo);
141 GL_CHECK_ERROR;
142 }
143
144
145 class UBOBuffer {
146 public:
147 static constexpr size_t ubo_size = 20; // keep in sync with ubo.glsl
148 static_assert(static_cast<int>(ColorP::N_COLORS) == ubo_size, "ubo size mismatch");
149 std::array<std::array<float, 4>, ubo_size> colors;
150 std::array<std::array<float, 4>, 256> colors2; // keep in sync with shader
151 std::array<float, 12> screenmat;
152 std::array<float, 12> viewmat;
153 float alpha;
154 float scale;
155 std::array<float, 2> offset;
156 float min_line_width;
157 unsigned int layer_mode;
158 unsigned int stencil_mode;
159 };
160
operator +(const std::array<float,4> & a,float b)161 static std::array<float, 4> operator+(const std::array<float, 4> &a, float b)
162 {
163 return {a.at(0) + b, a.at(1) + b, a.at(2) + b, a.at(3)};
164 }
165
operator +(const std::array<float,4> & a,const std::array<float,4> & b)166 static std::array<float, 4> operator+(const std::array<float, 4> &a, const std::array<float, 4> &b)
167 {
168 return {a.at(0) + b.at(0), a.at(1) + b.at(1), a.at(2) + b.at(2), a.at(3)};
169 }
170
operator *(const std::array<float,4> & a,float b)171 static std::array<float, 4> operator*(const std::array<float, 4> &a, float b)
172 {
173 return {a.at(0) * b, a.at(1) * b, a.at(2) * b, a.at(3)};
174 }
175
apply_highlight(const Color & icolor,HighlightMode mode,int layer) const176 std::array<float, 4> TriangleRenderer::apply_highlight(const Color &icolor, HighlightMode mode, int layer) const
177 {
178 const std::array<float, 4> color = gl_array_from_color(icolor);
179 if (layer >= 20000 && layer < 30000) { // is annotation
180 if (!ca.annotations.at(layer).use_highlight)
181 return color;
182 }
183 if (ca.layer_mode == CanvasGL::LayerMode::SHADOW_OTHER) {
184 if (layer == ca.work_layer || ca.is_overlay_layer(layer, ca.work_layer)) {
185 // it's okay continue as usual
186 }
187 else {
188 if (mode == HighlightMode::ONLY)
189 return color;
190 else
191 return gl_array_from_color(ca.appearance.colors.at(ColorP::SHADOW));
192 }
193 }
194 if (!ca.highlight_enabled)
195 return color;
196 switch (ca.highlight_mode) {
197 case CanvasGL::HighlightMode::HIGHLIGHT:
198 if (mode == HighlightMode::ONLY)
199 return color + ca.appearance.highlight_lighten;
200 else
201 return color;
202
203 case CanvasGL::HighlightMode::DIM:
204 if (mode == HighlightMode::ONLY)
205 return color;
206 else
207 return (color * ca.appearance.highlight_dim)
208 + (gl_array_from_color(ca.appearance.colors.at(ColorP::BACKGROUND))
209 * (1 - ca.appearance.highlight_dim));
210
211 case CanvasGL::HighlightMode::SHADOW:
212 if (mode == HighlightMode::ONLY)
213 return color;
214 else
215 return gl_array_from_color(ca.appearance.colors.at(ColorP::SHADOW));
216 }
217 return color;
218 }
219
render_layer_batch(int layer,HighlightMode highlight_mode,bool ignore_flip,const Batch & batch,bool use_stencil,bool stencil_mode)220 void TriangleRenderer::render_layer_batch(int layer, HighlightMode highlight_mode, bool ignore_flip, const Batch &batch,
221 bool use_stencil, bool stencil_mode)
222 {
223 const auto &ld = ca.get_layer_display(layer);
224 UBOBuffer buf;
225
226 buf.alpha = ca.property_layer_opacity() / 100;
227 gl_mat3_to_array(buf.screenmat, ca.screenmat);
228 if (ignore_flip)
229 gl_mat3_to_array(buf.viewmat, ca.viewmat_noflip);
230 else
231 gl_mat3_to_array(buf.viewmat, ca.viewmat);
232
233 buf.layer_mode = static_cast<unsigned int>(ld.mode);
234 buf.scale = ca.scale;
235 buf.offset[0] = ca.offset.x;
236 buf.offset[1] = ca.offset.y;
237 buf.min_line_width = ca.appearance.min_line_width;
238 buf.stencil_mode = stencil_mode;
239
240 for (const auto &[key, span] : batch) {
241 bool skip = false;
242 switch (key.type) {
243 case Type::TRIANGLE:
244 glUseProgram(program_triangle);
245 if (ld.mode == LayerDisplay::Mode::OUTLINE)
246 skip = true;
247 break;
248
249 case Type::LINE0:
250 glUseProgram(program_line0);
251 break;
252
253 case Type::LINE_BUTT:
254 glUseProgram(program_line_butt);
255 break;
256
257 case Type::LINE:
258 glUseProgram(program_line);
259 break;
260
261 case Type::GLYPH:
262 glUseProgram(program_glyph);
263 break;
264
265 case Type::CIRCLE:
266 glUseProgram(program_circle);
267 break;
268
269 case Type::ARC0:
270 glUseProgram(program_arc0);
271 break;
272
273 case Type::ARC:
274 glUseProgram(program_arc);
275 break;
276 }
277 switch (highlight_mode) {
278 case HighlightMode::ONLY: // only highlighted, skip not highlighted
279 if (!key.highlight) {
280 skip = true;
281 }
282 break;
283 case HighlightMode::SKIP: // only not highlighted, skip highlighted
284 if (key.highlight) {
285 skip = true;
286 }
287 break;
288 }
289 if (!skip) {
290 for (size_t i = 0; i < buf.colors.size(); i++) {
291 auto k = static_cast<ColorP>(i);
292 if (ca.appearance.colors.count(k))
293 buf.colors[i] = apply_highlight(ca.appearance.colors.at(k), highlight_mode, layer);
294 }
295 auto lc = ca.get_layer_color(layer);
296 buf.colors[static_cast<int>(ColorP::AIRWIRE_ROUTER)] =
297 gl_array_from_color(ca.appearance.colors.at(ColorP::AIRWIRE_ROUTER));
298 buf.colors[static_cast<int>(ColorP::FROM_LAYER)] = apply_highlight(lc, highlight_mode, layer);
299 buf.colors[static_cast<int>(ColorP::LAYER_HIGHLIGHT)] =
300 gl_array_from_color(lc) + ca.appearance.highlight_lighten;
301 for (size_t i = 0; i < std::min(buf.colors2.size(), ca.colors2.size()); i++) {
302 buf.colors2[i] = apply_highlight(ca.colors2[i].to_color(), highlight_mode, layer);
303 }
304
305 if (ld.mode == LayerDisplay::Mode::FILL_ONLY || (key.stencil && use_stencil))
306 glStencilFunc(GL_GREATER, stencil, 0xff);
307 else
308 glStencilFunc(GL_ALWAYS, stencil, 0xff);
309
310
311 glBindBuffer(GL_UNIFORM_BUFFER, ubo);
312 glBufferData(GL_UNIFORM_BUFFER, sizeof(buf), &buf, GL_DYNAMIC_DRAW);
313 glBindBuffer(GL_UNIFORM_BUFFER, 0);
314 GL_CHECK_ERROR
315 glDrawElements(GL_POINTS, span.count, GL_UNSIGNED_INT, (void *)(span.offset * sizeof(unsigned int)));
316 }
317 }
318 }
319
render_layer(int layer,HighlightMode highlight_mode,bool ignore_flip)320 void TriangleRenderer::render_layer(int layer, HighlightMode highlight_mode, bool ignore_flip)
321 {
322 GL_CHECK_ERROR
323 if (layer_offsets.count(layer)) {
324 const auto &ld = ca.get_layer_display(layer);
325
326 const auto &batches = layer_offsets.at(layer);
327 Batch batch_stencil, batch_no_stencil;
328 for (const auto &[key, span] : batches) {
329 if (key.stencil)
330 batch_stencil.emplace_back(key, span);
331 else
332 batch_no_stencil.emplace_back(key, span);
333 }
334 if (ld.mode == LayerDisplay::Mode::FILL_ONLY) {
335 render_layer_batch(layer, highlight_mode, ignore_flip, batch_stencil, false, false);
336 render_layer_batch(layer, highlight_mode, ignore_flip, batch_no_stencil, false, false);
337 }
338 else {
339 // draw stencil first
340 render_layer_batch(layer, highlight_mode, ignore_flip, batch_stencil, true, true);
341 render_layer_batch(layer, highlight_mode, ignore_flip, batch_stencil, true, false);
342
343 render_layer_batch(layer, highlight_mode, ignore_flip, batch_no_stencil, false, false);
344 }
345 }
346 // glDrawArrays(GL_POINTS, layer_offsets[layer], triangles[layer].size());
347 stencil++;
348 }
349
render()350 void TriangleRenderer::render()
351 {
352 glBindVertexArray(vao);
353 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
354 glActiveTexture(GL_TEXTURE0);
355
356 GL_CHECK_ERROR
357
358 std::vector<int> layers;
359 layers.reserve(layer_offsets.size());
360 for (const auto &it : layer_offsets) {
361 if (ca.get_layer_display(it.first).visible)
362 layers.push_back(it.first);
363 }
364 std::sort(layers.begin(), layers.end());
365 if (ca.work_layer < 0) {
366 std::reverse(layers.begin(), layers.end());
367 }
368
369 glClear(GL_STENCIL_BUFFER_BIT);
370 glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);
371 glEnable(GL_STENCIL_TEST);
372 stencil = 1;
373
374 static const std::vector<std::vector<HighlightMode>> modes_on_top = {{HighlightMode::SKIP}, {HighlightMode::ONLY}};
375 static const std::vector<std::vector<HighlightMode>> modes_normal = {{HighlightMode::SKIP, HighlightMode::ONLY}};
376
377 const auto &modes = ca.highlight_on_top ? modes_on_top : modes_normal;
378
379 std::vector<std::pair<int, std::set<std::pair<int, bool>>>> normal_layers;
380 normal_layers.reserve(layers.size());
381 for (const auto layer : layers) {
382 const auto &ld = ca.get_layer_display(layer);
383 if (layer != ca.work_layer && layer < 10000 && ld.visible && !ca.layer_is_annotation(layer))
384 normal_layers.push_back({layer, {}});
385 }
386 normal_layers.push_back({ca.work_layer, {}});
387
388 for (const auto &[k, overlay_layer] : ca.overlay_layers) {
389 const auto layer = k.first;
390 const auto ignore_flip = k.second;
391 auto f = std::find_if(normal_layers.rbegin(), normal_layers.rend(),
392 [layer](const auto &x) { return layer.overlaps(x.first); });
393 if (f != normal_layers.rend()) {
394 f->second.emplace(overlay_layer, ignore_flip);
395 }
396 }
397
398 render_annotations(false); // annotation bottom
399 for (const auto &highlight_modes : modes) {
400 for (const auto &[layer, overlays] : normal_layers) {
401 for (const auto highlight_mode : highlight_modes) {
402 if (layer != ca.work_layer && ca.layer_mode == CanvasGL::LayerMode::WORK_ONLY
403 && highlight_mode == HighlightMode::SKIP)
404 continue;
405
406 render_layer(layer, highlight_mode);
407 for (const auto &[overlay, ignore_flip] : overlays) {
408 render_layer(overlay, highlight_mode, ignore_flip);
409 }
410 }
411 }
412 for (auto layer : layers) {
413 const auto &ld = ca.get_layer_display(layer);
414 if (layer >= 10000 && layer < Canvas::first_overlay_layer && ld.visible && !ca.layer_is_annotation(layer)) {
415 for (const auto highlight_mode : highlight_modes) {
416 render_layer(layer, highlight_mode);
417 }
418 }
419 }
420 }
421 render_annotations(true); // anotations top
422 glDisable(GL_STENCIL_TEST);
423
424 GL_CHECK_ERROR
425
426 glBindVertexArray(0);
427 glUseProgram(0);
428 GL_CHECK_ERROR
429 }
430
render_annotations(bool top)431 void TriangleRenderer::render_annotations(bool top)
432 {
433 for (const auto &it : ca.annotations) {
434 if (ca.get_layer_display(it.first).visible && it.second.on_top == top) {
435 render_layer(it.first, HighlightMode::SKIP);
436 render_layer(it.first, HighlightMode::ONLY);
437 }
438 }
439 }
440
push()441 void TriangleRenderer::push()
442 {
443 GL_CHECK_ERROR
444 glBindBuffer(GL_ARRAY_BUFFER, vbo);
445 n_tris = 0;
446 for (const auto &it : triangles) {
447 n_tris += it.second.size();
448 }
449 glBufferData(GL_ARRAY_BUFFER, sizeof(Triangle) * n_tris, nullptr, GL_STREAM_DRAW);
450 GL_CHECK_ERROR
451 size_t ofs = 0;
452 layer_offsets.clear();
453 std::vector<unsigned int> elements;
454 for (const auto &[layer, tris] : triangles) {
455 const auto &ld = ca.get_layer_display(layer);
456 glBufferSubData(GL_ARRAY_BUFFER, ofs * sizeof(Triangle), tris.size() * sizeof(Triangle), tris.first.data());
457 std::map<BatchKey, std::vector<unsigned int>> type_indices;
458 unsigned int i = 0;
459 for (const auto &[tri, tri_info] : tris) {
460 const bool hidden = tri_info.flags & TriangleInfo::FLAG_HIDDEN;
461 const bool type_visible = ld.types_visible & (1 << static_cast<int>(tri_info.type));
462 if (!hidden && type_visible) {
463 auto ty = Type::LINE;
464 if (tri_info.flags & TriangleInfo::FLAG_GLYPH) {
465 ty = Type::GLYPH;
466 }
467 else if ((tri_info.flags & TriangleInfo::FLAG_ARC) && tri.y2 == 0) {
468 ty = Type::ARC0;
469 }
470 else if (tri_info.flags & TriangleInfo::FLAG_ARC) {
471 ty = Type::ARC;
472 }
473 else if (tri_info.flags & TriangleInfo::FLAG_BUTT) {
474 ty = Type::LINE_BUTT;
475 }
476 else if (!std::isnan(tri.y2)) {
477 ty = Type::TRIANGLE;
478 }
479 else if (std::isnan(tri.y2) && tri.x2 == 0) {
480 ty = Type::LINE0;
481 }
482 else if (std::isnan(tri.y1) && std::isnan(tri.x2) && std::isnan(tri.y2)) {
483 ty = Type::CIRCLE;
484 }
485
486 else if (std::isnan(tri.y2)) {
487 ty = Type::LINE;
488 }
489 else {
490 throw std::runtime_error("unknown triangle type");
491 }
492 const bool highlight = (tri_info.flags & TriangleInfo::FLAG_HIGHLIGHT)
493 || (tri.color == static_cast<int>(ColorP::LAYER_HIGHLIGHT));
494 const bool do_stencil = tri_info.type == TriangleInfo::Type::PAD;
495 const BatchKey key{ty, highlight, do_stencil};
496 type_indices[key].push_back(i + ofs);
497 }
498 i++;
499 }
500 for (const auto &[key, elems] : type_indices) {
501 auto el_offset = elements.size();
502 elements.insert(elements.end(), elems.begin(), elems.end());
503 layer_offsets[layer][key] = {el_offset, elems.size()};
504 }
505 ofs += tris.size();
506 }
507 glBindBuffer(GL_ARRAY_BUFFER, 0);
508 GL_CHECK_ERROR
509 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
510 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * elements.size(), elements.data(), GL_STATIC_DRAW);
511 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
512
513 GL_CHECK_ERROR
514 }
515 } // namespace horizon
516