Update eigen to v5.0.1 and libigl to v2.6.0. (#11311)

* Update eigen from v3.3.7 to v5.0.1.

This updates eigen from v3.3.7 released on  December 11, 2018-12-11 to v5.0.1
released on 2025-11-11. There have be a large number of bug-fixes,
optimizations, and improvements between these releases. See the details at;

https://gitlab.com/libeigen/eigen/-/releases

It retains the previous custom minimal `CMakeLists.txt`, and adds a
README-OrcaSlicer.md that explains what version and parts of the upstream
eigen release have been included, and where the full release can be found.

* Update libigl from v2.0.0 (or older) to v2.6.0.

This updates libigl from what was probably v2.0.0 released on 2018-10-16 to
v2.6.0 released on 2025-05-15. It's possible the old version was even older
than that but there is no version indicators in the code and I ran out of
patience identifying missing changes and only went back as far as v2.0.0.

There have been a large number of bug-fixes, optimizations, and improvements
between these versions. See the following for details;

https://github.com/libigl/libigl/releases

I retained the minimal custom `CMakeLists.txt`, added `README.md` from the
libigl distribution which identifies the version, and added a
README-OrcaSlicer.md that details the version and parts that have been
included.

* Update libslic3r for libigl v2.6.0 changes.

This updates libslic3r for all changes moving to eigen v5.0.1 and libigl
v2.6.0. Despite the large number of updates to both dependencies, no changes
were required for the eigen update, and only one change was required for the
libigl update.

For libigl, `igl::Hit` was changed to a template taking the Scalar type to
use. Previously it was hard-coded to `float`, so to minimize possible impact
I've updated all places it is used from `igl::Hit` to `igl::Hit<float>`.

* Add compiler option `-DNOMINMAX` for libigl with MSVC.

MSVC by default defines `min(()` and `max()` macros that break
`std::numeric_limits<>::max()`. The upstream cmake that we don't include
adds `-DNOMINMAX` for the libigl module when compiling with MSVC, so we need
to add the same thing here.

* Fix src/libslic3r/TriangleMeshDeal.cpp for the unmodified upstream libigl.

This fixes `TriangleMeshDeal.cpp` to work with the unmodified upstream
libigl v2.6.0. loop.{h,cpp} implementation.

This file and feature was added in PR "BBS Port: Mesh Subdivision" (#12150)
which included changes to `loop.{h,cpp}` in the old version of libigl. This PR
avoids modifying the included dependencies, and uses the updated upstream
versions of those files without any modifications, which requires fixing
TriangleMeshDeal.cpp to work with them.

In particular, the modifications made to `loop.{h,cpp}` included changing the
return type from void to bool, adding additional validation checking of the
input meshes, and returning false if they failed validation. These added
checks looked unnecessary and would only have caught problems if the input
mesh was very corrupt.

To make `TriangleMeshDeal.cpp` work without this built-in checking
functionality, I removed checking/handling of any `false` return value.

There was also a hell of a lot of redundant copying and casting back and forth
between float and double, so I cleaned that up. The input and output meshs use
floats for the vertexes, and there would be no accuracy benefits from casting
to and from doubles for the simple weighted average operations done by
igl::loop(). So this just uses `Eigen:Map` to use the original input mesh
vertex data directly without requiring any copy or casting.

* Move eigen from included `deps_src` to externaly fetched `deps`.

This copys what PrusaSlicer did and moved it from an included dependency under
`deps_src` to an externaly fetched dependency under `deps`. This requires
updating some `CMakeList.txt` configs and removing the old and obsolete
`cmake/modules/FindEigen3.cmake`. The details of when this was done in
PrusaSlicer and the followup fixes are at;

* 21116995d7
* https://github.com/prusa3d/PrusaSlicer/issues/13608
* https://github.com/prusa3d/PrusaSlicer/pull/13609
* e3c277b9ee

For some reason I don't fully understand this also required fixing
`src/slic3r/GUI/GUI_App.cpp` by adding `#include <boost/nowide/cstdio.hpp>` to
fix an `error: ‘remove’ is not a member of ‘boost::nowide'`. The main thing I
don't understand is how it worked before. Note that this include is in the
PrusaSlicer version of this file, but it also significantly deviates from what
is currently in OrcaSlicer in many other ways.

* Whups... I missed adding the deps/Eigen/Eigen.cmake file...

* Tidy some whitespace indenting in CMakeLists.txt.

* Ugh... tabs indenting needing fixes.

* Change the include order of deps/Eigen.

It turns out that although Boost includes some references to Eigen, Eigen also
includes some references to Boost for supporting some of it's additional
numeric types.

I don't think it matters much since we are not using these features, but I
think technically its more correct to say Eigen depends on Boost than the
other way around, so I've re-ordered them.

* Add source for Eigen 5.0.1 download to flatpak yml config.

* Add explicit `DEPENDS dep_Boost to deps/Eigen.

I missed this before. This ensures we don't rely on include orders to make
sure Boost is installed before we configure Eigen.

* Add `DEPENDS dep_Boost dep_GMP dep_MPFR` to deps/Eigen.

It turns out Eigen can also use GMP and MPFR for multi-precision and
multi-precision-rounded numeric types if they are available.

Again, I don't think we are using these so it doesn't really matter, but it is
technically correct and ensures they are there if we ever do need them.

* Fix deps DEPENDENCY ordering for GMP, MPFR, Eigen, and CGAL.

I think this is finally correct. Apparently CGAL also optionally depends on
Eigen, so the correct dependency order from lowest to highest is GMP, MPFR, Eigen, and CGAL.

---------

Co-authored-by: Donovan Baarda <dbaarda@google.com>
Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
This commit is contained in:
Donovan Baarda
2026-05-12 17:09:13 +10:00
committed by GitHub
parent 797ee70b0b
commit dc5897d7b5
1573 changed files with 63323 additions and 161343 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -45,8 +45,8 @@ namespace glfw
// UI Enumerations
enum class MouseButton {Left, Middle, Right};
enum class MouseMode { None, Rotation, Zoom, Pan, Translation} mouse_mode;
IGL_INLINE int launch(bool resizable = true,bool fullscreen = false);
IGL_INLINE int launch_init(bool resizable = true,bool fullscreen = false);
IGL_INLINE int launch (bool fullscreen = false, const std::string &name = "libigl viewer", int width = 0, int height = 0);
IGL_INLINE int launch_init(bool fullscreen = false, const std::string &name = "libigl viewer", int width = 0, int height = 0);
IGL_INLINE bool launch_rendering(bool loop = true);
IGL_INLINE void launch_shut();
IGL_INLINE void init();
@@ -72,6 +72,34 @@ namespace glfw
IGL_INLINE bool save_scene(std::string fname);
// Draw everything
IGL_INLINE void draw();
// Render given ViewerCore to a buffer. The width and height are determined
// by non-zeros dimensions of R or if both are zero — are set to this
// core's viewport sizes. Other buffers are resized to fit if needed.
//
// Template:
// T image storage type, e.g., unsigned char (values ∈ [0,255]), double
// (values ∈ [0.0,1.0]).
// Inputs:
// data which ViewerData to draw
// update_matrices whether to update view, proj, and norm matrices in
// shaders
// Outputs:
// R width by height red pixel color values
// G width by height green pixel color values
// B width by height blue pixel color values
// A width by height alpha pixel color values
// D width by height depth pixel values. Depth values are _not_
// anti-aliased like RGBA.
//
template <typename T>
IGL_INLINE void draw_buffer(
// can't be const because of writing in and out of `core.viewport`
/*const*/ igl::opengl::ViewerCore & core,
Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & R,
Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & G,
Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & B,
Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & A,
Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & D);
// OpenGL context resize
IGL_INLINE void resize(int w,int h); // explicitly set window size
IGL_INLINE void post_resize(int w,int h); // external resize due to user interaction
@@ -79,17 +107,29 @@ namespace glfw
IGL_INLINE void snap_to_canonical_quaternion();
IGL_INLINE void open_dialog_load_mesh();
IGL_INLINE void open_dialog_save_mesh();
IGL_INLINE ViewerData& data();
// Append a new "slot" for a mesh (i.e., create empty entires at the end of
////////////////////////
// Multi-mesh methods //
////////////////////////
// Return the current mesh, or the mesh corresponding to a given unique identifier
//
// Inputs:
// mesh_id unique identifier associated to the desired mesh (current mesh if -1)
IGL_INLINE ViewerData& data(int mesh_id = -1);
IGL_INLINE const ViewerData& data(int mesh_id = -1) const;
// Append a new "slot" for a mesh (i.e., create empty entries at the end of
// the data_list and opengl_state_list.
//
// Inputs:
// visible If true, the new mesh is set to be visible on all existing viewports
// Returns the id of the last appended mesh
//
// Side Effects:
// selected_data_index is set this newly created, last entry (i.e.,
// #meshes-1)
IGL_INLINE int append_mesh();
IGL_INLINE int append_mesh(bool visible = true);
// Erase a mesh (i.e., its corresponding data and state entires in data_list
// and opengl_state_list)
@@ -113,6 +153,48 @@ namespace glfw
// Returns 0 if not found
IGL_INLINE size_t mesh_index(const int id) const;
////////////////////////////
// Multi-viewport methods //
////////////////////////////
// Return the current viewport, or the viewport corresponding to a given unique identifier
//
// Inputs:
// core_id unique identifier corresponding to the desired viewport (current viewport if 0)
IGL_INLINE ViewerCore& core(unsigned core_id = 0);
IGL_INLINE const ViewerCore& core(unsigned core_id = 0) const;
// Append a new "slot" for a viewport (i.e., copy properties of the current viewport, only
// changing the viewport size/position)
//
// Inputs:
// viewport Vector specifying the viewport origin and size in screen coordinates.
// append_empty If true, existing meshes are hidden on the new viewport.
//
// Returns the unique id of the newly inserted viewport. There can be a maximum of 31
// viewports created in the same viewport. Erasing a viewport does not change the id of
// other existing viewports
IGL_INLINE int append_core(Eigen::Vector4f viewport, bool append_empty = false);
// Erase a viewport
//
// Inputs:
// index index of the viewport to erase
IGL_INLINE bool erase_core(const size_t index);
// Retrieve viewport index from its unique identifier
// Returns 0 if not found
IGL_INLINE size_t core_index(const int id) const;
// Change selected_core_index to the viewport containing the mouse
// (current_mouse_x, current_mouse_y)
IGL_INLINE void select_hovered_core();
public:
//////////////////////
// Member variables //
//////////////////////
// Alec: I call this data_list instead of just data to avoid confusion with
// old "data" variable.
// Stores all the data that should be visualized
@@ -121,8 +203,12 @@ namespace glfw
size_t selected_data_index;
int next_data_id;
GLFWwindow* window;
// Stores all the viewing options
ViewerCore core;
std::vector<ViewerCore> core_list;
size_t selected_core_index;
int next_core_id;
// List of registered plugins
std::vector<ViewerPlugin*> plugins;
// Temporary data stored when the mouse button is pressed
@@ -148,6 +234,7 @@ namespace glfw
std::function<bool(Viewer& viewer, int mouse_x, int mouse_y)> callback_mouse_move;
std::function<bool(Viewer& viewer, float delta_y)> callback_mouse_scroll;
std::function<bool(Viewer& viewer, unsigned int key, int modifiers)> callback_key_pressed;
std::function<bool(Viewer& viewer, int w, int h)> callback_post_resize;
// THESE SHOULD BE DEPRECATED:
std::function<bool(Viewer& viewer, unsigned int key, int modifiers)> callback_key_down;
std::function<bool(Viewer& viewer, unsigned int key, int modifiers)> callback_key_up;
@@ -164,7 +251,7 @@ namespace glfw
void* callback_key_up_data;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
} // end namespace

View File

@@ -14,169 +14,89 @@
// * remove Preview3D from comments
// * clean comments
#include <string>
#include <igl/igl_inline.h>
#include "../../igl_inline.h"
#include <vector>
namespace igl
{
namespace opengl
{
namespace glfw
{
// Abstract class for plugins
// All plugins MUST have this class as their parent and may implement any/all
// the callbacks marked `virtual` here.
//
// /////For an example of a basic plugins see plugins/skeleton.h
//
// Return value of callbacks: returning true to any of the callbacks tells
// Viewer that the event has been handled and that it should not be passed to
// other plugins or to other internal functions of Viewer
// Forward declaration of the viewer
class Viewer;
class ViewerPlugin
{
public:
IGL_INLINE ViewerPlugin()
{plugin_name = "dummy";}
virtual ~ViewerPlugin(){}
// This function is called when the viewer is initialized (no mesh will be loaded at this stage)
IGL_INLINE virtual void init(Viewer *_viewer)
namespace opengl
{
viewer = _viewer;
namespace glfw
{
// Forward declaration of the viewer
class Viewer;
/// Abstract class for plugins
/// All plugins MUST have this class as their parent and may implement any/all
/// the callbacks marked `virtual` here.
///
/// Return value of callbacks: returning true to any of the callbacks tells
/// Viewer that the event has been handled and that it should not be passed to
/// other plugins or to other internal functions of Viewer
class ViewerPlugin
{
public:
IGL_INLINE ViewerPlugin() {plugin_name = "dummy";}
virtual ~ViewerPlugin(){}
/// This function is called when the viewer is initialized (no mesh will be loaded at this stage)
IGL_INLINE virtual void init(Viewer *_viewer) { viewer = _viewer; }
/// This function is called before shutdown
IGL_INLINE virtual void shutdown() { }
/// This function is called before a mesh is loaded
IGL_INLINE virtual bool load(std::string /*filename*/) { return false; }
/// This function is called before a mesh is saved
IGL_INLINE virtual bool save(std::string /*filename*/) { return false; }
/// This function is called when the scene is serialized
IGL_INLINE virtual bool serialize(std::vector<char>& /*buffer*/) const
{ return false; }
/// This function is called when the scene is deserialized
IGL_INLINE virtual bool deserialize(const std::vector<char>& /*buffer*/)
{ return false; }
/// Runs immediately after a new mesh has been loaded.
IGL_INLINE virtual bool post_load() { return false; }
/// This function is called before the draw procedure of Viewer
IGL_INLINE virtual bool pre_draw() { return false; }
/// This function is called after the draw procedure of Viewer
IGL_INLINE virtual bool post_draw() { return false; }
/// This function is called after the window has been resized
IGL_INLINE virtual void post_resize(int /*w*/, int /*h*/) { }
IGL_INLINE virtual bool mouse_down(int /*button*/, int /*modifier*/)
{ return false; }
IGL_INLINE virtual bool mouse_up(int /*button*/, int /*modifier*/)
{ return false; }
IGL_INLINE virtual bool mouse_move(int /*mouse_x*/, int /*mouse_y*/)
{ return false; }
IGL_INLINE virtual bool mouse_scroll(float /*delta_y*/)
{ return false; }
IGL_INLINE virtual bool key_pressed(unsigned int /*key*/, int /*modifiers*/)
{ return false; }
IGL_INLINE virtual bool key_down(int /*key*/, int /*modifiers*/)
{ return false; }
IGL_INLINE virtual bool key_up(int /*key*/, int /*modifiers*/)
{ return false; }
std::string plugin_name;
protected:
// Pointer to the main Viewer class
Viewer *viewer;
};
namespace serialization
{
inline void serialize(const ViewerPlugin& obj,std::vector<char>& buffer)
{
obj.serialize(buffer);
}
inline void deserialize(ViewerPlugin& obj,const std::vector<char>& buffer)
{
obj.deserialize(buffer);
}
}
}
}
// This function is called before shutdown
IGL_INLINE virtual void shutdown()
{
}
// This function is called before a mesh is loaded
IGL_INLINE virtual bool load(std::string filename)
{
return false;
}
// This function is called before a mesh is saved
IGL_INLINE virtual bool save(std::string filename)
{
return false;
}
// This function is called when the scene is serialized
IGL_INLINE virtual bool serialize(std::vector<char>& buffer) const
{
return false;
}
// This function is called when the scene is deserialized
IGL_INLINE virtual bool deserialize(const std::vector<char>& buffer)
{
return false;
}
// Runs immediately after a new mesh has been loaded.
IGL_INLINE virtual bool post_load()
{
return false;
}
// This function is called before the draw procedure of Preview3D
IGL_INLINE virtual bool pre_draw()
{
return false;
}
// This function is called after the draw procedure of Preview3D
IGL_INLINE virtual bool post_draw()
{
return false;
}
// This function is called after the window has been resized
IGL_INLINE virtual void post_resize(int w, int h)
{
}
// This function is called when the mouse button is pressed
// - button can be GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON or GLUT_RIGHT_BUTTON
// - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT;
IGL_INLINE virtual bool mouse_down(int button, int modifier)
{
return false;
}
// This function is called when the mouse button is released
// - button can be GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON or GLUT_RIGHT_BUTTON
// - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT;
IGL_INLINE virtual bool mouse_up(int button, int modifier)
{
return false;
}
// This function is called every time the mouse is moved
// - mouse_x and mouse_y are the new coordinates of the mouse pointer in screen coordinates
IGL_INLINE virtual bool mouse_move(int mouse_x, int mouse_y)
{
return false;
}
// This function is called every time the scroll wheel is moved
// Note: this callback is not working with every glut implementation
IGL_INLINE virtual bool mouse_scroll(float delta_y)
{
return false;
}
// This function is called when a keyboard key is pressed. Unlike key_down
// this will reveal the actual character being sent (not just the physical
// key)
// - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT;
IGL_INLINE virtual bool key_pressed(unsigned int key, int modifiers)
{
return false;
}
// This function is called when a keyboard key is down
// - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT;
IGL_INLINE virtual bool key_down(int key, int modifiers)
{
return false;
}
// This function is called when a keyboard key is release
// - modifiers is a bitfield that might one or more of the following bits Preview3D::NO_KEY, Preview3D::SHIFT, Preview3D::CTRL, Preview3D::ALT;
IGL_INLINE virtual bool key_up(int key, int modifiers)
{
return false;
}
std::string plugin_name;
protected:
// Pointer to the main Viewer class
Viewer *viewer;
};
namespace serialization
{
inline void serialize(const ViewerPlugin& obj,std::vector<char>& buffer)
{
obj.serialize(buffer);
}
inline void deserialize(ViewerPlugin& obj,const std::vector<char>& buffer)
{
obj.deserialize(buffer);
}
}
}
}
}
#endif

View File

@@ -5,11 +5,11 @@
IGL_INLINE bool igl::opengl::glfw::background_window(GLFWwindow* & window)
{
if(!glfwInit()) return false;
glfwSetErrorCallback([](int id,const char* m){std::cerr<<m<<std::endl;});
glfwSetErrorCallback([](int /*id*/,const char* m){std::cerr<<m<<std::endl;});
glfwWindowHint(GLFW_SAMPLES, 4);
// Use 3.2 core profile
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
// Use 4.1 core profile
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// Use background window

View File

@@ -11,17 +11,16 @@ namespace igl
{
namespace glfw
{
// Create a background window with a valid core profile opengl context
// set to current.
//
// After you're finished with this window you may call
// `glfwDestroyWindow(window)`
//
// After you're finished with glfw you should call `glfwTerminate()`
//
// Outputs:
// window pointer to glfw window
// Returns true iff success
/// Create a background window with a valid core profile opengl context
/// set to current.
///
/// After you're finished with this window you may call
/// `glfwDestroyWindow(window)`
///
/// After you're finished with glfw you should call `glfwTerminate()`
///
/// @param[out] window pointer to glfw window
/// @return true iff success
IGL_INLINE bool background_window(GLFWwindow* & window);
}
}

View File

@@ -9,11 +9,13 @@
#define IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H
////////////////////////////////////////////////////////////////////////////////
#include <imgui/imgui.h>
#include "ImGuiTraits.h"
#include <imgui.h>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
#include <cstddef>
////////////////////////////////////////////////////////////////////////////////
// Extend ImGui by populating its namespace directly
@@ -25,38 +27,38 @@ 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;
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());
if (values.empty()) { return false; }
return Combo(label, idx, vector_getter,
static_cast<void*>(&values), values.size());
}
inline bool Combo(const char* label, int* idx, std::function<const char *(int)> getter, int items_count)
{
auto func = [](void* data, int i, const char** out_text) {
auto &getter = *reinterpret_cast<std::function<const char *(int)> *>(data);
const char *s = getter(i);
if (s) { *out_text = s; return true; }
else { return false; }
};
return Combo(label, idx, func, reinterpret_cast<void *>(&getter), items_count);
auto func = [](void* data, int i, const char** out_text) {
auto &getter = *reinterpret_cast<std::function<const char *(int)> *>(data);
const char *s = getter(i);
if (s) { *out_text = s; return true; }
else { return false; }
};
return Combo(label, idx, func, reinterpret_cast<void *>(&getter), items_count);
}
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());
if (values.empty()) { return false; }
return ListBox(label, idx, vector_getter,
static_cast<void*>(&values), values.size());
}
inline bool InputText(const char* label, std::string &str, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL)
inline bool InputText(const char* label, std::string &str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL)
{
char buf[1024];
std::fill_n(buf, 1024, 0);
@@ -69,6 +71,45 @@ inline bool InputText(const char* label, std::string &str, ImGuiInputTextFlags f
return false;
}
// template<typename T>
// inline bool DragScalar(const char *label, T* value, void* v, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f)
// {
// const char *fmt = format;
// if (format == nullptr) {
// fmt = ImGuiDataTypeTraits<T>::format;
// }
// return DragScalar(label, ImGuiDataTypeTraits<T>::value, value, &min, &max, fmt);
// }
// template<typename T>
// inline bool InputScalar(const char *label, T* value, T min = 0, T max = 0, const char* format = nulltptr)
// {
// const char *fmt = format;
// if (format == nullptr) {
// fmt = ImGuiDataTypeTraits<T>::format;
// }
// return InputScalar(label, ImGuiDataTypeTraits<T>::value, value, &min, &max, fmt);
// }
template<typename T>
inline bool SliderScalar(const char *label, T* value, T min = 0, T max = 0, const char* format = "")
{
const char *fmt = format;
if (format == nullptr) {
fmt = ImGuiDataTypeTraits<T>::format;
}
return SliderScalar(label, ImGuiDataTypeTraits<T>::value, value, &min, &max, fmt);
}
template<typename Getter, typename Setter>
inline bool Checkbox(const char* label, Getter get, Setter set)
{
bool value = get();
bool ret = ImGui::Checkbox(label, &value);
set(value);
return ret;
}
} // namespace ImGui
#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H

View File

@@ -1,5 +1,6 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2022 Alec Jacobson <alecjacobson@gmail.com>
// Copyright (C) 2018 Jérémie Dumas <jeremie.dumas@ens-lyon.org>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
@@ -7,13 +8,9 @@
// obtain one at http://mozilla.org/MPL/2.0/.
////////////////////////////////////////////////////////////////////////////////
#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 "ImGuiHelpers.h"
#include <imgui.h>
#include <iostream>
////////////////////////////////////////////////////////////////////////////////
namespace igl
{
@@ -24,124 +21,14 @@ namespace glfw
namespace imgui
{
IGL_INLINE void ImGuiMenu::init(igl::opengl::glfw::Viewer *_viewer)
// Is this needed?
IGL_INLINE void ImGuiMenu::init( Viewer *_viewer, ImGuiPlugin *_plugin)
{ viewer = _viewer; plugin = _plugin; }
IGL_INLINE void ImGuiMenu::shutdown() { }
IGL_INLINE void ImGuiMenu::draw()
{
ViewerPlugin::init(_viewer);
// Setup ImGui binding
if (_viewer)
{
if (context_ == nullptr)
{
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)
{
hidpi_scaling_ = hidpi_scaling();
pixel_ratio_ = pixel_ratio();
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
io.Fonts->AddFontFromMemoryCompressedTTF(droid_sans_compressed_data,
droid_sans_compressed_size, font_size * hidpi_scaling_);
io.FontGlobalScale = 1.0 / pixel_ratio_;
}
IGL_INLINE void ImGuiMenu::shutdown()
{
// Cleanup
ImGui_ImplGlfwGL3_Shutdown();
ImGui::DestroyContext(context_);
context_ = nullptr;
}
IGL_INLINE bool ImGuiMenu::pre_draw()
{
glfwPollEvents();
// Check whether window dpi has changed
float scaling = hidpi_scaling();
if (std::abs(scaling - hidpi_scaling_) > 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 (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_window();
// Viewer settings
if (callback_draw_viewer_window) { callback_draw_viewer_window(); }
else { draw_viewer_window(); }
@@ -153,10 +40,8 @@ IGL_INLINE void ImGuiMenu::draw_menu()
IGL_INLINE void ImGuiMenu::draw_viewer_window()
{
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));
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiCond_FirstUseEver);
bool _viewer_menu_visible = true;
ImGui::Begin(
"Viewer", &_viewer_menu_visible,
@@ -175,7 +60,7 @@ IGL_INLINE void ImGuiMenu::draw_viewer_menu()
// Workspace
if (ImGui::CollapsingHeader("Workspace", ImGuiTreeNodeFlags_DefaultOpen))
{
float w = ImGui::GetContentRegionAvailWidth();
float w = ImGui::GetContentRegionAvail().x;
float p = ImGui::GetStyle().FramePadding.x;
if (ImGui::Button("Load##Workspace", ImVec2((w-p)/2.f, 0)))
{
@@ -191,7 +76,7 @@ IGL_INLINE void ImGuiMenu::draw_viewer_menu()
// Mesh
if (ImGui::CollapsingHeader("Mesh", ImGuiTreeNodeFlags_DefaultOpen))
{
float w = ImGui::GetContentRegionAvailWidth();
float w = ImGui::GetContentRegionAvail().x;
float p = ImGui::GetStyle().FramePadding.x;
if (ImGui::Button("Load##Mesh", ImVec2((w-p)/2.f, 0)))
{
@@ -209,7 +94,7 @@ IGL_INLINE void ImGuiMenu::draw_viewer_menu()
{
if (ImGui::Button("Center object", ImVec2(-1, 0)))
{
viewer->core.align_camera_center(viewer->data().V, viewer->data().F);
viewer->core().align_camera_center(viewer->data().V, viewer->data().F);
}
if (ImGui::Button("Snap canonical view", ImVec2(-1, 0)))
{
@@ -218,54 +103,63 @@ IGL_INLINE void ImGuiMenu::draw_viewer_menu()
// Zoom
ImGui::PushItemWidth(80 * menu_scaling());
ImGui::DragFloat("Zoom", &(viewer->core.camera_zoom), 0.05f, 0.1f, 20.0f);
ImGui::DragFloat("Zoom", &(viewer->core().camera_zoom), 0.05f, 0.1f, 20.0f);
// Select rotation type
int rotation_type = static_cast<int>(viewer->core.rotation_type);
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 != 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;
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)
else if (viewer->core().rotation_type == RT::ROTATION_TYPE_NO_ROTATION)
{
viewer->core.trackball_angle = trackball_angle;
viewer->core.orthographic = orthographic;
viewer->core().trackball_angle = trackball_angle;
viewer->core().orthographic = orthographic;
}
viewer->core.set_rotation_type(new_type);
viewer->core().set_rotation_type(new_type);
}
}
// Orthographic view
ImGui::Checkbox("Orthographic view", &(viewer->core.orthographic));
ImGui::Checkbox("Orthographic view", &(viewer->core().orthographic));
ImGui::PopItemWidth();
}
// Helper for setting viewport specific mesh options
auto make_checkbox = [&](const char *label, unsigned int &option)
{
return ImGui::Checkbox(label,
[&]() { return viewer->core().is_set(option); },
[&](bool value) { return viewer->core().set(option, value); }
);
};
// 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);
viewer->data().dirty = MeshGL::DIRTY_ALL;
}
ImGui::Checkbox("Show texture", &(viewer->data().show_texture));
make_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(),
make_checkbox("Show overlay", viewer->data().show_overlay);
make_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);
@@ -277,107 +171,14 @@ IGL_INLINE void ImGuiMenu::draw_viewer_menu()
// 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));
make_checkbox("Wireframe", viewer->data().show_lines);
make_checkbox("Fill", viewer->data().show_faces);
make_checkbox("Show vertex labels", viewer->data().show_vertex_labels);
make_checkbox("Show faces labels", viewer->data().show_face_labels);
make_checkbox("Show extra labels", viewer->data().show_custom_labels);
}
}
IGL_INLINE void ImGuiMenu::draw_labels_window()
{
// 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::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
ImGui::Begin("ViewerLabels", &visible,
ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoScrollWithMouse
| ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_NoInputs);
for (const auto & data : viewer->data_list)
{
draw_labels(data);
}
ImGui::End();
ImGui::PopStyleColor();
ImGui::PopStyleVar();
}
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)
{
pos += normal * 0.005f * viewer->core.object_scale;
Eigen::Vector3f coord = igl::project(Eigen::Vector3f(pos.cast<float>()),
viewer->core.view, 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]/pixel_ratio_, (viewer->core.viewport[3] - coord[1])/pixel_ratio_),
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

View File

@@ -1,5 +1,6 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2022 Alec Jacobson <alecjacobson@gmail.com>
// Copyright (C) 2018 Jérémie Dumas <jeremie.dumas@ens-lyon.org>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
@@ -8,103 +9,47 @@
#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;
#include "ImGuiPlugin.h"
#include "ImGuiWidget.h"
#include "../../../igl_inline.h"
#include <memory>
namespace igl
{
namespace opengl
{
namespace glfw
{
namespace imgui
{
namespace opengl
{
namespace glfw
{
namespace imgui
{
/// Widget for a menu bar and a viewer window.
class ImGuiMenu : public ImGuiWidget
{
public:
IGL_INLINE virtual void init(Viewer *_viewer, ImGuiPlugin *_plugin) override;
IGL_INLINE virtual void shutdown() override;
IGL_INLINE virtual void draw() override;
// Can be overwritten by `callback_draw_viewer_window`
IGL_INLINE virtual void draw_viewer_window();
// Can be overwritten by `callback_draw_viewer_menu`
IGL_INLINE virtual void draw_viewer_menu();
// Can be overwritten by `callback_draw_custom_window`
IGL_INLINE virtual void draw_custom_window() { }
// Customizable callbacks
std::function<void(void)> callback_draw_viewer_window;
std::function<void(void)> callback_draw_viewer_menu;
std::function<void(void)> callback_draw_custom_window;
float menu_scaling()
{ return plugin->hidpi_scaling() / plugin->pixel_ratio(); }
};
class ImGuiMenu : public igl::opengl::glfw::ViewerPlugin
{
protected:
// Hidpi scaling to be used for text rendering.
float hidpi_scaling_;
// Ratio between the framebuffer size and the window size.
// May be different from the hipdi scaling!
float pixel_ratio_;
// ImGui Context
ImGuiContext * 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();
// Can be overwritten by `callback_draw_viewer_window`
IGL_INLINE virtual void draw_viewer_window();
// Can be overwritten by `callback_draw_viewer_menu`
IGL_INLINE virtual void draw_viewer_menu();
// Can be overwritten by `callback_draw_custom_window`
IGL_INLINE virtual void draw_custom_window() { }
// Easy-to-customize callbacks
std::function<void(void)> callback_draw_viewer_window;
std::function<void(void)> callback_draw_viewer_menu;
std::function<void(void)> callback_draw_custom_window;
IGL_INLINE void draw_labels_window();
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 hidpi_scaling_ / pixel_ratio_; }
};
} // end namespace
} // end namespace
} // end namespace
} // end namespace
}
}
}
}
#ifndef IGL_STATIC_LIBRARY
# include "ImGuiMenu.cpp"
#endif
#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIMENU_H
#endif

View File

@@ -0,0 +1,211 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2022 Alec Jacobson <alecjacobson@gmail.com>
// Copyright (C) 2018 Jérémie Dumas <jeremie.dumas@ens-lyon.org>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
////////////////////////////////////////////////////////////////////////////////
#include "ImGuiPlugin.h"
#include "ImGuiHelpers.h"
#include "../../../project.h"
#include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_opengl3.h>
#include <imgui.h>
#include <imgui_fonts_droid_sans.h>
#include <GLFW/glfw3.h>
#include <iostream>
namespace igl
{
namespace opengl
{
namespace glfw
{
namespace imgui
{
IGL_INLINE void ImGuiPlugin::init(igl::opengl::glfw::Viewer *_viewer)
{
ViewerPlugin::init(_viewer);
// Setup ImGui binding
if (_viewer)
{
IMGUI_CHECKVERSION();
if (!context_)
{
// Single global context by default, but can be overridden by the user
static ImGuiContext * __global_context = ImGui::CreateContext();
context_ = __global_context;
}
const char* glsl_version = "#version 150";
ImGui_ImplGlfw_InitForOpenGL(viewer->window, false);
ImGui_ImplOpenGL3_Init(glsl_version);
ImGui::GetIO().IniFilename = nullptr;
ImGui::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
style.FrameRounding = 5.0f;
reload_font();
}
init_widgets();
}
IGL_INLINE void ImGuiPlugin::init_widgets()
{
// Init all widgets
for(auto & widget : widgets) { widget->init(viewer, this); }
}
IGL_INLINE void ImGuiPlugin::reload_font(int font_size)
{
hidpi_scaling_ = hidpi_scaling();
pixel_ratio_ = pixel_ratio();
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
io.Fonts->AddFontFromMemoryCompressedTTF(droid_sans_compressed_data,
droid_sans_compressed_size, font_size * hidpi_scaling_);
io.FontGlobalScale = 1.0 / pixel_ratio_;
}
IGL_INLINE void ImGuiPlugin::shutdown()
{
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
// User is responsible for destroying context if a custom context is given
// ImGui::DestroyContext(*context_);
}
IGL_INLINE bool ImGuiPlugin::pre_draw()
{
glfwPollEvents();
// Check whether window dpi has changed
float scaling = hidpi_scaling();
if (std::abs(scaling - hidpi_scaling_) > 1e-5)
{
reload_font();
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
return false;
}
IGL_INLINE bool ImGuiPlugin::post_draw()
{
for(auto & widget : widgets){ widget->draw(); }
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
return false;
}
IGL_INLINE void ImGuiPlugin::post_resize(int width, int height)
{
if (context_)
{
ImGui::GetIO().DisplaySize.x = float(width);
ImGui::GetIO().DisplaySize.y = float(height);
}
}
// Mouse IO
IGL_INLINE bool ImGuiPlugin::mouse_down(int button, int modifier)
{
ImGui_ImplGlfw_MouseButtonCallback(viewer->window, button, GLFW_PRESS, modifier);
if(ImGui::GetIO().WantCaptureMouse){ return true; }
for( auto & widget : widgets)
{
if(widget->mouse_down(button, modifier)) { return true; }
}
return false;
}
IGL_INLINE bool ImGuiPlugin::mouse_up(int button, int modifier)
{
//return ImGui::GetIO().WantCaptureMouse;
// !! Should not steal mouse up
for( auto & widget : widgets)
{
widget->mouse_up(button, modifier);
}
return false;
}
IGL_INLINE bool ImGuiPlugin::mouse_move(int mouse_x, int mouse_y)
{
if(ImGui::GetIO().WantCaptureMouse){ return true; }
for( auto & widget : widgets)
{
if(widget->mouse_move(mouse_x, mouse_y)) { return true; }
}
return false;
}
IGL_INLINE bool ImGuiPlugin::mouse_scroll(float delta_y)
{
ImGui_ImplGlfw_ScrollCallback(viewer->window, 0.f, delta_y);
return ImGui::GetIO().WantCaptureMouse;
}
// Keyboard IO
IGL_INLINE bool ImGuiPlugin::key_pressed(unsigned int key, int modifiers)
{
ImGui_ImplGlfw_CharCallback(nullptr, key);
if(ImGui::GetIO().WantCaptureKeyboard) { return true; }
for(auto & widget : widgets)
{
if(widget->key_pressed(key,modifiers)) {return true; }
}
return false;
}
IGL_INLINE bool ImGuiPlugin::key_down(int key, int modifiers)
{
ImGui_ImplGlfw_KeyCallback(viewer->window, key, 0, GLFW_PRESS, modifiers);
if(ImGui::GetIO().WantCaptureKeyboard) { return true; }
for(auto & widget : widgets)
{
if(widget->key_down(key,modifiers)) {return true; }
}
return false;
}
IGL_INLINE bool ImGuiPlugin::key_up(int key, int modifiers)
{
ImGui_ImplGlfw_KeyCallback(viewer->window, key, 0, GLFW_RELEASE, modifiers);
if(ImGui::GetIO().WantCaptureKeyboard) { return true; }
for(auto & widget : widgets)
{
if(widget->key_up(key,modifiers)) { return true; }
}
return false;
}
IGL_INLINE float ImGuiPlugin::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 ImGuiPlugin::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

View File

@@ -0,0 +1,78 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2022 Alec Jacobson <alecjacobson@gmail.com>
// Copyright (C) 2018 Jérémie Dumas <jeremie.dumas@ens-lyon.org>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUIPLUGIN_H
#define IGL_OPENGL_GLFW_IMGUI_IMGUIPLUGIN_H
#include "../Viewer.h"
#include "../ViewerPlugin.h"
#include "../../../igl_inline.h"
#include "ImGuiWidget.h"
// Forward declarations
struct ImGuiContext;
namespace igl
{
namespace opengl
{
namespace glfw
{
namespace imgui
{
// Forward declaration of child widget abstract type
class ImGuiWidget;
/// Plugin for the viewer to enable imgui widgets
class ImGuiPlugin : public igl::opengl::glfw::ViewerPlugin
{
protected:
// Hidpi scaling to be used for text rendering.
float hidpi_scaling_;
// Ratio between the framebuffer size and the window size.
// May be different from the hipdi scaling!
float pixel_ratio_;
// ImGui Context
ImGuiContext * context_ = nullptr;
public:
// List of registered widgets
std::vector<ImGuiWidget*> widgets;
public:
IGL_INLINE virtual void init(igl::opengl::glfw::Viewer *_viewer) override;
IGL_INLINE void init_widgets();
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;
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;
IGL_INLINE void draw_text(
const Eigen::Vector3d pos,
const Eigen::Vector3d normal,
const std::string &text,
const Eigen::Vector4f color = Eigen::Vector4f(0,0,0.04,1)); // old default color
IGL_INLINE float pixel_ratio();
IGL_INLINE float hidpi_scaling();
};
}
}
}
}
#ifndef IGL_STATIC_LIBRARY
# include "ImGuiPlugin.cpp"
#endif
#endif

View File

@@ -0,0 +1,69 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2018 Jérémie Dumas <jeremie.dumas@ens-lyon.org>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUITRAITS_H
#define IGL_OPENGL_GLFW_IMGUI_IMGUITRAITS_H
#include <imgui.h>
// Extend ImGui by populating its namespace directly
namespace ImGui
{
// Infer ImGuiDataType enum based on actual type
template<typename T>
class ImGuiDataTypeTraits
{
static const ImGuiDataType value; // link error
static const char * format;
};
template<>
class ImGuiDataTypeTraits<int>
{
static constexpr ImGuiDataType value = ImGuiDataType_S32;
static constexpr const char *format = "%d";
};
template<>
class ImGuiDataTypeTraits<unsigned int>
{
static constexpr ImGuiDataType value = ImGuiDataType_U32;
static constexpr const char *format = "%u";
};
template<>
class ImGuiDataTypeTraits<long long>
{
static constexpr ImGuiDataType value = ImGuiDataType_S64;
static constexpr const char *format = "%lld";
};
template<>
class ImGuiDataTypeTraits<unsigned long long>
{
static constexpr ImGuiDataType value = ImGuiDataType_U64;
static constexpr const char *format = "%llu";
};
template<>
class ImGuiDataTypeTraits<float>
{
static constexpr ImGuiDataType value = ImGuiDataType_Float;
static constexpr const char *format = "%.3f";
};
template<>
class ImGuiDataTypeTraits<double>
{
static constexpr ImGuiDataType value = ImGuiDataType_Double;
static constexpr const char *format = "%.6f";
};
} // namespace ImGui
#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H

View File

@@ -0,0 +1,65 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2022 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_OPENGL_GLFW_IMGUI_IMGUIWIDGET_H
#define IGL_OPENGL_GLFW_IMGUI_IMGUIWIDGET_H
#include "ImGuiPlugin.h"
#include "ImGuiWidget.h"
#include "../../../igl_inline.h"
#include <memory>
namespace igl
{
namespace opengl
{
namespace glfw
{
class Viewer;
namespace imgui
{
// Forward declaration of the parent plugin
class ImGuiPlugin;
/// Abstract class for imgui "widgets". A widget is something that uses
/// imgui, but doesn't own the entire imgui IO stack: the single
/// ImGuiPlugin owns that and widgets are registered with it.
class ImGuiWidget
{
public:
IGL_INLINE ImGuiWidget(){ name = "dummy"; }
virtual ~ImGuiWidget(){}
IGL_INLINE virtual void init(Viewer *_viewer, ImGuiPlugin *_plugin)
{ viewer = _viewer; plugin = _plugin; }
IGL_INLINE virtual void shutdown() {}
IGL_INLINE virtual void draw() {}
IGL_INLINE virtual bool mouse_down(int /*button*/, int /*modifier*/)
{ return false;}
IGL_INLINE virtual bool mouse_up(int /*button*/, int /*modifier*/)
{ return false;}
IGL_INLINE virtual bool mouse_move(int /*mouse_x*/, int /*mouse_y*/)
{ return false;}
IGL_INLINE virtual bool key_pressed(unsigned int /*key*/, int /*modifiers*/)
{ return false;}
IGL_INLINE virtual bool key_down(int /*key*/, int /*modifiers*/)
{ return false;}
IGL_INLINE virtual bool key_up(int /*key*/, int /*modifiers*/)
{ return false;}
std::string name;
protected:
// Pointer to ImGuiPlugin's parent viewer
Viewer *viewer;
// Pointer to parent ImGuiPlugin class
ImGuiPlugin *plugin;
};
}
}
}
}
#endif

View File

@@ -0,0 +1,43 @@
#include "ImGuizmoWidget.h"
#include <imgui.h>
#include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_opengl3.h>
#include <imgui_fonts_droid_sans.h>
#include <GLFW/glfw3.h>
namespace igl{ namespace opengl{ namespace glfw{ namespace imgui{
IGL_INLINE void ImGuizmoWidget::init(Viewer *_viewer, ImGuiPlugin *_plugin)
{
ImGuiWidget::init(_viewer,_plugin);
}
IGL_INLINE void ImGuizmoWidget::draw()
{
if(!visible){ return; }
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
ImGuizmo::BeginFrame();
ImGui::PopStyleVar();
// Don't draw the Viewer's default menu: draw just the ImGuizmo
Eigen::Matrix4f view = (viewer->core().view / viewer->core().camera_zoom);
Eigen::Matrix4f proj = viewer->core().proj;
if(viewer->core().orthographic){ view(2,3) -= 1000;/* Hack depth */ }
// ImGuizmo doesn't like a lot of scaling in view, shift it to T
const float z = viewer->core().camera_base_zoom;
const Eigen::Matrix4f S =
(Eigen::Matrix4f()<< z,0,0,0, 0,z,0,0, 0,0,z,0, 0,0,0,1).finished();
view = (view * S.inverse()).eval();
const Eigen::Matrix4f T0 = T;
T = (S * T).eval();
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(
view.data(),proj.data(),operation,ImGuizmo::LOCAL,T.data(),NULL,NULL);
// invert scaling that was shifted onto T
T = (S.inverse() * T).eval();
const float diff = (T-T0).array().abs().maxCoeff();
// Only call if actually changed; otherwise, triggers on all mouse events
if( diff > 1e-7) { callback(T); }
}
}}}}

View File

@@ -0,0 +1,36 @@
#ifndef IGL_OPENGL_GFLW_IMGUI_IMGUIZMOPLUGIN_H
#define IGL_OPENGL_GFLW_IMGUI_IMGUIZMOPLUGIN_H
#include "../../../igl_inline.h"
#include "ImGuiMenu.h"
#include <imgui.h>
#include <imgui_internal.h>
#include <ImGuizmo.h>
#include <Eigen/Dense>
namespace igl{ namespace opengl{ namespace glfw{ namespace imgui{
/// Widget for a guizmo (3D transform manipulator)
class ImGuizmoWidget : public ImGuiWidget
{
public:
// callback(T) called when the stored transform T changes
std::function<void(const Eigen::Matrix4f &)> callback;
// Whether to display
bool visible = true;
// whether rotating, translating or scaling
ImGuizmo::OPERATION operation;
// stored transformation
Eigen::Matrix4f T;
// Initilize with rotate operation on an identity transform (at origin)
ImGuizmoWidget():operation(ImGuizmo::ROTATE),T(Eigen::Matrix4f::Identity()){};
IGL_INLINE virtual void init(Viewer *_viewer, ImGuiPlugin *_plugin) override;
IGL_INLINE virtual void draw() override;
};
}}}}
#ifndef IGL_STATIC_LIBRARY
# include "ImGuizmoWidget.cpp"
#endif
#endif

View File

@@ -0,0 +1,215 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2022 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "SelectionWidget.h"
#include <imgui.h>
#include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_opengl3.h>
#include <imgui_fonts_droid_sans.h>
#include <GLFW/glfw3.h>
#include "../../../PI.h"
namespace igl{ namespace opengl{ namespace glfw{ namespace imgui{
IGL_INLINE void SelectionWidget::init(Viewer *_viewer, ImGuiPlugin *_plugin)
{
ImGuiWidget::init(_viewer,_plugin);
std::cout<<R"(
igl::opengl::glfw::imgui::SelectionWidget usage:
[drag] Draw a 2D selection
l Turn on and toggle between lasso and polygonal lasso tool
M,m Turn on and toggle between rectangular and circular marquee tool
V,v Turn off interactive selection
)";
}
IGL_INLINE void SelectionWidget::draw()
{
if(mode == OFF){ return; }
// Is this call necessary?
ImGui::GetIO();
float width, height;
float highdpi = 1.0;
{
int fwidth, fheight;
glfwGetFramebufferSize(viewer->window, &fwidth, &fheight);
int iwidth, iheight;
glfwGetWindowSize(viewer->window, &iwidth, &iheight);
highdpi = float(iwidth)/float(fwidth);
// highdpi
width = (float)iwidth;
height = (float)iheight;
}
ImGui::SetNextWindowPos( ImVec2(0,0) );
ImGui::SetNextWindowSize(ImVec2(width,height), ImGuiCond_Always);
ImGui::Begin("testing", nullptr,
ImGuiWindowFlags_NoBackground
| ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_NoInputs);
ImDrawList* list = ImGui::GetWindowDrawList();
for(int pass = 0;pass<2;pass++)
{
for(auto & p : L)
{
list->PathLineTo({ highdpi*p(0),height-highdpi*p(1) });
}
const bool closed = !(mode==LASSO || mode==POLYGONAL_LASSO) || !(is_down || is_drawing);
if(pass == 0)
{
list->PathStroke(IM_COL32(255, 255, 255, 255), closed, 2);
}else
{
list->PathStroke(IM_COL32(0, 0, 0, 255), closed, 1);
}
}
ImGui::End();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
IGL_INLINE bool SelectionWidget::mouse_down(int /*button*/, int modifier)
{
if(mode == OFF || (modifier & IGL_MOD_ALT) ){ return false;}
is_down = true;
has_moved_since_down = false;
if(!is_drawing)
{
L.clear();
is_drawing = true;
}
M.row(0) = xy(viewer);
M.row(1) = M.row(0);
L.emplace_back(M.row(0));
return true;
}
IGL_INLINE bool SelectionWidget::mouse_up(int /*button*/, int /*modifier*/)
{
is_down = false;
// are we done? Check first and last lasso point (need at least 3 (2 real
// points + 1 mouse-mouse point))
if(is_drawing &&
(mode!=POLYGONAL_LASSO ||(L.size()>=3&&(L[0]-L[L.size()-1]).norm()<=10.0)))
{
if(callback){ callback();}
is_drawing = false;
}
return false;
}
IGL_INLINE bool SelectionWidget::mouse_move(int /*mouse_x*/, int /*mouse_y*/)
{
if(!is_drawing){ return false; }
if(!has_moved_since_down)
{
if(mode == POLYGONAL_LASSO) { L.emplace_back(L[L.size()-1]); }
has_moved_since_down = true;
}
M.row(1) = xy(viewer);
switch(mode)
{
case RECTANGULAR_MARQUEE:
rect(M,L);
break;
case ELLIPTICAL_MARQUEE:
circle(M,L);
break;
case POLYGONAL_LASSO:
// Over write last point
L[L.size()-1] = xy(viewer);
break;
case LASSO:
L.emplace_back(xy(viewer));
break;
default: assert(false);
}
return true;
}
IGL_INLINE bool SelectionWidget::key_pressed(unsigned int key, int /*modifiers*/)
{
Mode old = mode;
if(OFF_KEY.find(char(key)) != std::string::npos)
{
mode = OFF;
}else if(LASSO_KEY.find(char(key)) != std::string::npos)
{
if(mode == LASSO)
{
mode = POLYGONAL_LASSO;
}else/*if(mode == POLYGONAL_LASSO)*/
{
mode = LASSO;
}
}else if(MARQUEE_KEY.find(char(key)) != std::string::npos)
{
if(mode == RECTANGULAR_MARQUEE)
{
mode = ELLIPTICAL_MARQUEE;
}else/*if(mode == ELLIPTICAL_MARQUEE)*/
{
mode = RECTANGULAR_MARQUEE;
}
}
if(old != mode)
{
clear();
if(callback_post_mode_change){ callback_post_mode_change(old); }
return true;
}
return false;
}
IGL_INLINE void SelectionWidget::clear()
{
M.setZero();
L.clear();
is_drawing = false;
is_down = false;
};
IGL_INLINE void SelectionWidget::circle(const Eigen::Matrix<float,2,2> & M, std::vector<Eigen::RowVector2f> & L)
{
L.clear();
L.reserve(64);
const float r = (M.row(1)-M.row(0)).norm();
for(float th = 0;th<2.*igl::PI;th+=0.1)
{
L.emplace_back(M(0,0)+r*cos(th),M(0,1)+r*sin(th));
}
}
IGL_INLINE void SelectionWidget::rect(const Eigen::Matrix<float,2,2> & M, std::vector<Eigen::RowVector2f> & L)
{
L.resize(4);
L[0] = Eigen::RowVector2f(M(0,0),M(0,1));
L[1] = Eigen::RowVector2f(M(1,0),M(0,1));
L[2] = Eigen::RowVector2f(M(1,0),M(1,1));
L[3] = Eigen::RowVector2f(M(0,0),M(1,1));
}
IGL_INLINE Eigen::RowVector2f SelectionWidget::xy(const Viewer * vr)
{
return Eigen::RowVector2f(
vr->current_mouse_x,
vr->core().viewport(3) - vr->current_mouse_y);
}
}}}}

View File

@@ -0,0 +1,65 @@
#ifndef IGL_OPENGL_GFLW_IMGUI_SELECTIONWIDGET_H
#define IGL_OPENGL_GFLW_IMGUI_SELECTIONWIDGET_H
#include "../../../igl_inline.h"
#include "ImGuiWidget.h"
#include <Eigen/Dense>
#include <vector>
#include <string>
#include <functional>
namespace igl{ namespace opengl{ namespace glfw{ namespace imgui{
/// Widget for selecting a region of the screen
class SelectionWidget: public ImGuiWidget
{
public:
// customizable hotkeys
/// Hot key to start marquee
std::string MARQUEE_KEY = "Mm";
// leave 'L' for show_lines in viewer
/// Hot key to start lasso
std::string LASSO_KEY = "l";
/// Hot key to stop selection
std::string OFF_KEY = "Vv";
/// Selection modes
enum Mode
{
OFF = 0,
RECTANGULAR_MARQUEE = 1,
ELLIPTICAL_MARQUEE = 2,
POLYGONAL_LASSO = 3,
LASSO = 4,
NUM_MODES = 5
} mode = RECTANGULAR_MARQUEE;
bool is_down = false;
bool has_moved_since_down = false;
bool is_drawing = false;
// min and max corners of 2D rectangular marquee
Eigen::Matrix<float,2,2> M = Eigen::Matrix<float,2,2>::Zero();
// list of points of 2D lasso marquee
std::vector<Eigen::RowVector2f> L;
// callback called when slection is completed (usually on mouse_up)
std::function<void(void)> callback;
// callback called after mode is changed
std::function<void(Mode)> callback_post_mode_change;
IGL_INLINE virtual void init(Viewer *_viewer, ImGuiPlugin *_plugin) override;
IGL_INLINE virtual void draw() override;
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 key_pressed(unsigned int key, int modifiers) override;
IGL_INLINE void clear();
// helpers
IGL_INLINE static void circle(const Eigen::Matrix<float,2,2> & M, std::vector<Eigen::RowVector2f> & L);
IGL_INLINE static void rect(const Eigen::Matrix<float,2,2> & M, std::vector<Eigen::RowVector2f> & L);
IGL_INLINE static Eigen::RowVector2f xy(const Viewer * v);
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
}}}}
#ifndef IGL_STATIC_LIBRARY
#include "SelectionWidget.cpp"
#endif
#endif

View File

@@ -1,8 +1,7 @@
#ifdef IGL_OPENGL_4
#include "map_texture.h"
#include "background_window.h"
#include "../create_shader_program.h"
#include "../bind_vertex_attrib_array.h"
#include "../gl.h"
#include <GLFW/glfw3.h>
@@ -30,75 +29,45 @@ IGL_INLINE bool igl::opengl::glfw::map_texture(
template <typename DerivedV, typename DerivedF, typename DerivedU>
IGL_INLINE bool igl::opengl::glfw::map_texture(
const Eigen::MatrixBase<DerivedV> & _V,
const Eigen::MatrixBase<DerivedF> & _F,
const Eigen::MatrixBase<DerivedU> & _U,
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
const Eigen::MatrixBase<DerivedU> & U,
const unsigned char * in_data,
const int w,
const int h,
const int nc,
const int in_w,
const int in_h,
const int in_nc,
std::vector<unsigned char> & out_data,
int & out_w,
int & out_h,
int & out_nc)
{
const auto fail = [](const std::string msg)
GLenum format = -1;
switch(in_nc)
{
std::cerr<<msg<<std::endl;
glfwTerminate();
return false;
};
// Force inputs to be RowMajor at the cost of a copy
Eigen::Matrix<
double,
DerivedV::RowsAtCompileTime,
DerivedV::ColsAtCompileTime,
Eigen::RowMajor> V = _V.template cast<double>();
Eigen::Matrix<
double,
DerivedU::RowsAtCompileTime,
DerivedU::ColsAtCompileTime,
Eigen::RowMajor> U = _U.template cast<double>();
Eigen::Matrix<
int,
DerivedF::RowsAtCompileTime,
DerivedF::ColsAtCompileTime,
Eigen::RowMajor> F = _F.template cast<int>();
const int dim = U.cols();
GLFWwindow * window;
if(!background_window(window))
{
fail("Could not initialize glfw window");
case 1: format = GL_RED; break;
case 2: format = GL_RG; break;
case 3: format = GL_RGB; break;
case 4: format = GL_RGBA; break;
default: assert(false && "Unsupported number of channels"); break;
}
GLFWwindow * window = nullptr;
igl::opengl::glfw::background_window(window);
// Compile each shader
std::string vertex_shader = dim == 2 ?
R"(
#version 330 core
layout(location = 0) in vec2 position;
layout(location = 1) in vec2 tex_coord_v;
out vec2 tex_coord_f;
void main()
{
tex_coord_f = vec2(tex_coord_v.x,1.-tex_coord_v.y);
gl_Position = vec4( 2.*position.x-1., 2.*(1.-position.y)-1., 0.,1.);
}
)"
:
R"(
#version 330 core
std::string vertex_shader = R"(
#version 400
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 tex_coord_v;
out vec2 tex_coord_f;
void main()
{
tex_coord_f = vec2(tex_coord_v.x,1.-tex_coord_v.y);
gl_Position = vec4( 2.*position.x-1., 2.*(1.-position.y)-1., position.z,1.);
gl_Position = vec4(2.*position.x-1.,2.*(1.-position.y)-1., position.z,1.);
}
)"
;
std::string fragment_shader = R"(
#version 330 core
#version 400
layout(location = 0) out vec3 color;
uniform sampler2D tex;
in vec2 tex_coord_f;
@@ -109,53 +78,44 @@ void main()
)";
GLuint prog_id =
igl::opengl::create_shader_program(vertex_shader,fragment_shader,{});
glUniform1i(glGetUniformLocation(prog_id, "tex"),0);
using Scalar = float;
using MatrixXS3 = Eigen::Matrix<Scalar,Eigen::Dynamic,3,Eigen::RowMajor>;
MatrixXS3 V_vbo = MatrixXS3::Zero(V.rows(),3);
V_vbo.leftCols(V.cols()) = V.template cast<Scalar>();
MatrixXS3 U_vbo = MatrixXS3::Zero(U.rows(),3);
U_vbo.leftCols(U.cols()) = U.template cast<Scalar>();
Eigen::Matrix<unsigned, Eigen::Dynamic, 3, Eigen::RowMajor> F_vbo =
F.template cast<unsigned>();
// Generate and attach buffers to vertex array
glDisable(GL_CULL_FACE);
GLuint VAO = 0;
glGenVertexArrays(1,&VAO);
glBindVertexArray(VAO);
GLuint ibo,vbo,tbo;
GLuint ibo,vbo,tbo,tex;
glGenBuffers(1,&ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*F.size(), F.data(), GL_STATIC_DRAW);
glGenBuffers(1,&vbo);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER,vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(double)*U.size(), U.data(), GL_STATIC_DRAW);
glVertexAttribLPointer(0, U.cols(), GL_DOUBLE, U.cols() * sizeof(GLdouble), (GLvoid*)0);
glGenBuffers(1,&tbo);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER,tbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(double)*V.size(), V.data(), GL_STATIC_DRAW);
glVertexAttribLPointer(1, V.cols(), GL_DOUBLE, V.cols() * sizeof(GLdouble), (GLvoid*)0);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// Prepare texture
GLuint in_tex;
GLenum format;
{
format = nc==1 ? GL_RED : (nc==3 ? GL_RGB : (nc == 4 ? GL_RGBA : GL_FALSE));
glGenTextures(1, &in_tex);
glBindTexture(GL_TEXTURE_2D, in_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, w, h, 0,format, GL_UNSIGNED_BYTE, in_data);
}
glGenTextures(1,&tex);
out_w = in_w;
out_h = in_h;
glClearColor(0,0,1,1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0,0,out_w,out_h);
// Prepare framebuffer
GLuint fb = 0;
GLuint out_tex;
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
GLuint out_tex;
glGenTextures(1, &out_tex);
glBindTexture(GL_TEXTURE_2D, out_tex);
// always use float for internal storage
assert(out_nc == 3);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, out_w, out_h, 0,GL_RGB, GL_FLOAT, 0);
glTexImage2D(GL_TEXTURE_2D, 0,format, out_w, out_h, 0,format, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, out_tex, 0);
@@ -165,40 +125,39 @@ void main()
}
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
fail("framebuffer setup failed.");
glfwTerminate();
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, fb);
// clear screen and set viewport
glClearColor(0.0,1.0,0.0,0.);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0,0,out_w,out_h);
// Attach shader program
glUseProgram(prog_id);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, in_tex);
// Draw mesh as wireframe
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, format, in_w, in_h, 0, format, GL_UNSIGNED_BYTE, in_data);
glUniform1i(glGetUniformLocation(prog_id,"tex"), 0);
igl::opengl::bind_vertex_attrib_array(prog_id,"position", vbo, U_vbo, true);
igl::opengl::bind_vertex_attrib_array(prog_id,"tex_coord_v", tbo, V_vbo, true);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*F_vbo.size(), F_vbo.data(), GL_DYNAMIC_DRAW);
glDrawElements(GL_TRIANGLES, F.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
// Write into memory
assert(out_nc == 3);
out_nc = in_nc;
out_data.resize(out_nc*out_w*out_h);
glBindTexture(GL_TEXTURE_2D, out_tex);
glGetTexImage(GL_TEXTURE_2D, 0, format, GL_UNSIGNED_BYTE, &out_data[0]);
// OpenGL cleanup
glDeleteBuffers(1,&fb);
glDeleteBuffers(1,&ibo);
glDeleteBuffers(1,&vbo);
glDeleteBuffers(1,&tbo);
glDeleteTextures(1,&in_tex);
glDeleteTextures(1,&out_tex);
glDeleteVertexArrays(1,&VAO);
glUseProgram(0);
glDeleteProgram(prog_id);
// GLFW cleanup
glGetTexImage(GL_TEXTURE_2D, 0, format, GL_UNSIGNED_BYTE, out_data.data());
glfwDestroyWindow(window);
glfwTerminate();
return true;
}
@@ -206,6 +165,5 @@ void main()
// Explicit template instantiation
// generated by autoexplicit.sh
template bool igl::opengl::glfw::map_texture<Eigen::Matrix<double, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, -1, -1, 1, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> > const&, unsigned char const*, int, int, int, std::vector<unsigned char, std::allocator<unsigned char> >&);
template bool igl::opengl::glfw::map_texture<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, unsigned char const*, int, int, int, std::vector<unsigned char, std::allocator<unsigned char> >&, int&, int&, int&);
#endif
#endif // IGL_OPENGL_4

View File

@@ -1,8 +1,6 @@
#ifndef IGL_OPENGL_GLFW_MAP_TEXTURE_H
#define IGL_OPENGL_GLFW_MAP_TEXTURE_H
#ifdef IGL_OPENGL_4
#include "../../igl_inline.h"
#include <Eigen/Core>
#include <vector>
@@ -13,30 +11,22 @@ namespace igl
{
namespace glfw
{
// Given a mesh (V,F) in [0,1]² and new positions (U) and a texture image
// (in_data), _render_ a new image (out_data) of the same size.
// Inputs:
// V #V by 2 list of undeformed mesh vertex positions (matching texture)
// F #F by 3 list of mesh triangle indices into V
// U #U by 2 list of deformed vertex positions
// in_data w*h*nc array of color values, channels, then columns, then
// rows (e.g., what stbi_image returns and expects)
// w width
// h height
// nc number of channels
// Outputs:
// out_data h*w*nc list of output colors in same order as input
//
template <typename DerivedV, typename DerivedF, typename DerivedU>
IGL_INLINE bool map_texture(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
const Eigen::MatrixBase<DerivedU> & U,
const unsigned char * in_data,
const int w,
const int h,
const int nc,
std::vector<unsigned char> & out_data);
/// Given a mesh (V,F) in [0,1]² and new positions (U) and a texture image
/// (in_data), _render_ a new image (out_data) of the same size.
/// @param[in] V #V by 2 list of undeformed mesh vertex positions ∈ [0,1]²
/// @param[in] F #F by 3 list of mesh triangle indices into V
/// @param[in] U #U by 2 list of deformed vertex positions ∈ [0,1]²
/// @param[in] in_data w*h*nc array of color values, channels, then columns, then
/// rows (e.g., what stbi_image returns and expects)
/// @param[in] w width
/// @param[in] h height
/// @param[in] nc number of channels
/// @param[out] out_data h*w*nc list of output colors in same order as input
/// @param[out] out_w width of output image
/// @param[out] out_h height of output image
/// @param[out] out_nc number of channels of output image
///
/// \pre Seems like w,h should be equal.
template <typename DerivedV, typename DerivedF, typename DerivedU>
IGL_INLINE bool map_texture(
const Eigen::MatrixBase<DerivedV> & _V,
@@ -50,6 +40,17 @@ namespace igl
int & out_w,
int & out_h,
int & out_nc);
/// \overload
template <typename DerivedV, typename DerivedF, typename DerivedU>
IGL_INLINE bool map_texture(
const Eigen::MatrixBase<DerivedV> & V,
const Eigen::MatrixBase<DerivedF> & F,
const Eigen::MatrixBase<DerivedU> & U,
const unsigned char * in_data,
const int w,
const int h,
const int nc,
std::vector<unsigned char> & out_data);
}
}
}
@@ -58,6 +59,4 @@ namespace igl
# include "map_texture.cpp"
#endif
#endif // IGL_OPENGL_4
#endif