Przeglądaj źródła

Update ViewerMenu tutorial with ImGui.

Former-commit-id: 519297bd2827feb68a6e7d19a4b5c3616a2b4b07
Jérémie Dumas 8 lat temu
rodzic
commit
8155326141

+ 1 - 1
.gitmodules

@@ -25,7 +25,7 @@
 	path = external/cork
 	url = https://github.com/libigl/cork.git
 [submodule "external/imgui"]
-	path = external/imgui
+	path = external/imgui/imgui
 	url = https://github.com/ocornut/imgui.git
 [submodule "external/glfw"]
 	path = external/glfw

+ 2 - 2
include/igl/opengl/glfw/Viewer.cpp

@@ -221,10 +221,10 @@ namespace glfw
     // Load OpenGL and its extensions
     if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
     {
-      printf("Failed to load OpenGL and its extensions");
+      printf("Failed to load OpenGL and its extensions\n");
       return(-1);
     }
-    printf("OpenGL Version %d.%d loaded", GLVersion.major, GLVersion.minor);
+    printf("OpenGL Version %d.%d loaded\n", GLVersion.major, GLVersion.minor);
     #if defined(DEBUG) || defined(_DEBUG)
       int major, minor, rev;
       major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR);

+ 41 - 0
include/igl/opengl/glfw/imgui/ImGuiHelpers.h

@@ -0,0 +1,41 @@
+#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H
+#define IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H
+
+////////////////////////////////////////////////////////////////////////////////
+#include <imgui/imgui.h>
+#include <vector>
+#include <string>
+////////////////////////////////////////////////////////////////////////////////
+
+// Extend ImGui by populating its namespace directly
+//
+// Code snippets taken from there:
+// https://eliasdaler.github.io/using-imgui-with-sfml-pt2/
+namespace ImGui
+{
+
+static auto vector_getter = [](void* vec, int idx, const char** out_text)
+{
+	auto& vector = *static_cast<std::vector<std::string>*>(vec);
+	if (idx < 0 || idx >= static_cast<int>(vector.size())) { return false; }
+	*out_text = vector.at(idx).c_str();
+	return true;
+};
+
+inline bool Combo(const char* label, int* idx, std::vector<std::string>& values)
+{
+	if (values.empty()) { return false; }
+	return Combo(label, idx, vector_getter,
+		static_cast<void*>(&values), values.size());
+}
+
+inline bool ListBox(const char* label, int* idx, std::vector<std::string>& values)
+{
+	if (values.empty()) { return false; }
+	return ListBox(label, idx, vector_getter,
+		static_cast<void*>(&values), values.size());
+}
+
+} // namespace ImGui
+
+#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H

+ 366 - 0
include/igl/opengl/glfw/imgui/ImGuiMenu.cpp

@@ -0,0 +1,366 @@
+////////////////////////////////////////////////////////////////////////////////
+#include "ImGuiMenu.h"
+#include <igl/project.h>
+#include <imgui/imgui.h>
+#include <imgui_impl_glfw_gl3.h>
+#include <imgui_fonts_droid_sans.h>
+#include <GLFW/glfw3.h>
+#include <iostream>
+////////////////////////////////////////////////////////////////////////////////
+
+namespace igl
+{
+namespace opengl
+{
+namespace glfw
+{
+namespace imgui
+{
+
+IGL_INLINE void ImGuiMenu::init(igl::opengl::glfw::Viewer *_viewer)
+{
+  ViewerPlugin::init(_viewer);
+  // Setup ImGui binding
+  if (_viewer)
+  {
+    if (m_Context == nullptr)
+    {
+      m_Context = ImGui::CreateContext();
+    }
+    ImGui_ImplGlfwGL3_Init(viewer->window, false);
+    ImGui::GetIO().IniFilename = nullptr;
+    ImGui::StyleColorsDark();
+    ImGuiStyle& style = ImGui::GetStyle();
+    style.FrameRounding = 5.0f;
+    reload_font();
+  }
+}
+
+IGL_INLINE void ImGuiMenu::reload_font(int font_size)
+{
+  m_HidpiScaling = hidpi_scaling();
+  m_PixelRatio = pixel_ratio();
+  ImGuiIO& io = ImGui::GetIO();
+  io.Fonts->Clear();
+  io.Fonts->AddFontFromMemoryCompressedTTF(droid_sans_compressed_data,
+    droid_sans_compressed_size, font_size * m_HidpiScaling);
+  io.FontGlobalScale = 1.0 / m_PixelRatio;
+}
+
+IGL_INLINE void ImGuiMenu::shutdown()
+{
+  // Cleanup
+  ImGui_ImplGlfwGL3_Shutdown();
+  ImGui::DestroyContext(m_Context);
+  m_Context = nullptr;
+}
+
+IGL_INLINE bool ImGuiMenu::pre_draw()
+{
+  glfwPollEvents();
+
+  // Check whether window dpi has changed
+  float scaling = hidpi_scaling();
+  if (std::abs(scaling - m_HidpiScaling) > 1e-5)
+  {
+    reload_font();
+    ImGui_ImplGlfwGL3_InvalidateDeviceObjects();
+  }
+
+  ImGui_ImplGlfwGL3_NewFrame();
+  return false;
+}
+
+IGL_INLINE bool ImGuiMenu::post_draw()
+{
+  draw_menu();
+  ImGui::Render();
+  return false;
+}
+
+IGL_INLINE void ImGuiMenu::post_resize(int width, int height)
+{
+  if (m_Context)
+  {
+    ImGui::GetIO().DisplaySize.x = float(width);
+    ImGui::GetIO().DisplaySize.y = float(height);
+  }
+}
+
+// Mouse IO
+IGL_INLINE bool ImGuiMenu::mouse_down(int button, int modifier)
+{
+  ImGui_ImplGlfwGL3_MouseButtonCallback(viewer->window, button, GLFW_PRESS, modifier);
+  return ImGui::GetIO().WantCaptureMouse;
+}
+
+IGL_INLINE bool ImGuiMenu::mouse_up(int button, int modifier)
+{
+  return ImGui::GetIO().WantCaptureMouse;
+}
+
+IGL_INLINE bool ImGuiMenu::mouse_move(int mouse_x, int mouse_y)
+{
+  return ImGui::GetIO().WantCaptureMouse;
+}
+
+IGL_INLINE bool ImGuiMenu::mouse_scroll(float delta_y)
+{
+  ImGui_ImplGlfwGL3_ScrollCallback(viewer->window, 0.f, delta_y);
+  return ImGui::GetIO().WantCaptureMouse;
+}
+
+// Keyboard IO
+IGL_INLINE bool ImGuiMenu::key_pressed(unsigned int key, int modifiers)
+{
+  ImGui_ImplGlfwGL3_CharCallback(nullptr, key);
+  return ImGui::GetIO().WantCaptureKeyboard;
+}
+
+IGL_INLINE bool ImGuiMenu::key_down(int key, int modifiers)
+{
+  ImGui_ImplGlfwGL3_KeyCallback(viewer->window, key, 0, GLFW_PRESS, modifiers);
+  return ImGui::GetIO().WantCaptureKeyboard;
+}
+
+IGL_INLINE bool ImGuiMenu::key_up(int key, int modifiers)
+{
+  ImGui_ImplGlfwGL3_KeyCallback(viewer->window, key, 0, GLFW_RELEASE, modifiers);
+  return ImGui::GetIO().WantCaptureKeyboard;
+}
+
+// Draw menu
+IGL_INLINE void ImGuiMenu::draw_menu()
+{
+  // Text labels
+  draw_labels_menu();
+
+  // Viewer settings
+  float menu_width = 180.f * menu_scaling();
+  ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver);
+  ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiSetCond_FirstUseEver);
+  ImGui::SetNextWindowSizeConstraints(ImVec2(menu_width, -1.0f), ImVec2(menu_width, -1.0f));
+  bool _viewer_menu_visible = true;
+  ImGui::Begin(
+      "Viewer", &_viewer_menu_visible,
+      ImGuiWindowFlags_NoSavedSettings
+      | ImGuiWindowFlags_AlwaysAutoResize
+  );
+  ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.4f);
+  draw_viewer_menu();
+  ImGui::PopItemWidth();
+  ImGui::End();
+
+  // Other menus
+  draw_other_menu();
+}
+
+IGL_INLINE void ImGuiMenu::draw_viewer_menu()
+{
+  // Workspace
+  if (ImGui::CollapsingHeader("Workspace", ImGuiTreeNodeFlags_DefaultOpen))
+  {
+    ImGui::Columns(2, nullptr, false);
+    if (ImGui::Button("Load##Workspace", ImVec2(-1, 0)))
+    {
+      viewer->load_scene();
+    }
+    ImGui::NextColumn();
+    if (ImGui::Button("Save##Workspace", ImVec2(-1, 0)))
+    {
+      viewer->save_scene();
+    }
+    ImGui::Columns(1);
+  }
+
+  // Mesh
+  if (ImGui::CollapsingHeader("Mesh", ImGuiTreeNodeFlags_DefaultOpen))
+  {
+    ImGui::Columns(2, nullptr, false);
+    if (ImGui::Button("Load##Mesh", ImVec2(-1, 0)))
+    {
+      viewer->open_dialog_load_mesh();
+    }
+    ImGui::NextColumn();
+    if (ImGui::Button("Save##Mesh", ImVec2(-1, 0)))
+    {
+      viewer->open_dialog_save_mesh();
+    }
+    ImGui::Columns(1);
+  }
+
+  // Viewing options
+  if (ImGui::CollapsingHeader("Viewing Options", ImGuiTreeNodeFlags_DefaultOpen))
+  {
+    if (ImGui::Button("Center object", ImVec2(-1, 0)))
+    {
+      viewer->core.align_camera_center(viewer->data().V, viewer->data().F);
+    }
+    if (ImGui::Button("Snap canonical view", ImVec2(-1, 0)))
+    {
+      viewer->snap_to_canonical_quaternion();
+    }
+
+    // Zoom
+    ImGui::PushItemWidth(80 * menu_scaling());
+    ImGui::DragFloat("Zoom", &(viewer->core.camera_zoom), 0.05f, 0.1f, 20.0f);
+
+    // Select rotation type
+    static int rotation_type = static_cast<int>(viewer->core.rotation_type);
+    static Eigen::Quaternionf trackball_angle = Eigen::Quaternionf::Identity();
+    static bool orthographic = true;
+    if (ImGui::Combo("Camera Type", &rotation_type, "Trackball\0Two Axes\0002D Mode\0\0"))
+    {
+      using RT = igl::opengl::ViewerCore::RotationType;
+      auto new_type = static_cast<RT>(rotation_type);
+      if (new_type != viewer->core.rotation_type)
+      {
+        if (new_type == RT::ROTATION_TYPE_NO_ROTATION)
+        {
+          trackball_angle = viewer->core.trackball_angle;
+          orthographic = viewer->core.orthographic;
+          viewer->core.trackball_angle = Eigen::Quaternionf::Identity();
+          viewer->core.orthographic = true;
+        }
+        else if (viewer->core.rotation_type == RT::ROTATION_TYPE_NO_ROTATION)
+        {
+          viewer->core.trackball_angle = trackball_angle;
+          viewer->core.orthographic = orthographic;
+        }
+        viewer->core.set_rotation_type(new_type);
+      }
+    }
+
+    // Orthographic view
+    ImGui::Checkbox("Orthographic view", &(viewer->core.orthographic));
+    ImGui::PopItemWidth();
+  }
+
+  // Draw options
+  if (ImGui::CollapsingHeader("Draw Options", ImGuiTreeNodeFlags_DefaultOpen))
+  {
+    if (ImGui::Checkbox("Face-based", &(viewer->data().face_based)))
+    {
+      viewer->data().set_face_based(viewer->data().face_based);
+    }
+    ImGui::Checkbox("Show texture", &(viewer->data().show_texture));
+    if (ImGui::Checkbox("Invert normals", &(viewer->data().invert_normals)))
+    {
+      viewer->data().dirty |= igl::opengl::MeshGL::DIRTY_NORMAL;
+    }
+    ImGui::Checkbox("Show overlay", &(viewer->data().show_overlay));
+    ImGui::Checkbox("Show overlay depth", &(viewer->data().show_overlay_depth));
+    ImGui::ColorEdit4("Background", viewer->core.background_color.data(),
+        ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_PickerHueWheel);
+    ImGui::ColorEdit4("Line color", viewer->data().line_color.data(),
+        ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_PickerHueWheel);
+    ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.3f);
+    ImGui::DragFloat("Shininess", &(viewer->data().shininess), 0.05f, 0.0f, 100.0f);
+    ImGui::PopItemWidth();
+  }
+
+  // Overlays
+  if (ImGui::CollapsingHeader("Overlays", ImGuiTreeNodeFlags_DefaultOpen))
+  {
+    ImGui::Checkbox("Wireframe", &(viewer->data().show_lines));
+    ImGui::Checkbox("Fill", &(viewer->data().show_faces));
+    ImGui::Checkbox("Show vertex labels", &(viewer->data().show_vertid));
+    ImGui::Checkbox("Show faces labels", &(viewer->data().show_faceid));
+  }
+}
+
+IGL_INLINE void ImGuiMenu::draw_labels_menu()
+{
+  // Text labels
+  ImGui::SetNextWindowPos(ImVec2(0,0), ImGuiSetCond_Always);
+  ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiSetCond_Always);
+  bool visible = true;
+  ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0,0,0,0));
+  ImGui::Begin("ViewerLabels", &visible,
+      ImGuiWindowFlags_NoTitleBar
+      | ImGuiWindowFlags_NoResize
+      | ImGuiWindowFlags_NoMove
+      | ImGuiWindowFlags_NoScrollbar
+      | ImGuiWindowFlags_NoScrollWithMouse
+      | ImGuiWindowFlags_NoCollapse
+      | ImGuiWindowFlags_NoSavedSettings
+      | ImGuiWindowFlags_NoInputs);
+  draw_labels(viewer->data());
+  ImGui::End();
+  ImGui::PopStyleColor();
+}
+
+IGL_INLINE void ImGuiMenu::draw_labels(const igl::opengl::ViewerData &data)
+{
+  if (data.show_vertid)
+  {
+    for (int i = 0; i < data.V.rows(); ++i)
+    {
+      draw_text(data.V.row(i), data.V_normals.row(i), std::to_string(i));
+    }
+  }
+
+  if (data.show_faceid)
+  {
+    for (int i = 0; i < data.F.rows(); ++i)
+    {
+      Eigen::RowVector3d p = Eigen::RowVector3d::Zero();
+      for (int j = 0; j < data.F.cols(); ++j)
+      {
+        p += data.V.row(data.F(i,j));
+      }
+      p /= (double) data.F.cols();
+
+      draw_text(p, data.F_normals.row(i), std::to_string(i));
+    }
+  }
+
+  if (data.labels_positions.rows() > 0)
+  {
+    for (int i = 0; i < data.labels_positions.rows(); ++i)
+    {
+      draw_text(data.labels_positions.row(i), Eigen::Vector3d(0.0,0.0,0.0),
+        data.labels_strings[i]);
+    }
+  }
+}
+
+IGL_INLINE void ImGuiMenu::draw_text(Eigen::Vector3d pos, Eigen::Vector3d normal, const std::string &text)
+{
+  Eigen::Matrix4f view_matrix = viewer->core.view * viewer->core.model;
+  pos += normal * 0.005f * viewer->core.object_scale;
+  Eigen::Vector3f coord = igl::project(Eigen::Vector3f(pos.cast<float>()),
+    view_matrix, viewer->core.proj, viewer->core.viewport);
+
+  // Draw text labels slightly bigger than normal text
+  ImDrawList* drawList = ImGui::GetWindowDrawList();
+  drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize() * 1.2,
+      ImVec2(coord[0]/m_PixelRatio, (viewer->core.viewport[3] - coord[1])/m_PixelRatio),
+      ImGui::GetColorU32(ImVec4(0, 0, 10, 255)),
+      &text[0], &text[0] + text.size());
+}
+
+IGL_INLINE float ImGuiMenu::pixel_ratio()
+{
+  // Computes pixel ratio for hidpi devices
+  int buf_size[2];
+  int win_size[2];
+  GLFWwindow* window = glfwGetCurrentContext();
+  glfwGetFramebufferSize(window, &buf_size[0], &buf_size[1]);
+  glfwGetWindowSize(window, &win_size[0], &win_size[1]);
+  return (float) buf_size[0] / (float) win_size[0];
+}
+
+IGL_INLINE float ImGuiMenu::hidpi_scaling()
+{
+  // Computes scaling factor for hidpi devices
+  float xscale, yscale;
+  GLFWwindow* window = glfwGetCurrentContext();
+  glfwGetWindowContentScale(window, &xscale, &yscale);
+  return 0.5 * (xscale + yscale);
+}
+
+} // end namespace
+} // end namespace
+} // end namespace
+} // end namespace

+ 93 - 0
include/igl/opengl/glfw/imgui/ImGuiMenu.h

@@ -0,0 +1,93 @@
+#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H
+#define IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H
+
+////////////////////////////////////////////////////////////////////////////////
+#include <igl/opengl/glfw/Viewer.h>
+#include <igl/opengl/glfw/ViewerPlugin.h>
+#include <igl/igl_inline.h>
+////////////////////////////////////////////////////////////////////////////////
+
+// Forward declarations
+struct ImGuiContext;
+
+namespace igl
+{
+namespace opengl
+{
+namespace glfw
+{
+namespace imgui
+{
+
+class ImGuiMenu : public igl::opengl::glfw::ViewerPlugin
+{
+protected:
+  // Hidpi scaling to be used for text rendering.
+  float m_HidpiScaling;
+
+  // Ratio between the framebuffer size and the window size.
+  // May be different from the hipdi scaling!
+  float m_PixelRatio;
+
+  // ImGui Context
+  ImGuiContext * m_Context = nullptr;
+
+public:
+  IGL_INLINE virtual void init(igl::opengl::glfw::Viewer *_viewer) override;
+
+  IGL_INLINE virtual void reload_font(int font_size = 13);
+
+  IGL_INLINE virtual void shutdown() override;
+
+  IGL_INLINE virtual bool pre_draw() override;
+
+  IGL_INLINE  virtual bool post_draw() override;
+
+  IGL_INLINE virtual void post_resize(int width, int height) override;
+
+  // Mouse IO
+  IGL_INLINE virtual bool mouse_down(int button, int modifier) override;
+
+  IGL_INLINE virtual bool mouse_up(int button, int modifier) override;
+
+  IGL_INLINE virtual bool mouse_move(int mouse_x, int mouse_y) override;
+
+  IGL_INLINE virtual bool mouse_scroll(float delta_y) override;
+
+  // Keyboard IO
+  IGL_INLINE virtual bool key_pressed(unsigned int key, int modifiers) override;
+
+  IGL_INLINE virtual bool key_down(int key, int modifiers) override;
+
+  IGL_INLINE virtual bool key_up(int key, int modifiers) override;
+
+  // Draw menu
+  IGL_INLINE virtual void draw_menu();
+
+  IGL_INLINE virtual void draw_viewer_menu();
+
+  IGL_INLINE virtual void draw_other_menu() { }
+
+  IGL_INLINE void draw_labels_menu();
+
+  IGL_INLINE void draw_labels(const igl::opengl::ViewerData &data);
+
+  IGL_INLINE void draw_text(Eigen::Vector3d pos, Eigen::Vector3d normal, const std::string &text);
+
+  IGL_INLINE float pixel_ratio();
+
+  IGL_INLINE float hidpi_scaling();
+
+  float menu_scaling() { return m_HidpiScaling / m_PixelRatio; }
+};
+
+} // end namespace
+} // end namespace
+} // end namespace
+} // end namespace
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "ImGuiMenu.cpp"
+#endif
+
+#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H

+ 44 - 19
shared/cmake/libigl.cmake

@@ -1,22 +1,23 @@
 cmake_minimum_required(VERSION 3.1)
 
 ### Available options ###
-option(LIBIGL_USE_STATIC_LIBRARY    "Use libigl as static library" OFF)
-option(LIBIGL_WITH_ANTTWEAKBAR      "Use AntTweakBar"    OFF)
-option(LIBIGL_WITH_CGAL             "Use CGAL"           ON)
-option(LIBIGL_WITH_COMISO           "Use CoMiso"         ON)
-option(LIBIGL_WITH_CORK             "Use Cork"           OFF)
-option(LIBIGL_WITH_EMBREE           "Use Embree"         OFF)
-option(LIBIGL_WITH_LIM              "Use LIM"            ON)
-option(LIBIGL_WITH_MATLAB           "Use Matlab"         ON)
-option(LIBIGL_WITH_MOSEK            "Use MOSEK"          ON)
-option(LIBIGL_WITH_OPENGL           "Use OpenGL"         ON)
-option(LIBIGL_WITH_OPENGL_GLFW      "Use GLFW"           ON)
-option(LIBIGL_WITH_PNG              "Use PNG"            ON)
-option(LIBIGL_WITH_TETGEN           "Use Tetgen"         ON)
-option(LIBIGL_WITH_TRIANGLE         "Use Triangle"       ON)
-option(LIBIGL_WITH_XML              "Use XML"            ON)
-option(LIBIGL_WITH_PYTHON           "Use Python"         OFF)
+option(LIBIGL_USE_STATIC_LIBRARY     "Use libigl as static library" OFF)
+option(LIBIGL_WITH_ANTTWEAKBAR       "Use AntTweakBar"    OFF)
+option(LIBIGL_WITH_CGAL              "Use CGAL"           ON)
+option(LIBIGL_WITH_COMISO            "Use CoMiso"         ON)
+option(LIBIGL_WITH_CORK              "Use Cork"           OFF)
+option(LIBIGL_WITH_EMBREE            "Use Embree"         OFF)
+option(LIBIGL_WITH_LIM               "Use LIM"            ON)
+option(LIBIGL_WITH_MATLAB            "Use Matlab"         ON)
+option(LIBIGL_WITH_MOSEK             "Use MOSEK"          ON)
+option(LIBIGL_WITH_OPENGL            "Use OpenGL"         ON)
+option(LIBIGL_WITH_OPENGL_GLFW       "Use GLFW"           ON)
+option(LIBIGL_WITH_OPENGL_GLFW_IMGUI "Use ImGui"          ON)
+option(LIBIGL_WITH_PNG               "Use PNG"            ON)
+option(LIBIGL_WITH_TETGEN            "Use Tetgen"         ON)
+option(LIBIGL_WITH_TRIANGLE          "Use Triangle"       ON)
+option(LIBIGL_WITH_XML               "Use XML"            ON)
+option(LIBIGL_WITH_PYTHON            "Use Python"         OFF)
 
 ################################################################################
 
@@ -283,9 +284,14 @@ if(LIBIGL_WITH_OPENGL)
   endif()
   target_link_libraries(igl_opengl ${IGL_SCOPE} glad)
   # target_link_libraries(igl_opengl2 ${IGL_SCOPE} glad)
+endif()
+
+################################################################################
+### Compile the GLFW part ###
 
-  # GLFW module
-  if(LIBIGL_WITH_OPENGL_GLFW)
+if(LIBIGL_WITH_OPENGL_GLFW)
+  if(TARGET igl::opengl)
+    # GLFW module
     compile_igl_module("opengl/glfw")
     if(NOT TARGET glfw)
       set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL " " FORCE)
@@ -295,12 +301,31 @@ if(LIBIGL_WITH_OPENGL)
       add_subdirectory(${LIBIGL_EXTERNAL}/glfw glfw)
     endif()
     target_link_libraries(igl_opengl_glfw ${IGL_SCOPE} igl_opengl glfw)
+  else()
+    message(WARNING "GLFW module could not be compiled")
+    set(LIBIGL_WITH_OPENGL_GLFW OFF CACHE BOOL "" FORCE)
   endif()
+endif()
 
+################################################################################
+### Compile the ImGui part ###
+
+if(LIBIGL_WITH_OPENGL_GLFW_IMGUI)
+  if(TARGET igl::opengl_glfw)
+    # ImGui module
+    compile_igl_module("opengl/glfw/imgui")
+    if(NOT TARGET imgui)
+      add_subdirectory(${LIBIGL_EXTERNAL}/imgui imgui)
+    endif()
+    target_link_libraries(igl_opengl_glfw_imgui ${IGL_SCOPE} igl_opengl_glfw imgui)
+  else()
+    message(WARNING "ImGui module could not be compiled")
+    set(LIBIGL_WITH_OPENGL_GLFW_IMGUI OFF CACHE BOOL "" FORCE)
+  endif()
 endif()
 
 ################################################################################
-### Compile the png parts ###
+### Compile the png part ###
 if(LIBIGL_WITH_PNG)
   # png/ module is anomalous because it also depends on opengl it really should
   # be moved into the opengl/ directory and namespace ...

+ 1 - 1
tutorial/106_ViewerMenu/CMakeLists.txt

@@ -2,4 +2,4 @@ cmake_minimum_required(VERSION 2.8.12)
 project(106_ViewerMenu)
 
 add_executable(${PROJECT_NAME}_bin main.cpp)
-target_link_libraries(${PROJECT_NAME}_bin igl::core igl::opengl igl::opengl_glfw nanogui tutorials)
+target_link_libraries(${PROJECT_NAME}_bin igl::core igl::opengl igl::opengl_glfw igl::opengl_glfw_imgui tutorials)

+ 79 - 37
tutorial/106_ViewerMenu/main.cpp

@@ -1,58 +1,100 @@
 #include <igl/readOFF.h>
 #include <igl/opengl/glfw/Viewer.h>
-#include <nanogui/formhelper.h>
-#include <nanogui/screen.h>
+#include <igl/opengl/glfw/imgui/ImGuiMenu.h>
+#include <igl/opengl/glfw/imgui/ImGuiHelpers.h>
+#include <imgui/imgui.h>
 #include <iostream>
 #include "tutorial_shared_path.h"
 
-int main(int argc, char *argv[])
+class MyMenu : public igl::opengl::glfw::imgui::ImGuiMenu
 {
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-
-  bool boolVariable = true;
-  float floatVariable = 0.1f;
-  enum Orientation { Up=0,Down,Left,Right } dir = Up;
-
-  // Load a mesh in OFF format
-  igl::readOFF(TUTORIAL_SHARED_PATH "/bunny.off", V, F);
-
-  // Init the viewer
-  igl::opengl::glfw::Viewer viewer;
+  float floatVariable = 0.1f; // Shared between two menus
 
-  // Extend viewer menu
-  viewer.callback_init = [&](igl::opengl::glfw::Viewer& viewer)
+  virtual void draw_viewer_menu() override
   {
+    // Draw parent menu
+    ImGuiMenu::draw_viewer_menu();
+
     // Add new group
-    viewer.ngui->addGroup("New Group");
+    if (ImGui::CollapsingHeader("New Group", ImGuiTreeNodeFlags_DefaultOpen))
+    {
+      // Expose variable directly ...
+      ImGui::InputFloat("float", &floatVariable, 0, 0, 3);
 
-    // Expose variable directly ...
-    viewer.ngui->addVariable("float",floatVariable);
+      // ... or using a custom callback
+      static bool boolVariable = true;
+      if (ImGui::Checkbox("bool", &boolVariable))
+      {
+        // do something
+        std::cout << "boolVariable: " << std::boolalpha << boolVariable << std::endl;
+      }
 
-    // ... or using a custom callback
-    viewer.ngui->addVariable<bool>("bool",[&](bool val) {
-      boolVariable = val; // set
-    },[&]() {
-      return boolVariable; // get
-    });
+      // Expose an enumeration type
+      enum Orientation { Up=0, Down, Left, Right };
+      static Orientation dir = Up;
+      ImGui::Combo("Direction", (int *)(&dir), "Up\0Down\0Left\0Right\0\0");
 
-    // Expose an enumaration type
-    viewer.ngui->addVariable<Orientation>("Direction",dir)->setItems({"Up","Down","Left","Right"});
+      // We can also use a std::vector<std::string> defined dynamically
+      static int num_choices = 3;
+      static std::vector<std::string> choices;
+      static int idx_choice = 0;
+      if (ImGui::InputInt("Num letters", &num_choices))
+      {
+        num_choices = std::max(1, std::min(26, num_choices));
+      }
+      if (num_choices != (int) choices.size())
+      {
+        choices.resize(num_choices);
+        for (int i = 0; i < num_choices; ++i)
+          choices[i] = std::string(1, 'A' + i);
+        if (idx_choice >= num_choices)
+          idx_choice = num_choices - 1;
+      }
+      ImGui::Combo("Letter", &idx_choice, choices);
 
-    // Add a button
-    viewer.ngui->addButton("Print Hello",[](){ std::cout << "Hello\n"; });
+      // Add a button
+      if (ImGui::Button("Print Hello", ImVec2(-1,0)))
+      {
+        std::cout << "Hello\n";
+      }
+    }
+  }
 
-    // Add an additional menu window
-    viewer.ngui->addWindow(Eigen::Vector2i(220,10),"New Window");
+  virtual void draw_other_menu() override
+  {
+    // Define next window position + size
+    ImGui::SetNextWindowPos(ImVec2(180.f * menu_scaling(), 10), ImGuiSetCond_FirstUseEver);
+    ImGui::SetNextWindowSize(ImVec2(200, 80), ImGuiSetCond_FirstUseEver);
+    ImGui::Begin(
+        "New Window", nullptr,
+        ImGuiWindowFlags_NoSavedSettings
+    );
 
     // Expose the same variable directly ...
-    viewer.ngui->addVariable("float",floatVariable);
+    ImGui::PushItemWidth(-80);
+    ImGui::DragFloat("float", &floatVariable, 0.0, 0.0, 3.0);
+    ImGui::PopItemWidth();
 
-    // Generate menu
-    viewer.screen->performLayout();
+    ImGui::End();
+  }
+
+};
+
+int main(int argc, char *argv[])
+{
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+
+  // Load a mesh in OFF format
+  igl::readOFF(TUTORIAL_SHARED_PATH "/bunny.off", V, F);
+
+  // Init the viewer
+  igl::opengl::glfw::Viewer viewer;
 
-    return false;
-  };
+  // Attach a custom menu
+  MyMenu menu;
+  viewer.core.is_animating = true;
+  viewer.plugins.push_back(&menu);
 
   // Plot the mesh
   viewer.data().set_mesh(V, F);

+ 1 - 4
tutorial/CMakeLists.txt

@@ -11,7 +11,6 @@ find_package(MOSEK)
 
 ### libIGL options: choose between header only and compiled static library
 option(LIBIGL_USE_STATIC_LIBRARY "Use LibIGL as static library" ON)
-option(LIBIGL_WITH_NANOGUI     "Use Nanogui menu"   OFF)
 option(LIBIGL_WITH_EMBREE      "Use Embree"         ON)
 
 ### libIGL options: choose your dependencies (by default everything is OFF, in this example we need the viewer)
@@ -54,9 +53,7 @@ if(TUTORIALS_CHAPTER1)
   add_subdirectory("103_Events")
   add_subdirectory("104_Colors")
   add_subdirectory("105_Overlays")
-  if(LIBIGL_WITH_NANOGUI)
-    add_subdirectory("106_ViewerMenu")
-  endif()
+  add_subdirectory("106_ViewerMenu")
   add_subdirectory("107_MultipleMeshes")
 endif()