Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Matena <lukasmatena@seznam.cz>2018-10-26 16:45:52 +0300
committerLukas Matena <lukasmatena@seznam.cz>2018-10-26 16:45:52 +0300
commit7681d00ee50c58a06b0ab61f776a08a693a6e510 (patch)
treef9bb0a57375af93113e52e31d05bc4f86f1a3907 /src/igl/opengl
parentc1e6eda554813763378f959435df766a600b896c (diff)
parentf65118210124d460d9cb6478a84976bb98160595 (diff)
Merged branch 'dev_native' into lm_sla_supports_auto
Added igl library files
Diffstat (limited to 'src/igl/opengl')
-rw-r--r--src/igl/opengl/MeshGL.cpp313
-rw-r--r--src/igl/opengl/MeshGL.h133
-rw-r--r--src/igl/opengl/ViewerCore.cpp391
-rw-r--r--src/igl/opengl/ViewerCore.h199
-rw-r--r--src/igl/opengl/ViewerData.cpp691
-rw-r--r--src/igl/opengl/ViewerData.h276
-rw-r--r--src/igl/opengl/bind_vertex_attrib_array.cpp24
-rw-r--r--src/igl/opengl/bind_vertex_attrib_array.h32
-rw-r--r--src/igl/opengl/create_index_vbo.cpp46
-rw-r--r--src/igl/opengl/create_index_vbo.h35
-rw-r--r--src/igl/opengl/create_mesh_vbo.cpp43
-rw-r--r--src/igl/opengl/create_mesh_vbo.h61
-rw-r--r--src/igl/opengl/create_shader_program.cpp141
-rw-r--r--src/igl/opengl/create_shader_program.h64
-rw-r--r--src/igl/opengl/create_vector_vbo.cpp57
-rw-r--r--src/igl/opengl/create_vector_vbo.h38
-rw-r--r--src/igl/opengl/destroy_shader_program.cpp50
-rw-r--r--src/igl/opengl/destroy_shader_program.h35
-rw-r--r--src/igl/opengl/gl.h25
-rw-r--r--src/igl/opengl/gl_type_size.cpp30
-rw-r--r--src/igl/opengl/gl_type_size.h28
-rw-r--r--src/igl/opengl/glfw/Viewer.cpp950
-rw-r--r--src/igl/opengl/glfw/Viewer.h178
-rw-r--r--src/igl/opengl/glfw/ViewerPlugin.h182
-rw-r--r--src/igl/opengl/glfw/background_window.cpp30
-rw-r--r--src/igl/opengl/glfw/background_window.h34
-rw-r--r--src/igl/opengl/glfw/imgui/ImGuiHelpers.h74
-rw-r--r--src/igl/opengl/glfw/imgui/ImGuiMenu.cpp385
-rw-r--r--src/igl/opengl/glfw/imgui/ImGuiMenu.h110
-rw-r--r--src/igl/opengl/glfw/map_texture.cpp211
-rw-r--r--src/igl/opengl/glfw/map_texture.h63
-rw-r--r--src/igl/opengl/init_render_to_texture.cpp86
-rw-r--r--src/igl/opengl/init_render_to_texture.h77
-rw-r--r--src/igl/opengl/load_shader.cpp34
-rw-r--r--src/igl/opengl/load_shader.h38
-rw-r--r--src/igl/opengl/print_program_info_log.cpp28
-rw-r--r--src/igl/opengl/print_program_info_log.h27
-rw-r--r--src/igl/opengl/print_shader_info_log.cpp29
-rw-r--r--src/igl/opengl/print_shader_info_log.h27
-rw-r--r--src/igl/opengl/report_gl_error.cpp61
-rw-r--r--src/igl/opengl/report_gl_error.h36
-rw-r--r--src/igl/opengl/uniform_type_to_string.cpp71
-rw-r--r--src/igl/opengl/uniform_type_to_string.h31
-rw-r--r--src/igl/opengl/vertex_array.cpp61
-rw-r--r--src/igl/opengl/vertex_array.h38
45 files changed, 5573 insertions, 0 deletions
diff --git a/src/igl/opengl/MeshGL.cpp b/src/igl/opengl/MeshGL.cpp
new file mode 100644
index 000000000..f49924324
--- /dev/null
+++ b/src/igl/opengl/MeshGL.cpp
@@ -0,0 +1,313 @@
+// 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 "MeshGL.h"
+#include "bind_vertex_attrib_array.h"
+#include "create_shader_program.h"
+#include "destroy_shader_program.h"
+#include <iostream>
+
+IGL_INLINE void igl::opengl::MeshGL::init_buffers()
+{
+ // Mesh: Vertex Array Object & Buffer objects
+ glGenVertexArrays(1, &vao_mesh);
+ glBindVertexArray(vao_mesh);
+ glGenBuffers(1, &vbo_V);
+ glGenBuffers(1, &vbo_V_normals);
+ glGenBuffers(1, &vbo_V_ambient);
+ glGenBuffers(1, &vbo_V_diffuse);
+ glGenBuffers(1, &vbo_V_specular);
+ glGenBuffers(1, &vbo_V_uv);
+ glGenBuffers(1, &vbo_F);
+ glGenTextures(1, &vbo_tex);
+
+ // Line overlay
+ glGenVertexArrays(1, &vao_overlay_lines);
+ glBindVertexArray(vao_overlay_lines);
+ glGenBuffers(1, &vbo_lines_F);
+ glGenBuffers(1, &vbo_lines_V);
+ glGenBuffers(1, &vbo_lines_V_colors);
+
+ // Point overlay
+ glGenVertexArrays(1, &vao_overlay_points);
+ glBindVertexArray(vao_overlay_points);
+ glGenBuffers(1, &vbo_points_F);
+ glGenBuffers(1, &vbo_points_V);
+ glGenBuffers(1, &vbo_points_V_colors);
+
+ dirty = MeshGL::DIRTY_ALL;
+}
+
+IGL_INLINE void igl::opengl::MeshGL::free_buffers()
+{
+ if (is_initialized)
+ {
+ glDeleteVertexArrays(1, &vao_mesh);
+ glDeleteVertexArrays(1, &vao_overlay_lines);
+ glDeleteVertexArrays(1, &vao_overlay_points);
+
+ glDeleteBuffers(1, &vbo_V);
+ glDeleteBuffers(1, &vbo_V_normals);
+ glDeleteBuffers(1, &vbo_V_ambient);
+ glDeleteBuffers(1, &vbo_V_diffuse);
+ glDeleteBuffers(1, &vbo_V_specular);
+ glDeleteBuffers(1, &vbo_V_uv);
+ glDeleteBuffers(1, &vbo_F);
+ glDeleteBuffers(1, &vbo_lines_F);
+ glDeleteBuffers(1, &vbo_lines_V);
+ glDeleteBuffers(1, &vbo_lines_V_colors);
+ glDeleteBuffers(1, &vbo_points_F);
+ glDeleteBuffers(1, &vbo_points_V);
+ glDeleteBuffers(1, &vbo_points_V_colors);
+
+ glDeleteTextures(1, &vbo_tex);
+ }
+}
+
+IGL_INLINE void igl::opengl::MeshGL::bind_mesh()
+{
+ glBindVertexArray(vao_mesh);
+ glUseProgram(shader_mesh);
+ bind_vertex_attrib_array(shader_mesh,"position", vbo_V, V_vbo, dirty & MeshGL::DIRTY_POSITION);
+ bind_vertex_attrib_array(shader_mesh,"normal", vbo_V_normals, V_normals_vbo, dirty & MeshGL::DIRTY_NORMAL);
+ bind_vertex_attrib_array(shader_mesh,"Ka", vbo_V_ambient, V_ambient_vbo, dirty & MeshGL::DIRTY_AMBIENT);
+ bind_vertex_attrib_array(shader_mesh,"Kd", vbo_V_diffuse, V_diffuse_vbo, dirty & MeshGL::DIRTY_DIFFUSE);
+ bind_vertex_attrib_array(shader_mesh,"Ks", vbo_V_specular, V_specular_vbo, dirty & MeshGL::DIRTY_SPECULAR);
+ bind_vertex_attrib_array(shader_mesh,"texcoord", vbo_V_uv, V_uv_vbo, dirty & MeshGL::DIRTY_UV);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_F);
+ if (dirty & MeshGL::DIRTY_FACE)
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*F_vbo.size(), F_vbo.data(), GL_DYNAMIC_DRAW);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, vbo_tex);
+ if (dirty & MeshGL::DIRTY_TEXTURE)
+ {
+ 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, GL_RGBA, tex_u, tex_v, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.data());
+ }
+ glUniform1i(glGetUniformLocation(shader_mesh,"tex"), 0);
+ dirty &= ~MeshGL::DIRTY_MESH;
+}
+
+IGL_INLINE void igl::opengl::MeshGL::bind_overlay_lines()
+{
+ bool is_dirty = dirty & MeshGL::DIRTY_OVERLAY_LINES;
+
+ glBindVertexArray(vao_overlay_lines);
+ glUseProgram(shader_overlay_lines);
+ bind_vertex_attrib_array(shader_overlay_lines,"position", vbo_lines_V, lines_V_vbo, is_dirty);
+ bind_vertex_attrib_array(shader_overlay_lines,"color", vbo_lines_V_colors, lines_V_colors_vbo, is_dirty);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_lines_F);
+ if (is_dirty)
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*lines_F_vbo.size(), lines_F_vbo.data(), GL_DYNAMIC_DRAW);
+
+ dirty &= ~MeshGL::DIRTY_OVERLAY_LINES;
+}
+
+IGL_INLINE void igl::opengl::MeshGL::bind_overlay_points()
+{
+ bool is_dirty = dirty & MeshGL::DIRTY_OVERLAY_POINTS;
+
+ glBindVertexArray(vao_overlay_points);
+ glUseProgram(shader_overlay_points);
+ bind_vertex_attrib_array(shader_overlay_points,"position", vbo_points_V, points_V_vbo, is_dirty);
+ bind_vertex_attrib_array(shader_overlay_points,"color", vbo_points_V_colors, points_V_colors_vbo, is_dirty);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_points_F);
+ if (is_dirty)
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*points_F_vbo.size(), points_F_vbo.data(), GL_DYNAMIC_DRAW);
+
+ dirty &= ~MeshGL::DIRTY_OVERLAY_POINTS;
+}
+
+IGL_INLINE void igl::opengl::MeshGL::draw_mesh(bool solid)
+{
+ glPolygonMode(GL_FRONT_AND_BACK, solid ? GL_FILL : GL_LINE);
+
+ /* Avoid Z-buffer fighting between filled triangles & wireframe lines */
+ if (solid)
+ {
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(1.0, 1.0);
+ }
+ glDrawElements(GL_TRIANGLES, 3*F_vbo.rows(), GL_UNSIGNED_INT, 0);
+
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+}
+
+IGL_INLINE void igl::opengl::MeshGL::draw_overlay_lines()
+{
+ glDrawElements(GL_LINES, lines_F_vbo.rows(), GL_UNSIGNED_INT, 0);
+}
+
+IGL_INLINE void igl::opengl::MeshGL::draw_overlay_points()
+{
+ glDrawElements(GL_POINTS, points_F_vbo.rows(), GL_UNSIGNED_INT, 0);
+}
+
+IGL_INLINE void igl::opengl::MeshGL::init()
+{
+ if(is_initialized)
+ {
+ return;
+ }
+ is_initialized = true;
+ std::string mesh_vertex_shader_string =
+R"(#version 150
+ uniform mat4 view;
+ uniform mat4 proj;
+ uniform mat4 normal_matrix;
+ in vec3 position;
+ in vec3 normal;
+ out vec3 position_eye;
+ out vec3 normal_eye;
+ in vec4 Ka;
+ in vec4 Kd;
+ in vec4 Ks;
+ in vec2 texcoord;
+ out vec2 texcoordi;
+ out vec4 Kai;
+ out vec4 Kdi;
+ out vec4 Ksi;
+
+ void main()
+ {
+ position_eye = vec3 (view * vec4 (position, 1.0));
+ normal_eye = vec3 (normal_matrix * vec4 (normal, 0.0));
+ normal_eye = normalize(normal_eye);
+ gl_Position = proj * vec4 (position_eye, 1.0); //proj * view * vec4(position, 1.0);"
+ Kai = Ka;
+ Kdi = Kd;
+ Ksi = Ks;
+ texcoordi = texcoord;
+ }
+)";
+
+ std::string mesh_fragment_shader_string =
+R"(#version 150
+ uniform mat4 view;
+ uniform mat4 proj;
+ uniform vec4 fixed_color;
+ in vec3 position_eye;
+ in vec3 normal_eye;
+ uniform vec3 light_position_eye;
+ vec3 Ls = vec3 (1, 1, 1);
+ vec3 Ld = vec3 (1, 1, 1);
+ vec3 La = vec3 (1, 1, 1);
+ in vec4 Ksi;
+ in vec4 Kdi;
+ in vec4 Kai;
+ in vec2 texcoordi;
+ uniform sampler2D tex;
+ uniform float specular_exponent;
+ uniform float lighting_factor;
+ uniform float texture_factor;
+ out vec4 outColor;
+ void main()
+ {
+ vec3 Ia = La * vec3(Kai); // ambient intensity
+
+ vec3 vector_to_light_eye = light_position_eye - position_eye;
+ vec3 direction_to_light_eye = normalize (vector_to_light_eye);
+ float dot_prod = dot (direction_to_light_eye, normalize(normal_eye));
+ float clamped_dot_prod = max (dot_prod, 0.0);
+ vec3 Id = Ld * vec3(Kdi) * clamped_dot_prod; // Diffuse intensity
+
+ vec3 reflection_eye = reflect (-direction_to_light_eye, normalize(normal_eye));
+ vec3 surface_to_viewer_eye = normalize (-position_eye);
+ float dot_prod_specular = dot (reflection_eye, surface_to_viewer_eye);
+ dot_prod_specular = float(abs(dot_prod)==dot_prod) * max (dot_prod_specular, 0.0);
+ float specular_factor = pow (dot_prod_specular, specular_exponent);
+ vec3 Is = Ls * vec3(Ksi) * specular_factor; // specular intensity
+ vec4 color = vec4(lighting_factor * (Is + Id) + Ia + (1.0-lighting_factor) * vec3(Kdi),(Kai.a+Ksi.a+Kdi.a)/3);
+ outColor = mix(vec4(1,1,1,1), texture(tex, texcoordi), texture_factor) * color;
+ if (fixed_color != vec4(0.0)) outColor = fixed_color;
+ }
+)";
+
+ std::string overlay_vertex_shader_string =
+R"(#version 150
+ uniform mat4 view;
+ uniform mat4 proj;
+ in vec3 position;
+ in vec3 color;
+ out vec3 color_frag;
+
+ void main()
+ {
+ gl_Position = proj * view * vec4 (position, 1.0);
+ color_frag = color;
+ }
+)";
+
+ std::string overlay_fragment_shader_string =
+R"(#version 150
+ in vec3 color_frag;
+ out vec4 outColor;
+ void main()
+ {
+ outColor = vec4(color_frag, 1.0);
+ }
+)";
+
+ std::string overlay_point_fragment_shader_string =
+R"(#version 150
+ in vec3 color_frag;
+ out vec4 outColor;
+ void main()
+ {
+ if (length(gl_PointCoord - vec2(0.5)) > 0.5)
+ discard;
+ outColor = vec4(color_frag, 1.0);
+ }
+)";
+
+ init_buffers();
+ create_shader_program(
+ mesh_vertex_shader_string,
+ mesh_fragment_shader_string,
+ {},
+ shader_mesh);
+ create_shader_program(
+ overlay_vertex_shader_string,
+ overlay_fragment_shader_string,
+ {},
+ shader_overlay_lines);
+ create_shader_program(
+ overlay_vertex_shader_string,
+ overlay_point_fragment_shader_string,
+ {},
+ shader_overlay_points);
+}
+
+IGL_INLINE void igl::opengl::MeshGL::free()
+{
+ const auto free = [](GLuint & id)
+ {
+ if(id)
+ {
+ destroy_shader_program(id);
+ id = 0;
+ }
+ };
+
+ if (is_initialized)
+ {
+ free(shader_mesh);
+ free(shader_overlay_lines);
+ free(shader_overlay_points);
+ free_buffers();
+ }
+}
diff --git a/src/igl/opengl/MeshGL.h b/src/igl/opengl/MeshGL.h
new file mode 100644
index 000000000..74c358903
--- /dev/null
+++ b/src/igl/opengl/MeshGL.h
@@ -0,0 +1,133 @@
+// 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_MESHGL_H
+#define IGL_OPENGL_MESHGL_H
+
+// Coverts mesh data inside a igl::ViewerData class in an OpenGL
+// compatible format The class includes a shader and the opengl calls to plot
+// the data
+
+#include <igl/igl_inline.h>
+#include <Eigen/Core>
+
+namespace igl
+{
+namespace opengl
+{
+
+class MeshGL
+{
+public:
+ typedef unsigned int GLuint;
+
+ enum DirtyFlags
+ {
+ DIRTY_NONE = 0x0000,
+ DIRTY_POSITION = 0x0001,
+ DIRTY_UV = 0x0002,
+ DIRTY_NORMAL = 0x0004,
+ DIRTY_AMBIENT = 0x0008,
+ DIRTY_DIFFUSE = 0x0010,
+ DIRTY_SPECULAR = 0x0020,
+ DIRTY_TEXTURE = 0x0040,
+ DIRTY_FACE = 0x0080,
+ DIRTY_MESH = 0x00FF,
+ DIRTY_OVERLAY_LINES = 0x0100,
+ DIRTY_OVERLAY_POINTS = 0x0200,
+ DIRTY_ALL = 0x03FF
+ };
+
+ bool is_initialized = false;
+ GLuint vao_mesh;
+ GLuint vao_overlay_lines;
+ GLuint vao_overlay_points;
+ GLuint shader_mesh;
+ GLuint shader_overlay_lines;
+ GLuint shader_overlay_points;
+
+ GLuint vbo_V; // Vertices of the current mesh (#V x 3)
+ GLuint vbo_V_uv; // UV coordinates for the current mesh (#V x 2)
+ GLuint vbo_V_normals; // Vertices of the current mesh (#V x 3)
+ GLuint vbo_V_ambient; // Ambient material (#V x 3)
+ GLuint vbo_V_diffuse; // Diffuse material (#V x 3)
+ GLuint vbo_V_specular; // Specular material (#V x 3)
+
+ GLuint vbo_F; // Faces of the mesh (#F x 3)
+ GLuint vbo_tex; // Texture
+
+ GLuint vbo_lines_F; // Indices of the line overlay
+ GLuint vbo_lines_V; // Vertices of the line overlay
+ GLuint vbo_lines_V_colors; // Color values of the line overlay
+ GLuint vbo_points_F; // Indices of the point overlay
+ GLuint vbo_points_V; // Vertices of the point overlay
+ GLuint vbo_points_V_colors; // Color values of the point overlay
+
+ // Temporary copy of the content of each VBO
+ typedef Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> RowMatrixXf;
+ RowMatrixXf V_vbo;
+ RowMatrixXf V_normals_vbo;
+ RowMatrixXf V_ambient_vbo;
+ RowMatrixXf V_diffuse_vbo;
+ RowMatrixXf V_specular_vbo;
+ RowMatrixXf V_uv_vbo;
+ RowMatrixXf lines_V_vbo;
+ RowMatrixXf lines_V_colors_vbo;
+ RowMatrixXf points_V_vbo;
+ RowMatrixXf points_V_colors_vbo;
+
+ int tex_u;
+ int tex_v;
+ Eigen::Matrix<char,Eigen::Dynamic,1> tex;
+
+ Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> F_vbo;
+ Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> lines_F_vbo;
+ Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> points_F_vbo;
+
+ // Marks dirty buffers that need to be uploaded to OpenGL
+ uint32_t dirty;
+
+ // Initialize shaders and buffers
+ IGL_INLINE void init();
+
+ // Release all resources
+ IGL_INLINE void free();
+
+ // Create a new set of OpenGL buffer objects
+ IGL_INLINE void init_buffers();
+
+ // Bind the underlying OpenGL buffer objects for subsequent mesh draw calls
+ IGL_INLINE void bind_mesh();
+
+ /// Draw the currently buffered mesh (either solid or wireframe)
+ IGL_INLINE void draw_mesh(bool solid);
+
+ // Bind the underlying OpenGL buffer objects for subsequent line overlay draw calls
+ IGL_INLINE void bind_overlay_lines();
+
+ /// Draw the currently buffered line overlay
+ IGL_INLINE void draw_overlay_lines();
+
+ // Bind the underlying OpenGL buffer objects for subsequent point overlay draw calls
+ IGL_INLINE void bind_overlay_points();
+
+ /// Draw the currently buffered point overlay
+ IGL_INLINE void draw_overlay_points();
+
+ // Release the OpenGL buffer objects
+ IGL_INLINE void free_buffers();
+
+};
+
+}
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "MeshGL.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/ViewerCore.cpp b/src/igl/opengl/ViewerCore.cpp
new file mode 100644
index 000000000..fc5f4664b
--- /dev/null
+++ b/src/igl/opengl/ViewerCore.cpp
@@ -0,0 +1,391 @@
+// 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 "ViewerCore.h"
+#include "gl.h"
+#include "../quat_to_mat.h"
+#include "../snap_to_fixed_up.h"
+#include "../look_at.h"
+#include "../frustum.h"
+#include "../ortho.h"
+#include "../massmatrix.h"
+#include "../barycenter.h"
+#include "../PI.h"
+#include <Eigen/Geometry>
+#include <iostream>
+
+IGL_INLINE void igl::opengl::ViewerCore::align_camera_center(
+ const Eigen::MatrixXd& V,
+ const Eigen::MatrixXi& F)
+{
+ if(V.rows() == 0)
+ return;
+
+ get_scale_and_shift_to_fit_mesh(V,F,camera_base_zoom,camera_base_translation);
+ // Rather than crash on empty mesh...
+ if(V.size() > 0)
+ {
+ object_scale = (V.colwise().maxCoeff() - V.colwise().minCoeff()).norm();
+ }
+}
+
+IGL_INLINE void igl::opengl::ViewerCore::get_scale_and_shift_to_fit_mesh(
+ const Eigen::MatrixXd& V,
+ const Eigen::MatrixXi& F,
+ float& zoom,
+ Eigen::Vector3f& shift)
+{
+ if (V.rows() == 0)
+ return;
+
+ Eigen::MatrixXd BC;
+ if (F.rows() <= 1)
+ {
+ BC = V;
+ } else
+ {
+ igl::barycenter(V,F,BC);
+ }
+ return get_scale_and_shift_to_fit_mesh(BC,zoom,shift);
+}
+
+IGL_INLINE void igl::opengl::ViewerCore::align_camera_center(
+ const Eigen::MatrixXd& V)
+{
+ if(V.rows() == 0)
+ return;
+
+ get_scale_and_shift_to_fit_mesh(V,camera_base_zoom,camera_base_translation);
+ // Rather than crash on empty mesh...
+ if(V.size() > 0)
+ {
+ object_scale = (V.colwise().maxCoeff() - V.colwise().minCoeff()).norm();
+ }
+}
+
+IGL_INLINE void igl::opengl::ViewerCore::get_scale_and_shift_to_fit_mesh(
+ const Eigen::MatrixXd& V,
+ float& zoom,
+ Eigen::Vector3f& shift)
+{
+ if (V.rows() == 0)
+ return;
+
+ auto min_point = V.colwise().minCoeff();
+ auto max_point = V.colwise().maxCoeff();
+ auto centroid = (0.5*(min_point + max_point)).eval();
+ shift.setConstant(0);
+ shift.head(centroid.size()) = -centroid.cast<float>();
+ zoom = 2.0 / (max_point-min_point).array().abs().maxCoeff();
+}
+
+
+IGL_INLINE void igl::opengl::ViewerCore::clear_framebuffers()
+{
+ glClearColor(background_color[0],
+ background_color[1],
+ background_color[2],
+ background_color[3]);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+IGL_INLINE void igl::opengl::ViewerCore::draw(
+ ViewerData& data,
+ bool update_matrices)
+{
+ using namespace std;
+ using namespace Eigen;
+
+ if (depth_test)
+ glEnable(GL_DEPTH_TEST);
+ else
+ glDisable(GL_DEPTH_TEST);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ /* Bind and potentially refresh mesh/line/point data */
+ if (data.dirty)
+ {
+ data.updateGL(data, data.invert_normals,data.meshgl);
+ data.dirty = MeshGL::DIRTY_NONE;
+ }
+ data.meshgl.bind_mesh();
+
+ // Initialize uniform
+ glViewport(viewport(0), viewport(1), viewport(2), viewport(3));
+
+ if(update_matrices)
+ {
+ view = Eigen::Matrix4f::Identity();
+ proj = Eigen::Matrix4f::Identity();
+ norm = Eigen::Matrix4f::Identity();
+
+ float width = viewport(2);
+ float height = viewport(3);
+
+ // Set view
+ look_at( camera_eye, camera_center, camera_up, view);
+ view = view
+ * (trackball_angle * Eigen::Scaling(camera_zoom * camera_base_zoom)
+ * Eigen::Translation3f(camera_translation + camera_base_translation)).matrix();
+
+ norm = view.inverse().transpose();
+
+ // Set projection
+ if (orthographic)
+ {
+ float length = (camera_eye - camera_center).norm();
+ float h = tan(camera_view_angle/360.0 * igl::PI) * (length);
+ ortho(-h*width/height, h*width/height, -h, h, camera_dnear, camera_dfar,proj);
+ }
+ else
+ {
+ float fH = tan(camera_view_angle / 360.0 * igl::PI) * camera_dnear;
+ float fW = fH * (double)width/(double)height;
+ frustum(-fW, fW, -fH, fH, camera_dnear, camera_dfar,proj);
+ }
+ }
+
+ // Send transformations to the GPU
+ GLint viewi = glGetUniformLocation(data.meshgl.shader_mesh,"view");
+ GLint proji = glGetUniformLocation(data.meshgl.shader_mesh,"proj");
+ GLint normi = glGetUniformLocation(data.meshgl.shader_mesh,"normal_matrix");
+ glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data());
+ glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data());
+ glUniformMatrix4fv(normi, 1, GL_FALSE, norm.data());
+
+ // Light parameters
+ GLint specular_exponenti = glGetUniformLocation(data.meshgl.shader_mesh,"specular_exponent");
+ GLint light_position_eyei = glGetUniformLocation(data.meshgl.shader_mesh,"light_position_eye");
+ GLint lighting_factori = glGetUniformLocation(data.meshgl.shader_mesh,"lighting_factor");
+ GLint fixed_colori = glGetUniformLocation(data.meshgl.shader_mesh,"fixed_color");
+ GLint texture_factori = glGetUniformLocation(data.meshgl.shader_mesh,"texture_factor");
+
+ glUniform1f(specular_exponenti, data.shininess);
+ glUniform3fv(light_position_eyei, 1, light_position.data());
+ glUniform1f(lighting_factori, lighting_factor); // enables lighting
+ glUniform4f(fixed_colori, 0.0, 0.0, 0.0, 0.0);
+
+ if (data.V.rows()>0)
+ {
+ // Render fill
+ if (data.show_faces)
+ {
+ // Texture
+ glUniform1f(texture_factori, data.show_texture ? 1.0f : 0.0f);
+ data.meshgl.draw_mesh(true);
+ glUniform1f(texture_factori, 0.0f);
+ }
+
+ // Render wireframe
+ if (data.show_lines)
+ {
+ glLineWidth(data.line_width);
+ glUniform4f(fixed_colori,
+ data.line_color[0],
+ data.line_color[1],
+ data.line_color[2], 1.0f);
+ data.meshgl.draw_mesh(false);
+ glUniform4f(fixed_colori, 0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ }
+
+ if (data.show_overlay)
+ {
+ if (data.show_overlay_depth)
+ glEnable(GL_DEPTH_TEST);
+ else
+ glDisable(GL_DEPTH_TEST);
+
+ if (data.lines.rows() > 0)
+ {
+ data.meshgl.bind_overlay_lines();
+ viewi = glGetUniformLocation(data.meshgl.shader_overlay_lines,"view");
+ proji = glGetUniformLocation(data.meshgl.shader_overlay_lines,"proj");
+
+ glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data());
+ glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data());
+ // This must be enabled, otherwise glLineWidth has no effect
+ glEnable(GL_LINE_SMOOTH);
+ glLineWidth(data.line_width);
+
+ data.meshgl.draw_overlay_lines();
+ }
+
+ if (data.points.rows() > 0)
+ {
+ data.meshgl.bind_overlay_points();
+ viewi = glGetUniformLocation(data.meshgl.shader_overlay_points,"view");
+ proji = glGetUniformLocation(data.meshgl.shader_overlay_points,"proj");
+
+ glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data());
+ glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data());
+ glPointSize(data.point_size);
+
+ data.meshgl.draw_overlay_points();
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ }
+
+}
+
+IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(ViewerData& data,
+ bool update_matrices,
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R,
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B,
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A)
+{
+ assert(R.rows() == G.rows() && G.rows() == B.rows() && B.rows() == A.rows());
+ assert(R.cols() == G.cols() && G.cols() == B.cols() && B.cols() == A.cols());
+
+ unsigned width = R.rows();
+ unsigned height = R.cols();
+
+ // https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing
+ unsigned int framebuffer;
+ glGenFramebuffers(1, &framebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ // create a multisampled color attachment texture
+ unsigned int textureColorBufferMultiSampled;
+ glGenTextures(1, &textureColorBufferMultiSampled);
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled);
+ glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA, width, height, GL_TRUE);
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);
+ // create a (also multisampled) renderbuffer object for depth and stencil attachments
+ unsigned int rbo;
+ glGenRenderbuffers(1, &rbo);
+ glBindRenderbuffer(GL_RENDERBUFFER, rbo);
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
+ assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ // configure second post-processing framebuffer
+ unsigned int intermediateFBO;
+ glGenFramebuffers(1, &intermediateFBO);
+ glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO);
+ // create a color attachment texture
+ unsigned int screenTexture;
+ glGenTextures(1, &screenTexture);
+ glBindTexture(GL_TEXTURE_2D, screenTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0); // we only need a color buffer
+ assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+
+ // Clear the buffer
+ glClearColor(background_color(0), background_color(1), background_color(2), 0.f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ // Save old viewport
+ Eigen::Vector4f viewport_ori = viewport;
+ viewport << 0,0,width,height;
+ // Draw
+ draw(data,update_matrices);
+ // Restore viewport
+ viewport = viewport_ori;
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO);
+ glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO);
+ // Copy back in the given Eigen matrices
+ GLubyte* pixels = (GLubyte*)calloc(width*height*4,sizeof(GLubyte));
+ glReadPixels(0, 0,width, height,GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+ // Clean up
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glDeleteTextures(1, &screenTexture);
+ glDeleteTextures(1, &textureColorBufferMultiSampled);
+ glDeleteFramebuffers(1, &framebuffer);
+ glDeleteFramebuffers(1, &intermediateFBO);
+ glDeleteRenderbuffers(1, &rbo);
+
+ int count = 0;
+ for (unsigned j=0; j<height; ++j)
+ {
+ for (unsigned i=0; i<width; ++i)
+ {
+ R(i,j) = pixels[count*4+0];
+ G(i,j) = pixels[count*4+1];
+ B(i,j) = pixels[count*4+2];
+ A(i,j) = pixels[count*4+3];
+ ++count;
+ }
+ }
+ // Clean up
+ free(pixels);
+}
+
+IGL_INLINE void igl::opengl::ViewerCore::set_rotation_type(
+ const igl::opengl::ViewerCore::RotationType & value)
+{
+ using namespace Eigen;
+ using namespace std;
+ const RotationType old_rotation_type = rotation_type;
+ rotation_type = value;
+ if(rotation_type == ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP &&
+ old_rotation_type != ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP)
+ {
+ snap_to_fixed_up(Quaternionf(trackball_angle),trackball_angle);
+ }
+}
+
+
+IGL_INLINE igl::opengl::ViewerCore::ViewerCore()
+{
+ // Default colors
+ background_color << 0.3f, 0.3f, 0.5f, 1.0f;
+
+ // Default lights settings
+ light_position << 0.0f, 0.3f, 0.0f;
+ lighting_factor = 1.0f; //on
+
+ // Default trackball
+ trackball_angle = Eigen::Quaternionf::Identity();
+ set_rotation_type(ViewerCore::ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP);
+
+ // Camera parameters
+ camera_base_zoom = 1.0f;
+ camera_zoom = 1.0f;
+ orthographic = false;
+ camera_view_angle = 45.0;
+ camera_dnear = 1.0;
+ camera_dfar = 100.0;
+ camera_base_translation << 0, 0, 0;
+ camera_translation << 0, 0, 0;
+ camera_eye << 0, 0, 5;
+ camera_center << 0, 0, 0;
+ camera_up << 0, 1, 0;
+
+ depth_test = true;
+
+ is_animating = false;
+ animation_max_fps = 30.;
+
+ viewport.setZero();
+}
+
+IGL_INLINE void igl::opengl::ViewerCore::init()
+{
+}
+
+IGL_INLINE void igl::opengl::ViewerCore::shut()
+{
+}
diff --git a/src/igl/opengl/ViewerCore.h b/src/igl/opengl/ViewerCore.h
new file mode 100644
index 000000000..94d3367e0
--- /dev/null
+++ b/src/igl/opengl/ViewerCore.h
@@ -0,0 +1,199 @@
+// 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_VIEWERCORE_H
+#define IGL_OPENGL_VIEWERCORE_H
+
+#include <igl/opengl/MeshGL.h>
+#include <igl/opengl/ViewerData.h>
+
+#include <igl/igl_inline.h>
+#include <Eigen/Geometry>
+#include <Eigen/Core>
+
+namespace igl
+{
+namespace opengl
+{
+
+// Basic class of the 3D mesh viewer
+// TODO: write documentation
+
+class ViewerCore
+{
+public:
+ IGL_INLINE ViewerCore();
+
+ // Initialization
+ IGL_INLINE void init();
+
+ // Shutdown
+ IGL_INLINE void shut();
+
+ // Serialization code
+ IGL_INLINE void InitSerialization();
+
+
+ // ------------------- Camera control functions
+
+ // Adjust the view to see the entire model
+ IGL_INLINE void align_camera_center(
+ const Eigen::MatrixXd& V,
+ const Eigen::MatrixXi& F);
+
+ // Determines how much to zoom and shift such that the mesh fills the unit
+ // box (centered at the origin)
+ IGL_INLINE void get_scale_and_shift_to_fit_mesh(
+ const Eigen::MatrixXd& V,
+ const Eigen::MatrixXi& F,
+ float & zoom,
+ Eigen::Vector3f& shift);
+
+ // Adjust the view to see the entire model
+ IGL_INLINE void align_camera_center(
+ const Eigen::MatrixXd& V);
+
+ // Determines how much to zoom and shift such that the mesh fills the unit
+ // box (centered at the origin)
+ IGL_INLINE void get_scale_and_shift_to_fit_mesh(
+ const Eigen::MatrixXd& V,
+ float & zoom,
+ Eigen::Vector3f& shift);
+
+ // ------------------- Drawing functions
+
+ // Clear the frame buffers
+ IGL_INLINE void clear_framebuffers();
+
+ // Draw everything
+ //
+ // data cannot be const because it is being set to "clean"
+ IGL_INLINE void draw(ViewerData& data, bool update_matrices = true);
+ IGL_INLINE void draw_buffer(
+ ViewerData& data,
+ bool update_matrices,
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R,
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B,
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A);
+
+ // Trackball angle (quaternion)
+ enum RotationType
+ {
+ ROTATION_TYPE_TRACKBALL = 0,
+ ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1,
+ ROTATION_TYPE_NO_ROTATION = 2,
+ NUM_ROTATION_TYPES = 3
+ };
+ IGL_INLINE void set_rotation_type(const RotationType & value);
+
+ // ------------------- Properties
+
+ // Colors
+ Eigen::Vector4f background_color;
+
+ // Lighting
+ Eigen::Vector3f light_position;
+ float lighting_factor;
+
+ RotationType rotation_type;
+ Eigen::Quaternionf trackball_angle;
+
+ // Camera parameters
+ float camera_base_zoom;
+ float camera_zoom;
+ bool orthographic;
+ Eigen::Vector3f camera_base_translation;
+ Eigen::Vector3f camera_translation;
+ Eigen::Vector3f camera_eye;
+ Eigen::Vector3f camera_up;
+ Eigen::Vector3f camera_center;
+ float camera_view_angle;
+ float camera_dnear;
+ float camera_dfar;
+
+ bool depth_test;
+
+ // Animation
+ bool is_animating;
+ double animation_max_fps;
+
+ // Caches the two-norm between the min/max point of the bounding box
+ float object_scale;
+
+ // Viewport size
+ Eigen::Vector4f viewport;
+
+ // Save the OpenGL transformation matrices used for the previous rendering pass
+ Eigen::Matrix4f view;
+ Eigen::Matrix4f proj;
+ Eigen::Matrix4f norm;
+ public:
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+};
+
+}
+}
+
+#include <igl/serialize.h>
+namespace igl {
+ namespace serialization {
+
+ inline void serialization(bool s, igl::opengl::ViewerCore& obj, std::vector<char>& buffer)
+ {
+
+ SERIALIZE_MEMBER(background_color);
+
+ SERIALIZE_MEMBER(light_position);
+ SERIALIZE_MEMBER(lighting_factor);
+
+ SERIALIZE_MEMBER(trackball_angle);
+ SERIALIZE_MEMBER(rotation_type);
+
+ SERIALIZE_MEMBER(camera_base_zoom);
+ SERIALIZE_MEMBER(camera_zoom);
+ SERIALIZE_MEMBER(orthographic);
+ SERIALIZE_MEMBER(camera_base_translation);
+ SERIALIZE_MEMBER(camera_translation);
+ SERIALIZE_MEMBER(camera_view_angle);
+ SERIALIZE_MEMBER(camera_dnear);
+ SERIALIZE_MEMBER(camera_dfar);
+ SERIALIZE_MEMBER(camera_eye);
+ SERIALIZE_MEMBER(camera_center);
+ SERIALIZE_MEMBER(camera_up);
+
+ SERIALIZE_MEMBER(depth_test);
+ SERIALIZE_MEMBER(is_animating);
+ SERIALIZE_MEMBER(animation_max_fps);
+
+ SERIALIZE_MEMBER(object_scale);
+
+ SERIALIZE_MEMBER(viewport);
+ SERIALIZE_MEMBER(view);
+ SERIALIZE_MEMBER(proj);
+ SERIALIZE_MEMBER(norm);
+ }
+
+ template<>
+ inline void serialize(const igl::opengl::ViewerCore& obj, std::vector<char>& buffer)
+ {
+ serialization(true, const_cast<igl::opengl::ViewerCore&>(obj), buffer);
+ }
+
+ template<>
+ inline void deserialize(igl::opengl::ViewerCore& obj, const std::vector<char>& buffer)
+ {
+ serialization(false, obj, const_cast<std::vector<char>&>(buffer));
+ }
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "ViewerCore.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/ViewerData.cpp b/src/igl/opengl/ViewerData.cpp
new file mode 100644
index 000000000..0a7f2c44b
--- /dev/null
+++ b/src/igl/opengl/ViewerData.cpp
@@ -0,0 +1,691 @@
+// 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 "ViewerData.h"
+
+#include "../per_face_normals.h"
+#include "../material_colors.h"
+#include "../parula.h"
+#include "../per_vertex_normals.h"
+
+#include <iostream>
+
+
+IGL_INLINE igl::opengl::ViewerData::ViewerData()
+: dirty(MeshGL::DIRTY_ALL),
+ show_faces(true),
+ show_lines(true),
+ invert_normals(false),
+ show_overlay(true),
+ show_overlay_depth(true),
+ show_vertid(false),
+ show_faceid(false),
+ show_texture(false),
+ point_size(30),
+ line_width(0.5f),
+ line_color(0,0,0,1),
+ shininess(35.0f),
+ id(-1)
+{
+ clear();
+};
+
+IGL_INLINE void igl::opengl::ViewerData::set_face_based(bool newvalue)
+{
+ if (face_based != newvalue)
+ {
+ face_based = newvalue;
+ dirty = MeshGL::DIRTY_ALL;
+ }
+}
+
+// Helpers that draws the most common meshes
+IGL_INLINE void igl::opengl::ViewerData::set_mesh(
+ const Eigen::MatrixXd& _V, const Eigen::MatrixXi& _F)
+{
+ using namespace std;
+
+ Eigen::MatrixXd V_temp;
+
+ // If V only has two columns, pad with a column of zeros
+ if (_V.cols() == 2)
+ {
+ V_temp = Eigen::MatrixXd::Zero(_V.rows(),3);
+ V_temp.block(0,0,_V.rows(),2) = _V;
+ }
+ else
+ V_temp = _V;
+
+ if (V.rows() == 0 && F.rows() == 0)
+ {
+ V = V_temp;
+ F = _F;
+
+ compute_normals();
+ uniform_colors(
+ Eigen::Vector3d(GOLD_AMBIENT[0], GOLD_AMBIENT[1], GOLD_AMBIENT[2]),
+ Eigen::Vector3d(GOLD_DIFFUSE[0], GOLD_DIFFUSE[1], GOLD_DIFFUSE[2]),
+ Eigen::Vector3d(GOLD_SPECULAR[0], GOLD_SPECULAR[1], GOLD_SPECULAR[2]));
+
+ grid_texture();
+ }
+ else
+ {
+ if (_V.rows() == V.rows() && _F.rows() == F.rows())
+ {
+ V = V_temp;
+ F = _F;
+ }
+ else
+ cerr << "ERROR (set_mesh): The new mesh has a different number of vertices/faces. Please clear the mesh before plotting."<<endl;
+ }
+ dirty |= MeshGL::DIRTY_FACE | MeshGL::DIRTY_POSITION;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::set_vertices(const Eigen::MatrixXd& _V)
+{
+ V = _V;
+ assert(F.size() == 0 || F.maxCoeff() < V.rows());
+ dirty |= MeshGL::DIRTY_POSITION;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::set_normals(const Eigen::MatrixXd& N)
+{
+ using namespace std;
+ if (N.rows() == V.rows())
+ {
+ set_face_based(false);
+ V_normals = N;
+ }
+ else if (N.rows() == F.rows() || N.rows() == F.rows()*3)
+ {
+ set_face_based(true);
+ F_normals = N;
+ }
+ else
+ cerr << "ERROR (set_normals): Please provide a normal per face, per corner or per vertex."<<endl;
+ dirty |= MeshGL::DIRTY_NORMAL;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::set_colors(const Eigen::MatrixXd &C)
+{
+ using namespace std;
+ using namespace Eigen;
+ if(C.rows()>0 && C.cols() == 1)
+ {
+ Eigen::MatrixXd C3;
+ igl::parula(C,true,C3);
+ return set_colors(C3);
+ }
+ // Ambient color should be darker color
+ const auto ambient = [](const MatrixXd & C)->MatrixXd
+ {
+ MatrixXd T = 0.1*C;
+ T.col(3) = C.col(3);
+ return T;
+ };
+ // Specular color should be a less saturated and darker color: dampened
+ // highlights
+ const auto specular = [](const MatrixXd & C)->MatrixXd
+ {
+ const double grey = 0.3;
+ MatrixXd T = grey+0.1*(C.array()-grey);
+ T.col(3) = C.col(3);
+ return T;
+ };
+ if (C.rows() == 1)
+ {
+ for (unsigned i=0;i<V_material_diffuse.rows();++i)
+ {
+ if (C.cols() == 3)
+ V_material_diffuse.row(i) << C.row(0),1;
+ else if (C.cols() == 4)
+ V_material_diffuse.row(i) << C.row(0);
+ }
+ V_material_ambient = ambient(V_material_diffuse);
+ V_material_specular = specular(V_material_diffuse);
+
+ for (unsigned i=0;i<F_material_diffuse.rows();++i)
+ {
+ if (C.cols() == 3)
+ F_material_diffuse.row(i) << C.row(0),1;
+ else if (C.cols() == 4)
+ F_material_diffuse.row(i) << C.row(0);
+ }
+ F_material_ambient = ambient(F_material_diffuse);
+ F_material_specular = specular(F_material_diffuse);
+ }
+ else if (C.rows() == V.rows())
+ {
+ set_face_based(false);
+ for (unsigned i=0;i<V_material_diffuse.rows();++i)
+ {
+ if (C.cols() == 3)
+ V_material_diffuse.row(i) << C.row(i), 1;
+ else if (C.cols() == 4)
+ V_material_diffuse.row(i) << C.row(i);
+ }
+ V_material_ambient = ambient(V_material_diffuse);
+ V_material_specular = specular(V_material_diffuse);
+ }
+ else if (C.rows() == F.rows())
+ {
+ set_face_based(true);
+ for (unsigned i=0;i<F_material_diffuse.rows();++i)
+ {
+ if (C.cols() == 3)
+ F_material_diffuse.row(i) << C.row(i), 1;
+ else if (C.cols() == 4)
+ F_material_diffuse.row(i) << C.row(i);
+ }
+ F_material_ambient = ambient(F_material_diffuse);
+ F_material_specular = specular(F_material_diffuse);
+ }
+ else
+ cerr << "ERROR (set_colors): Please provide a single color, or a color per face or per vertex."<<endl;
+ dirty |= MeshGL::DIRTY_DIFFUSE;
+
+}
+
+IGL_INLINE void igl::opengl::ViewerData::set_uv(const Eigen::MatrixXd& UV)
+{
+ using namespace std;
+ if (UV.rows() == V.rows())
+ {
+ set_face_based(false);
+ V_uv = UV;
+ }
+ else
+ cerr << "ERROR (set_UV): Please provide uv per vertex."<<endl;;
+ dirty |= MeshGL::DIRTY_UV;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::set_uv(const Eigen::MatrixXd& UV_V, const Eigen::MatrixXi& UV_F)
+{
+ set_face_based(true);
+ V_uv = UV_V.block(0,0,UV_V.rows(),2);
+ F_uv = UV_F;
+ dirty |= MeshGL::DIRTY_UV;
+}
+
+
+IGL_INLINE void igl::opengl::ViewerData::set_texture(
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B)
+{
+ texture_R = R;
+ texture_G = G;
+ texture_B = B;
+ texture_A = Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>::Constant(R.rows(),R.cols(),255);
+ dirty |= MeshGL::DIRTY_TEXTURE;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::set_texture(
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A)
+{
+ texture_R = R;
+ texture_G = G;
+ texture_B = B;
+ texture_A = A;
+ dirty |= MeshGL::DIRTY_TEXTURE;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::set_points(
+ const Eigen::MatrixXd& P,
+ const Eigen::MatrixXd& C)
+{
+ // clear existing points
+ points.resize(0,0);
+ add_points(P,C);
+}
+
+IGL_INLINE void igl::opengl::ViewerData::add_points(const Eigen::MatrixXd& P, const Eigen::MatrixXd& C)
+{
+ Eigen::MatrixXd P_temp;
+
+ // If P only has two columns, pad with a column of zeros
+ if (P.cols() == 2)
+ {
+ P_temp = Eigen::MatrixXd::Zero(P.rows(),3);
+ P_temp.block(0,0,P.rows(),2) = P;
+ }
+ else
+ P_temp = P;
+
+ int lastid = points.rows();
+ points.conservativeResize(points.rows() + P_temp.rows(),6);
+ for (unsigned i=0; i<P_temp.rows(); ++i)
+ points.row(lastid+i) << P_temp.row(i), i<C.rows() ? C.row(i) : C.row(C.rows()-1);
+
+ dirty |= MeshGL::DIRTY_OVERLAY_POINTS;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::set_edges(
+ const Eigen::MatrixXd& P,
+ const Eigen::MatrixXi& E,
+ const Eigen::MatrixXd& C)
+{
+ using namespace Eigen;
+ lines.resize(E.rows(),9);
+ assert(C.cols() == 3);
+ for(int e = 0;e<E.rows();e++)
+ {
+ RowVector3d color;
+ if(C.size() == 3)
+ {
+ color<<C;
+ }else if(C.rows() == E.rows())
+ {
+ color<<C.row(e);
+ }
+ lines.row(e)<< P.row(E(e,0)), P.row(E(e,1)), color;
+ }
+ dirty |= MeshGL::DIRTY_OVERLAY_LINES;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::add_edges(const Eigen::MatrixXd& P1, const Eigen::MatrixXd& P2, const Eigen::MatrixXd& C)
+{
+ Eigen::MatrixXd P1_temp,P2_temp;
+
+ // If P1 only has two columns, pad with a column of zeros
+ if (P1.cols() == 2)
+ {
+ P1_temp = Eigen::MatrixXd::Zero(P1.rows(),3);
+ P1_temp.block(0,0,P1.rows(),2) = P1;
+ P2_temp = Eigen::MatrixXd::Zero(P2.rows(),3);
+ P2_temp.block(0,0,P2.rows(),2) = P2;
+ }
+ else
+ {
+ P1_temp = P1;
+ P2_temp = P2;
+ }
+
+ int lastid = lines.rows();
+ lines.conservativeResize(lines.rows() + P1_temp.rows(),9);
+ for (unsigned i=0; i<P1_temp.rows(); ++i)
+ lines.row(lastid+i) << P1_temp.row(i), P2_temp.row(i), i<C.rows() ? C.row(i) : C.row(C.rows()-1);
+
+ dirty |= MeshGL::DIRTY_OVERLAY_LINES;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::add_label(const Eigen::VectorXd& P, const std::string& str)
+{
+ Eigen::RowVectorXd P_temp;
+
+ // If P only has two columns, pad with a column of zeros
+ if (P.size() == 2)
+ {
+ P_temp = Eigen::RowVectorXd::Zero(3);
+ P_temp << P.transpose(), 0;
+ }
+ else
+ P_temp = P;
+
+ int lastid = labels_positions.rows();
+ labels_positions.conservativeResize(lastid+1, 3);
+ labels_positions.row(lastid) = P_temp;
+ labels_strings.push_back(str);
+}
+
+IGL_INLINE void igl::opengl::ViewerData::clear()
+{
+ V = Eigen::MatrixXd (0,3);
+ F = Eigen::MatrixXi (0,3);
+
+ F_material_ambient = Eigen::MatrixXd (0,4);
+ F_material_diffuse = Eigen::MatrixXd (0,4);
+ F_material_specular = Eigen::MatrixXd (0,4);
+
+ V_material_ambient = Eigen::MatrixXd (0,4);
+ V_material_diffuse = Eigen::MatrixXd (0,4);
+ V_material_specular = Eigen::MatrixXd (0,4);
+
+ F_normals = Eigen::MatrixXd (0,3);
+ V_normals = Eigen::MatrixXd (0,3);
+
+ V_uv = Eigen::MatrixXd (0,2);
+ F_uv = Eigen::MatrixXi (0,3);
+
+ lines = Eigen::MatrixXd (0,9);
+ points = Eigen::MatrixXd (0,6);
+ labels_positions = Eigen::MatrixXd (0,3);
+ labels_strings.clear();
+
+ face_based = false;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::compute_normals()
+{
+ igl::per_face_normals(V, F, F_normals);
+ igl::per_vertex_normals(V, F, F_normals, V_normals);
+ dirty |= MeshGL::DIRTY_NORMAL;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::uniform_colors(
+ const Eigen::Vector3d& ambient,
+ const Eigen::Vector3d& diffuse,
+ const Eigen::Vector3d& specular)
+{
+ Eigen::Vector4d ambient4;
+ Eigen::Vector4d diffuse4;
+ Eigen::Vector4d specular4;
+
+ ambient4 << ambient, 1;
+ diffuse4 << diffuse, 1;
+ specular4 << specular, 1;
+
+ uniform_colors(ambient4,diffuse4,specular4);
+}
+
+IGL_INLINE void igl::opengl::ViewerData::uniform_colors(
+ const Eigen::Vector4d& ambient,
+ const Eigen::Vector4d& diffuse,
+ const Eigen::Vector4d& specular)
+{
+ V_material_ambient.resize(V.rows(),4);
+ V_material_diffuse.resize(V.rows(),4);
+ V_material_specular.resize(V.rows(),4);
+
+ for (unsigned i=0; i<V.rows();++i)
+ {
+ V_material_ambient.row(i) = ambient;
+ V_material_diffuse.row(i) = diffuse;
+ V_material_specular.row(i) = specular;
+ }
+
+ F_material_ambient.resize(F.rows(),4);
+ F_material_diffuse.resize(F.rows(),4);
+ F_material_specular.resize(F.rows(),4);
+
+ for (unsigned i=0; i<F.rows();++i)
+ {
+ F_material_ambient.row(i) = ambient;
+ F_material_diffuse.row(i) = diffuse;
+ F_material_specular.row(i) = specular;
+ }
+ dirty |= MeshGL::DIRTY_SPECULAR | MeshGL::DIRTY_DIFFUSE | MeshGL::DIRTY_AMBIENT;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::grid_texture()
+{
+ // Don't do anything for an empty mesh
+ if(V.rows() == 0)
+ {
+ V_uv.resize(V.rows(),2);
+ return;
+ }
+ if (V_uv.rows() == 0)
+ {
+ V_uv = V.block(0, 0, V.rows(), 2);
+ V_uv.col(0) = V_uv.col(0).array() - V_uv.col(0).minCoeff();
+ V_uv.col(0) = V_uv.col(0).array() / V_uv.col(0).maxCoeff();
+ V_uv.col(1) = V_uv.col(1).array() - V_uv.col(1).minCoeff();
+ V_uv.col(1) = V_uv.col(1).array() / V_uv.col(1).maxCoeff();
+ V_uv = V_uv.array() * 10;
+ dirty |= MeshGL::DIRTY_TEXTURE;
+ }
+
+ unsigned size = 128;
+ unsigned size2 = size/2;
+ texture_R.resize(size, size);
+ for (unsigned i=0; i<size; ++i)
+ {
+ for (unsigned j=0; j<size; ++j)
+ {
+ texture_R(i,j) = 0;
+ if ((i<size2 && j<size2) || (i>=size2 && j>=size2))
+ texture_R(i,j) = 255;
+ }
+ }
+
+ texture_G = texture_R;
+ texture_B = texture_R;
+ texture_A = Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>::Constant(texture_R.rows(),texture_R.cols(),255);
+ dirty |= MeshGL::DIRTY_TEXTURE;
+}
+
+IGL_INLINE void igl::opengl::ViewerData::updateGL(
+ const igl::opengl::ViewerData& data,
+ const bool invert_normals,
+ igl::opengl::MeshGL& meshgl
+ )
+{
+ if (!meshgl.is_initialized)
+ {
+ meshgl.init();
+ }
+
+ bool per_corner_uv = (data.F_uv.rows() == data.F.rows());
+ bool per_corner_normals = (data.F_normals.rows() == 3 * data.F.rows());
+
+ meshgl.dirty |= data.dirty;
+
+ // Input:
+ // X #F by dim quantity
+ // Output:
+ // X_vbo #F*3 by dim scattering per corner
+ const auto per_face = [&data](
+ const Eigen::MatrixXd & X,
+ MeshGL::RowMatrixXf & X_vbo)
+ {
+ assert(X.cols() == 4);
+ X_vbo.resize(data.F.rows()*3,4);
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ X_vbo.row(i*3+j) = X.row(i).cast<float>();
+ };
+
+ // Input:
+ // X #V by dim quantity
+ // Output:
+ // X_vbo #F*3 by dim scattering per corner
+ const auto per_corner = [&data](
+ const Eigen::MatrixXd & X,
+ MeshGL::RowMatrixXf & X_vbo)
+ {
+ X_vbo.resize(data.F.rows()*3,X.cols());
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ X_vbo.row(i*3+j) = X.row(data.F(i,j)).cast<float>();
+ };
+
+ if (!data.face_based)
+ {
+ if (!(per_corner_uv || per_corner_normals))
+ {
+ // Vertex positions
+ if (meshgl.dirty & MeshGL::DIRTY_POSITION)
+ meshgl.V_vbo = data.V.cast<float>();
+
+ // Vertex normals
+ if (meshgl.dirty & MeshGL::DIRTY_NORMAL)
+ {
+ meshgl.V_normals_vbo = data.V_normals.cast<float>();
+ if (invert_normals)
+ meshgl.V_normals_vbo = -meshgl.V_normals_vbo;
+ }
+
+ // Per-vertex material settings
+ if (meshgl.dirty & MeshGL::DIRTY_AMBIENT)
+ meshgl.V_ambient_vbo = data.V_material_ambient.cast<float>();
+ if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE)
+ meshgl.V_diffuse_vbo = data.V_material_diffuse.cast<float>();
+ if (meshgl.dirty & MeshGL::DIRTY_SPECULAR)
+ meshgl.V_specular_vbo = data.V_material_specular.cast<float>();
+
+ // Face indices
+ if (meshgl.dirty & MeshGL::DIRTY_FACE)
+ meshgl.F_vbo = data.F.cast<unsigned>();
+
+ // Texture coordinates
+ if (meshgl.dirty & MeshGL::DIRTY_UV)
+ {
+ meshgl.V_uv_vbo = data.V_uv.cast<float>();
+ }
+ }
+ else
+ {
+
+ // Per vertex properties with per corner UVs
+ if (meshgl.dirty & MeshGL::DIRTY_POSITION)
+ {
+ per_corner(data.V,meshgl.V_vbo);
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_AMBIENT)
+ {
+ meshgl.V_ambient_vbo.resize(data.F.rows()*3,4);
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ meshgl.V_ambient_vbo.row(i*3+j) = data.V_material_ambient.row(data.F(i,j)).cast<float>();
+ }
+ if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE)
+ {
+ meshgl.V_diffuse_vbo.resize(data.F.rows()*3,4);
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ meshgl.V_diffuse_vbo.row(i*3+j) = data.V_material_diffuse.row(data.F(i,j)).cast<float>();
+ }
+ if (meshgl.dirty & MeshGL::DIRTY_SPECULAR)
+ {
+ meshgl.V_specular_vbo.resize(data.F.rows()*3,4);
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ meshgl.V_specular_vbo.row(i*3+j) = data.V_material_specular.row(data.F(i,j)).cast<float>();
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_NORMAL)
+ {
+ meshgl.V_normals_vbo.resize(data.F.rows()*3,3);
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ meshgl.V_normals_vbo.row(i*3+j) =
+ per_corner_normals ?
+ data.F_normals.row(i*3+j).cast<float>() :
+ data.V_normals.row(data.F(i,j)).cast<float>();
+
+
+ if (invert_normals)
+ meshgl.V_normals_vbo = -meshgl.V_normals_vbo;
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_FACE)
+ {
+ meshgl.F_vbo.resize(data.F.rows(),3);
+ for (unsigned i=0; i<data.F.rows();++i)
+ meshgl.F_vbo.row(i) << i*3+0, i*3+1, i*3+2;
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_UV)
+ {
+ meshgl.V_uv_vbo.resize(data.F.rows()*3,2);
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ meshgl.V_uv_vbo.row(i*3+j) =
+ data.V_uv.row(per_corner_uv ?
+ data.F_uv(i,j) : data.F(i,j)).cast<float>();
+ }
+ }
+ }
+ else
+ {
+ if (meshgl.dirty & MeshGL::DIRTY_POSITION)
+ {
+ per_corner(data.V,meshgl.V_vbo);
+ }
+ if (meshgl.dirty & MeshGL::DIRTY_AMBIENT)
+ {
+ per_face(data.F_material_ambient,meshgl.V_ambient_vbo);
+ }
+ if (meshgl.dirty & MeshGL::DIRTY_DIFFUSE)
+ {
+ per_face(data.F_material_diffuse,meshgl.V_diffuse_vbo);
+ }
+ if (meshgl.dirty & MeshGL::DIRTY_SPECULAR)
+ {
+ per_face(data.F_material_specular,meshgl.V_specular_vbo);
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_NORMAL)
+ {
+ meshgl.V_normals_vbo.resize(data.F.rows()*3,3);
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ meshgl.V_normals_vbo.row(i*3+j) =
+ per_corner_normals ?
+ data.F_normals.row(i*3+j).cast<float>() :
+ data.F_normals.row(i).cast<float>();
+
+ if (invert_normals)
+ meshgl.V_normals_vbo = -meshgl.V_normals_vbo;
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_FACE)
+ {
+ meshgl.F_vbo.resize(data.F.rows(),3);
+ for (unsigned i=0; i<data.F.rows();++i)
+ meshgl.F_vbo.row(i) << i*3+0, i*3+1, i*3+2;
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_UV)
+ {
+ meshgl.V_uv_vbo.resize(data.F.rows()*3,2);
+ for (unsigned i=0; i<data.F.rows();++i)
+ for (unsigned j=0;j<3;++j)
+ meshgl.V_uv_vbo.row(i*3+j) = data.V_uv.row(per_corner_uv ? data.F_uv(i,j) : data.F(i,j)).cast<float>();
+ }
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_TEXTURE)
+ {
+ meshgl.tex_u = data.texture_R.rows();
+ meshgl.tex_v = data.texture_R.cols();
+ meshgl.tex.resize(data.texture_R.size()*4);
+ for (unsigned i=0;i<data.texture_R.size();++i)
+ {
+ meshgl.tex(i*4+0) = data.texture_R(i);
+ meshgl.tex(i*4+1) = data.texture_G(i);
+ meshgl.tex(i*4+2) = data.texture_B(i);
+ meshgl.tex(i*4+3) = data.texture_A(i);
+ }
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_OVERLAY_LINES)
+ {
+ meshgl.lines_V_vbo.resize(data.lines.rows()*2,3);
+ meshgl.lines_V_colors_vbo.resize(data.lines.rows()*2,3);
+ meshgl.lines_F_vbo.resize(data.lines.rows()*2,1);
+ for (unsigned i=0; i<data.lines.rows();++i)
+ {
+ meshgl.lines_V_vbo.row(2*i+0) = data.lines.block<1, 3>(i, 0).cast<float>();
+ meshgl.lines_V_vbo.row(2*i+1) = data.lines.block<1, 3>(i, 3).cast<float>();
+ meshgl.lines_V_colors_vbo.row(2*i+0) = data.lines.block<1, 3>(i, 6).cast<float>();
+ meshgl.lines_V_colors_vbo.row(2*i+1) = data.lines.block<1, 3>(i, 6).cast<float>();
+ meshgl.lines_F_vbo(2*i+0) = 2*i+0;
+ meshgl.lines_F_vbo(2*i+1) = 2*i+1;
+ }
+ }
+
+ if (meshgl.dirty & MeshGL::DIRTY_OVERLAY_POINTS)
+ {
+ meshgl.points_V_vbo.resize(data.points.rows(),3);
+ meshgl.points_V_colors_vbo.resize(data.points.rows(),3);
+ meshgl.points_F_vbo.resize(data.points.rows(),1);
+ for (unsigned i=0; i<data.points.rows();++i)
+ {
+ meshgl.points_V_vbo.row(i) = data.points.block<1, 3>(i, 0).cast<float>();
+ meshgl.points_V_colors_vbo.row(i) = data.points.block<1, 3>(i, 3).cast<float>();
+ meshgl.points_F_vbo(i) = i;
+ }
+ }
+}
diff --git a/src/igl/opengl/ViewerData.h b/src/igl/opengl/ViewerData.h
new file mode 100644
index 000000000..b3ec9178c
--- /dev/null
+++ b/src/igl/opengl/ViewerData.h
@@ -0,0 +1,276 @@
+// 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_VIEWERDATA_H
+#define IGL_VIEWERDATA_H
+
+#include "../igl_inline.h"
+#include "MeshGL.h"
+#include <cassert>
+#include <cstdint>
+#include <Eigen/Core>
+#include <memory>
+#include <vector>
+
+// Alec: This is a mesh class containing a variety of data types (normals,
+// overlays, material colors, etc.)
+//
+namespace igl
+{
+
+// TODO: write documentation
+namespace opengl
+{
+
+class ViewerData
+{
+public:
+ ViewerData();
+
+ // Empty all fields
+ IGL_INLINE void clear();
+
+ // Change the visualization mode, invalidating the cache if necessary
+ IGL_INLINE void set_face_based(bool newvalue);
+
+ // Helpers that can draw the most common meshes
+ IGL_INLINE void set_mesh(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F);
+ IGL_INLINE void set_vertices(const Eigen::MatrixXd& V);
+ IGL_INLINE void set_normals(const Eigen::MatrixXd& N);
+
+ // Set the color of the mesh
+ //
+ // Inputs:
+ // C #V|#F|1 by 3 list of colors
+ IGL_INLINE void set_colors(const Eigen::MatrixXd &C);
+ // Set per-vertex UV coordinates
+ //
+ // Inputs:
+ // UV #V by 2 list of UV coordinates (indexed by F)
+ IGL_INLINE void set_uv(const Eigen::MatrixXd& UV);
+ // Set per-corner UV coordinates
+ //
+ // Inputs:
+ // UV_V #UV by 2 list of UV coordinates
+ // UV_F #F by 3 list of UV indices into UV_V
+ IGL_INLINE void set_uv(const Eigen::MatrixXd& UV_V, const Eigen::MatrixXi& UV_F);
+ // Set the texture associated with the mesh.
+ //
+ // Inputs:
+ // R width by height image matrix of red channel
+ // G width by height image matrix of green channel
+ // B width by height image matrix of blue channel
+ //
+ IGL_INLINE void set_texture(
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B);
+
+ // Set the texture associated with the mesh.
+ //
+ // Inputs:
+ // R width by height image matrix of red channel
+ // G width by height image matrix of green channel
+ // B width by height image matrix of blue channel
+ // A width by height image matrix of alpha channel
+ //
+ IGL_INLINE void set_texture(
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& R,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B,
+ const Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A);
+
+ // Sets points given a list of point vertices. In constrast to `set_points`
+ // this will (purposefully) clober existing points.
+ //
+ // Inputs:
+ // P #P by 3 list of vertex positions
+ // C #P|1 by 3 color(s)
+ IGL_INLINE void set_points(
+ const Eigen::MatrixXd& P,
+ const Eigen::MatrixXd& C);
+ IGL_INLINE void add_points(const Eigen::MatrixXd& P, const Eigen::MatrixXd& C);
+ // Sets edges given a list of edge vertices and edge indices. In constrast
+ // to `add_edges` this will (purposefully) clober existing edges.
+ //
+ // Inputs:
+ // P #P by 3 list of vertex positions
+ // E #E by 2 list of edge indices into P
+ // C #E|1 by 3 color(s)
+ IGL_INLINE void set_edges (const Eigen::MatrixXd& P, const Eigen::MatrixXi& E, const Eigen::MatrixXd& C);
+ // Alec: This is very confusing. Why does add_edges have a different API from
+ // set_edges?
+ IGL_INLINE void add_edges (const Eigen::MatrixXd& P1, const Eigen::MatrixXd& P2, const Eigen::MatrixXd& C);
+ IGL_INLINE void add_label (const Eigen::VectorXd& P, const std::string& str);
+
+ // Computes the normals of the mesh
+ IGL_INLINE void compute_normals();
+
+ // Assigns uniform colors to all faces/vertices
+ IGL_INLINE void uniform_colors(
+ const Eigen::Vector3d& diffuse,
+ const Eigen::Vector3d& ambient,
+ const Eigen::Vector3d& specular);
+
+ // Assigns uniform colors to all faces/vertices
+ IGL_INLINE void uniform_colors(
+ const Eigen::Vector4d& ambient,
+ const Eigen::Vector4d& diffuse,
+ const Eigen::Vector4d& specular);
+
+ // Generates a default grid texture
+ IGL_INLINE void grid_texture();
+
+ Eigen::MatrixXd V; // Vertices of the current mesh (#V x 3)
+ Eigen::MatrixXi F; // Faces of the mesh (#F x 3)
+
+ // Per face attributes
+ Eigen::MatrixXd F_normals; // One normal per face
+
+ Eigen::MatrixXd F_material_ambient; // Per face ambient color
+ Eigen::MatrixXd F_material_diffuse; // Per face diffuse color
+ Eigen::MatrixXd F_material_specular; // Per face specular color
+
+ // Per vertex attributes
+ Eigen::MatrixXd V_normals; // One normal per vertex
+
+ Eigen::MatrixXd V_material_ambient; // Per vertex ambient color
+ Eigen::MatrixXd V_material_diffuse; // Per vertex diffuse color
+ Eigen::MatrixXd V_material_specular; // Per vertex specular color
+
+ // UV parametrization
+ Eigen::MatrixXd V_uv; // UV vertices
+ Eigen::MatrixXi F_uv; // optional faces for UVs
+
+ // Texture
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_R;
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_G;
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_B;
+ Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic> texture_A;
+
+ // Overlays
+
+ // Lines plotted over the scene
+ // (Every row contains 9 doubles in the following format S_x, S_y, S_z, T_x, T_y, T_z, C_r, C_g, C_b),
+ // with S and T the coordinates of the two vertices of the line in global coordinates, and C the color in floating point rgb format
+ Eigen::MatrixXd lines;
+
+ // Points plotted over the scene
+ // (Every row contains 6 doubles in the following format P_x, P_y, P_z, C_r, C_g, C_b),
+ // with P the position in global coordinates of the center of the point, and C the color in floating point rgb format
+ Eigen::MatrixXd points;
+
+ // Text labels plotted over the scene
+ // Textp contains, in the i-th row, the position in global coordinates where the i-th label should be anchored
+ // Texts contains in the i-th position the text of the i-th label
+ Eigen::MatrixXd labels_positions;
+ std::vector<std::string> labels_strings;
+
+ // Marks dirty buffers that need to be uploaded to OpenGL
+ uint32_t dirty;
+
+ // Enable per-face or per-vertex properties
+ bool face_based;
+
+ // Visualization options
+ bool show_overlay;
+ bool show_overlay_depth;
+ bool show_texture;
+ bool show_faces;
+ bool show_lines;
+ bool show_vertid;
+ bool show_faceid;
+ bool invert_normals;
+
+ // Point size / line width
+ float point_size;
+ float line_width;
+ Eigen::Vector4f line_color;
+
+ // Shape material
+ float shininess;
+
+ // Unique identifier
+ int id;
+
+ // OpenGL representation of the mesh
+ igl::opengl::MeshGL meshgl;
+
+ // Update contents from a 'Data' instance
+ IGL_INLINE void updateGL(
+ const igl::opengl::ViewerData& data,
+ const bool invert_normals,
+ igl::opengl::MeshGL& meshgl);
+};
+
+} // namespace opengl
+} // namespace igl
+
+////////////////////////////////////////////////////////////////////////////////
+
+#include <igl/serialize.h>
+namespace igl
+{
+ namespace serialization
+ {
+ inline void serialization(bool s, igl::opengl::ViewerData& obj, std::vector<char>& buffer)
+ {
+ SERIALIZE_MEMBER(V);
+ SERIALIZE_MEMBER(F);
+ SERIALIZE_MEMBER(F_normals);
+ SERIALIZE_MEMBER(F_material_ambient);
+ SERIALIZE_MEMBER(F_material_diffuse);
+ SERIALIZE_MEMBER(F_material_specular);
+ SERIALIZE_MEMBER(V_normals);
+ SERIALIZE_MEMBER(V_material_ambient);
+ SERIALIZE_MEMBER(V_material_diffuse);
+ SERIALIZE_MEMBER(V_material_specular);
+ SERIALIZE_MEMBER(V_uv);
+ SERIALIZE_MEMBER(F_uv);
+ SERIALIZE_MEMBER(texture_R);
+ SERIALIZE_MEMBER(texture_G);
+ SERIALIZE_MEMBER(texture_B);
+ SERIALIZE_MEMBER(texture_A);
+ SERIALIZE_MEMBER(lines);
+ SERIALIZE_MEMBER(points);
+ SERIALIZE_MEMBER(labels_positions);
+ SERIALIZE_MEMBER(labels_strings);
+ SERIALIZE_MEMBER(dirty);
+ SERIALIZE_MEMBER(face_based);
+ SERIALIZE_MEMBER(show_faces);
+ SERIALIZE_MEMBER(show_lines);
+ SERIALIZE_MEMBER(invert_normals);
+ SERIALIZE_MEMBER(show_overlay);
+ SERIALIZE_MEMBER(show_overlay_depth);
+ SERIALIZE_MEMBER(show_vertid);
+ SERIALIZE_MEMBER(show_faceid);
+ SERIALIZE_MEMBER(show_texture);
+ SERIALIZE_MEMBER(point_size);
+ SERIALIZE_MEMBER(line_width);
+ SERIALIZE_MEMBER(line_color);
+ SERIALIZE_MEMBER(shininess);
+ SERIALIZE_MEMBER(id);
+ }
+ template<>
+ inline void serialize(const igl::opengl::ViewerData& obj, std::vector<char>& buffer)
+ {
+ serialization(true, const_cast<igl::opengl::ViewerData&>(obj), buffer);
+ }
+ template<>
+ inline void deserialize(igl::opengl::ViewerData& obj, const std::vector<char>& buffer)
+ {
+ serialization(false, obj, const_cast<std::vector<char>&>(buffer));
+ obj.dirty = igl::opengl::MeshGL::DIRTY_ALL;
+ }
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "ViewerData.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/bind_vertex_attrib_array.cpp b/src/igl/opengl/bind_vertex_attrib_array.cpp
new file mode 100644
index 000000000..4aa807c18
--- /dev/null
+++ b/src/igl/opengl/bind_vertex_attrib_array.cpp
@@ -0,0 +1,24 @@
+#include "bind_vertex_attrib_array.h"
+
+IGL_INLINE GLint igl::opengl::bind_vertex_attrib_array(
+ const GLuint program_shader,
+ const std::string &name,
+ GLuint bufferID,
+ const Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> &M,
+ bool refresh)
+{
+ GLint id = glGetAttribLocation(program_shader, name.c_str());
+ if (id < 0)
+ return id;
+ if (M.size() == 0)
+ {
+ glDisableVertexAttribArray(id);
+ return id;
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, bufferID);
+ if (refresh)
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float)*M.size(), M.data(), GL_DYNAMIC_DRAW);
+ glVertexAttribPointer(id, M.cols(), GL_FLOAT, GL_FALSE, 0, 0);
+ glEnableVertexAttribArray(id);
+ return id;
+}
diff --git a/src/igl/opengl/bind_vertex_attrib_array.h b/src/igl/opengl/bind_vertex_attrib_array.h
new file mode 100644
index 000000000..4e9443136
--- /dev/null
+++ b/src/igl/opengl/bind_vertex_attrib_array.h
@@ -0,0 +1,32 @@
+#ifndef IGL_OPENGL_BIND_VERTEX_ATTRIB_ARRAY_H
+#define IGL_OPENGL_BIND_VERTEX_ATTRIB_ARRAY_H
+#include "gl.h"
+#include "../igl_inline.h"
+#include <Eigen/Core>
+#include <string>
+namespace igl
+{
+ namespace opengl
+ {
+ // Bind a per-vertex array attribute and refresh its contents from an Eigen
+ // matrix
+ //
+ // Inputs:
+ // program_shader id of shader program
+ // name name of attribute in vertex shader
+ // bufferID id of buffer to bind to
+ // M #V by dim matrix of per-vertex data
+ // refresh whether to actually call glBufferData or just bind the buffer
+ // Returns id of named attribute in shader
+ IGL_INLINE GLint bind_vertex_attrib_array(
+ const GLuint program_shader,
+ const std::string &name,
+ GLuint bufferID,
+ const Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> &M,
+ bool refresh);
+ }
+}
+#ifndef IGL_STATIC_LIBRARY
+#include "bind_vertex_attrib_array.cpp"
+#endif
+#endif
diff --git a/src/igl/opengl/create_index_vbo.cpp b/src/igl/opengl/create_index_vbo.cpp
new file mode 100644
index 000000000..76f6248c0
--- /dev/null
+++ b/src/igl/opengl/create_index_vbo.cpp
@@ -0,0 +1,46 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "create_index_vbo.h"
+
+// http://www.songho.ca/opengl/gl_vbo.html#create
+IGL_INLINE void igl::opengl::create_index_vbo(
+ const Eigen::MatrixXi & F,
+ GLuint & F_vbo_id)
+{
+ // Generate Buffers
+ glGenBuffers(1,&F_vbo_id);
+ // Bind Buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,F_vbo_id);
+ // Copy data to buffers
+ // We expect a matrix with each vertex position on a row, we then want to
+ // pass this data to OpenGL reading across rows (row-major)
+ if(F.Options & Eigen::RowMajor)
+ {
+ glBufferData(
+ GL_ELEMENT_ARRAY_BUFFER,
+ sizeof(int)*F.size(),
+ F.data(),
+ GL_STATIC_DRAW);
+ }else
+ {
+ // Create temporary copy of transpose
+ Eigen::MatrixXi FT = F.transpose();
+ // If its column major then we need to temporarily store a transpose
+ glBufferData(
+ GL_ELEMENT_ARRAY_BUFFER,
+ sizeof(int)*F.size(),
+ FT.data(),
+ GL_STATIC_DRAW);
+ }
+ // bind with 0, so, switch back to normal pointer operation
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+#endif
diff --git a/src/igl/opengl/create_index_vbo.h b/src/igl/opengl/create_index_vbo.h
new file mode 100644
index 000000000..e48f3a489
--- /dev/null
+++ b/src/igl/opengl/create_index_vbo.h
@@ -0,0 +1,35 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_CREATE_INDEX_VBO_H
+#define IGL_OPENGL_CREATE_INDEX_VBO_H
+#include "../igl_inline.h"
+#include "gl.h"
+#include <Eigen/Core>
+
+// Create a VBO (Vertex Buffer Object) for a list of indices:
+// GL_ELEMENT_ARRAY_BUFFER_ARB for the triangle indices (F)
+namespace igl
+{
+ namespace opengl
+ {
+ // Inputs:
+ // F #F by 3 eigen Matrix of face (triangle) indices
+ // Outputs:
+ // F_vbo_id buffer id for face indices
+ //
+ IGL_INLINE void create_index_vbo(
+ const Eigen::MatrixXi & F,
+ GLuint & F_vbo_id);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "create_index_vbo.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/create_mesh_vbo.cpp b/src/igl/opengl/create_mesh_vbo.cpp
new file mode 100644
index 000000000..3876e1dfa
--- /dev/null
+++ b/src/igl/opengl/create_mesh_vbo.cpp
@@ -0,0 +1,43 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "create_mesh_vbo.h"
+
+#include "create_vector_vbo.h"
+#include "create_index_vbo.h"
+
+// http://www.songho.ca/opengl/gl_vbo.html#create
+IGL_INLINE void igl::opengl::create_mesh_vbo(
+ const Eigen::MatrixXd & V,
+ const Eigen::MatrixXi & F,
+ GLuint & V_vbo_id,
+ GLuint & F_vbo_id)
+{
+ // Create VBO for vertex position vectors
+ create_vector_vbo(V,V_vbo_id);
+ // Create VBO for face index lists
+ create_index_vbo(F,F_vbo_id);
+}
+
+// http://www.songho.ca/opengl/gl_vbo.html#create
+IGL_INLINE void igl::opengl::create_mesh_vbo(
+ const Eigen::MatrixXd & V,
+ const Eigen::MatrixXi & F,
+ const Eigen::MatrixXd & N,
+ GLuint & V_vbo_id,
+ GLuint & F_vbo_id,
+ GLuint & N_vbo_id)
+{
+ // Create VBOs for faces and vertices
+ create_mesh_vbo(V,F,V_vbo_id,F_vbo_id);
+ // Create VBO for normal vectors
+ create_vector_vbo(N,N_vbo_id);
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+#endif
diff --git a/src/igl/opengl/create_mesh_vbo.h b/src/igl/opengl/create_mesh_vbo.h
new file mode 100644
index 000000000..ecb303b09
--- /dev/null
+++ b/src/igl/opengl/create_mesh_vbo.h
@@ -0,0 +1,61 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_CREATE_MESH_VBO_H
+#define IGL_OPENGL_CREATE_MESH_VBO_H
+#include "../igl_inline.h"
+#include "gl.h"
+#include <Eigen/Core>
+
+// Create a VBO (Vertex Buffer Object) for a mesh. Actually two VBOs: one
+// GL_ARRAY_BUFFER for the vertex positions (V) and one
+// GL_ELEMENT_ARRAY_BUFFER for the triangle indices (F)
+namespace igl
+{
+ namespace opengl
+ {
+
+ // Inputs:
+ // V #V by 3 eigen Matrix of mesh vertex 3D positions
+ // F #F by 3 eigen Matrix of face (triangle) indices
+ // Outputs:
+ // V_vbo_id buffer id for vertex positions
+ // F_vbo_id buffer id for face indices
+ //
+ // NOTE: when using glDrawElements VBOs for V and F using MatrixXd and
+ // MatrixXi will have types GL_DOUBLE and GL_UNSIGNED_INT respectively
+ //
+ IGL_INLINE void create_mesh_vbo(
+ const Eigen::MatrixXd & V,
+ const Eigen::MatrixXi & F,
+ GLuint & V_vbo_id,
+ GLuint & F_vbo_id);
+
+ // Inputs:
+ // V #V by 3 eigen Matrix of mesh vertex 3D positions
+ // F #F by 3 eigen Matrix of face (triangle) indices
+ // N #V by 3 eigen Matrix of mesh vertex 3D normals
+ // Outputs:
+ // V_vbo_id buffer id for vertex positions
+ // F_vbo_id buffer id for face indices
+ // N_vbo_id buffer id for vertex positions
+ IGL_INLINE void create_mesh_vbo(
+ const Eigen::MatrixXd & V,
+ const Eigen::MatrixXi & F,
+ const Eigen::MatrixXd & N,
+ GLuint & V_vbo_id,
+ GLuint & F_vbo_id,
+ GLuint & N_vbo_id);
+ }
+
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "create_mesh_vbo.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/create_shader_program.cpp b/src/igl/opengl/create_shader_program.cpp
new file mode 100644
index 000000000..6c4b31a2f
--- /dev/null
+++ b/src/igl/opengl/create_shader_program.cpp
@@ -0,0 +1,141 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "create_shader_program.h"
+
+#include "load_shader.h"
+#include "print_program_info_log.h"
+#include <iostream>
+#include <cstdio>
+
+IGL_INLINE bool igl::opengl::create_shader_program(
+ const std::string & geom_source,
+ const std::string & vert_source,
+ const std::string & frag_source,
+ const std::map<std::string,GLuint> & attrib,
+ GLuint & id)
+{
+ using namespace std;
+ if(vert_source == "" && frag_source == "")
+ {
+ cerr<<
+ "create_shader_program() could not create shader program,"
+ " both .vert and .frag source given were empty"<<endl;
+ return false;
+ }
+
+ // create program
+ id = glCreateProgram();
+ if(id == 0)
+ {
+ cerr<<"create_shader_program() could not create shader program."<<endl;
+ return false;
+ }
+ GLuint g = 0,f = 0,v = 0;
+
+ if(geom_source != "")
+ {
+ // load vertex shader
+ g = igl::opengl::load_shader(geom_source.c_str(),GL_GEOMETRY_SHADER);
+ if(g == 0)
+ {
+ cerr<<"geometry shader failed to compile."<<endl;
+ return false;
+ }
+ glAttachShader(id,g);
+ }
+
+ if(vert_source != "")
+ {
+ // load vertex shader
+ v = igl::opengl::load_shader(vert_source.c_str(),GL_VERTEX_SHADER);
+ if(v == 0)
+ {
+ cerr<<"vertex shader failed to compile."<<endl;
+ return false;
+ }
+ glAttachShader(id,v);
+ }
+
+ if(frag_source != "")
+ {
+ // load fragment shader
+ f = igl::opengl::load_shader(frag_source.c_str(),GL_FRAGMENT_SHADER);
+ if(f == 0)
+ {
+ cerr<<"fragment shader failed to compile."<<endl;
+ return false;
+ }
+ glAttachShader(id,f);
+ }
+
+ // loop over attributes
+ for(
+ std::map<std::string,GLuint>::const_iterator ait = attrib.begin();
+ ait != attrib.end();
+ ait++)
+ {
+ glBindAttribLocation(
+ id,
+ (*ait).second,
+ (*ait).first.c_str());
+ }
+ // Link program
+ glLinkProgram(id);
+ const auto & detach = [&id](const GLuint shader)
+ {
+ if(shader)
+ {
+ glDetachShader(id,shader);
+ glDeleteShader(shader);
+ }
+ };
+ detach(g);
+ detach(f);
+ detach(v);
+
+ // print log if any
+ igl::opengl::print_program_info_log(id);
+
+ return true;
+}
+
+IGL_INLINE bool igl::opengl::create_shader_program(
+ const std::string & vert_source,
+ const std::string & frag_source,
+ const std::map<std::string,GLuint> & attrib,
+ GLuint & prog_id)
+{
+ return create_shader_program("",vert_source,frag_source,attrib,prog_id);
+}
+
+
+IGL_INLINE GLuint igl::opengl::create_shader_program(
+ const std::string & geom_source,
+ const std::string & vert_source,
+ const std::string & frag_source,
+ const std::map<std::string,GLuint> & attrib)
+{
+ GLuint prog_id = 0;
+ create_shader_program(geom_source,vert_source,frag_source,attrib,prog_id);
+ return prog_id;
+}
+
+IGL_INLINE GLuint igl::opengl::create_shader_program(
+ const std::string & vert_source,
+ const std::string & frag_source,
+ const std::map<std::string,GLuint> & attrib)
+{
+ GLuint prog_id = 0;
+ create_shader_program(vert_source,frag_source,attrib,prog_id);
+ return prog_id;
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+#endif
+
diff --git a/src/igl/opengl/create_shader_program.h b/src/igl/opengl/create_shader_program.h
new file mode 100644
index 000000000..5dfdc701d
--- /dev/null
+++ b/src/igl/opengl/create_shader_program.h
@@ -0,0 +1,64 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_CREATE_SHADER_PROGRAM_H
+#define IGL_OPENGL_CREATE_SHADER_PROGRAM_H
+#include "../igl_inline.h"
+#include "gl.h"
+#include <string>
+#include <map>
+
+namespace igl
+{
+ namespace opengl
+ {
+ // Create a shader program with a vertex and fragments shader loading from
+ // source strings and vertex attributes assigned from a map before linking the
+ // shaders to the program, making it ready to use with glUseProgram(id)
+ // Inputs:
+ // geom_source string containing source code of geometry shader (can be
+ // "" to mean use default pass-through)
+ // vert_source string containing source code of vertex shader
+ // frag_source string containing source code of fragment shader
+ // attrib map containing table of vertex attribute strings add their
+ // correspondingly ids (generated previously using glBindAttribLocation)
+ // Outputs:
+ // id index id of created shader, set to 0 on error
+ // Returns true on success, false on error
+ //
+ // Note: Caller is responsible for making sure that current value of id is not
+ // leaking a shader (since it will be overwritten)
+ //
+ // See also: destroy_shader_program
+ IGL_INLINE bool create_shader_program(
+ const std::string &geom_source,
+ const std::string &vert_source,
+ const std::string &frag_source,
+ const std::map<std::string,GLuint> &attrib,
+ GLuint & id);
+ IGL_INLINE bool create_shader_program(
+ const std::string &vert_source,
+ const std::string &frag_source,
+ const std::map<std::string,GLuint> &attrib,
+ GLuint & id);
+ IGL_INLINE GLuint create_shader_program(
+ const std::string & geom_source,
+ const std::string & vert_source,
+ const std::string & frag_source,
+ const std::map<std::string,GLuint> &attrib);
+ IGL_INLINE GLuint create_shader_program(
+ const std::string & vert_source,
+ const std::string & frag_source,
+ const std::map<std::string,GLuint> &attrib);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "create_shader_program.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/create_vector_vbo.cpp b/src/igl/opengl/create_vector_vbo.cpp
new file mode 100644
index 000000000..8690359b7
--- /dev/null
+++ b/src/igl/opengl/create_vector_vbo.cpp
@@ -0,0 +1,57 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "create_vector_vbo.h"
+
+#include <cassert>
+
+// http://www.songho.ca/opengl/gl_vbo.html#create
+template <typename T>
+IGL_INLINE void igl::opengl::create_vector_vbo(
+ const Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & V,
+ GLuint & V_vbo_id)
+{
+ //// Expects that input is list of 3D vectors along rows
+ //assert(V.cols() == 3);
+
+ // Generate Buffers
+ glGenBuffers(1,&V_vbo_id);
+ // Bind Buffers
+ glBindBuffer(GL_ARRAY_BUFFER,V_vbo_id);
+ // Copy data to buffers
+ // We expect a matrix with each vertex position on a row, we then want to
+ // pass this data to OpenGL reading across rows (row-major)
+ if(V.Options & Eigen::RowMajor)
+ {
+ glBufferData(
+ GL_ARRAY_BUFFER,
+ sizeof(T)*V.size(),
+ V.data(),
+ GL_STATIC_DRAW);
+ }else
+ {
+ // Create temporary copy of transpose
+ Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> VT = V.transpose();
+ // If its column major then we need to temporarily store a transpose
+ glBufferData(
+ GL_ARRAY_BUFFER,
+ sizeof(T)*V.size(),
+ VT.data(),
+ GL_STATIC_DRAW);
+ }
+ // bind with 0, so, switch back to normal pointer operation
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+// generated by autoexplicit.sh
+template void igl::opengl::create_vector_vbo<int>(Eigen::Matrix<int, -1, -1, 0, -1, -1> const&, unsigned int&);
+// generated by autoexplicit.sh
+template void igl::opengl::create_vector_vbo<double>(Eigen::Matrix<double, -1, -1, 0, -1, -1> const&, unsigned int&);
+#endif
+
diff --git a/src/igl/opengl/create_vector_vbo.h b/src/igl/opengl/create_vector_vbo.h
new file mode 100644
index 000000000..757c1a199
--- /dev/null
+++ b/src/igl/opengl/create_vector_vbo.h
@@ -0,0 +1,38 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_CREATE_VECTOR_VBO_H
+#define IGL_OPENGL_CREATE_VECTOR_VBO_H
+#include "../igl_inline.h"
+#include "gl.h"
+#include <Eigen/Core>
+
+// Create a VBO (Vertex Buffer Object) for a list of vectors:
+// GL_ARRAY_BUFFER for the vectors (V)
+namespace igl
+{
+ namespace opengl
+ {
+ // Templates:
+ // T should be a eigen matrix primitive type like int or double
+ // Inputs:
+ // V m by n eigen Matrix of type T values
+ // Outputs:
+ // V_vbo_id buffer id for vectors
+ //
+ template <typename T>
+ IGL_INLINE void create_vector_vbo(
+ const Eigen::Matrix<T,Eigen::Dynamic,Eigen::Dynamic> & V,
+ GLuint & V_vbo_id);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "create_vector_vbo.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/destroy_shader_program.cpp b/src/igl/opengl/destroy_shader_program.cpp
new file mode 100644
index 000000000..2ceace7c2
--- /dev/null
+++ b/src/igl/opengl/destroy_shader_program.cpp
@@ -0,0 +1,50 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "destroy_shader_program.h"
+#include "report_gl_error.h"
+#include <cstdio>
+
+IGL_INLINE bool igl::opengl::destroy_shader_program(const GLuint id)
+{
+ // Don't try to destroy id == 0 (no shader program)
+ if(id == 0)
+ {
+ fprintf(stderr,"Error: destroy_shader_program() id = %d"
+ " but must should be positive\n",id);
+ return false;
+ }
+ // Get each attached shader one by one and detach and delete it
+ GLsizei count;
+ // shader id
+ GLuint s;
+ do
+ {
+ // Try to get at most *1* attached shader
+ glGetAttachedShaders(id,1,&count,&s);
+ GLenum err = igl::opengl::report_gl_error();
+ if (GL_NO_ERROR != err)
+ {
+ return false;
+ }
+ // Check that we actually got *1*
+ if(count == 1)
+ {
+ // Detach and delete this shader
+ glDetachShader(id,s);
+ glDeleteShader(s);
+ }
+ }while(count > 0);
+ // Now that all of the shaders are gone we can just delete the program
+ glDeleteProgram(id);
+ return true;
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+#endif
+
diff --git a/src/igl/opengl/destroy_shader_program.h b/src/igl/opengl/destroy_shader_program.h
new file mode 100644
index 000000000..5d53d8c3e
--- /dev/null
+++ b/src/igl/opengl/destroy_shader_program.h
@@ -0,0 +1,35 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_DESTROY_SHADER_PROGRAM_H
+#define IGL_OPENGL_DESTROY_SHADER_PROGRAM_H
+#include "../igl_inline.h"
+#include "gl.h"
+
+namespace igl
+{
+ namespace opengl
+ {
+ // Properly destroy a shader program. Detach and delete each of its shaders
+ // and delete it
+ // Inputs:
+ // id index id of created shader, set to 0 on error
+ // Returns true on success, false on error
+ //
+ // Note: caller is responsible for making sure he doesn't foolishly continue
+ // to use id as if it still contains a program
+ //
+ // See also: create_shader_program
+ IGL_INLINE bool destroy_shader_program(const GLuint id);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "destroy_shader_program.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/gl.h b/src/igl/opengl/gl.h
new file mode 100644
index 000000000..fc037c198
--- /dev/null
+++ b/src/igl/opengl/gl.h
@@ -0,0 +1,25 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013, 2017 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_GL_H
+#define IGL_OPENGL_GL_H
+
+#ifdef IGL_OPENGL2_GL_H
+# error "igl/opengl2/gl.h already included"
+#endif
+
+// Always use this:
+// #include "gl.h"
+// Instead of:
+// #include <OpenGL/gl3.h>
+// or
+// #include <GL/gl.h>
+//
+
+#include <glad/glad.h>
+
+#endif
diff --git a/src/igl/opengl/gl_type_size.cpp b/src/igl/opengl/gl_type_size.cpp
new file mode 100644
index 000000000..0fd02bf05
--- /dev/null
+++ b/src/igl/opengl/gl_type_size.cpp
@@ -0,0 +1,30 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "gl_type_size.h"
+#include <cassert>
+
+IGL_INLINE int igl::opengl::gl_type_size(const GLenum type)
+{
+ switch(type)
+ {
+ case GL_DOUBLE:
+ return 8;
+ break;
+ case GL_FLOAT:
+ return 4;
+ break;
+ case GL_INT:
+ return 4;
+ break;
+ default:
+ // should handle all other GL_[types]
+ assert(false && "Implementation incomplete.");
+ break;
+ }
+ return -1;
+}
diff --git a/src/igl/opengl/gl_type_size.h b/src/igl/opengl/gl_type_size.h
new file mode 100644
index 000000000..a74119549
--- /dev/null
+++ b/src/igl/opengl/gl_type_size.h
@@ -0,0 +1,28 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_GL_TYPE_SIZE_H
+#define IGL_OPENGL_GL_TYPE_SIZE_H
+#include "../igl_inline.h"
+#include "gl.h"
+
+namespace igl
+{
+ namespace opengl
+ {
+ // Return the number of bytes for a given OpenGL type // Inputs:
+ // type enum value of opengl type
+ // Returns size in bytes of type
+ IGL_INLINE int gl_type_size(const GLenum type);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "gl_type_size.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/glfw/Viewer.cpp b/src/igl/opengl/glfw/Viewer.cpp
new file mode 100644
index 000000000..76d1604a0
--- /dev/null
+++ b/src/igl/opengl/glfw/Viewer.cpp
@@ -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
+}
diff --git a/src/igl/opengl/glfw/Viewer.h b/src/igl/opengl/glfw/Viewer.h
new file mode 100644
index 000000000..35aa7dfa2
--- /dev/null
+++ b/src/igl/opengl/glfw/Viewer.h
@@ -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
diff --git a/src/igl/opengl/glfw/ViewerPlugin.h b/src/igl/opengl/glfw/ViewerPlugin.h
new file mode 100644
index 000000000..9ba7903a8
--- /dev/null
+++ b/src/igl/opengl/glfw/ViewerPlugin.h
@@ -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
diff --git a/src/igl/opengl/glfw/background_window.cpp b/src/igl/opengl/glfw/background_window.cpp
new file mode 100644
index 000000000..964202cc2
--- /dev/null
+++ b/src/igl/opengl/glfw/background_window.cpp
@@ -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
diff --git a/src/igl/opengl/glfw/background_window.h b/src/igl/opengl/glfw/background_window.h
new file mode 100644
index 000000000..a37515b89
--- /dev/null
+++ b/src/igl/opengl/glfw/background_window.h
@@ -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
diff --git a/src/igl/opengl/glfw/imgui/ImGuiHelpers.h b/src/igl/opengl/glfw/imgui/ImGuiHelpers.h
new file mode 100644
index 000000000..892274b0f
--- /dev/null
+++ b/src/igl/opengl/glfw/imgui/ImGuiHelpers.h
@@ -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
diff --git a/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp b/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp
new file mode 100644
index 000000000..627e055cb
--- /dev/null
+++ b/src/igl/opengl/glfw/imgui/ImGuiMenu.cpp
@@ -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
diff --git a/src/igl/opengl/glfw/imgui/ImGuiMenu.h b/src/igl/opengl/glfw/imgui/ImGuiMenu.h
new file mode 100644
index 000000000..e7144a675
--- /dev/null
+++ b/src/igl/opengl/glfw/imgui/ImGuiMenu.h
@@ -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
diff --git a/src/igl/opengl/glfw/map_texture.cpp b/src/igl/opengl/glfw/map_texture.cpp
new file mode 100644
index 000000000..7975d87d4
--- /dev/null
+++ b/src/igl/opengl/glfw/map_texture.cpp
@@ -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
diff --git a/src/igl/opengl/glfw/map_texture.h b/src/igl/opengl/glfw/map_texture.h
new file mode 100644
index 000000000..ee3b30a45
--- /dev/null
+++ b/src/igl/opengl/glfw/map_texture.h
@@ -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
diff --git a/src/igl/opengl/init_render_to_texture.cpp b/src/igl/opengl/init_render_to_texture.cpp
new file mode 100644
index 000000000..7fbcfea10
--- /dev/null
+++ b/src/igl/opengl/init_render_to_texture.cpp
@@ -0,0 +1,86 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2015 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 "init_render_to_texture.h"
+#include "gl.h"
+#include <cassert>
+
+IGL_INLINE void igl::opengl::init_render_to_texture(
+ const size_t width,
+ const size_t height,
+ const bool depth_texture,
+ GLuint & tex_id,
+ GLuint & fbo_id,
+ GLuint & d_id)
+{
+ using namespace std;
+ // http://www.opengl.org/wiki/Framebuffer_Object_Examples#Quick_example.2C_render_to_texture_.282D.29
+ const auto & gen_tex = [](GLuint & tex_id)
+ {
+ glGenTextures(1, &tex_id);
+ glBindTexture(GL_TEXTURE_2D, tex_id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ };
+ gen_tex(tex_id);
+ //NULL means reserve texture memory, but texels are undefined
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_BGRA, GL_FLOAT, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glGenFramebuffers(1, &fbo_id);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id);
+ //Attach 2D texture to this FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_id, 0);
+ if(depth_texture)
+ {
+ // Generate a depth texture
+ gen_tex(d_id);
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ GL_DEPTH_COMPONENT32,
+ width,
+ height,
+ 0,
+ GL_DEPTH_COMPONENT,
+ GL_FLOAT,
+ NULL);
+ glFramebufferTexture2D(
+ GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,d_id,0);
+ }else
+ {
+ // Attach a depth buffer
+ glGenRenderbuffers(1, &d_id);
+ glBindRenderbuffer(GL_RENDERBUFFER, d_id);
+ glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT24,width,height);
+ glFramebufferRenderbuffer(
+ GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,d_id);
+ }
+
+ //Does the GPU support current FBO configuration?
+ GLenum status;
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ assert(status == GL_FRAMEBUFFER_COMPLETE);
+ // Unbind to clean up
+ if(!depth_texture)
+ {
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+IGL_INLINE void igl::opengl::init_render_to_texture(
+ const size_t width,
+ const size_t height,
+ GLuint & tex_id,
+ GLuint & fbo_id,
+ GLuint & dfbo_id)
+{
+ return init_render_to_texture(width,height,false,tex_id,fbo_id,dfbo_id);
+}
diff --git a/src/igl/opengl/init_render_to_texture.h b/src/igl/opengl/init_render_to_texture.h
new file mode 100644
index 000000000..ae854e770
--- /dev/null
+++ b/src/igl/opengl/init_render_to_texture.h
@@ -0,0 +1,77 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2015 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_INIT_RENDER_TO_TEXTURE_H
+#define IGL_OPENGL_INIT_RENDER_TO_TEXTURE_H
+#include "../igl_inline.h"
+#include "gl.h"
+#include <cstdlib>
+namespace igl
+{
+ namespace opengl
+ {
+ // Create a frame buffer that renders color to a RGBA texture a depth to a
+ // "render buffer".
+ //
+ // After calling this, you can use with something like:
+ //
+ // glBindFramebuffer(GL_FRAMEBUFFER, fbo_id);
+ // if(!depth_texture)
+ // {
+ // glBindRenderbuffer(GL_RENDERBUFFER, d_id);
+ // }
+ // //
+ // // draw scene ...
+ // //
+ // // clean up
+ // glBindFramebuffer(GL_FRAMEBUFFER,0);
+ // if(!depth_texture)
+ // {
+ // glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ // }
+ // // Later ...
+ // glActiveTexture(GL_TEXTURE0+0);
+ // glBindTexture(GL_TEXTURE_2D,tex_id);
+ // if(depth_texture)
+ // {
+ // glActiveTexture(GL_TEXTURE0+1);
+ // glBindTexture(GL_TEXTURE_2D,d_id);
+ // }
+ // // draw textures
+ //
+ //
+ //
+ // Inputs:
+ // width image width
+ // height image height
+ // depth_texture whether to create a texture for depth or to create a
+ // render buffer for depth
+ // Outputs:
+ // tex_id id of the texture
+ // fbo_id id of the frame buffer object
+ // d_id id of the depth texture or frame buffer object
+ //
+ IGL_INLINE void init_render_to_texture(
+ const size_t width,
+ const size_t height,
+ const bool depth_texture,
+ GLuint & tex_id,
+ GLuint & fbo_id,
+ GLuint & d_id);
+ // Wrapper with depth_texture = false for legacy reasons
+ IGL_INLINE void init_render_to_texture(
+ const size_t width,
+ const size_t height,
+ GLuint & tex_id,
+ GLuint & fbo_id,
+ GLuint & dfbo_id);
+ }
+}
+#ifndef IGL_STATIC_LIBRARY
+# include "init_render_to_texture.cpp"
+#endif
+#endif
diff --git a/src/igl/opengl/load_shader.cpp b/src/igl/opengl/load_shader.cpp
new file mode 100644
index 000000000..0ee0a978f
--- /dev/null
+++ b/src/igl/opengl/load_shader.cpp
@@ -0,0 +1,34 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "load_shader.h"
+
+// Copyright Denis Kovacs 4/10/08
+#include "print_shader_info_log.h"
+#include <cstdio>
+IGL_INLINE GLuint igl::opengl::load_shader(
+ const std::string & src,const GLenum type)
+{
+ if(src.empty())
+ {
+ return (GLuint) 0;
+ }
+
+ GLuint s = glCreateShader(type);
+ if(s == 0)
+ {
+ fprintf(stderr,"Error: load_shader() failed to create shader.\n");
+ return 0;
+ }
+ // Pass shader source string
+ const char *c = src.c_str();
+ glShaderSource(s, 1, &c, NULL);
+ glCompileShader(s);
+ // Print info log (if any)
+ igl::opengl::print_shader_info_log(s);
+ return s;
+}
diff --git a/src/igl/opengl/load_shader.h b/src/igl/opengl/load_shader.h
new file mode 100644
index 000000000..6c234d8fc
--- /dev/null
+++ b/src/igl/opengl/load_shader.h
@@ -0,0 +1,38 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_LOAD_SHADER_H
+#define IGL_OPENGL_LOAD_SHADER_H
+#include "../igl_inline.h"
+#include "gl.h"
+#include <string>
+
+namespace igl
+{
+ namespace opengl
+ {
+ // Creates and compiles a shader from a given string
+ //
+ // Inputs:
+ // src string containing GLSL shader code
+ // type GLSL type of shader, one of:
+ // GL_VERTEX_SHADER
+ // GL_FRAGMENT_SHADER
+ // GL_GEOMETRY_SHADER
+ // Returns index id of the newly created shader, 0 on error
+ //
+ // Will immediately return 0 if src is empty.
+ IGL_INLINE GLuint load_shader(
+ const std::string & src,const GLenum type);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "load_shader.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/print_program_info_log.cpp b/src/igl/opengl/print_program_info_log.cpp
new file mode 100644
index 000000000..41b37b7fb
--- /dev/null
+++ b/src/igl/opengl/print_program_info_log.cpp
@@ -0,0 +1,28 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "print_program_info_log.h"
+
+#include <cstdio>
+#include <stdlib.h>
+// Copyright Denis Kovacs 4/10/08
+IGL_INLINE void igl::opengl::print_program_info_log(const GLuint obj)
+{
+ GLint infologLength = 0;
+ GLint charsWritten = 0;
+ char *infoLog;
+
+ glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
+
+ if (infologLength > 0)
+ {
+ infoLog = (char *)malloc(infologLength);
+ glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
+ printf("%s\n",infoLog);
+ free(infoLog);
+ }
+}
diff --git a/src/igl/opengl/print_program_info_log.h b/src/igl/opengl/print_program_info_log.h
new file mode 100644
index 000000000..43bcc0974
--- /dev/null
+++ b/src/igl/opengl/print_program_info_log.h
@@ -0,0 +1,27 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_PRINT_PROGRAM_INFO_LOG_H
+#define IGL_OPENGL_PRINT_PROGRAM_INFO_LOG_H
+#include "../igl_inline.h"
+#include "gl.h"
+
+namespace igl
+{
+ namespace opengl
+ {
+ // Inputs:
+ // obj OpenGL index of program to print info log about
+ IGL_INLINE void print_program_info_log(const GLuint obj);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "print_program_info_log.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/print_shader_info_log.cpp b/src/igl/opengl/print_shader_info_log.cpp
new file mode 100644
index 000000000..2d3554387
--- /dev/null
+++ b/src/igl/opengl/print_shader_info_log.cpp
@@ -0,0 +1,29 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "print_shader_info_log.h"
+
+#include <cstdio>
+#include <stdlib.h>
+// Copyright Denis Kovacs 4/10/08
+IGL_INLINE void igl::opengl::print_shader_info_log(const GLuint obj)
+{
+ GLint infologLength = 0;
+ GLint charsWritten = 0;
+ char *infoLog;
+
+ // Get shader info log from opengl
+ glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
+ // Only print if there is something in the log
+ if (infologLength > 0)
+ {
+ infoLog = (char *)malloc(infologLength);
+ glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
+ printf("%s\n",infoLog);
+ free(infoLog);
+ }
+}
diff --git a/src/igl/opengl/print_shader_info_log.h b/src/igl/opengl/print_shader_info_log.h
new file mode 100644
index 000000000..5a571fb11
--- /dev/null
+++ b/src/igl/opengl/print_shader_info_log.h
@@ -0,0 +1,27 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_PRINT_SHADER_INFO_LOG_H
+#define IGL_OPENGL_PRINT_SHADER_INFO_LOG_H
+#include "../igl_inline.h"
+#include "gl.h"
+
+namespace igl
+{
+ namespace opengl
+ {
+ // Inputs:
+ // obj OpenGL index of shader to print info log about
+ IGL_INLINE void print_shader_info_log(const GLuint obj);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "print_shader_info_log.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/report_gl_error.cpp b/src/igl/opengl/report_gl_error.cpp
new file mode 100644
index 000000000..2dcc0b632
--- /dev/null
+++ b/src/igl/opengl/report_gl_error.cpp
@@ -0,0 +1,61 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "report_gl_error.h"
+#include "../verbose.h"
+#include <cstdio>
+
+IGL_INLINE GLenum igl::opengl::report_gl_error(const std::string id)
+{
+ // http://stackoverflow.com/q/28485180/148668
+
+ // gluErrorString was deprecated
+ const auto gluErrorString = [](GLenum errorCode)->const char *
+ {
+ switch(errorCode)
+ {
+ default:
+ return "unknown error code";
+ case GL_NO_ERROR:
+ return "no error";
+ case GL_INVALID_ENUM:
+ return "invalid enumerant";
+ case GL_INVALID_VALUE:
+ return "invalid value";
+ case GL_INVALID_OPERATION:
+ return "invalid operation";
+#ifndef GL_VERSION_3_0
+ case GL_STACK_OVERFLOW:
+ return "stack overflow";
+ case GL_STACK_UNDERFLOW:
+ return "stack underflow";
+ case GL_TABLE_TOO_LARGE:
+ return "table too large";
+#endif
+ case GL_OUT_OF_MEMORY:
+ return "out of memory";
+#ifdef GL_EXT_framebuffer_object
+ case GL_INVALID_FRAMEBUFFER_OPERATION_EXT:
+ return "invalid framebuffer operation";
+#endif
+ }
+ };
+
+ GLenum err = glGetError();
+ if(GL_NO_ERROR != err)
+ {
+ verbose("GL_ERROR: ");
+ fprintf(stderr,"%s%s\n",id.c_str(),gluErrorString(err));
+ }
+ return err;
+}
+
+IGL_INLINE GLenum igl::opengl::report_gl_error()
+{
+ return igl::opengl::report_gl_error(std::string(""));
+}
+
diff --git a/src/igl/opengl/report_gl_error.h b/src/igl/opengl/report_gl_error.h
new file mode 100644
index 000000000..70e5c7d74
--- /dev/null
+++ b/src/igl/opengl/report_gl_error.h
@@ -0,0 +1,36 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_REPORT_GL_ERROR_H
+#define IGL_OPENGL_REPORT_GL_ERROR_H
+#include "../igl_inline.h"
+
+// Hack to allow both opengl/ and opengl2 to use this (we shouldn't allow this)
+#ifndef __gl_h_
+# include "gl.h"
+#endif
+#include <string>
+
+namespace igl
+{
+ namespace opengl
+ {
+ // Print last OpenGL error to stderr prefixed by specified id string
+ // Inputs:
+ // id string to appear before any error msgs
+ // Returns result of glGetError()
+ IGL_INLINE GLenum report_gl_error(const std::string id);
+ // No prefix
+ IGL_INLINE GLenum report_gl_error();
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "report_gl_error.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/uniform_type_to_string.cpp b/src/igl/opengl/uniform_type_to_string.cpp
new file mode 100644
index 000000000..b2f9a2fdb
--- /dev/null
+++ b/src/igl/opengl/uniform_type_to_string.cpp
@@ -0,0 +1,71 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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 "uniform_type_to_string.h"
+
+IGL_INLINE std::string igl::opengl::uniform_type_to_string(const GLenum type)
+{
+ switch(type)
+ {
+ case GL_FLOAT:
+ return "GL_FLOAT";
+ case GL_FLOAT_VEC2:
+ return "GL_FLOAT_VEC2";
+ case GL_FLOAT_VEC3:
+ return "GL_FLOAT_VEC3";
+ case GL_FLOAT_VEC4:
+ return "GL_FLOAT_VEC4";
+ case GL_INT:
+ return "GL_INT";
+ case GL_INT_VEC2:
+ return "GL_INT_VEC2";
+ case GL_INT_VEC3:
+ return "GL_INT_VEC3";
+ case GL_INT_VEC4:
+ return "GL_INT_VEC4";
+ case GL_BOOL:
+ return "GL_BOOL";
+ case GL_BOOL_VEC2:
+ return "GL_BOOL_VEC2";
+ case GL_BOOL_VEC3:
+ return "GL_BOOL_VEC3";
+ case GL_BOOL_VEC4:
+ return "GL_BOOL_VEC4";
+ case GL_FLOAT_MAT2:
+ return "GL_FLOAT_MAT2";
+ case GL_FLOAT_MAT3:
+ return "GL_FLOAT_MAT3";
+ case GL_FLOAT_MAT4:
+ return "GL_FLOAT_MAT4";
+ case GL_FLOAT_MAT2x3:
+ return "GL_FLOAT_MAT2x3";
+ case GL_FLOAT_MAT2x4:
+ return "GL_FLOAT_MAT2x4";
+ case GL_FLOAT_MAT3x2:
+ return "GL_FLOAT_MAT3x2";
+ case GL_FLOAT_MAT3x4:
+ return "GL_FLOAT_MAT3x4";
+ case GL_FLOAT_MAT4x2:
+ return "GL_FLOAT_MAT4x2";
+ case GL_FLOAT_MAT4x3:
+ return "GL_FLOAT_MAT4x3";
+ case GL_SAMPLER_1D:
+ return "GL_SAMPLER_1D";
+ case GL_SAMPLER_2D:
+ return "GL_SAMPLER_2D";
+ case GL_SAMPLER_3D:
+ return "GL_SAMPLER_3D";
+ case GL_SAMPLER_CUBE:
+ return "GL_SAMPLER_CUBE";
+ case GL_SAMPLER_1D_SHADOW:
+ return "GL_SAMPLER_1D_SHADOW";
+ case GL_SAMPLER_2D_SHADOW:
+ return "GL_SAMPLER_2D_SHADOW";
+ default:
+ return "UNKNOWN_TYPE";
+ }
+}
diff --git a/src/igl/opengl/uniform_type_to_string.h b/src/igl/opengl/uniform_type_to_string.h
new file mode 100644
index 000000000..ed0a93e90
--- /dev/null
+++ b/src/igl/opengl/uniform_type_to_string.h
@@ -0,0 +1,31 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2013 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_UNIFORM_TYPE_TO_STRING_H
+#define IGL_OPENGL_UNIFORM_TYPE_TO_STRING_H
+#include "../igl_inline.h"
+#include "gl.h"
+#include <string>
+
+namespace igl
+{
+ namespace opengl
+ {
+ // Convert a GL uniform variable type (say, returned from
+ // glGetActiveUniform) and output a string naming that type
+ // Inputs:
+ // type enum for given type
+ // Returns string name of that type
+ IGL_INLINE std::string uniform_type_to_string(const GLenum type);
+ }
+}
+
+#ifndef IGL_STATIC_LIBRARY
+# include "uniform_type_to_string.cpp"
+#endif
+
+#endif
diff --git a/src/igl/opengl/vertex_array.cpp b/src/igl/opengl/vertex_array.cpp
new file mode 100644
index 000000000..c07113ad3
--- /dev/null
+++ b/src/igl/opengl/vertex_array.cpp
@@ -0,0 +1,61 @@
+#include "vertex_array.h"
+#include <igl/opengl/report_gl_error.h>
+
+template <
+ typename DerivedV,
+ typename DerivedF>
+IGL_INLINE void igl::opengl::vertex_array(
+ const Eigen::PlainObjectBase<DerivedV> & V,
+ const Eigen::PlainObjectBase<DerivedF> & F,
+ GLuint & va_id,
+ GLuint & ab_id,
+ GLuint & eab_id)
+{
+ // Inputs should be in RowMajor storage. If not, we have no choice but to
+ // create a copy.
+ if(!(V.Options & Eigen::RowMajor))
+ {
+ Eigen::Matrix<
+ typename DerivedV::Scalar,
+ DerivedV::RowsAtCompileTime,
+ DerivedV::ColsAtCompileTime,
+ Eigen::RowMajor> VR = V;
+ return vertex_array(VR,F,va_id,ab_id,eab_id);
+ }
+ if(!(F.Options & Eigen::RowMajor))
+ {
+ Eigen::Matrix<
+ typename DerivedF::Scalar,
+ DerivedF::RowsAtCompileTime,
+ DerivedF::ColsAtCompileTime,
+ Eigen::RowMajor> FR = F;
+ return vertex_array(V,FR,va_id,ab_id,eab_id);
+ }
+ // Generate and attach buffers to vertex array
+ glGenVertexArrays(1, &va_id);
+ glGenBuffers(1, &ab_id);
+ glGenBuffers(1, &eab_id);
+ glBindVertexArray(va_id);
+ glBindBuffer(GL_ARRAY_BUFFER, ab_id);
+ const auto size_VScalar = sizeof(typename DerivedV::Scalar);
+ const auto size_FScalar = sizeof(typename DerivedF::Scalar);
+ glBufferData(GL_ARRAY_BUFFER,size_VScalar*V.size(),V.data(),GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eab_id);
+ assert(sizeof(GLuint) == size_FScalar && "F type does not match GLuint");
+ glBufferData(
+ GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*F.size(), F.data(), GL_STATIC_DRAW);
+ glVertexAttribPointer(
+ 0,
+ V.cols(),
+ size_VScalar==sizeof(float)?GL_FLOAT:GL_DOUBLE,
+ GL_FALSE,
+ V.cols()*size_VScalar,
+ (GLvoid*)0);
+ glEnableVertexAttribArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+}
+
+#ifdef IGL_STATIC_LIBRARY
+template void igl::opengl::vertex_array<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, unsigned int&, unsigned int&, unsigned int&);
+#endif
diff --git a/src/igl/opengl/vertex_array.h b/src/igl/opengl/vertex_array.h
new file mode 100644
index 000000000..1342a9c90
--- /dev/null
+++ b/src/igl/opengl/vertex_array.h
@@ -0,0 +1,38 @@
+#ifndef IGL_OPENGL_VERTEX_ARRAY_H
+#define IGL_OPENGL_VERTEX_ARRAY_H
+#include <igl/opengl/../igl_inline.h>
+#include <igl/opengl/gl.h>
+#include <Eigen/Core>
+namespace igl
+{
+ namespace opengl
+ {
+ // Create a GL_VERTEX_ARRAY for a given mesh (V,F)
+ //
+ // Inputs:
+ // V #V by dim list of mesh vertex positions
+ // F #F by 3 list of mesh triangle indices into V
+ // Outputs:
+ // va_id id of vertex array
+ // ab_id id of array buffer (vertex buffer object)
+ // eab_id id of element array buffer (element/face buffer object)
+ //
+ template <
+ typename DerivedV,
+ typename DerivedF>
+ IGL_INLINE void vertex_array(
+ // Note: Unlike most libigl functions, the **input** Eigen matrices must
+ // be `Eigen::PlainObjectBase` because we want to directly access it's
+ // underlying storage. It cannot be `Eigen::MatrixBase` (see
+ // http://stackoverflow.com/questions/25094948/)
+ const Eigen::PlainObjectBase<DerivedV> & V,
+ const Eigen::PlainObjectBase<DerivedF> & F,
+ GLuint & va_id,
+ GLuint & ab_id,
+ GLuint & eab_id);
+ }
+}
+#ifndef IGL_STATIC_LIBRARY
+# include "vertex_array.cpp"
+#endif
+#endif