Visual Computing Library
Loading...
Searching...
No Matches
mesh_viewer_imgui_drawer.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_IMGUI_MESH_VIEWER_IMGUI_DRAWER_H
24#define VCL_IMGUI_MESH_VIEWER_IMGUI_DRAWER_H
25
26#include "imgui_helpers.h"
27
28#include <vclib/render/drawable/drawable_mesh.h>
29#include <vclib/render/drawers/viewer_drawer.h>
30
31#include <imgui.h>
32
33#include <algorithm>
34#include <iterator>
35#include <numeric>
36
37namespace vcl::imgui {
38
39template<typename DerivedRenderApp>
40class MeshViewerDrawerImgui : public vcl::ViewerDrawer<DerivedRenderApp>
41{
43
44 int mMeshIndex = 0;
45
46public:
47 using Base::Base;
48
49 virtual void onDraw(vcl::uint viewId) override
50 {
51 // draw parent
52 Base::onDraw(viewId);
53
54 // draw imgui
55 ImGui::Begin("Meshes");
56
57 // mesh table
58 {
61 ImGui::BeginChild(
62 "##ListContainer",
63 ImVec2(ImGui::GetContentRegionAvail().x, 260),
66 drawMeshList();
67 ImGui::EndChild();
68 }
69
70 // drawable mesh info and settings for selected mesh
71 if (mMeshIndex >= 0 && mMeshIndex < Base::mDrawList->size()) {
72 auto drawable =
73 std::dynamic_pointer_cast<vcl::AbstractDrawableMesh>(
74 Base::mDrawList->at(mMeshIndex));
75 if (drawable) {
76 drawMeshSettings(*drawable);
77 }
78 }
79
80 ImGui::End();
81 }
82
83private:
84 void drawMeshList()
85 {
86 if (!Base::mDrawList || Base::mDrawList->empty()) {
87 ImGui::Text("No objects loaded");
88 return;
89 }
90
91 int meshId = 0;
93 if (ImGui::BeginTable("meshtable", 2, meshTableFlags)) {
94 ImGui::TableSetupColumn(
96 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
97 for (auto& d : *(Base::mDrawList)) {
98 auto& drawable = *d;
99 ImGui::TableNextRow();
100
101 ImGui::PushID(meshId++);
102
103 ImGui::TableSetColumnIndex(0);
104
105 // visibility checkbox
106 ImGui::Checkbox(
107 "##Visible",
108 [&] {
109 return drawable.isVisible();
110 },
111 [&](bool vis) {
112 drawable.setVisibility(vis);
113 });
114
115 ImGui::TableSetColumnIndex(1);
116
117 // row selection
118 bool isSelected = (mMeshIndex == meshId - 1);
119 if (ImGui::Selectable(
120 drawable.name().c_str(),
121 isSelected,
123 mMeshIndex = meshId - 1;
124 }
125 // tooltip with info
126 if (!drawable.info().empty() &&
127 ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) {
128 ImGui::BeginTooltip();
129 ImGui::Text("%s", drawable.info().c_str());
130 ImGui::EndTooltip();
131 }
132 ImGui::PopID();
133 }
134 ImGui::EndTable();
135 }
136 }
137
138 static void drawMeshPointSettings(
140 vcl::MeshRenderSettings& settings)
141 {
143
144 ImGui::BeginDisabled(!settings.canPoints(VISIBLE));
145
146 // visibility
147 ImGui::Checkbox(
148 "Visible",
149 [&] {
150 return settings.isPoints(VISIBLE);
151 },
152 [&](bool vis) {
153 settings.setPoints(VISIBLE, vis);
154 });
155
156 // shape
157 ImGui::Text("Shape:");
158 ImGui::SameLine();
159 ImGui::BeginDisabled(true);
160 ImGui::RadioButton(
161 "Circle",
162 [&] {
163 return false;
164 },
165 [&](bool v) {
166 });
167 ImGui::EndDisabled();
168 ImGui::SameLine();
169 ImGui::BeginDisabled(false);
170 ImGui::RadioButton(
171 "Pixel",
172 [&] {
173 return true;
174 },
175 [&](bool v) {
176 });
177 ImGui::EndDisabled();
178
179 // shading
180 ImGui::Text("Shading:");
181 ImGui::SameLine();
182 ImGui::BeginDisabled(!settings.canPoints(SHADING_VERT));
183 ImGui::RadioButton(
184 "Vertex",
185 [&] {
186 return settings.isPoints(SHADING_VERT);
187 },
188 [&](bool v) {
189 if (v)
190 settings.setPoints(SHADING_VERT);
191 });
192 ImGui::SameLine();
193 ImGui::EndDisabled();
194 ImGui::RadioButton(
195 "None",
196 [&] {
197 return settings.isPoints(SHADING_NONE);
198 },
199 [&](bool vis) {
200 if (vis)
201 settings.setPoints(SHADING_NONE);
202 });
203
204 // color
205 ImGui::Text("Color:");
206 ImGui::SameLine();
207 const char* pointColorNames[] = {"Vertex", "Mesh", "User"};
208 const std::array<bool, 3> colorSelected = {
209 settings.isPoints(COLOR_VERTEX),
210 settings.isPoints(COLOR_MESH),
211 settings.isPoints(COLOR_USER)};
212
213 assert(
214 std::accumulate(
215 std::begin(colorSelected), std::end(colorSelected), 0) == 1);
216 int idx = std::distance(
217 std::begin(colorSelected),
218 std::find(
219 std::begin(colorSelected), std::end(colorSelected), true));
220 assert(idx >= 0 && idx < 3);
221
222 ImGui::SetNextItemWidth(-40);
223 if (ImGui::BeginCombo("##ComboPointColor", pointColorNames[idx])) {
224 for (int n = 0; n < IM_ARRAYSIZE(pointColorNames); n++) {
225 const bool selected = (n == idx);
226
227 switch (n) {
228 case 0:
229 ImGui::BeginDisabled(!settings.canPoints(COLOR_VERTEX));
230 if (ImGui::Selectable(pointColorNames[n], selected))
231 settings.setPoints(COLOR_VERTEX);
232 ImGui::EndDisabled();
233 break;
234 case 1:
235 ImGui::BeginDisabled(!settings.canPoints(COLOR_MESH));
236 if (ImGui::Selectable(pointColorNames[n], selected))
237 settings.setPoints(COLOR_MESH);
238 ImGui::EndDisabled();
239 break;
240 case 2:
241 if (ImGui::Selectable(pointColorNames[n], selected))
242 settings.setPoints(COLOR_USER);
243 break;
244 default: assert(false); break;
245 }
246 if (selected)
247 ImGui::SetItemDefaultFocus();
248 }
249 ImGui::EndCombo();
250 }
251 // user color picker
252 ImGui::SameLine();
253 ImGui::BeginDisabled(!settings.isPoints(COLOR_USER));
254 ImGui::ColorEdit4(
255 "##PointColor",
256 [&] {
257 return settings.pointUserColor();
258 },
259 [&](vcl::Color c) {
260 settings.setPointsUserColor(c);
261 },
263 ImGui::EndDisabled();
264
265 // point size
266 ImGui::Text("Size:");
267 // set the width of the window minus the width of the label
268 ImGui::SameLine();
269 ImGui::SetNextItemWidth(-10);
270 ImGui::SliderFloat(
271 "##PointSize",
272 [&] {
273 return settings.pointWidth();
274 },
275 [&](float v) {
276 settings.setPointsWidth(v);
277 },
278 1.0f,
279 10.0f);
280
281 ImGui::EndDisabled();
282 }
283
284 static void drawMeshSurfaceSettings(
286 vcl::MeshRenderSettings& settings)
287 {
289
290 ImGui::BeginDisabled(!settings.canSurface(VISIBLE));
291
292 // visibility
293 ImGui::Checkbox(
294 "Visible",
295 [&] {
296 return settings.isSurface(VISIBLE);
297 },
298 [&](bool vis) {
299 settings.setSurface(VISIBLE, vis);
300 });
301
302 // shading
303 assert(
304 (settings.isSurface(SHADING_SMOOTH) +
305 settings.isSurface(SHADING_FLAT) +
306 settings.isSurface(SHADING_NONE)) == 1);
307 ImGui::Text("Shading:");
308 ImGui::SameLine();
309 ImGui::RadioButton(
310 "Smooth",
311 [&] {
312 return settings.isSurface(SHADING_SMOOTH);
313 },
314 [&](bool vis) {
315 if (vis)
316 settings.setSurface(SHADING_SMOOTH);
317 });
318 ImGui::SameLine();
319 ImGui::RadioButton(
320 "Flat",
321 [&] {
322 return settings.isSurface(SHADING_FLAT);
323 },
324 [&](bool vis) {
325 if (vis)
326 settings.setSurface(SHADING_FLAT);
327 });
328 ImGui::SameLine();
329 ImGui::RadioButton(
330 "None",
331 [&] {
332 return settings.isSurface(SHADING_NONE);
333 },
334 [&](bool vis) {
335 if (vis)
336 settings.setSurface(SHADING_NONE);
337 });
338
339 // color
340 ImGui::Text("Color:");
341 ImGui::SameLine();
342 const char* surfColorNames[] = {
343 "Vertex", "Face", "Mesh", "PerVertexTex", "PerWedgeTex", "User"};
344 const std::array<bool, 6> colorSelected = {
345 settings.isSurface(COLOR_VERTEX),
346 settings.isSurface(COLOR_FACE),
347 settings.isSurface(COLOR_MESH),
348 settings.isSurface(COLOR_VERTEX_TEX),
349 settings.isSurface(COLOR_WEDGE_TEX),
350 settings.isSurface(COLOR_USER)};
351 assert(
352 std::accumulate(
353 std::begin(colorSelected), std::end(colorSelected), 0) == 1);
354 int idx = std::distance(
355 std::begin(colorSelected),
356 std::find(
357 std::begin(colorSelected), std::end(colorSelected), true));
358 assert(idx >= 0 && idx < 6);
359 ImGui::SetNextItemWidth(-40);
360 if (ImGui::BeginCombo("##ComboSurfColor", surfColorNames[idx])) {
361 for (int n = 0; n < IM_ARRAYSIZE(surfColorNames); n++) {
362 const bool selected = (n == idx);
363
364 switch (n) {
365 case 0:
366 ImGui::BeginDisabled(!settings.canSurface(COLOR_VERTEX));
367 if (ImGui::Selectable(surfColorNames[n], selected))
368 settings.setSurface(COLOR_VERTEX);
369 ImGui::EndDisabled();
370 break;
371 case 1:
372 ImGui::BeginDisabled(!settings.canSurface(COLOR_FACE));
373 if (ImGui::Selectable(surfColorNames[n], selected))
374 settings.setSurface(COLOR_FACE);
375 ImGui::EndDisabled();
376 break;
377 case 2:
378 ImGui::BeginDisabled(!settings.canSurface(COLOR_MESH));
379 if (ImGui::Selectable(surfColorNames[n], selected))
380 settings.setSurface(COLOR_MESH);
381 ImGui::EndDisabled();
382 break;
383 case 3:
384 ImGui::BeginDisabled(
385 !settings.canSurface(COLOR_VERTEX_TEX));
386 if (ImGui::Selectable(surfColorNames[n], selected))
387 settings.setSurface(COLOR_VERTEX_TEX);
388 ImGui::EndDisabled();
389 break;
390 case 4:
391 ImGui::BeginDisabled(!settings.canSurface(COLOR_WEDGE_TEX));
392 if (ImGui::Selectable(surfColorNames[n], selected))
393 settings.setSurface(COLOR_WEDGE_TEX);
394 ImGui::EndDisabled();
395 break;
396 case 5:
397 if (ImGui::Selectable(surfColorNames[n], selected))
398 settings.setSurface(COLOR_USER);
399 break;
400 default: assert(false); break;
401 }
402 if (selected)
403 ImGui::SetItemDefaultFocus();
404 }
405 ImGui::EndCombo();
406 }
407 // user color picker
408 ImGui::SameLine();
409 ImGui::BeginDisabled(!settings.isSurface(COLOR_USER));
410 ImGui::ColorEdit4(
411 "##SurfUserColor",
412 [&] {
413 return settings.surfaceUserColor();
414 },
415 [&](vcl::Color c) {
416 settings.setSurfaceUserColor(c);
417 },
419 ImGui::EndDisabled();
420
421 ImGui::EndDisabled();
422 }
423
424 static void drawMeshWireframeSettings(
426 vcl::MeshRenderSettings& settings)
427 {
429
430 ImGui::BeginDisabled(!settings.canWireframe(VISIBLE));
431
432 // visibility
433 ImGui::Checkbox(
434 "Visible",
435 [&] {
436 return settings.isWireframe(VISIBLE);
437 },
438 [&](bool v) {
439 settings.setWireframe(VISIBLE, v);
440 });
441
442 // shading
443 assert(
444 settings.isWireframe(SHADING_VERT) !=
445 settings.isWireframe(SHADING_NONE));
446 ImGui::Text("Shading:");
447 ImGui::SameLine();
448 ImGui::RadioButton(
449 "Vertex",
450 [&] {
451 return settings.isWireframe(SHADING_VERT);
452 },
453 [&](bool vis) {
454 if (vis)
455 settings.setWireframe(SHADING_VERT);
456 });
457 ImGui::SameLine();
458 ImGui::RadioButton(
459 "None",
460 [&] {
461 return settings.isWireframe(SHADING_NONE);
462 },
463 [&](bool vis) {
464 if (vis)
465 settings.setWireframe(SHADING_NONE);
466 });
467
468 // color
469 ImGui::Text("Color:");
470 ImGui::SameLine();
471 const char* wireColorNames[] = {"Vertex", "Mesh", "User"};
472 const std::array<bool, 3> colorSelected = {
473 settings.isWireframe(COLOR_VERTEX),
474 settings.isWireframe(COLOR_MESH),
475 settings.isWireframe(COLOR_USER)};
476 assert(
477 std::accumulate(
478 std::begin(colorSelected), std::end(colorSelected), 0) == 1);
479 int idx = std::distance(
480 std::begin(colorSelected),
481 std::find(
482 std::begin(colorSelected), std::end(colorSelected), true));
483 assert(idx >= 0 && idx < 3);
484 ImGui::SetNextItemWidth(-40);
485 if (ImGui::BeginCombo("##ComboWireColor", wireColorNames[idx])) {
486 for (int n = 0; n < IM_ARRAYSIZE(wireColorNames); n++) {
487 const bool selected = (n == idx);
488
489 switch (n) {
490 case 0:
491 ImGui::BeginDisabled(!settings.canWireframe(COLOR_VERTEX));
492 if (ImGui::Selectable(wireColorNames[n], selected))
493 settings.setWireframe(COLOR_VERTEX);
494 ImGui::EndDisabled();
495 break;
496 case 1:
497 ImGui::BeginDisabled(!settings.canWireframe(COLOR_MESH));
498 if (ImGui::Selectable(wireColorNames[n], selected))
499 settings.setWireframe(COLOR_MESH);
500 ImGui::EndDisabled();
501 break;
502 case 2:
503 if (ImGui::Selectable(wireColorNames[n], selected))
504 settings.setWireframe(COLOR_USER);
505 break;
506 default: assert(false); break;
507 }
508 if (selected)
509 ImGui::SetItemDefaultFocus();
510 }
511 ImGui::EndCombo();
512 }
513 // user color picker
514 ImGui::SameLine();
515 ImGui::BeginDisabled(!settings.isWireframe(COLOR_USER));
516 ImGui::ColorEdit4(
517 "##WireUserColor",
518 [&] {
519 return settings.wireframeUserColor();
520 },
521 [&](vcl::Color c) {
522 settings.setWireframeUserColor(c);
523 },
525 ImGui::EndDisabled();
526
527 ImGui::EndDisabled();
528 }
529
530 static void drawMeshEdgeSettings(
532 vcl::MeshRenderSettings& settings)
533 {
535
536 ImGui::BeginDisabled(!settings.canEdges(VISIBLE));
537
538 // visibility
539 ImGui::Checkbox(
540 "Visible",
541 [&] {
542 return settings.isEdges(VISIBLE);
543 },
544 [&](bool v) {
545 settings.setEdges(VISIBLE, v);
546 });
547
548 // shading
549 assert(
550 (settings.isEdges(SHADING_SMOOTH) + settings.isEdges(SHADING_FLAT) +
551 settings.isEdges(SHADING_NONE)) == 1);
552 ImGui::Text("Shading:");
553 ImGui::SameLine();
554 ImGui::RadioButton(
555 "Smooth",
556 [&] {
557 return settings.isEdges(SHADING_SMOOTH);
558 },
559 [&](bool v) {
560 if (v)
561 settings.setEdges(SHADING_SMOOTH);
562 });
563 ImGui::SameLine();
564 ImGui::RadioButton(
565 "Flat",
566 [&] {
567 return settings.isEdges(SHADING_FLAT);
568 },
569 [&](bool v) {
570 if (v)
571 settings.setEdges(SHADING_FLAT);
572 });
573 ImGui::RadioButton(
574 "None",
575 [&] {
576 return settings.isEdges(SHADING_NONE);
577 },
578 [&](bool vis) {
579 if (vis)
580 settings.setEdges(SHADING_NONE);
581 });
582
583 // color
584 ImGui::Text("Color:");
585 ImGui::SameLine();
586 const char* edgeColorNames[] = {"Vertex", "Edge", "Mesh", "User"};
587 const std::array<bool, 4> colorSelected = {
588 settings.isEdges(COLOR_VERTEX),
589 settings.isEdges(COLOR_EDGE),
590 settings.isEdges(COLOR_MESH),
591 settings.isEdges(COLOR_USER)};
592 assert(
593 std::accumulate(
594 std::begin(colorSelected), std::end(colorSelected), 0) == 1);
595 int idx = std::distance(
596 std::begin(colorSelected),
597 std::find(
598 std::begin(colorSelected), std::end(colorSelected), true));
599 assert(idx >= 0 && idx < 4);
600 ImGui::SetNextItemWidth(-40);
601 if (ImGui::BeginCombo("##ComboEdgeColor", edgeColorNames[idx])) {
602 for (int n = 0; n < IM_ARRAYSIZE(edgeColorNames); n++) {
603 const bool selected = (n == idx);
604
605 switch (n) {
606 case 0:
607 ImGui::BeginDisabled(!settings.canEdges(COLOR_VERTEX));
608 if (ImGui::Selectable(edgeColorNames[n], selected))
609 settings.setEdges(COLOR_VERTEX);
610 ImGui::EndDisabled();
611 break;
612 case 1:
613 ImGui::BeginDisabled(!settings.canEdges(COLOR_EDGE));
614 if (ImGui::Selectable(edgeColorNames[n], selected))
615 settings.setEdges(COLOR_EDGE);
616 ImGui::EndDisabled();
617 break;
618 case 2:
619 ImGui::BeginDisabled(!settings.canEdges(COLOR_MESH));
620 if (ImGui::Selectable(edgeColorNames[n], selected))
621 settings.setEdges(COLOR_MESH);
622 ImGui::EndDisabled();
623 break;
624 case 3:
625 if (ImGui::Selectable(edgeColorNames[n], selected))
626 settings.setEdges(COLOR_USER);
627 break;
628 default: assert(false); break;
629 }
630 if (selected)
631 ImGui::SetItemDefaultFocus();
632 }
633 // color picker
634 ImGui::SameLine();
635 ImGui::BeginDisabled(!settings.isEdges(COLOR_USER));
636 ImGui::ColorEdit4(
637 "##EdgeUserColor",
638 [&] {
639 return settings.edgesUserColor();
640 },
641 [&](vcl::Color c) {
642 settings.setEdgesUserColor(c);
643 },
645 ImGui::EndCombo();
646 }
647 ImGui::EndDisabled();
648 }
649
650 static void drawMeshSettings(vcl::AbstractDrawableMesh& drawable)
651 {
652 using MRI = vcl::MeshRenderInfo;
653
654 ImGui::Separator();
655 // mesh settings
656 const auto settings = drawable.renderSettings();
657 auto newSettings = settings;
658
659 // points
661 if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) {
662 // points
663 if (newSettings.canPoints(MRI::Points::VISIBLE) &&
664 ImGui::BeginTabItem("Points")) {
665 drawMeshPointSettings(drawable, newSettings);
666 ImGui::EndTabItem();
667 }
668
669 // surface + wireframe
670 if (newSettings.canSurface(MRI::Surface::VISIBLE)) {
671 if (ImGui::BeginTabItem("Surface")) {
672 drawMeshSurfaceSettings(drawable, newSettings);
673 ImGui::EndTabItem();
674 }
675 if (ImGui::BeginTabItem("Wireframe")) {
676 drawMeshWireframeSettings(drawable, newSettings);
677 ImGui::EndTabItem();
678 }
679 }
680
681 // edges
682 if (newSettings.canEdges(MRI::Edges::VISIBLE) &&
683 ImGui::BeginTabItem("Edges")) {
684 drawMeshEdgeSettings(drawable, newSettings);
685 ImGui::EndTabItem();
686 }
687
688 ImGui::EndTabBar();
689 }
690
691 if (newSettings != settings) {
692 drawable.setRenderSettings(newSettings);
693 }
694 }
695};
696
697} // namespace vcl::imgui
698
699#endif // VCL_IMGUI_MESH_VIEWER_IMGUI_DRAWER_H
Definition abstract_drawable_mesh.h:32
The Color class represents a 32 bit color.
Definition color.h:48
The MeshRenderInfo class is a collection of rendering settings for a Mesh.
Definition mesh_render_info.h:61
Wireframe
List of possible settings for the wireframe primitive.
Definition mesh_render_info.h:164
Surface
List of possible settings for the surface primitive.
Definition mesh_render_info.h:146
Edges
List of possible settings for the edges primitive.
Definition mesh_render_info.h:178
Points
List of possible settings for the points primitive.
Definition mesh_render_info.h:130
The MeshRenderSettings class allows an easy management of render settings of a Mesh....
Definition mesh_render_settings.h:71
bool isPoints(MeshRenderInfo::Points p) const
Returns whether the given points option is set.
Definition mesh_render_settings.h:226
bool canWireframe(MeshRenderInfo::Wireframe w) const
Returns the capability of a given option for the wireframe primitive.
Definition mesh_render_settings.h:178
bool setSurface(MeshRenderInfo::Surface s, bool b=true)
Sets the given shading option of the surface.
Definition mesh_render_settings.h:459
bool setEdges(MeshRenderInfo::Edges e, bool b=true)
Sets the given shading option of the edges.
Definition mesh_render_settings.h:569
bool setPoints(MeshRenderInfo::Points p, bool b=true)
Sets the given shading option of the points.
Definition mesh_render_settings.h:398
bool canSurface(MeshRenderInfo::Surface s) const
Returns the capability of a given option for the surface primitive.
Definition mesh_render_settings.h:166
bool canPoints(MeshRenderInfo::Points p) const
Returns the capability of a given option for the points primitive.
Definition mesh_render_settings.h:154
bool canEdges(MeshRenderInfo::Edges e) const
Returns the capability of a given option for the edges primitive.
Definition mesh_render_settings.h:189
bool isSurface(MeshRenderInfo::Surface s) const
Returns whether the given surface option is set.
Definition mesh_render_settings.h:251
bool isWireframe(MeshRenderInfo::Wireframe w) const
Returns whether the given wireframe option is set.
Definition mesh_render_settings.h:271
bool setWireframe(MeshRenderInfo::Wireframe w, bool b=true)
Sets the given shading option of the wireframe.
Definition mesh_render_settings.h:508
bool isEdges(MeshRenderInfo::Edges e) const
Returns whether the given edges option is set.
Definition mesh_render_settings.h:296
A class representing a line segment in n-dimensional space. The class is parameterized by a PointConc...
Definition segment.h:43
Definition mesh_viewer_imgui_drawer.h:41