update bundled_deps

This commit is contained in:
QIDI TECH
2024-11-09 14:05:44 +08:00
parent 17c9bfd127
commit cfc606fea9
1662 changed files with 710 additions and 168 deletions

View File

@@ -0,0 +1,950 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@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 "Viewer.h"
#include <chrono>
#include <thread>
#include <Eigen/LU>
#include "../gl.h"
#include <GLFW/glfw3.h>
#include <cmath>
#include <cstdio>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <limits>
#include <cassert>
#include <igl/project.h>
#include <igl/get_seconds.h>
#include <igl/readOBJ.h>
#include <igl/readOFF.h>
#include <igl/adjacency_list.h>
#include <igl/writeOBJ.h>
#include <igl/writeOFF.h>
#include <igl/massmatrix.h>
#include <igl/file_dialog_open.h>
#include <igl/file_dialog_save.h>
#include <igl/quat_mult.h>
#include <igl/axis_angle_to_quat.h>
#include <igl/trackball.h>
#include <igl/two_axis_valuator_fixed_up.h>
#include <igl/snap_to_canonical_view_quat.h>
#include <igl/unproject.h>
#include <igl/serialize.h>
// Internal global variables used for glfw event handling
static igl::opengl::glfw::Viewer * __viewer;
static double highdpi = 1;
static double scroll_x = 0;
static double scroll_y = 0;
static void glfw_mouse_press(GLFWwindow* window, int button, int action, int modifier)
{
igl::opengl::glfw::Viewer::MouseButton mb;
if (button == GLFW_MOUSE_BUTTON_1)
mb = igl::opengl::glfw::Viewer::MouseButton::Left;
else if (button == GLFW_MOUSE_BUTTON_2)
mb = igl::opengl::glfw::Viewer::MouseButton::Right;
else //if (button == GLFW_MOUSE_BUTTON_3)
mb = igl::opengl::glfw::Viewer::MouseButton::Middle;
if (action == GLFW_PRESS)
__viewer->mouse_down(mb,modifier);
else
__viewer->mouse_up(mb,modifier);
}
static void glfw_error_callback(int error, const char* description)
{
fputs(description, stderr);
}
static void glfw_char_mods_callback(GLFWwindow* window, unsigned int codepoint, int modifier)
{
__viewer->key_pressed(codepoint, modifier);
}
static void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int modifier)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if (action == GLFW_PRESS)
__viewer->key_down(key, modifier);
else if(action == GLFW_RELEASE)
__viewer->key_up(key, modifier);
}
static void glfw_window_size(GLFWwindow* window, int width, int height)
{
int w = width*highdpi;
int h = height*highdpi;
__viewer->post_resize(w, h);
}
static void glfw_mouse_move(GLFWwindow* window, double x, double y)
{
__viewer->mouse_move(x*highdpi, y*highdpi);
}
static void glfw_mouse_scroll(GLFWwindow* window, double x, double y)
{
using namespace std;
scroll_x += x;
scroll_y += y;
__viewer->mouse_scroll(y);
}
static void glfw_drop_callback(GLFWwindow *window,int count,const char **filenames)
{
}
namespace igl
{
namespace opengl
{
namespace glfw
{
IGL_INLINE int Viewer::launch(bool resizable,bool fullscreen)
{
// TODO return values are being ignored...
launch_init(resizable,fullscreen);
launch_rendering(true);
launch_shut();
return EXIT_SUCCESS;
}
IGL_INLINE int Viewer::launch_init(bool resizable,bool fullscreen)
{
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
{
return EXIT_FAILURE;
}
glfwWindowHint(GLFW_SAMPLES, 8);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
if(fullscreen)
{
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
window = glfwCreateWindow(mode->width,mode->height,"libigl viewer",monitor,nullptr);
}
else
{
if (core.viewport.tail<2>().any()) {
window = glfwCreateWindow(core.viewport(2),core.viewport(3),"libigl viewer",nullptr,nullptr);
} else {
window = glfwCreateWindow(1280,800,"libigl viewer",nullptr,nullptr);
}
}
if (!window)
{
glfwTerminate();
return EXIT_FAILURE;
}
glfwMakeContextCurrent(window);
// Load OpenGL and its extensions
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
{
printf("Failed to load OpenGL and its extensions\n");
return(-1);
}
#if defined(DEBUG) || defined(_DEBUG)
printf("OpenGL Version %d.%d loaded\n", GLVersion.major, GLVersion.minor);
int major, minor, rev;
major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR);
minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR);
rev = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION);
printf("OpenGL version received: %d.%d.%d\n", major, minor, rev);
printf("Supported OpenGL is %s\n", (const char*)glGetString(GL_VERSION));
printf("Supported GLSL is %s\n", (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
#endif
glfwSetInputMode(window,GLFW_CURSOR,GLFW_CURSOR_NORMAL);
// Initialize FormScreen
__viewer = this;
// Register callbacks
glfwSetKeyCallback(window, glfw_key_callback);
glfwSetCursorPosCallback(window,glfw_mouse_move);
glfwSetWindowSizeCallback(window,glfw_window_size);
glfwSetMouseButtonCallback(window,glfw_mouse_press);
glfwSetScrollCallback(window,glfw_mouse_scroll);
glfwSetCharModsCallback(window,glfw_char_mods_callback);
glfwSetDropCallback(window,glfw_drop_callback);
// Handle retina displays (windows and mac)
int width, height;
glfwGetFramebufferSize(window, &width, &height);
int width_window, height_window;
glfwGetWindowSize(window, &width_window, &height_window);
highdpi = width/width_window;
glfw_window_size(window,width_window,height_window);
//opengl.init();
core.align_camera_center(data().V,data().F);
// Initialize IGL viewer
init();
return EXIT_SUCCESS;
}
IGL_INLINE bool Viewer::launch_rendering(bool loop)
{
// glfwMakeContextCurrent(window);
// Rendering loop
const int num_extra_frames = 5;
int frame_counter = 0;
while (!glfwWindowShouldClose(window))
{
double tic = get_seconds();
draw();
glfwSwapBuffers(window);
if(core.is_animating || frame_counter++ < num_extra_frames)
{
glfwPollEvents();
// In microseconds
double duration = 1000000.*(get_seconds()-tic);
const double min_duration = 1000000./core.animation_max_fps;
if(duration<min_duration)
{
std::this_thread::sleep_for(std::chrono::microseconds((int)(min_duration-duration)));
}
}
else
{
glfwWaitEvents();
frame_counter = 0;
}
if (!loop)
return !glfwWindowShouldClose(window);
}
return EXIT_SUCCESS;
}
IGL_INLINE void Viewer::launch_shut()
{
for(auto & data : data_list)
{
data.meshgl.free();
}
core.shut();
shutdown_plugins();
glfwDestroyWindow(window);
glfwTerminate();
return;
}
IGL_INLINE void Viewer::init()
{
core.init();
if (callback_init)
if (callback_init(*this))
return;
init_plugins();
}
IGL_INLINE void Viewer::init_plugins()
{
// Init all plugins
for (unsigned int i = 0; i<plugins.size(); ++i)
{
plugins[i]->init(this);
}
}
IGL_INLINE void Viewer::shutdown_plugins()
{
for (unsigned int i = 0; i<plugins.size(); ++i)
{
plugins[i]->shutdown();
}
}
IGL_INLINE Viewer::Viewer():
data_list(1),
selected_data_index(0),
next_data_id(1)
{
window = nullptr;
data_list.front().id = 0;
// Temporary variables initialization
down = false;
hack_never_moved = true;
scroll_position = 0.0f;
// Per face
data().set_face_based(false);
// C-style callbacks
callback_init = nullptr;
callback_pre_draw = nullptr;
callback_post_draw = nullptr;
callback_mouse_down = nullptr;
callback_mouse_up = nullptr;
callback_mouse_move = nullptr;
callback_mouse_scroll = nullptr;
callback_key_down = nullptr;
callback_key_up = nullptr;
callback_init_data = nullptr;
callback_pre_draw_data = nullptr;
callback_post_draw_data = nullptr;
callback_mouse_down_data = nullptr;
callback_mouse_up_data = nullptr;
callback_mouse_move_data = nullptr;
callback_mouse_scroll_data = nullptr;
callback_key_down_data = nullptr;
callback_key_up_data = nullptr;
#ifndef IGL_VIEWER_VIEWER_QUIET
const std::string usage(R"(igl::opengl::glfw::Viewer usage:
[drag] Rotate scene
A,a Toggle animation (tight draw loop)
F,f Toggle face based
I,i Toggle invert normals
L,l Toggle wireframe
O,o Toggle orthographic/perspective projection
T,t Toggle filled faces
Z Snap to canonical view
[,] Toggle between rotation control types (trackball, two-axis
valuator with fixed up, 2D mode with no rotation))
<,> Toggle between models
; Toggle vertex labels
: Toggle face labels)"
);
std::cout<<usage<<std::endl;
#endif
}
IGL_INLINE Viewer::~Viewer()
{
}
IGL_INLINE bool Viewer::load_mesh_from_file(
const std::string & mesh_file_name_string)
{
// first try to load it with a plugin
for (unsigned int i = 0; i<plugins.size(); ++i)
{
if (plugins[i]->load(mesh_file_name_string))
{
return true;
}
}
// Create new data slot and set to selected
if(!(data().F.rows() == 0 && data().V.rows() == 0))
{
append_mesh();
}
data().clear();
size_t last_dot = mesh_file_name_string.rfind('.');
if (last_dot == std::string::npos)
{
std::cerr<<"Error: No file extension found in "<<
mesh_file_name_string<<std::endl;
return false;
}
std::string extension = mesh_file_name_string.substr(last_dot+1);
if (extension == "off" || extension =="OFF")
{
Eigen::MatrixXd V;
Eigen::MatrixXi F;
if (!igl::readOFF(mesh_file_name_string, V, F))
return false;
data().set_mesh(V,F);
}
else if (extension == "obj" || extension =="OBJ")
{
Eigen::MatrixXd corner_normals;
Eigen::MatrixXi fNormIndices;
Eigen::MatrixXd UV_V;
Eigen::MatrixXi UV_F;
Eigen::MatrixXd V;
Eigen::MatrixXi F;
if (!(
igl::readOBJ(
mesh_file_name_string,
V, UV_V, corner_normals, F, UV_F, fNormIndices)))
{
return false;
}
data().set_mesh(V,F);
data().set_uv(UV_V,UV_F);
}
else
{
// unrecognized file type
printf("Error: %s is not a recognized file type.\n",extension.c_str());
return false;
}
data().compute_normals();
data().uniform_colors(Eigen::Vector3d(51.0/255.0,43.0/255.0,33.3/255.0),
Eigen::Vector3d(255.0/255.0,228.0/255.0,58.0/255.0),
Eigen::Vector3d(255.0/255.0,235.0/255.0,80.0/255.0));
// Alec: why?
if (data().V_uv.rows() == 0)
{
data().grid_texture();
}
core.align_camera_center(data().V,data().F);
for (unsigned int i = 0; i<plugins.size(); ++i)
if (plugins[i]->post_load())
return true;
return true;
}
IGL_INLINE bool Viewer::save_mesh_to_file(
const std::string & mesh_file_name_string)
{
// first try to load it with a plugin
for (unsigned int i = 0; i<plugins.size(); ++i)
if (plugins[i]->save(mesh_file_name_string))
return true;
size_t last_dot = mesh_file_name_string.rfind('.');
if (last_dot == std::string::npos)
{
// No file type determined
std::cerr<<"Error: No file extension found in "<<
mesh_file_name_string<<std::endl;
return false;
}
std::string extension = mesh_file_name_string.substr(last_dot+1);
if (extension == "off" || extension =="OFF")
{
return igl::writeOFF(
mesh_file_name_string,data().V,data().F);
}
else if (extension == "obj" || extension =="OBJ")
{
Eigen::MatrixXd corner_normals;
Eigen::MatrixXi fNormIndices;
Eigen::MatrixXd UV_V;
Eigen::MatrixXi UV_F;
return igl::writeOBJ(mesh_file_name_string,
data().V,
data().F,
corner_normals, fNormIndices, UV_V, UV_F);
}
else
{
// unrecognized file type
printf("Error: %s is not a recognized file type.\n",extension.c_str());
return false;
}
return true;
}
IGL_INLINE bool Viewer::key_pressed(unsigned int unicode_key,int modifiers)
{
if (callback_key_pressed)
if (callback_key_pressed(*this,unicode_key,modifiers))
return true;
for (unsigned int i = 0; i<plugins.size(); ++i)
{
if (plugins[i]->key_pressed(unicode_key, modifiers))
{
return true;
}
}
switch(unicode_key)
{
case 'A':
case 'a':
{
core.is_animating = !core.is_animating;
return true;
}
case 'F':
case 'f':
{
data().set_face_based(!data().face_based);
return true;
}
case 'I':
case 'i':
{
data().dirty |= MeshGL::DIRTY_NORMAL;
data().invert_normals = !data().invert_normals;
return true;
}
case 'L':
case 'l':
{
data().show_lines = !data().show_lines;
return true;
}
case 'O':
case 'o':
{
core.orthographic = !core.orthographic;
return true;
}
case 'T':
case 't':
{
data().show_faces = !data().show_faces;
return true;
}
case 'Z':
{
snap_to_canonical_quaternion();
return true;
}
case '[':
case ']':
{
if(core.rotation_type == ViewerCore::ROTATION_TYPE_TRACKBALL)
core.set_rotation_type(ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP);
else
core.set_rotation_type(ViewerCore::ROTATION_TYPE_TRACKBALL);
return true;
}
case '<':
case '>':
{
selected_data_index =
(selected_data_index + data_list.size() + (unicode_key=='>'?1:-1))%data_list.size();
return true;
}
case ';':
data().show_vertid = !data().show_vertid;
return true;
case ':':
data().show_faceid = !data().show_faceid;
return true;
default: break;//do nothing
}
return false;
}
IGL_INLINE bool Viewer::key_down(int key,int modifiers)
{
if (callback_key_down)
if (callback_key_down(*this,key,modifiers))
return true;
for (unsigned int i = 0; i<plugins.size(); ++i)
if (plugins[i]->key_down(key, modifiers))
return true;
return false;
}
IGL_INLINE bool Viewer::key_up(int key,int modifiers)
{
if (callback_key_up)
if (callback_key_up(*this,key,modifiers))
return true;
for (unsigned int i = 0; i<plugins.size(); ++i)
if (plugins[i]->key_up(key, modifiers))
return true;
return false;
}
IGL_INLINE bool Viewer::mouse_down(MouseButton button,int modifier)
{
// Remember mouse location at down even if used by callback/plugin
down_mouse_x = current_mouse_x;
down_mouse_y = current_mouse_y;
if (callback_mouse_down)
if (callback_mouse_down(*this,static_cast<int>(button),modifier))
return true;
for (unsigned int i = 0; i<plugins.size(); ++i)
if(plugins[i]->mouse_down(static_cast<int>(button),modifier))
return true;
down = true;
down_translation = core.camera_translation;
// Initialization code for the trackball
Eigen::RowVector3d center;
if (data().V.rows() == 0)
{
center << 0,0,0;
}else
{
center = data().V.colwise().sum()/data().V.rows();
}
Eigen::Vector3f coord =
igl::project(
Eigen::Vector3f(center(0),center(1),center(2)),
core.view,
core.proj,
core.viewport);
down_mouse_z = coord[2];
down_rotation = core.trackball_angle;
mouse_mode = MouseMode::Rotation;
switch (button)
{
case MouseButton::Left:
if (core.rotation_type == ViewerCore::ROTATION_TYPE_NO_ROTATION) {
mouse_mode = MouseMode::Translation;
} else {
mouse_mode = MouseMode::Rotation;
}
break;
case MouseButton::Right:
mouse_mode = MouseMode::Translation;
break;
default:
mouse_mode = MouseMode::None;
break;
}
return true;
}
IGL_INLINE bool Viewer::mouse_up(MouseButton button,int modifier)
{
down = false;
if (callback_mouse_up)
if (callback_mouse_up(*this,static_cast<int>(button),modifier))
return true;
for (unsigned int i = 0; i<plugins.size(); ++i)
if(plugins[i]->mouse_up(static_cast<int>(button),modifier))
return true;
mouse_mode = MouseMode::None;
return true;
}
IGL_INLINE bool Viewer::mouse_move(int mouse_x,int mouse_y)
{
if(hack_never_moved)
{
down_mouse_x = mouse_x;
down_mouse_y = mouse_y;
hack_never_moved = false;
}
current_mouse_x = mouse_x;
current_mouse_y = mouse_y;
if (callback_mouse_move)
if (callback_mouse_move(*this,mouse_x,mouse_y))
return true;
for (unsigned int i = 0; i<plugins.size(); ++i)
if (plugins[i]->mouse_move(mouse_x, mouse_y))
return true;
if (down)
{
switch (mouse_mode)
{
case MouseMode::Rotation:
{
switch(core.rotation_type)
{
default:
assert(false && "Unknown rotation type");
case ViewerCore::ROTATION_TYPE_NO_ROTATION:
break;
case ViewerCore::ROTATION_TYPE_TRACKBALL:
igl::trackball(
core.viewport(2),
core.viewport(3),
2.0f,
down_rotation,
down_mouse_x,
down_mouse_y,
mouse_x,
mouse_y,
core.trackball_angle);
break;
case ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP:
igl::two_axis_valuator_fixed_up(
core.viewport(2),core.viewport(3),
2.0,
down_rotation,
down_mouse_x, down_mouse_y, mouse_x, mouse_y,
core.trackball_angle);
break;
}
//Eigen::Vector4f snapq = core.trackball_angle;
break;
}
case MouseMode::Translation:
{
//translation
Eigen::Vector3f pos1 = igl::unproject(Eigen::Vector3f(mouse_x, core.viewport[3] - mouse_y, down_mouse_z), core.view, core.proj, core.viewport);
Eigen::Vector3f pos0 = igl::unproject(Eigen::Vector3f(down_mouse_x, core.viewport[3] - down_mouse_y, down_mouse_z), core.view, core.proj, core.viewport);
Eigen::Vector3f diff = pos1 - pos0;
core.camera_translation = down_translation + Eigen::Vector3f(diff[0],diff[1],diff[2]);
break;
}
case MouseMode::Zoom:
{
float delta = 0.001f * (mouse_x - down_mouse_x + mouse_y - down_mouse_y);
core.camera_zoom *= 1 + delta;
down_mouse_x = mouse_x;
down_mouse_y = mouse_y;
break;
}
default:
break;
}
}
return true;
}
IGL_INLINE bool Viewer::mouse_scroll(float delta_y)
{
scroll_position += delta_y;
if (callback_mouse_scroll)
if (callback_mouse_scroll(*this,delta_y))
return true;
for (unsigned int i = 0; i<plugins.size(); ++i)
if (plugins[i]->mouse_scroll(delta_y))
return true;
// Only zoom if there's actually a change
if(delta_y != 0)
{
float mult = (1.0+((delta_y>0)?1.:-1.)*0.05);
const float min_zoom = 0.1f;
core.camera_zoom = (core.camera_zoom * mult > min_zoom ? core.camera_zoom * mult : min_zoom);
}
return true;
}
IGL_INLINE bool Viewer::load_scene()
{
std::string fname = igl::file_dialog_open();
if(fname.length() == 0)
return false;
return load_scene(fname);
}
IGL_INLINE bool Viewer::load_scene(std::string fname)
{
igl::deserialize(core,"Core",fname.c_str());
igl::deserialize(data(),"Data",fname.c_str());
return true;
}
IGL_INLINE bool Viewer::save_scene()
{
std::string fname = igl::file_dialog_save();
if (fname.length() == 0)
return false;
return save_scene(fname);
}
IGL_INLINE bool Viewer::save_scene(std::string fname)
{
igl::serialize(core,"Core",fname.c_str(),true);
igl::serialize(data(),"Data",fname.c_str());
return true;
}
IGL_INLINE void Viewer::draw()
{
using namespace std;
using namespace Eigen;
int width, height;
glfwGetFramebufferSize(window, &width, &height);
int width_window, height_window;
glfwGetWindowSize(window, &width_window, &height_window);
auto highdpi_tmp = width/width_window;
if(fabs(highdpi_tmp-highdpi)>1e-8)
{
post_resize(width, height);
highdpi=highdpi_tmp;
}
core.clear_framebuffers();
if (callback_pre_draw)
{
if (callback_pre_draw(*this))
{
return;
}
}
for (unsigned int i = 0; i<plugins.size(); ++i)
{
if (plugins[i]->pre_draw())
{
return;
}
}
for(int i = 0;i<data_list.size();i++)
{
core.draw(data_list[i]);
}
if (callback_post_draw)
{
if (callback_post_draw(*this))
{
return;
}
}
for (unsigned int i = 0; i<plugins.size(); ++i)
{
if (plugins[i]->post_draw())
{
break;
}
}
}
IGL_INLINE void Viewer::resize(int w,int h)
{
if (window) {
glfwSetWindowSize(window, w/highdpi, h/highdpi);
}
post_resize(w, h);
}
IGL_INLINE void Viewer::post_resize(int w,int h)
{
core.viewport = Eigen::Vector4f(0,0,w,h);
for (unsigned int i = 0; i<plugins.size(); ++i)
{
plugins[i]->post_resize(w, h);
}
}
IGL_INLINE void Viewer::snap_to_canonical_quaternion()
{
Eigen::Quaternionf snapq = this->core.trackball_angle;
igl::snap_to_canonical_view_quat(snapq,1.0f,this->core.trackball_angle);
}
IGL_INLINE void Viewer::open_dialog_load_mesh()
{
std::string fname = igl::file_dialog_open();
if (fname.length() == 0)
return;
this->load_mesh_from_file(fname.c_str());
}
IGL_INLINE void Viewer::open_dialog_save_mesh()
{
std::string fname = igl::file_dialog_save();
if(fname.length() == 0)
return;
this->save_mesh_to_file(fname.c_str());
}
IGL_INLINE ViewerData& Viewer::data()
{
assert(!data_list.empty() && "data_list should never be empty");
assert(
(selected_data_index >= 0 && selected_data_index < data_list.size()) &&
"selected_data_index should be in bounds");
return data_list[selected_data_index];
}
IGL_INLINE int Viewer::append_mesh()
{
assert(data_list.size() >= 1);
data_list.emplace_back();
selected_data_index = data_list.size()-1;
data_list.back().id = next_data_id++;
return data_list.back().id;
}
IGL_INLINE bool Viewer::erase_mesh(const size_t index)
{
assert((index >= 0 && index < data_list.size()) && "index should be in bounds");
assert(data_list.size() >= 1);
if(data_list.size() == 1)
{
// Cannot remove last mesh
return false;
}
data_list[index].meshgl.free();
data_list.erase(data_list.begin() + index);
if(selected_data_index >= index && selected_data_index>0)
{
selected_data_index--;
}
return true;
}
IGL_INLINE size_t Viewer::mesh_index(const int id) const {
for (size_t i = 0; i < data_list.size(); ++i)
{
if (data_list[i].id == id)
return i;
}
return 0;
}
} // end namespace
} // end namespace
}

View File

@@ -0,0 +1,178 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@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_VIEWER_H
#define IGL_OPENGL_GLFW_VIEWER_H
#ifndef IGL_OPENGL_4
#define IGL_OPENGL_4
#endif
#include "../../igl_inline.h"
#include "../MeshGL.h"
#include "../ViewerCore.h"
#include "../ViewerData.h"
#include "ViewerPlugin.h"
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <vector>
#include <string>
#include <cstdint>
#define IGL_MOD_SHIFT 0x0001
#define IGL_MOD_CONTROL 0x0002
#define IGL_MOD_ALT 0x0004
#define IGL_MOD_SUPER 0x0008
struct GLFWwindow;
namespace igl
{
namespace opengl
{
namespace glfw
{
// GLFW-based mesh viewer
class Viewer
{
public:
// 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 bool launch_rendering(bool loop = true);
IGL_INLINE void launch_shut();
IGL_INLINE void init();
IGL_INLINE void init_plugins();
IGL_INLINE void shutdown_plugins();
Viewer();
~Viewer();
// Mesh IO
IGL_INLINE bool load_mesh_from_file(const std::string & mesh_file_name);
IGL_INLINE bool save_mesh_to_file(const std::string & mesh_file_name);
// Callbacks
IGL_INLINE bool key_pressed(unsigned int unicode_key,int modifier);
IGL_INLINE bool key_down(int key,int modifier);
IGL_INLINE bool key_up(int key,int modifier);
IGL_INLINE bool mouse_down(MouseButton button,int modifier);
IGL_INLINE bool mouse_up(MouseButton button,int modifier);
IGL_INLINE bool mouse_move(int mouse_x,int mouse_y);
IGL_INLINE bool mouse_scroll(float delta_y);
// Scene IO
IGL_INLINE bool load_scene();
IGL_INLINE bool load_scene(std::string fname);
IGL_INLINE bool save_scene();
IGL_INLINE bool save_scene(std::string fname);
// Draw everything
IGL_INLINE void draw();
// 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
// Helper functions
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
// the data_list and opengl_state_list.
//
// 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();
// Erase a mesh (i.e., its corresponding data and state entires in data_list
// and opengl_state_list)
//
// Inputs:
// index index of mesh to erase
// Returns whether erasure was successful <=> cannot erase last mesh
//
// Side Effects:
// If selected_data_index is greater than or equal to index then it is
// decremented
// Example:
// // Erase all mesh slots except first and clear remaining mesh
// viewer.selected_data_index = viewer.data_list.size()-1;
// while(viewer.erase_mesh(viewer.selected_data_index)){};
// viewer.data().clear();
//
IGL_INLINE bool erase_mesh(const size_t index);
// Retrieve mesh index from its unique identifier
// Returns 0 if not found
IGL_INLINE size_t mesh_index(const int id) const;
// 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
std::vector<ViewerData> data_list;
size_t selected_data_index;
int next_data_id;
GLFWwindow* window;
// Stores all the viewing options
ViewerCore core;
// List of registered plugins
std::vector<ViewerPlugin*> plugins;
// Temporary data stored when the mouse button is pressed
Eigen::Quaternionf down_rotation;
int current_mouse_x;
int current_mouse_y;
int down_mouse_x;
int down_mouse_y;
float down_mouse_z;
Eigen::Vector3f down_translation;
bool down;
bool hack_never_moved;
// Keep track of the global position of the scrollwheel
float scroll_position;
// C++-style functions
//
// Returns **true** if action should be cancelled.
std::function<bool(Viewer& viewer)> callback_init;
std::function<bool(Viewer& viewer)> callback_pre_draw;
std::function<bool(Viewer& viewer)> callback_post_draw;
std::function<bool(Viewer& viewer, int button, int modifier)> callback_mouse_down;
std::function<bool(Viewer& viewer, int button, int modifier)> callback_mouse_up;
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;
// 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;
// Pointers to per-callback data
void* callback_init_data;
void* callback_pre_draw_data;
void* callback_post_draw_data;
void* callback_mouse_down_data;
void* callback_mouse_up_data;
void* callback_mouse_move_data;
void* callback_mouse_scroll_data;
void* callback_key_pressed_data;
void* callback_key_down_data;
void* callback_key_up_data;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
} // end namespace
} // end namespace
} // end namespace
#ifndef IGL_STATIC_LIBRARY
# include "Viewer.cpp"
#endif
#endif

View File

@@ -0,0 +1,182 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2014 Daniele Panozzo <daniele.panozzo@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_VIEWERPLUGIN_H
#define IGL_OPENGL_GLFW_VIEWERPLUGIN_H
// TODO:
// * create plugins/skeleton.h
// * pass time in draw function
// * remove Preview3D from comments
// * clean comments
#include <string>
#include <igl/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)
{
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 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

@@ -0,0 +1,30 @@
#include "background_window.h"
#include <iostream>
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;});
glfwWindowHint(GLFW_SAMPLES, 4);
// Use 3.2 core profile
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// Use background window
glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
window = glfwCreateWindow(1, 1,"", NULL, NULL);
if(!window) return false;
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
{
printf("Failed to load OpenGL and its extensions");
}
glGetError(); // pull and safely ignore unhandled errors like GL_INVALID_ENUM
return true;
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
#endif

View File

@@ -0,0 +1,34 @@
#ifndef IGL_OPENGL_GLFW_BACKGROUND_WINDOW_H
#define IGL_OPENGL_GLFW_BACKGROUND_WINDOW_H
#include "../../igl_inline.h"
#include "../gl.h"
#include <GLFW/glfw3.h>
namespace igl
{
namespace opengl
{
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
IGL_INLINE bool background_window(GLFWwindow* & window);
}
}
}
#ifndef IGL_STATIC_LIBRARY
# include "background_window.cpp"
#endif
#endif

View File

@@ -0,0 +1,74 @@
// 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_IMGUIHELPERS_H
#define IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H
////////////////////////////////////////////////////////////////////////////////
#include <imgui/imgui.h>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
////////////////////////////////////////////////////////////////////////////////
// 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 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);
}
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());
}
inline bool InputText(const char* label, std::string &str, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL)
{
char buf[1024];
std::fill_n(buf, 1024, 0);
std::copy_n(str.begin(), std::min(1024, (int) str.size()), buf);
if (ImGui::InputText(label, buf, 1024, flags, callback, user_data))
{
str = std::string(buf);
return true;
}
return false;
}
} // namespace ImGui
#endif // IGL_OPENGL_GLFW_IMGUI_IMGUIHELPERS_H

View File

@@ -0,0 +1,385 @@
// 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/.
////////////////////////////////////////////////////////////////////////////////
#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 (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(); }
// Other windows
if (callback_draw_custom_window) { callback_draw_custom_window(); }
else { draw_custom_window(); }
}
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));
bool _viewer_menu_visible = true;
ImGui::Begin(
"Viewer", &_viewer_menu_visible,
ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_AlwaysAutoResize
);
ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.4f);
if (callback_draw_viewer_menu) { callback_draw_viewer_menu(); }
else { draw_viewer_menu(); }
ImGui::PopItemWidth();
ImGui::End();
}
IGL_INLINE void ImGuiMenu::draw_viewer_menu()
{
// Workspace
if (ImGui::CollapsingHeader("Workspace", ImGuiTreeNodeFlags_DefaultOpen))
{
float w = ImGui::GetContentRegionAvailWidth();
float p = ImGui::GetStyle().FramePadding.x;
if (ImGui::Button("Load##Workspace", ImVec2((w-p)/2.f, 0)))
{
viewer->load_scene();
}
ImGui::SameLine(0, p);
if (ImGui::Button("Save##Workspace", ImVec2((w-p)/2.f, 0)))
{
viewer->save_scene();
}
}
// Mesh
if (ImGui::CollapsingHeader("Mesh", ImGuiTreeNodeFlags_DefaultOpen))
{
float w = ImGui::GetContentRegionAvailWidth();
float p = ImGui::GetStyle().FramePadding.x;
if (ImGui::Button("Load##Mesh", ImVec2((w-p)/2.f, 0)))
{
viewer->open_dialog_load_mesh();
}
ImGui::SameLine(0, p);
if (ImGui::Button("Save##Mesh", ImVec2((w-p)/2.f, 0)))
{
viewer->open_dialog_save_mesh();
}
}
// 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
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_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
} // end namespace
} // end namespace

View File

@@ -0,0 +1,110 @@
// 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_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 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

View File

@@ -0,0 +1,211 @@
#ifdef IGL_OPENGL_4
#include "map_texture.h"
#include "background_window.h"
#include "../create_shader_program.h"
#include "../gl.h"
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
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 unsigned char * in_data,
const int w,
const int h,
const int nc,
std::vector<unsigned char> & out_data)
{
int out_w = w;
int out_h = h;
int out_nc = nc;
return map_texture(_V,_F,_U,in_data,w,h,nc,out_data,out_w,out_h,out_nc);
}
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 unsigned char * in_data,
const int w,
const int h,
const int nc,
std::vector<unsigned char> & out_data,
int & out_w,
int & out_h,
int & out_nc)
{
const auto fail = [](const std::string msg)
{
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");
}
// 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
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.);
}
)"
;
std::string fragment_shader = R"(
#version 330 core
layout(location = 0) out vec3 color;
uniform sampler2D tex;
in vec2 tex_coord_f;
void main()
{
color = texture(tex,tex_coord_f).rgb;
}
)";
GLuint prog_id =
igl::opengl::create_shader_program(vertex_shader,fragment_shader,{});
glUniform1i(glGetUniformLocation(prog_id, "tex"),0);
// Generate and attach buffers to vertex array
glDisable(GL_CULL_FACE);
GLuint VAO = 0;
glGenVertexArrays(1,&VAO);
glBindVertexArray(VAO);
GLuint ibo,vbo,tbo;
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);
}
// Prepare framebuffer
GLuint fb = 0;
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);
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);
{
GLenum bufs[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, bufs);
}
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
fail("framebuffer setup failed.");
}
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
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, F.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
// Write into memory
assert(out_nc == 3);
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
glfwDestroyWindow(window);
glfwTerminate();
return true;
}
#ifdef IGL_STATIC_LIBRARY
// 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> >&);
#endif
#endif // IGL_OPENGL_4

View File

@@ -0,0 +1,63 @@
#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>
namespace igl
{
namespace opengl
{
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);
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,
int & out_w,
int & out_h,
int & out_nc);
}
}
}
#ifndef IGL_STATIC_LIBRARY
# include "map_texture.cpp"
#endif
#endif // IGL_OPENGL_4
#endif