Kaynağa Gözat

merge

Former-commit-id: 7bd313b5232e64030d201d871ee9f5ac2ff05b17
Alec Jacobson 11 yıl önce
ebeveyn
işleme
5e1f3eb1d4

+ 3 - 3
build/Makefile_viewer

@@ -16,7 +16,7 @@ OBJ_FILES=$(addprefix obj/,$(notdir $(CPP_FILES:.cpp=.o)))
 # include igl headers
 INC+=-I../include/
 
-# EXPECTS THAT CFLAGS IS ALREADY SET APPROPRIATELY 
+# EXPECTS THAT CFLAGS IS ALREADY SET APPROPRIATELY
 
 # Eigen dependency
 EIGEN3_INC=-I$(DEFAULT_PREFIX)/include/eigen3 -I$(DEFAULT_PREFIX)/include/eigen3/unsupported
@@ -35,7 +35,7 @@ endif
 ANTTWEAKBAR_INC=-I$(ANTTWEAKBAR)/include -I$(ANTTWEAKBAR)/src
 INC+=$(ANTTWEAKBAR_INC)
 
-obj: 
+obj:
 	mkdir -p obj
 
 ../lib/libiglviewer.a: $(OBJ_FILES)
@@ -43,7 +43,7 @@ obj:
 	ar cqs $@ $(OBJ_FILES)
 
 obj/%.o: $(SRC_DIR)/%.cpp $(SRC_DIR)/%.h
-	g++ $(AFLAGS) $(CFLAGS) -c -o $@ $< $(INC)
+	$(GG) $(AFLAGS) $(CFLAGS) -c -o $@ $< $(INC)
 
 clean:
 	rm -f obj/*.o

+ 5 - 7
include/igl/column_to_quats.h

@@ -1,9 +1,9 @@
 // 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 
+//
+// 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_COLUMN_TO_QUATS_H
 #define IGL_COLUMN_TO_QUATS_H
@@ -28,9 +28,7 @@ namespace igl
 }
 
 #ifndef IGL_STATIC_LIBRARY
-#  include "columns_to_quats.cpp"
+#  include "column_to_quats.cpp"
 #endif
 
 #endif
-
-

+ 3 - 3
include/igl/deform_skeleton.cpp

@@ -13,7 +13,7 @@ void igl::deform_skeleton(
   BET.resize(BE.rows(),2);
   for(int e = 0;e<BE.rows();e++)
   {
-    BET(e,0) = 2*e; 
+    BET(e,0) = 2*e;
     BET(e,1) = 2*e+1;
     Affine3d a = vA[e];
     Vector3d c0 = C.row(BE(e,0));
@@ -32,12 +32,12 @@ IGL_INLINE void igl::deform_skeleton(
   Eigen::MatrixXi & BET)
 {
   using namespace Eigen;
-  assert(BE.rows() == (int)vA.size());
+  //assert(BE.rows() == (int)vA.size());
   CT.resize(2*BE.rows(),C.cols());
   BET.resize(BE.rows(),2);
   for(int e = 0;e<BE.rows();e++)
   {
-    BET(e,0) = 2*e; 
+    BET(e,0) = 2*e;
     BET(e,1) = 2*e+1;
     Affine3d a;
     a.matrix() = T.block(e*4,0,4,3).transpose();

+ 33 - 4
include/igl/project.cpp

@@ -1,12 +1,13 @@
 // 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 
+//
+// 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 "project.h"
 #ifndef IGL_NO_OPENGL
+#ifndef IGL_OPENGL_4
 #include <iostream>
 #include "report_gl_error.h"
 
@@ -110,7 +111,34 @@ IGL_INLINE Eigen::PlainObjectBase<Derivedobj> igl::project(
   return win;
 }
 
+#endif
+#endif
+
+Eigen::Vector3f igl::project(const Eigen::Vector3f&  obj,
+                        const Eigen::Matrix4f& model,
+                        const Eigen::Matrix4f& proj,
+                        const Eigen::Vector4f&  viewport)
+{
+  Eigen::Vector4f tmp;
+  tmp << obj,1;
+
+  tmp = model * tmp;
+
+  tmp = proj * tmp;
+
+  tmp = tmp.array() / tmp(3);
+  tmp = tmp.array() * 0.5f + 0.5f;
+  tmp(0) = tmp(0) * viewport(2) + viewport(0);
+  tmp(1) = tmp(1) * viewport(3) + viewport(1);
+
+  return tmp.head(3);
+}
+
+
 #ifdef IGL_STATIC_LIBRARY
+
+#ifndef IGL_NO_OPENGL
+#ifndef IGL_OPENGL_4
 // Explicit template instanciations
 template int igl::project<Eigen::Matrix<double, 3, 1, 0, 3, 1>, Eigen::Matrix<double, 3, 1, 0, 3, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> >&);
 template Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> > igl::project<Eigen::Matrix<double, 3, 1, 0, 3, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> > const&);
@@ -119,5 +147,6 @@ template Eigen::PlainObjectBase<Eigen::Matrix<float, 3, 1, 0, 3, 1> > igl::proje
 template Eigen::PlainObjectBase<Eigen::Matrix<double, 1, -1, 1, 1, -1> > igl::project<Eigen::Matrix<double, 1, -1, 1, 1, -1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, 1, -1, 1, 1, -1> > const&);
 template int igl::project<Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3> >(Eigen::PlainObjectBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> >&);
 #endif
+#endif
 
 #endif

+ 19 - 4
include/igl/project.h

@@ -1,9 +1,9 @@
 // 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 
+//
+// 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_PROJECT_H
 #define IGL_PROJECT_H
@@ -34,6 +34,21 @@ namespace igl
   template <typename Derivedobj>
   IGL_INLINE Eigen::PlainObjectBase<Derivedobj> project(
     const Eigen::PlainObjectBase<Derivedobj> & obj);
+
+  // Eigen reimplementation of gluProject
+// Inputs:
+//   obj*  3D objects' x, y, and z coordinates respectively
+// model        model matrix
+// proj         projection matrix
+// viewport     viewport vector
+// Returns:
+//   screen space x, y, and z coordinates respectively
+// Returns return value of gluProject call
+  Eigen::Vector3f project(const Eigen::Vector3f&  obj,
+                          const Eigen::Matrix4f& model,
+                          const Eigen::Matrix4f& proj,
+                          const Eigen::Vector4f&  viewport);
+
 }
 
 #ifndef IGL_STATIC_LIBRARY

+ 1 - 0
include/igl/unproject.h

@@ -44,6 +44,7 @@ namespace igl
     const Eigen::Matrix4f& model,
     const Eigen::Matrix4f& proj,
     const Eigen::Vector4f& viewport);
+    
   template <typename Derivedwin, typename Derivedobj>
   IGL_INLINE int unproject(
     const Eigen::PlainObjectBase<Derivedwin> & win,

+ 175 - 0
include/igl/viewer/OpenGL_shader.cpp

@@ -0,0 +1,175 @@
+#include "OpenGL_shader.h"
+
+#ifdef __APPLE__
+#   include <OpenGL/gl3.h>
+#   define __gl_h_ /* Prevent inclusion of the old gl.h */
+#else
+#   ifdef _WIN32
+#       include <windows.h>
+#   endif
+#   include <GL/gl.h>
+#endif
+
+#include <iostream>
+#include <fstream>
+
+IGL_INLINE bool igl::OpenGL_shader::init_from_files(
+  const std::string &vertex_shader_filename,
+  const std::string &fragment_shader_filename,
+  const std::string &fragment_data_name,
+  const std::string &geometry_shader_filename,
+  int geometry_shader_max_vertices)
+{
+  auto file_to_string = [](const std::string &filename)
+  {
+    std::ifstream t(filename);
+    return std::string((std::istreambuf_iterator<char>(t)),
+                        std::istreambuf_iterator<char>());
+  };
+
+  return init(
+    file_to_string(vertex_shader_filename),
+    file_to_string(fragment_shader_filename),
+    fragment_data_name,
+    file_to_string(geometry_shader_filename),
+    geometry_shader_max_vertices
+ );
+}
+
+IGL_INLINE bool igl::OpenGL_shader::init(
+  const std::string &vertex_shader_string,
+  const std::string &fragment_shader_string,
+  const std::string &fragment_data_name,
+  const std::string &geometry_shader_string,
+  int geometry_shader_max_vertices)
+{
+  using namespace std;
+  vertex_shader = create_shader_helper(GL_VERTEX_SHADER, vertex_shader_string);
+  geometry_shader = create_shader_helper(GL_GEOMETRY_SHADER, geometry_shader_string);
+  fragment_shader = create_shader_helper(GL_FRAGMENT_SHADER, fragment_shader_string);
+
+  if (!vertex_shader || !fragment_shader)
+    return false;
+
+  program_shader = glCreateProgram();
+
+  glAttachShader(program_shader, vertex_shader);
+  glAttachShader(program_shader, fragment_shader);
+
+  if (geometry_shader)
+  {
+    glAttachShader(program_shader, geometry_shader);
+
+    /* This covers only basic cases and may need to be modified */
+    glProgramParameteri(program_shader, GL_GEOMETRY_INPUT_TYPE, GL_TRIANGLES);
+    glProgramParameteri(program_shader, GL_GEOMETRY_OUTPUT_TYPE, GL_TRIANGLES);
+    glProgramParameteri(program_shader, GL_GEOMETRY_VERTICES_OUT, geometry_shader_max_vertices);
+  }
+
+  glBindFragDataLocation(program_shader, 0, fragment_data_name.c_str());
+  glLinkProgram(program_shader);
+
+  GLint status;
+  glGetProgramiv(program_shader, GL_LINK_STATUS, &status);
+
+  if (status != GL_TRUE)
+  {
+    char buffer[512];
+    glGetProgramInfoLog(program_shader, 512, NULL, buffer);
+    cerr << "Linker error: " << endl << buffer << endl;
+    program_shader = 0;
+    return false;
+  }
+
+  return true;
+}
+
+IGL_INLINE void igl::OpenGL_shader::bind()
+{
+  glUseProgram(program_shader);
+}
+
+IGL_INLINE GLint igl::OpenGL_shader::attrib(const std::string &name) const
+{
+  return glGetAttribLocation(program_shader, name.c_str());
+}
+
+IGL_INLINE GLint igl::OpenGL_shader::uniform(const std::string &name) const
+{
+  return glGetUniformLocation(program_shader, name.c_str());
+}
+
+IGL_INLINE GLint igl::OpenGL_shader::bindVertexAttribArray(
+  const std::string &name, GLuint bufferID, const Eigen::MatrixXf &M, bool refresh) const
+{
+  GLint id = attrib(name);
+  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.rows(), GL_FLOAT, GL_FALSE, 0, 0);
+  glEnableVertexAttribArray(id);
+  return id;
+}
+
+IGL_INLINE void igl::OpenGL_shader::free()
+{
+  if (program_shader)
+  {
+    glDeleteProgram(program_shader);
+    program_shader = 0;
+  }
+  if (vertex_shader)
+  {
+    glDeleteShader(vertex_shader);
+    vertex_shader = 0;
+  }
+  if (fragment_shader)
+  {
+    glDeleteShader(fragment_shader);
+    fragment_shader = 0;
+  }
+  if (geometry_shader)
+  {
+    glDeleteShader(geometry_shader);
+    geometry_shader = 0;
+  }
+}
+
+IGL_INLINE GLuint igl::OpenGL_shader::create_shader_helper(GLint type, const std::string &shader_string)
+{
+  using namespace std;
+  if (shader_string.empty())
+    return (GLuint) 0;
+
+  GLuint id = glCreateShader(type);
+  const char *shader_string_const = shader_string.c_str();
+  glShaderSource(id, 1, &shader_string_const, NULL);
+  glCompileShader(id);
+
+  GLint status;
+  glGetShaderiv(id, GL_COMPILE_STATUS, &status);
+
+  if (status != GL_TRUE)
+  {
+    char buffer[512];
+    if (type == GL_VERTEX_SHADER)
+      cerr << "Vertex shader:" << endl;
+    else if (type == GL_FRAGMENT_SHADER)
+      cerr << "Fragment shader:" << endl;
+    else if (type == GL_GEOMETRY_SHADER)
+      cerr << "Geometry shader:" << endl;
+    cerr << shader_string << endl << endl;
+    glGetShaderInfoLog(id, 512, NULL, buffer);
+    cerr << "Error: " << endl << buffer << endl;
+    return (GLuint) 0;
+  }
+
+  return id;
+}

+ 75 - 0
include/igl/viewer/OpenGL_shader.h

@@ -0,0 +1,75 @@
+#ifndef IGL_OPENGL_SHADER_H
+#define IGL_OPENGL_SHADER_H
+
+#include <igl/igl_inline.h>
+#include <string>
+#include <Eigen/Core>
+
+#ifdef __APPLE__
+#   include <OpenGL/gl3.h>
+#   define __gl_h_ /* Prevent inclusion of the old gl.h */
+#else
+#   ifdef _WIN32
+#       include <windows.h>
+#   endif
+#   include <GL/gl.h>
+#endif
+
+namespace igl
+{
+
+class OpenGL_shader
+{
+public:
+  typedef unsigned int GLuint;
+  typedef int GLint;
+
+  GLuint vertex_shader;
+  GLuint fragment_shader;
+  GLuint geometry_shader;
+  GLuint program_shader;
+
+  IGL_INLINE OpenGL_shader() : vertex_shader(0), fragment_shader(0),
+    geometry_shader(0), program_shader(0) { }
+
+  // Create a new shader from the specified source strings
+  IGL_INLINE bool init(const std::string &vertex_shader_string,
+    const std::string &fragment_shader_string,
+    const std::string &fragment_data_name,
+    const std::string &geometry_shader_string = "",
+    int geometry_shader_max_vertices = 3);
+
+  // Create a new shader from the specified files on disk
+  IGL_INLINE bool init_from_files(const std::string &vertex_shader_filename,
+    const std::string &fragment_shader_filename,
+    const std::string &fragment_data_name,
+    const std::string &geometry_shader_filename = "",
+    int geometry_shader_max_vertices = 3);
+
+  // Select this shader for subsequent draw calls
+  IGL_INLINE void bind();
+
+  // Release all OpenGL objects
+  IGL_INLINE void free();
+
+  // Return the OpenGL handle of a named shader attribute (-1 if it does not exist)
+  IGL_INLINE GLint attrib(const std::string &name) const;
+
+  // Return the OpenGL handle of a uniform attribute (-1 if it does not exist)
+  IGL_INLINE GLint uniform(const std::string &name) const;
+
+  // Bind a per-vertex array attribute and refresh its contents from an Eigen amtrix
+  IGL_INLINE GLint bindVertexAttribArray(const std::string &name, GLuint bufferID,
+    const Eigen::MatrixXf &M, bool refresh) const;
+
+  IGL_INLINE GLuint create_shader_helper(GLint type, const std::string &shader_string);
+
+};
+
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "OpenGL_shader.cpp"
+#endif
+
+#endif

+ 477 - 0
include/igl/viewer/OpenGL_state.cpp

@@ -0,0 +1,477 @@
+#include "OpenGL_state.h"
+
+IGL_INLINE void igl::OpenGL_state::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 = DIRTY_ALL;
+}
+
+IGL_INLINE void igl::OpenGL_state::free_buffers()
+{
+  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_state::set_data(const igl::ViewerData &data, bool face_based, bool invert_normals)
+{
+  bool per_corner_uv = (data.F_uv.rows() == data.F.rows());
+  bool per_corner_normals = (data.F_normals.rows() == 3 * data.F.rows());
+
+  dirty |= data.dirty;
+
+  if (!face_based)
+  {
+    if (!per_corner_uv)
+    {
+      // Vertex positions
+      if (dirty & DIRTY_POSITION)
+        V_vbo = (data.V.transpose()).cast<float>();
+
+      // Vertex normals
+      if (dirty & DIRTY_NORMAL)
+      {
+        V_normals_vbo = (data.V_normals.transpose()).cast<float>();
+        if (invert_normals)
+          V_normals_vbo = -V_normals_vbo;
+      }
+
+      // Per-vertex material settings
+      if (dirty & DIRTY_AMBIENT)
+        V_ambient_vbo = (data.V_material_ambient.transpose()).cast<float>();
+      if (dirty & DIRTY_DIFFUSE)
+        V_diffuse_vbo = (data.V_material_diffuse.transpose()).cast<float>();
+      if (dirty & DIRTY_SPECULAR)
+        V_specular_vbo = (data.V_material_specular.transpose()).cast<float>();
+
+      // Face indices
+      if (dirty & DIRTY_FACE)
+        F_vbo = (data.F.transpose()).cast<unsigned>();
+
+      // Texture coordinates
+      if (dirty & DIRTY_UV)
+        V_uv_vbo = (data.V_uv.transpose()).cast<float>();
+    }
+    else
+    {
+      // Per vertex properties with per corner UVs
+      if (dirty & DIRTY_POSITION)
+      {
+        V_vbo.resize(3,data.F.rows()*3);
+        for (unsigned i=0; i<data.F.rows();++i)
+          for (unsigned j=0;j<3;++j)
+            V_vbo.col(i*3+j) = data.V.row(data.F(i,j)).transpose().cast<float>();
+      }
+
+      if (dirty & DIRTY_AMBIENT)
+      {
+        V_ambient_vbo.resize(3,data.F.rows()*3);
+        for (unsigned i=0; i<data.F.rows();++i)
+          for (unsigned j=0;j<3;++j)
+            V_ambient_vbo.col (i*3+j) = data.V_material_ambient.row(data.F(i,j)).transpose().cast<float>();
+      }
+
+      if (dirty & DIRTY_DIFFUSE)
+      {
+        V_diffuse_vbo.resize(3,data.F.rows()*3);
+        for (unsigned i=0; i<data.F.rows();++i)
+          for (unsigned j=0;j<3;++j)
+            V_diffuse_vbo.col (i*3+j) = data.V_material_diffuse.row(data.F(i,j)).transpose().cast<float>();
+      }
+
+      if (dirty & DIRTY_SPECULAR)
+      {
+        V_specular_vbo.resize(3,data.F.rows()*3);
+        for (unsigned i=0; i<data.F.rows();++i)
+          for (unsigned j=0;j<3;++j)
+            V_specular_vbo.col(i*3+j) = data.V_material_specular.row(data.F(i,j)).transpose().cast<float>();
+      }
+
+      if (dirty & DIRTY_NORMAL)
+      {
+        V_normals_vbo.resize(3,data.F.rows()*3);
+        for (unsigned i=0; i<data.F.rows();++i)
+          for (unsigned j=0;j<3;++j)
+            V_normals_vbo.col (i*3+j) = data.V_normals.row(data.F(i,j)).transpose().cast<float>();
+
+        if (invert_normals)
+          V_normals_vbo = -V_normals_vbo;
+      }
+
+      if (dirty & DIRTY_FACE)
+      {
+        F_vbo.resize(3,data.F.rows());
+        for (unsigned i=0; i<data.F.rows();++i)
+          F_vbo.col(i) << i*3+0, i*3+1, i*3+2;
+      }
+
+      if (dirty & DIRTY_UV)
+      {
+        V_uv_vbo.resize(2,data.F.rows()*3);
+        for (unsigned i=0; i<data.F.rows();++i)
+          for (unsigned j=0;j<3;++j)
+            V_uv_vbo.col(i*3+j) = data.V_uv.row(data.F(i,j)).transpose().cast<float>();
+      }
+    }
+  }
+  else
+  {
+    if (dirty & DIRTY_POSITION)
+    {
+      V_vbo.resize(3,data.F.rows()*3);
+      for (unsigned i=0; i<data.F.rows();++i)
+        for (unsigned j=0;j<3;++j)
+          V_vbo.col(i*3+j) = data.V.row(data.F(i,j)).transpose().cast<float>();
+    }
+
+    if (dirty & DIRTY_AMBIENT)
+    {
+      V_ambient_vbo.resize(3,data.F.rows()*3);
+      for (unsigned i=0; i<data.F.rows();++i)
+        for (unsigned j=0;j<3;++j)
+          V_ambient_vbo.col (i*3+j) = data.F_material_ambient.row(i).transpose().cast<float>();
+    }
+
+    if (dirty & DIRTY_DIFFUSE)
+    {
+      V_diffuse_vbo.resize(3,data.F.rows()*3);
+      for (unsigned i=0; i<data.F.rows();++i)
+        for (unsigned j=0;j<3;++j)
+          V_diffuse_vbo.col (i*3+j) = data.F_material_diffuse.row(i).transpose().cast<float>();
+    }
+
+    if (dirty & DIRTY_SPECULAR)
+    {
+      V_specular_vbo.resize(3,data.F.rows()*3);
+      for (unsigned i=0; i<data.F.rows();++i)
+        for (unsigned j=0;j<3;++j)
+          V_specular_vbo.col(i*3+j) = data.F_material_specular.row(i).transpose().cast<float>();
+    }
+
+    if (dirty & DIRTY_NORMAL)
+    {
+      V_normals_vbo.resize(3,data.F.rows()*3);
+      for (unsigned i=0; i<data.F.rows();++i)
+        for (unsigned j=0;j<3;++j)
+          V_normals_vbo.col (i*3+j) =
+             per_corner_normals ?
+               data.F_normals.row(i*3+j).transpose().cast<float>() :
+               data.F_normals.row(i).transpose().cast<float>();
+
+      if (invert_normals)
+        V_normals_vbo = -V_normals_vbo;
+    }
+
+    if (dirty & DIRTY_FACE)
+    {
+      F_vbo.resize(3,data.F.rows());
+      for (unsigned i=0; i<data.F.rows();++i)
+        F_vbo.col(i) << i*3+0, i*3+1, i*3+2;
+    }
+
+    if (dirty & DIRTY_UV)
+    {
+        V_uv_vbo.resize(2,data.F.rows()*3);
+        for (unsigned i=0; i<data.F.rows();++i)
+          for (unsigned j=0;j<3;++j)
+            V_uv_vbo.col(i*3+j) = data.V_uv.row(per_corner_uv ? data.F_uv(i,j) : data.F(i,j)).transpose().cast<float>();
+    }
+  }
+
+  if (dirty & DIRTY_TEXTURE)
+  {
+    tex_u = data.texture_R.rows();
+    tex_v = data.texture_R.cols();
+    tex.resize(data.texture_R.size()*3);
+    for (unsigned i=0;i<data.texture_R.size();++i)
+    {
+      tex(i*3+0) = data.texture_R(i);
+      tex(i*3+1) = data.texture_G(i);
+      tex(i*3+2) = data.texture_B(i);
+    }
+  }
+
+  if (dirty & DIRTY_OVERLAY_LINES)
+  {
+    lines_V_vbo.resize(3, data.lines.rows()*2);
+    lines_V_colors_vbo.resize(3, data.lines.rows()*2);
+    lines_F_vbo.resize(1, data.lines.rows()*2);
+    for (unsigned i=0; i<data.lines.rows();++i)
+    {
+      lines_V_vbo.col(2*i+0) = data.lines.block<1, 3>(i, 0).transpose().cast<float>();
+      lines_V_vbo.col(2*i+1) = data.lines.block<1, 3>(i, 3).transpose().cast<float>();
+      lines_V_colors_vbo.col(2*i+0) = data.lines.block<1, 3>(i, 6).transpose().cast<float>();
+      lines_V_colors_vbo.col(2*i+1) = data.lines.block<1, 3>(i, 6).transpose().cast<float>();
+      lines_F_vbo(2*i+0) = 2*i+0;
+      lines_F_vbo(2*i+1) = 2*i+1;
+    }
+  }
+
+  if (dirty & DIRTY_OVERLAY_POINTS)
+  {
+    points_V_vbo.resize(3, data.points.rows());
+    points_V_colors_vbo.resize(3, data.points.rows());
+    points_F_vbo.resize(1, data.points.rows());
+    for (unsigned i=0; i<data.points.rows();++i)
+    {
+      points_V_vbo.col(i) = data.points.block<1, 3>(i, 0).transpose().cast<float>();
+      points_V_colors_vbo.col(i) = data.points.block<1, 3>(i, 3).transpose().cast<float>();
+      points_F_vbo(i) = i;
+    }
+  }
+}
+
+IGL_INLINE void igl::OpenGL_state::bind_mesh()
+{
+  glBindVertexArray(vao_mesh);
+  shader_mesh.bind();
+  shader_mesh.bindVertexAttribArray("position", vbo_V, V_vbo, dirty & DIRTY_POSITION);
+  shader_mesh.bindVertexAttribArray("normal", vbo_V_normals, V_normals_vbo, dirty & DIRTY_NORMAL);
+  shader_mesh.bindVertexAttribArray("Ka", vbo_V_ambient, V_ambient_vbo, dirty & DIRTY_AMBIENT);
+  shader_mesh.bindVertexAttribArray("Kd", vbo_V_diffuse, V_diffuse_vbo, dirty & DIRTY_DIFFUSE);
+  shader_mesh.bindVertexAttribArray("Ks", vbo_V_specular, V_specular_vbo, dirty & DIRTY_SPECULAR);
+  shader_mesh.bindVertexAttribArray("texcoord", vbo_V_uv, V_uv_vbo, dirty & DIRTY_UV);
+
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_F);
+  if (dirty & 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 & 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);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_u, tex_v, 0, GL_RGB, GL_UNSIGNED_BYTE, tex.data());
+  }
+  glUniform1i(shader_mesh.uniform("tex"), 0);
+  dirty &= ~DIRTY_MESH;
+}
+
+IGL_INLINE void igl::OpenGL_state::bind_overlay_lines()
+{
+  bool is_dirty = dirty & DIRTY_OVERLAY_LINES;
+
+  glBindVertexArray(vao_overlay_lines);
+  shader_overlay_lines.bind();
+  shader_overlay_lines.bindVertexAttribArray("position", vbo_lines_V, lines_V_vbo, is_dirty);
+  shader_overlay_lines.bindVertexAttribArray("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 &= ~DIRTY_OVERLAY_LINES;
+}
+
+IGL_INLINE void igl::OpenGL_state::bind_overlay_points()
+{
+  bool is_dirty = dirty & DIRTY_OVERLAY_POINTS;
+
+  glBindVertexArray(vao_overlay_points);
+  shader_overlay_points.bind();
+  shader_overlay_points.bindVertexAttribArray("position", vbo_points_V, points_V_vbo, is_dirty);
+  shader_overlay_points.bindVertexAttribArray("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 &= ~DIRTY_OVERLAY_POINTS;
+}
+
+IGL_INLINE void igl::OpenGL_state::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.cols(), GL_UNSIGNED_INT, 0);
+
+  glDisable(GL_POLYGON_OFFSET_FILL);
+  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+}
+
+IGL_INLINE void igl::OpenGL_state::draw_overlay_lines()
+{
+  glDrawElements(GL_LINES, lines_F_vbo.cols(), GL_UNSIGNED_INT, 0);
+}
+
+IGL_INLINE void igl::OpenGL_state::draw_overlay_points()
+{
+  glDrawElements(GL_POINTS, points_F_vbo.cols(), GL_UNSIGNED_INT, 0);
+}
+
+IGL_INLINE void igl::OpenGL_state::init()
+{
+  std::string mesh_vertex_shader_string =
+  "#version 150\n"
+  "uniform mat4 model;"
+  "uniform mat4 view;"
+  "uniform mat4 proj;"
+  "in vec3 position;"
+  "in vec3 normal;"
+  "out vec3 position_eye;"
+  "out vec3 normal_eye;"
+  "in vec3 Ka;"
+  "in vec3 Kd;"
+  "in vec3 Ks;"
+  "in vec2 texcoord;"
+  "out vec2 texcoordi;"
+  "out vec3 Kai;"
+  "out vec3 Kdi;"
+  "out vec3 Ksi;"
+
+  "void main()"
+  "{"
+  "  position_eye = vec3 (view * model * vec4 (position, 1.0));"
+  "  normal_eye = vec3 (view * model * vec4 (normal, 0.0));"
+  "  normal_eye = normalize(normal_eye);"
+  "  gl_Position = proj * vec4 (position_eye, 1.0);" //proj * view * model * vec4(position, 1.0);"
+  "  Kai = Ka;"
+  "  Kdi = Kd;"
+  "  Ksi = Ks;"
+  "  texcoordi = texcoord;"
+  "}";
+
+  std::string mesh_fragment_shader_string =
+  "#version 150\n"
+  "uniform mat4 model;"
+  "uniform mat4 view;"
+  "uniform mat4 proj;"
+  "uniform vec4 fixed_color;"
+  "in vec3 position_eye;"
+  "in vec3 normal_eye;"
+  "uniform vec3 light_position_world;"
+  "vec3 Ls = vec3 (1, 1, 1);"
+  "vec3 Ld = vec3 (1, 1, 1);"
+  "vec3 La = vec3 (1, 1, 1);"
+  "in vec3 Ksi;"
+  "in vec3 Kdi;"
+  "in vec3 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 * Kai;"    // ambient intensity
+
+  "vec3 light_position_eye = vec3 (view * vec4 (light_position_world, 1.0));"
+  "vec3 distance_to_light_eye = light_position_eye - position_eye;"
+  "vec3 direction_to_light_eye = normalize (distance_to_light_eye);"
+  "float dot_prod = dot (direction_to_light_eye, normal_eye);"
+  "dot_prod = max (dot_prod, 0.0);"
+  "vec3 Id = Ld * Kdi * dot_prod;"    // Diffuse intensity
+
+  "vec3 reflection_eye = reflect (-direction_to_light_eye, normal_eye);"
+  "vec3 surface_to_viewer_eye = normalize (-position_eye);"
+  "float dot_prod_specular = dot (reflection_eye, surface_to_viewer_eye);"
+  "dot_prod_specular = max (dot_prod_specular, 0.0);"
+  "float specular_factor = pow (dot_prod_specular, specular_exponent);"
+  "vec3 Is = Ls * Ksi * specular_factor;"    // specular intensity
+  "vec4 color = vec4(lighting_factor * (Is + Id) + Ia, 1.0) + vec4((1.0-lighting_factor) * Kdi,1.0);"
+  "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 =
+  "#version 150\n"
+  "uniform mat4 model;"
+  "uniform mat4 view;"
+  "uniform mat4 proj;"
+  "in vec3 position;"
+  "in vec3 color;"
+  "out vec3 color_frag;"
+
+  "void main()"
+  "{"
+  "  gl_Position = proj * view * model * vec4 (position, 1.0);"
+  "  color_frag = color;"
+  "}";
+
+  std::string overlay_fragment_shader_string =
+  "#version 150\n"
+  "in vec3 color_frag;"
+  "out vec4 outColor;"
+  "void main()"
+  "{"
+  "  outColor = vec4(color_frag, 1.0);"
+  "}";
+
+  std::string overlay_point_fragment_shader_string =
+  "#version 150\n"
+  "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();
+  shader_mesh.init(mesh_vertex_shader_string,
+      mesh_fragment_shader_string, "outColor");
+  shader_overlay_lines.init(overlay_vertex_shader_string,
+      overlay_fragment_shader_string, "outColor");
+  shader_overlay_points.init(overlay_vertex_shader_string,
+      overlay_point_fragment_shader_string, "outColor");
+}
+
+IGL_INLINE void igl::OpenGL_state::free()
+{
+  shader_mesh.free();
+  shader_overlay_lines.free();
+  shader_overlay_points.free();
+  free_buffers();
+}

+ 124 - 0
include/igl/viewer/OpenGL_state.h

@@ -0,0 +1,124 @@
+#ifndef IGL_OPENGL_STATE_H
+#define IGL_OPENGL_STATE_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 <igl/viewer/OpenGL_shader.h>
+#include <igl/viewer/ViewerData.h>
+
+namespace igl
+{
+
+class OpenGL_state
+{
+public:
+  typedef unsigned int GLuint;
+
+  GLuint vao_mesh;
+  GLuint vao_overlay_lines;
+  GLuint vao_overlay_points;
+  OpenGL_shader shader_mesh;
+  OpenGL_shader shader_overlay_lines;
+  OpenGL_shader 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
+  Eigen::MatrixXf V_vbo;
+  Eigen::MatrixXf V_normals_vbo;
+  Eigen::MatrixXf V_ambient_vbo;
+  Eigen::MatrixXf V_diffuse_vbo;
+  Eigen::MatrixXf V_specular_vbo;
+  Eigen::MatrixXf V_uv_vbo;
+  Eigen::MatrixXf lines_V_vbo;
+  Eigen::MatrixXf lines_V_colors_vbo;
+  Eigen::MatrixXf points_V_vbo;
+  Eigen::MatrixXf points_V_colors_vbo;
+
+  int tex_u;
+  int tex_v;
+  Eigen::Matrix<char,Eigen::Dynamic,1> tex;
+
+  Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic> F_vbo;
+  Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic> lines_F_vbo;
+  Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic> 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();
+
+  // Update contents from a 'Data' instance
+  IGL_INLINE void set_data(const igl::ViewerData &data, bool face_based, bool invert_normals);
+
+  // 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();
+
+  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
+  };
+
+};
+
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "OpenGL_state.cpp"
+#endif
+
+#endif

+ 1 - 0
include/igl/viewer/TODOs.txt

@@ -1,3 +1,4 @@
+- depth test for overlays cannot be disabled
 - data.lines, data.points should not concatenate colors with coordinates
 - snap to canonical recenters origin but trackball does not
 - rewrite in libigl style

+ 111 - 0
include/igl/viewer/TextRenderer.cpp

@@ -0,0 +1,111 @@
+#include "TextRenderer.h"
+#include <igl/project.h>
+
+  IGL_INLINE igl::TextRenderer::TextRenderer() : m_shaderHandleBackup(0) { }
+
+  IGL_INLINE int igl::TextRenderer::Init()
+  {
+    int retval = CTwGraphOpenGLCore::Init();
+    if (retval == 1)
+    {
+      std::string vertexShader =
+          "#version 150\n"
+          "uniform vec2 offset;"
+          "uniform vec2 wndSize;"
+          "uniform vec4 color;"
+          "uniform float depth;"
+          "in vec2 vertex;"
+          "in vec2 uv;"
+          "out vec4 fcolor;"
+          "out vec2 fuv;"
+          "void main() {"
+          "  gl_Position = vec4(2.0*(vertex.x+offset.x-0.5)/wndSize.x - 1.0,"
+          "                     1.0 - 2.0*(vertex.y+offset.y-0.5)/wndSize.y,"
+          "                     depth, 1);"
+          " fuv = uv;"
+          " fcolor = color;"
+          "}";
+
+      std::string fragmentShader =
+        "#version 150\n"
+        "uniform sampler2D tex;"
+        "in vec2 fuv;"
+        "in vec4 fcolor;"
+        "out vec4 outColor;"
+        "void main() { outColor.rgb = fcolor.bgr; outColor.a = fcolor.a * texture(tex, fuv).r; }";
+
+      if (!m_shader.init(vertexShader, fragmentShader, "outColor"))
+        return 0;
+
+      /* Adjust location bindings */
+      glBindAttribLocation(m_shader.program_shader, 0, "vertex");
+      glBindAttribLocation(m_shader.program_shader, 1, "uv");
+      glBindAttribLocation(m_shader.program_shader, 2, "color");
+      glLinkProgram(m_shader.program_shader);
+
+      m_shaderHandleBackup = m_TriTexUniProgram;
+      m_TriTexUniProgram = m_shader.program_shader;
+      m_TriTexUniLocationOffset = m_shader.uniform("offset");
+      m_TriTexUniLocationWndSize = m_shader.uniform("wndSize");
+      m_TriTexUniLocationColor = m_shader.uniform("color");
+      m_TriTexUniLocationTexture = m_shader.uniform("tex");
+      m_TriTexUniLocationDepth = m_shader.uniform("depth");
+    }
+    return retval;
+  }
+
+  IGL_INLINE int igl::TextRenderer::Shut()
+  {
+    for (auto kv : m_textObjects)
+      DeleteTextObj(kv.second);
+    m_shader.free();
+    m_TriTexUniProgram = m_shaderHandleBackup;
+    return CTwGraphOpenGLCore::Shut();
+  }
+
+  IGL_INLINE void igl::TextRenderer::BeginDraw(const Eigen::Matrix4f &view, const Eigen::Matrix4f &proj,
+    const Eigen::Vector4f &_viewport, float _object_scale)
+  {
+    viewport = _viewport;
+    proj_matrix = proj;
+    view_matrix = view;
+    CTwGraphOpenGLCore::BeginDraw(viewport[2], viewport[3]);
+    glEnable(GL_DEPTH_TEST);
+    glDepthMask(GL_FALSE);
+    object_scale = _object_scale;
+  }
+
+  IGL_INLINE void igl::TextRenderer::EndDraw()
+  {
+    /* Limit the number of cached text objects */
+    for (auto it = m_textObjects.cbegin(); it != m_textObjects.cend(); )
+    {
+      if (m_textObjects.size() < 1000000)
+        break;
+      DeleteTextObj(it->second);
+      m_textObjects.erase(it++);
+    }
+
+    glDepthMask(GL_TRUE);
+    CTwGraphOpenGLCore::EndDraw();
+  }
+
+  IGL_INLINE void igl::TextRenderer::DrawText(Eigen::Vector3d pos, Eigen::Vector3d normal, const std::string &text)
+  {
+    pos += normal * 0.005f * object_scale;
+    Eigen::Vector3f coord = igl::project(Eigen::Vector3f(pos(0), pos(1), pos(2)),
+        view_matrix, proj_matrix, viewport);
+    auto it = m_textObjects.find(text);
+    void *text_obj = nullptr;
+    if (it == m_textObjects.end())
+    {
+      text_obj = NewTextObj();
+      BuildText(text_obj, &text, NULL, NULL, 1, g_DefaultNormalFont, 0, 0);
+      m_textObjects[text] = text_obj;
+    } else {
+      text_obj = it->second;
+    }
+    m_shader.bind();
+    glUniform1f(m_TriTexUniLocationDepth, 2*(coord(2)-0.5f));
+    CTwGraphOpenGLCore::DrawText(text_obj, coord[0], viewport[3] - coord[1], COLOR32_BLUE, 0);
+  }

+ 47 - 0
include/igl/viewer/TextRenderer.h

@@ -0,0 +1,47 @@
+/* This class extends the font rendering code in AntTweakBar
+   so that it can be used to render text at arbitrary 3D positions */
+
+#ifndef IGL_TEXT_RENDERER_H
+#define IGL_TEXT_RENDERER_H
+
+#include <igl/igl_inline.h>
+#include <igl/viewer/OpenGL_shader.h>
+#include <TwOpenGLCore.h>
+#include <map>
+
+
+namespace igl
+{
+
+class TextRenderer : public CTwGraphOpenGLCore
+{
+public:
+  IGL_INLINE TextRenderer();
+
+  IGL_INLINE virtual int Init();
+  IGL_INLINE virtual int Shut();
+
+  IGL_INLINE void BeginDraw(const Eigen::Matrix4f &view, const Eigen::Matrix4f &proj,
+    const Eigen::Vector4f &_viewport, float _object_scale);
+
+  IGL_INLINE void EndDraw();
+
+  IGL_INLINE void DrawText(Eigen::Vector3d pos, Eigen::Vector3d normal, const std::string &text);
+
+protected:
+  igl::OpenGL_shader m_shader;
+  std::map<std::string, void *> m_textObjects;
+  GLuint m_shaderHandleBackup;
+  GLuint m_TriTexUniLocationDepth;
+  Eigen::Matrix4f view_matrix, proj_matrix;
+  Eigen::Vector4f viewport;
+  float object_scale;
+};
+
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "TextRenderer.cpp"
+#endif
+
+#endif

+ 1693 - 0
include/igl/viewer/Viewer.cpp

@@ -0,0 +1,1693 @@
+#include "Viewer.h"
+#include <igl/get_seconds.h>
+
+#ifdef _WIN32
+#  include <windows.h>
+#  undef max
+#  undef min
+#endif
+
+// Todo: windows equivalent for `usleep`
+#include <unistd.h>
+
+#ifndef __APPLE__
+#  define GLEW_STATIC
+#  include <GL/glew.h>
+#endif
+
+#ifdef __APPLE__
+#   include <OpenGL/gl3.h>
+#   define __gl_h_ /* Prevent inclusion of the old gl.h */
+#else
+#   ifdef _WIN32
+#       include <windows.h>
+#   endif
+#   include <GL/gl.h>
+#endif
+
+#include <Eigen/LU>
+
+#define GLFW_INCLUDE_GLU
+#include <GLFW/glfw3.h>
+
+#include <cmath>
+#include <cstdio>
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+#include <fstream>
+
+#include <algorithm>
+#include <igl/project.h>
+
+Eigen::Matrix4f lookAt (
+                        const Eigen::Vector3f& eye,
+                        const Eigen::Vector3f& center,
+                        const Eigen::Vector3f& up)
+{
+  Eigen::Vector3f f = (center - eye).normalized();
+  Eigen::Vector3f s = f.cross(up).normalized();
+  Eigen::Vector3f u = s.cross(f);
+
+  Eigen::Matrix4f Result = Eigen::Matrix4f::Identity();
+  Result(0,0) = s(0);
+  Result(0,1) = s(1);
+  Result(0,2) = s(2);
+  Result(1,0) = u(0);
+  Result(1,1) = u(1);
+  Result(1,2) = u(2);
+  Result(2,0) =-f(0);
+  Result(2,1) =-f(1);
+  Result(2,2) =-f(2);
+  Result(0,3) =-s.transpose() * eye;
+  Result(1,3) =-u.transpose() * eye;
+  Result(2,3) = f.transpose() * eye;
+  return Result;
+}
+
+Eigen::Matrix4f ortho (
+                       const float left,
+                       const float right,
+                       const float bottom,
+                       const float top,
+                       const float zNear,
+                       const float zFar
+                       )
+{
+  Eigen::Matrix4f Result = Eigen::Matrix4f::Identity();
+  Result(0,0) = 2.0f / (right - left);
+  Result(1,1) = 2.0f / (top - bottom);
+  Result(2,2) = - 2.0f / (zFar - zNear);
+  Result(0,3) = - (right + left) / (right - left);
+  Result(1,3) = - (top + bottom) / (top - bottom);
+  Result(2,3) = - (zFar + zNear) / (zFar - zNear);
+  return Result;
+}
+
+Eigen::Matrix4f frustum (
+                         const float left,
+                         const float right,
+                         const float bottom,
+                         const float top,
+                         const float nearVal,
+                         const float farVal)
+{
+  Eigen::Matrix4f Result = Eigen::Matrix4f::Zero();
+  Result(0,0) = (2.0f * nearVal) / (right - left);
+  Result(1,1) = (2.0f * nearVal) / (top - bottom);
+  Result(0,2) = (right + left) / (right - left);
+  Result(1,2) = (top + bottom) / (top - bottom);
+  Result(2,2) = -(farVal + nearVal) / (farVal - nearVal);
+  Result(3,2) = -1.0f;
+  Result(2,3) = -(2.0f * farVal * nearVal) / (farVal - nearVal);
+  return Result;
+}
+
+Eigen::Matrix4f scale (const Eigen::Matrix4f& m,
+                       const Eigen::Vector3f& v)
+{
+  Eigen::Matrix4f Result;
+  Result.col(0) = m.col(0).array() * v(0);
+  Result.col(1) = m.col(1).array() * v(1);
+  Result.col(2) = m.col(2).array() * v(2);
+  Result.col(3) = m.col(3);
+  return Result;
+}
+
+Eigen::Matrix4f translate(
+                          const Eigen::Matrix4f& m,
+                          const Eigen::Vector3f& v)
+{
+  Eigen::Matrix4f Result = m;
+  Result.col(3) = m.col(0).array() * v(0) + m.col(1).array() * v(1) + m.col(2).array() * v(2) + m.col(3).array();
+  return Result;
+}
+
+
+#include <limits>
+#include <cassert>
+
+#ifdef ENABLE_XML_SERIALIZATION
+  #include "igl/xml/XMLSerializer.h"
+#endif
+
+#include <igl/readOBJ.h>
+#include <igl/readOFF.h>
+#include <igl/per_face_normals.h>
+#include <igl/per_vertex_normals.h>
+#include <igl/per_corner_normals.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_to_mat.h>
+#include <igl/quat_mult.h>
+#include <igl/axis_angle_to_quat.h>
+#include <igl/trackball.h>
+#include <igl/snap_to_canonical_view_quat.h>
+#include <igl/unproject.h>
+#include <igl/viewer/TextRenderer.h>
+
+// Internal global variables used for glfw event handling
+static igl::Viewer * __viewer;
+static double highdpi = 1;
+static double scroll_x = 0;
+static double scroll_y = 0;
+
+static igl::TextRenderer __font_renderer;
+
+static void glfw_mouse_press(GLFWwindow* window, int button, int action, int modifier)
+{
+  bool tw_used = TwEventMouseButtonGLFW(button, action);
+  igl::Viewer::MouseButton mb;
+
+  if (button == GLFW_MOUSE_BUTTON_1)
+    mb = igl::Viewer::IGL_LEFT;
+  else if (button == GLFW_MOUSE_BUTTON_2)
+    mb = igl::Viewer::IGL_RIGHT;
+  else //if (button == GLFW_MOUSE_BUTTON_3)
+    mb = igl::Viewer::IGL_MIDDLE;
+
+  if (action == GLFW_PRESS)
+  {
+    if(!tw_used)
+    {
+      __viewer->mouse_down(mb,modifier);
+    }
+  } else
+  {
+    // Always call mouse_up on up
+    __viewer->mouse_up(mb,modifier);
+  }
+
+}
+
+static void glfw_error_callback(int error, const char* description)
+{
+  fputs(description, stderr);
+}
+
+int global_KMod = 0;
+
+int TwEventKeyGLFW3(int glfwKey, int glfwAction)
+{
+  int handled = 0;
+
+  // Register of modifiers state
+  if (glfwAction==GLFW_PRESS)
+  {
+    switch (glfwKey)
+    {
+      case GLFW_KEY_LEFT_SHIFT:
+      case GLFW_KEY_RIGHT_SHIFT:
+        global_KMod |= TW_KMOD_SHIFT;
+        break;
+      case GLFW_KEY_LEFT_CONTROL:
+      case GLFW_KEY_RIGHT_CONTROL:
+        global_KMod |= TW_KMOD_CTRL;
+        break;
+      case GLFW_KEY_LEFT_ALT:
+      case GLFW_KEY_RIGHT_ALT:
+        global_KMod |= TW_KMOD_ALT;
+        break;
+    }
+  }
+  else
+  {
+    switch (glfwKey)
+    {
+      case GLFW_KEY_LEFT_SHIFT:
+      case GLFW_KEY_RIGHT_SHIFT:
+        global_KMod &= ~TW_KMOD_SHIFT;
+        break;
+      case GLFW_KEY_LEFT_CONTROL:
+      case GLFW_KEY_RIGHT_CONTROL:
+        global_KMod &= ~TW_KMOD_CTRL;
+        break;
+      case GLFW_KEY_LEFT_ALT:
+      case GLFW_KEY_RIGHT_ALT:
+        global_KMod &= ~TW_KMOD_ALT;
+        break;
+    }
+  }
+
+  // Process key pressed
+  if (glfwAction==GLFW_PRESS)
+  {
+    int mod = global_KMod;
+    int testkp = ((mod&TW_KMOD_CTRL) || (mod&TW_KMOD_ALT)) ? 1 : 0;
+
+    if ((mod&TW_KMOD_CTRL) && glfwKey>0 && glfwKey<GLFW_KEY_ESCAPE   )   // CTRL cases
+      handled = TwKeyPressed(glfwKey, mod);
+    else if (glfwKey>=GLFW_KEY_ESCAPE  )
+    {
+      int k = 0;
+
+      if (glfwKey>=GLFW_KEY_F1 && glfwKey<=GLFW_KEY_F15)
+        k = TW_KEY_F1 + (glfwKey-GLFW_KEY_F1);
+      else if (testkp && glfwKey>=GLFW_KEY_KP_0 && glfwKey<=GLFW_KEY_KP_9)
+        k = '0' + (glfwKey-GLFW_KEY_KP_0);
+      else
+      {
+        switch (glfwKey)
+        {
+          case GLFW_KEY_ESCAPE  :
+            k = TW_KEY_ESCAPE;
+            break;
+          case GLFW_KEY_UP:
+            k = TW_KEY_UP;
+            break;
+          case GLFW_KEY_DOWN:
+            k = TW_KEY_DOWN;
+            break;
+          case GLFW_KEY_LEFT:
+            k = TW_KEY_LEFT;
+            break;
+          case GLFW_KEY_RIGHT:
+            k = TW_KEY_RIGHT;
+            break;
+          case GLFW_KEY_TAB:
+            k = TW_KEY_TAB;
+            break;
+          case GLFW_KEY_ENTER:
+            k = TW_KEY_RETURN;
+            break;
+          case GLFW_KEY_BACKSPACE:
+            k = TW_KEY_BACKSPACE;
+            break;
+          case GLFW_KEY_INSERT:
+            k = TW_KEY_INSERT;
+            break;
+          case GLFW_KEY_DELETE:
+            k = TW_KEY_DELETE;
+            break;
+          case GLFW_KEY_PAGE_UP:
+            k = TW_KEY_PAGE_UP;
+            break;
+          case GLFW_KEY_PAGE_DOWN:
+            k = TW_KEY_PAGE_DOWN;
+            break;
+          case GLFW_KEY_HOME:
+            k = TW_KEY_HOME;
+            break;
+          case GLFW_KEY_END:
+            k = TW_KEY_END;
+            break;
+          case GLFW_KEY_KP_ENTER:
+            k = TW_KEY_RETURN;
+            break;
+          case GLFW_KEY_KP_DIVIDE:
+            if (testkp)
+              k = '/';
+            break;
+          case GLFW_KEY_KP_MULTIPLY:
+            if (testkp)
+              k = '*';
+            break;
+          case GLFW_KEY_KP_SUBTRACT:
+            if (testkp)
+              k = '-';
+            break;
+          case GLFW_KEY_KP_ADD:
+            if (testkp)
+              k = '+';
+            break;
+          case GLFW_KEY_KP_DECIMAL:
+            if (testkp)
+              k = '.';
+            break;
+          case GLFW_KEY_KP_EQUAL:
+            if (testkp)
+              k = '=';
+            break;
+        }
+      }
+
+      if (k>0)
+        handled = TwKeyPressed(k, mod);
+    }
+  }
+
+  return handled;
+}
+
+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 (!TwEventKeyGLFW3(key,action))
+  {
+    if (action == GLFW_PRESS)
+      __viewer->key_down(key, modifier);
+    else
+      __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->resize(w, h);
+
+  TwWindowSize(w, h);
+  const auto & bar = __viewer->bar;
+  // Keep AntTweakBar on right side of screen and height == opengl height
+  // get the current position of a bar
+  int size[2];
+  TwGetParam(bar, NULL, "size", TW_PARAM_INT32, 2, size);
+  int pos[2];
+  // Place bar on left side of opengl rect (padded by 10 pixels)
+  pos[0] = 10;//max(10,(int)width - size[0] - 10);
+  // place bar at top (padded by 10 pixels)
+  pos[1] = 10;
+  // Set height to new height of window (padded by 10 pixels on bottom)
+  size[1] = highdpi*(height-pos[1]-10);
+  TwSetParam(bar, NULL, "position", TW_PARAM_INT32, 2, pos);
+  TwSetParam(bar, NULL, "size", TW_PARAM_INT32, 2,size);
+}
+
+static void glfw_mouse_move(GLFWwindow* window, double x, double y)
+{
+  if(!TwEventMousePosGLFW(x*highdpi,y*highdpi) || __viewer->down)
+  {
+    // Call if TwBar hasn't used or if down
+    __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;
+
+  if (!TwEventMouseWheelGLFW(scroll_y))
+    __viewer->mouse_scroll(y);
+}
+
+static void glfw_char_callback(GLFWwindow* window, unsigned int c)
+{
+  if ((c & 0xff00)==0)
+    TwKeyPressed(c, global_KMod);
+}
+
+namespace igl
+{
+  void Viewer::init()
+  {
+    // Create a tweak bar
+    bar = TwNewBar("libIGL-Viewer");
+    TwDefine(" libIGL-Viewer help='This is a simple 3D mesh viewer.' "); // Message added to the help bar->
+    TwDefine(" libIGL-Viewer size='200 685'"); // change default tweak bar size
+    TwDefine(" libIGL-Viewer color='76 76 127' "); // change default tweak bar color
+    TwDefine(" libIGL-Viewer refresh=0.5"); // change refresh rate
+
+    // ---------------------- LOADING ----------------------
+
+    #ifdef ENABLE_XML_SERIALIZATION
+    TwAddButton(bar,"Load Scene", load_scene_cb,    this, "group='Workspace'");
+    TwAddButton(bar,"Save Scene", save_scene_cb,    this, "group='Workspace'");
+    #endif
+
+    #ifdef ENABLE_IO
+    TwAddButton(bar,"Load Mesh",  open_dialog_mesh, this, "group='Mesh' key=o");
+    #endif
+
+    // ---------------------- SCENE ----------------------
+
+    TwAddButton(bar,"Center object", align_camera_center_cb, this,
+                " group='Viewing Options'"
+                " label='Center object' key=A help='Set the center of the camera to the mesh center.'");
+    TwAddVarRW(bar, "Zoom", TW_TYPE_FLOAT, &(options.camera_zoom),
+               " min=0.05 max=50 step=0.1 keyIncr=+ keyDecr=- help='Scale the object (1=original size).' group='Scene'");
+    TwAddButton(bar,"SnapView", snap_to_canonical_quaternion_cb, this,
+                " group='Scene'"
+                " label='Snap to canonical view' key=Z "
+                " help='Snaps view to nearest canonical view.'");
+    TwAddVarRW(bar,"LightDir", TW_TYPE_DIR3F, options.light_position.data(),
+               " group='Scene'"
+               " label='Light direction' open help='Change the light direction.' ");
+
+    // ---------------------- DRAW OPTIONS ----------------------
+    TwAddVarRW(bar, "ToggleOrthographic", TW_TYPE_BOOLCPP, &(options.orthographic),
+               " group='Viewing Options'"
+               " label='Orthographic view' "
+               " help='Toggles orthographic / perspective view. Default: perspective.'");
+    TwAddVarRW(bar, "Rotation", TW_TYPE_QUAT4F, &(options.trackball_angle),
+      " group='Viewing Options'"
+      " label='Rotation'"
+      " help='Rotates view.'");
+    TwAddVarCB(bar,"Face-based Normals/Colors", TW_TYPE_BOOLCPP, set_face_based_cb, get_face_based_cb, this,
+               " group='Draw options'"
+               " label='Face-based' key=T help='Toggle per face shading/colors.' ");
+
+    TwAddVarRW(bar,"Show texture", TW_TYPE_BOOLCPP, &(options.show_texture),
+               " group='Draw options'");
+
+    TwAddVarCB(bar,"Invert Normals", TW_TYPE_BOOLCPP, set_invert_normals_cb, get_invert_normals_cb, this,
+               " group='Draw options'"
+               " label='Invert normals' key=i help='Invert normal directions for inside out meshes.' ");
+
+    TwAddVarRW(bar,"ShowOverlay", TW_TYPE_BOOLCPP, &(options.show_overlay),
+               " group='Draw options'"
+               " label='Show overlay' key=o help='Show the overlay layers.' ");
+    TwAddVarRW(bar,"ShowOverlayDepth", TW_TYPE_BOOLCPP, &(options.show_overlay_depth),
+               " group='Draw options'"
+               " label='Show overlay depth test' help='Enable the depth test for overlay layer.' ");
+    TwAddVarRW(bar,"Background color", TW_TYPE_COLOR3F,
+               options.background_color.data(),
+               " help='Select a background color' colormode=hls group='Draw options'");
+    TwAddVarRW(bar, "LineColor", TW_TYPE_COLOR3F,
+               options.line_color.data(),
+               " label='Line color' help='Select a outline color' group='Draw options'");
+    TwAddVarRW(bar,"Shininess",TW_TYPE_FLOAT,&options.shininess," group='Draw options'"
+               " min=1 max=128");
+
+    // ---------------------- Overlays ----------------------
+
+    TwAddVarRW(bar,"Wireframe", TW_TYPE_BOOLCPP, &(options.show_lines),
+               " group='Overlays'"
+               " label='Wireframe' key=l help='Toggle wire frame of mesh'");
+    TwAddVarRW(bar,"Fill", TW_TYPE_BOOLCPP, &(options.show_faces),
+               " group='Overlays'"
+               " label='Fill' key=t help='Display filled polygons of mesh'");
+    TwAddVarRW(bar,"ShowVertexId", TW_TYPE_BOOLCPP, &(options.show_vertid),
+               " group='Overlays'"
+               " label='Show Vertex Labels' key=';' help='Toggle vertex indices'");
+    TwAddVarRW(bar,"ShowFaceId", TW_TYPE_BOOLCPP, &(options.show_faceid),
+               " group='Overlays'"
+               " label='Show Faces Labels' key='CTRL+;' help='Toggle face"
+               " indices'");
+
+    __font_renderer.Init();
+
+    init_plugins();
+  }
+
+  Viewer::Viewer()
+  {
+    // Default shininess
+    options.shininess = 35.0f;
+
+    // Default colors
+    options.background_color << 0.3f, 0.3f, 0.5f;
+    options.line_color << 0.0f, 0.0f, 0.0f;
+
+    // Default lights settings
+    options.light_position << 0.0f, -0.30f, -5.0f;
+    options.lighting_factor = 1.0f; //on
+
+    // Default trackball
+    options.trackball_angle << 0.0f, 0.0f, 0.0f, 1.0f;
+
+    // Defalut model viewing parameters
+    options.model_zoom = 1.0f;
+    options.model_translation << 0,0,0;
+
+    // Camera parameters
+    options.camera_zoom = 1.0f;
+    options.orthographic = false;
+    options.camera_view_angle = 45.0;
+    options.camera_dnear = 1.0;
+    options.camera_dfar = 100.0;
+    options.camera_eye << 0, 0, 5;
+    options.camera_center << 0, 0, 0;
+    options.camera_up << 0, 1, 0;
+
+    // Default visualization options
+    options.show_faces = true;
+    options.show_lines = true;
+    options.invert_normals = false;
+    options.show_overlay = true;
+    options.show_overlay_depth = true;
+    options.show_vertid = false;
+    options.show_faceid = false;
+    options.show_texture = false;
+
+    // Default point size / line width
+    options.point_size = 15;
+    options.line_width = 0.5f;
+    options.face_based = false;
+    options.is_animating = false;
+    options.animation_max_fps = 30.;
+
+    // Temporary variables initialization
+    down = false;
+    hack_never_moved = true;
+    scroll_position = 0.0f;
+
+    // Per face
+    set_face_based(false);
+
+    // C-style callbacks
+    callback_pre_draw     = 0;
+    callback_post_draw    = 0;
+    callback_mouse_down   = 0;
+    callback_mouse_up     = 0;
+    callback_mouse_move   = 0;
+    callback_mouse_scroll = 0;
+    callback_key_down     = 0;
+    callback_key_up       = 0;
+
+    callback_pre_draw_data = 0;
+    callback_post_draw     = 0;
+    callback_mouse_down    = 0;
+    callback_mouse_up      = 0;
+    callback_mouse_move    = 0;
+    callback_mouse_scroll  = 0;
+    callback_key_down      = 0;
+    callback_key_up        = 0;
+  }
+
+  void Viewer::init_plugins()
+  {
+    // Init all plugins
+    for (unsigned int i = 0; i<plugins.size(); ++i)
+      plugins[i]->init(this);
+  }
+
+  Viewer::~Viewer()
+  {
+  }
+
+  void Viewer::shutdown_plugins()
+  {
+    for (unsigned int i = 0; i<plugins.size(); ++i)
+      plugins[i]->shutdown();
+  }
+
+  bool Viewer::load_mesh_from_file(const char* mesh_file_name)
+  {
+    std::string mesh_file_name_string = std::string(mesh_file_name);
+
+    // 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;
+
+    clear_mesh();
+
+    size_t last_dot = mesh_file_name_string.rfind('.');
+    if (last_dot == std::string::npos)
+    {
+      printf("Error: No file extension found in %s\n",mesh_file_name);
+      return false;
+    }
+
+    std::string extension = mesh_file_name_string.substr(last_dot+1);
+
+    if (extension == "off" || extension =="OFF")
+    {
+      if (!igl::readOFF(mesh_file_name_string, data.V, data.F))
+        return false;
+    }
+    else if (extension == "obj" || extension =="OBJ")
+    {
+      Eigen::MatrixXd corner_normals;
+      Eigen::MatrixXi fNormIndices;
+
+      Eigen::MatrixXd UV_V;
+      Eigen::MatrixXi UV_F;
+
+      if (!(igl::readOBJ(mesh_file_name_string, data.V, data.F, corner_normals, fNormIndices, UV_V, UV_F)))
+        return false;
+    }
+    else
+    {
+      // unrecognized file type
+      printf("Error: %s is not a recognized file type.\n",extension.c_str());
+      return false;
+    }
+
+    compute_normals();
+    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));
+    if (data.V_uv.rows() == 0)
+      grid_texture();
+
+    align_camera_center();
+
+    for (unsigned int i = 0; i<plugins.size(); ++i)
+      if (plugins[i]->post_load())
+        return true;
+
+    return true;
+  }
+
+  void Viewer::compute_normals()
+  {
+    igl::per_face_normals(data.V, data.F, data.F_normals);
+    igl::per_vertex_normals(data.V, data.F, data.F_normals, data.V_normals);
+    data.dirty |= OpenGL_state::DIRTY_NORMAL;
+  }
+
+  void Viewer::uniform_colors(Eigen::Vector3d ambient, Eigen::Vector3d diffuse, Eigen::Vector3d specular)
+  {
+    data.V_material_ambient.resize(data.V.rows(),3);
+    data.V_material_diffuse.resize(data.V.rows(),3);
+    data.V_material_specular.resize(data.V.rows(),3);
+
+    for (unsigned i=0; i<data.V.rows();++i)
+    {
+      data.V_material_ambient.row(i) = ambient;
+      data.V_material_diffuse.row(i) = diffuse;
+      data.V_material_specular.row(i) = specular;
+    }
+
+    data.F_material_ambient.resize(data.F.rows(),3);
+    data.F_material_diffuse.resize(data.F.rows(),3);
+    data.F_material_specular.resize(data.F.rows(),3);
+
+    for (unsigned i=0; i<data.F.rows();++i)
+    {
+      data.F_material_ambient.row(i) = ambient;
+      data.F_material_diffuse.row(i) = diffuse;
+      data.F_material_specular.row(i) = specular;
+    }
+    data.dirty |= OpenGL_state::DIRTY_SPECULAR | OpenGL_state::DIRTY_DIFFUSE | OpenGL_state::DIRTY_AMBIENT;
+  }
+
+  void Viewer::grid_texture()
+  {
+    if (data.V_uv.rows() == 0)
+    {
+      data.V_uv = data.V.block(0, 0, data.V.rows(), 2);
+      data.V_uv.col(0) = data.V_uv.col(0).array() - data.V_uv.col(0).minCoeff();
+      data.V_uv.col(0) = data.V_uv.col(0).array() / data.V_uv.col(0).maxCoeff();
+      data.V_uv.col(1) = data.V_uv.col(1).array() - data.V_uv.col(1).minCoeff();
+      data.V_uv.col(1) = data.V_uv.col(1).array() / data.V_uv.col(1).maxCoeff();
+      data.V_uv = data.V_uv.array() * 10;
+      data.dirty |= OpenGL_state::DIRTY_TEXTURE;
+    }
+
+    unsigned size = 128;
+    unsigned size2 = size/2;
+    data.texture_R.resize(size, size);
+    for (unsigned i=0; i<size; ++i)
+    {
+      for (unsigned j=0; j<size; ++j)
+      {
+        data.texture_R(i,j) = 0;
+        if ((i<size2 && j<size2) || (i>=size2 && j>=size2))
+          data.texture_R(i,j) = 255;
+      }
+    }
+
+    data.texture_G = data.texture_R;
+    data.texture_B = data.texture_R;
+    data.dirty |= OpenGL_state::DIRTY_TEXTURE;
+  }
+
+  bool Viewer::save_mesh_to_file(const char* mesh_file_name)
+  {
+    std::string mesh_file_name_string(mesh_file_name);
+
+    // 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
+      printf("Error: No file extension found in %s\n",mesh_file_name);
+      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;
+  }
+
+  void Viewer::clear_mesh()
+  {
+    data.V                       = Eigen::MatrixXd (0,3);
+    data.F                       = Eigen::MatrixXi (0,3);
+
+    data.F_material_ambient      = Eigen::MatrixXd (0,3);
+    data.F_material_diffuse      = Eigen::MatrixXd (0,3);
+    data.F_material_specular     = Eigen::MatrixXd (0,3);
+
+    data.V_material_ambient      = Eigen::MatrixXd (0,3);
+    data.V_material_diffuse      = Eigen::MatrixXd (0,3);
+    data.V_material_specular     = Eigen::MatrixXd (0,3);
+
+    data.F_normals               = Eigen::MatrixXd (0,3);
+    data.V_normals               = Eigen::MatrixXd (0,3);
+
+    data.V_uv                    = Eigen::MatrixXd (0,2);
+    data.F_uv                    = Eigen::MatrixXi (0,3);
+
+    data.lines                   = Eigen::MatrixXd (0,9);
+    data.points                  = Eigen::MatrixXd (0,6);
+    data.labels_positions        = Eigen::MatrixXd (0,3);
+    data.labels_strings.clear();
+  }
+
+  bool Viewer::key_down(unsigned char 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;
+
+    if (key == 'S')
+      mouse_scroll(1);
+
+    if (key == 'A')
+      mouse_scroll(-1);
+
+    // Why aren't these handled view AntTweakBar?
+    if (key == 'z') // Don't use 'Z' because that clobbers snap_to_canonical_view_quat
+      options.trackball_angle << 0.0f, 0.0f, 0.0f, 1.0f;
+
+    if (key == 'y')
+      options.trackball_angle << -sqrt(2.0f)/2.0f, 0.0f, 0.0f, sqrt(2.0f)/2.0f;
+
+    if (key == 'x')
+      options.trackball_angle << -0.5f, -0.5f, -0.5f, 0.5f;
+
+
+    return false;
+  }
+
+  bool Viewer::key_up(unsigned char 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;
+  }
+
+  bool Viewer::mouse_down(MouseButton button, int modifier)
+  {
+    if (callback_mouse_down)
+      if (callback_mouse_down(*this,button,modifier))
+        return true;
+
+    for (unsigned int i = 0; i<plugins.size(); ++i)
+      if (plugins[i]->mouse_down(button,modifier))
+        return true;
+
+    down = true;
+
+    down_mouse_x = current_mouse_x;
+    down_mouse_y = current_mouse_y;
+    down_translation = options.model_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)), view * model, proj, viewport);
+    down_mouse_z = coord[2];
+    down_rotation = options.trackball_angle;
+
+    mouse_mode = ROTATION;
+
+    switch (button)
+    {
+      case IGL_LEFT:
+        mouse_mode = ROTATION;
+        break;
+
+      case IGL_RIGHT:
+        mouse_mode = TRANSLATE;
+        break;
+
+      default:
+        mouse_mode = NOTHING;
+        break;
+    }
+
+    return true;
+  }
+
+  bool Viewer::mouse_up(MouseButton button, int modifier)
+  {
+    down = false;
+
+    if (callback_mouse_up)
+      if (callback_mouse_up(*this,button,modifier))
+        return true;
+
+    for (unsigned int i = 0; i<plugins.size(); ++i)
+        if (plugins[i]->mouse_up(button,modifier))
+          return true;
+
+    mouse_mode = NOTHING;
+
+    return true;
+  }
+
+  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 ROTATION :
+        {
+          igl::trackball(width,
+                         height,
+                         2.0f,
+                         down_rotation.data(),
+                         down_mouse_x,
+                         down_mouse_y,
+                         mouse_x,
+                         mouse_y,
+                         options.trackball_angle.data());
+          //Eigen::Vector4f snapq = options.trackball_angle;
+
+          break;
+        }
+
+        case TRANSLATE:
+        {
+          //translation
+          Eigen::Vector3f pos1 = igl::unproject(Eigen::Vector3f(mouse_x, viewport[3] - mouse_y, down_mouse_z), view * model, proj, viewport);
+          Eigen::Vector3f pos0 = igl::unproject(Eigen::Vector3f(down_mouse_x, viewport[3] - down_mouse_y, down_mouse_z), view * model, proj, viewport);
+
+          Eigen::Vector3f diff = pos1 - pos0;
+          options.model_translation = down_translation + Eigen::Vector3f(diff[0],diff[1],diff[2]);
+
+          break;
+        }
+        case ZOOM:
+        {
+          //float delta = 0.001f * (mouse_x - down_mouse_x + mouse_y - down_mouse_y);
+          float delta = 0.001f * (mouse_x - down_mouse_x + mouse_y - down_mouse_y);
+          options.camera_zoom *= 1 + delta;
+          down_mouse_x = mouse_x;
+          down_mouse_y = mouse_y;
+          break;
+        }
+
+        default:
+          break;
+      }
+    }
+    return true;
+  }
+
+  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;
+      options.camera_zoom = (options.camera_zoom * mult > min_zoom ? options.camera_zoom * mult : min_zoom);
+    }
+    return true;
+  }
+
+  void Viewer::draw()
+  {
+    using namespace std;
+    using namespace Eigen;
+    glClearColor(options.background_color[0],
+                 options.background_color[1],
+                 options.background_color[2],
+                 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glEnable(GL_DEPTH_TEST);
+
+    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;
+
+    /* Bind and potentially refresh mesh/line/point data */
+    if (data.dirty)
+    {
+      opengl.set_data(data, options.face_based, options.invert_normals);
+      data.dirty = OpenGL_state::DIRTY_NONE;
+    }
+    opengl.bind_mesh();
+
+    // Initialize uniform
+    glViewport(0, 0, width, height);
+
+    model = Eigen::Matrix4f::Identity();
+    view  = Eigen::Matrix4f::Identity();
+    proj  = Eigen::Matrix4f::Identity();
+
+    // Set view
+    view = lookAt(Eigen::Vector3f(options.camera_eye[0], options.camera_eye[1], options.camera_eye[2]),
+                  Eigen::Vector3f(options.camera_center[0], options.camera_center[1], options.camera_center[2]),
+                  Eigen::Vector3f(options.camera_up[0], options.camera_up[1], options.camera_up[2]));
+
+    // Set projection
+    if (options.orthographic)
+    {
+      float length = (options.camera_eye - options.camera_center).norm();
+      float h = tan(options.camera_view_angle/360.0 * M_PI) * (length);
+      proj = ortho(-h*width/height, h*width/height, -h, h, options.camera_dnear, options.camera_dfar);
+    }
+    else
+    {
+      float fH = tan(options.camera_view_angle / 360.0 * M_PI) * options.camera_dnear;
+      float fW = fH * (double)width/(double)height;
+      proj = frustum(-fW, fW, -fH, fH, options.camera_dnear, options.camera_dfar);
+    }
+    // end projection
+
+    // Set model transformation
+    float mat[16];
+    igl::quat_to_mat(options.trackball_angle.data(), mat);
+
+    for (unsigned i=0;i<4;++i)
+      for (unsigned j=0;j<4;++j)
+        model(i,j) = mat[i+4*j];
+
+    model = scale(model, Eigen::Vector3f(options.camera_zoom,options.camera_zoom,options.camera_zoom));
+    model = scale(model, Eigen::Vector3f(options.model_zoom,options.model_zoom,options.model_zoom));
+    model = translate(model, Eigen::Vector3f(options.model_translation[0],options.model_translation[1],options.model_translation[2]));
+
+    // Send transformations to the GPU
+    GLint modeli = opengl.shader_mesh.uniform("model");
+    GLint viewi  = opengl.shader_mesh.uniform("view");
+    GLint proji  = opengl.shader_mesh.uniform("proj");
+    glUniformMatrix4fv(modeli, 1, GL_FALSE, model.data());
+    glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data());
+    glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data());
+
+    // Light parameters
+    GLint specular_exponenti    = opengl.shader_mesh.uniform("specular_exponent");
+    GLint light_position_worldi = opengl.shader_mesh.uniform("light_position_world");
+    GLint lighting_factori      = opengl.shader_mesh.uniform("lighting_factor");
+    GLint fixed_colori          = opengl.shader_mesh.uniform("fixed_color");
+    GLint texture_factori       = opengl.shader_mesh.uniform("texture_factor");
+
+    glUniform1f(specular_exponenti, options.shininess);
+    Vector3f rev_light = -1.*options.light_position;
+    glUniform3fv(light_position_worldi, 1, rev_light.data());
+    glUniform1f(lighting_factori, options.lighting_factor); // enables lighting
+    glUniform4f(fixed_colori, 0.0, 0.0, 0.0, 0.0);
+
+    if (data.V.rows()>0)
+    {
+      // Render fill
+      if (options.show_faces)
+      {
+        // Texture
+        glUniform1f(texture_factori, options.show_texture ? 1.0f : 0.0f);
+        opengl.draw_mesh(true);
+        glUniform1f(texture_factori, 0.0f);
+      }
+
+      // Render wireframe
+      if (options.show_lines)
+      {
+        glLineWidth(options.line_width);
+        glUniform4f(fixed_colori, options.line_color[0], options.line_color[1],
+          options.line_color[2], 1.0f);
+        opengl.draw_mesh(false);
+        glUniform4f(fixed_colori, 0.0f, 0.0f, 0.0f, 0.0f);
+      }
+
+      if (options.show_vertid)
+      {
+        __font_renderer.BeginDraw(view*model, proj, viewport, data.object_scale);
+        for (int i=0; i<data.V.rows(); ++i)
+          __font_renderer.DrawText(data.V.row(i), data.V_normals.row(i), to_string(i));
+        __font_renderer.EndDraw();
+      }
+
+      if (options.show_faceid)
+      {
+        __font_renderer.BeginDraw(view*model, proj, viewport, data.object_scale);
+
+        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 /= data.F.cols();
+
+          __font_renderer.DrawText(p, data.F_normals.row(i), to_string(i));
+        }
+        __font_renderer.EndDraw();
+      }
+    }
+
+    if (options.show_overlay)
+    {
+      if (options.show_overlay_depth)
+        glEnable(GL_DEPTH_TEST);
+      else
+        glDisable(GL_DEPTH_TEST);
+
+      if (data.lines.rows() > 0)
+      {
+        opengl.bind_overlay_lines();
+        modeli = opengl.shader_overlay_lines.uniform("model");
+        viewi  = opengl.shader_overlay_lines.uniform("view");
+        proji  = opengl.shader_overlay_lines.uniform("proj");
+
+        glUniformMatrix4fv(modeli, 1, GL_FALSE, model.data());
+        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(options.line_width);
+
+        opengl.draw_overlay_lines();
+      }
+
+      if (data.points.rows() > 0)
+      {
+        opengl.bind_overlay_points();
+        modeli = opengl.shader_overlay_points.uniform("model");
+        viewi  = opengl.shader_overlay_points.uniform("view");
+        proji  = opengl.shader_overlay_points.uniform("proj");
+
+        glUniformMatrix4fv(modeli, 1, GL_FALSE, model.data());
+        glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data());
+        glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data());
+        glPointSize(options.point_size);
+
+        opengl.draw_overlay_points();
+      }
+
+      if (data.labels_positions.rows() > 0)
+      {
+        __font_renderer.BeginDraw(view*model, proj, viewport, data.object_scale);
+        for (int i=0; i<data.labels_positions.rows(); ++i)
+          __font_renderer.DrawText(data.labels_positions.row(i), Eigen::Vector3d(0.0,0.0,0.0),
+              data.labels_strings[i]);
+        __font_renderer.EndDraw();
+      }
+
+      glEnable(GL_DEPTH_TEST);
+    }
+
+    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;
+
+    TwDraw();
+  }
+
+  bool Viewer::save_scene()
+  {
+    #ifdef ENABLE_XML_SERIALIZATION
+    string fname = igl::file_dialog_save();
+    if (fname.length() == 0)
+      return false;
+
+    ::igl::XMLSerializer serializer("Viewer");
+    serializer.Add(data,"Data");
+    serializer.Add(options,"Options");
+
+    if (plugin_manager)
+      for (unsigned int i = 0; i <plugin_manager->plugin_list.size(); ++i)
+        serializer.Add(*(plugin_manager->plugin_list[i]),plugin_manager->plugin_list[i]->plugin_name);
+
+    serializer.Save(fname.c_str(),true);
+
+    #endif
+    return true;
+  }
+
+  bool Viewer::load_scene()
+  {
+    #ifdef ENABLE_XML_SERIALIZATION
+    string fname = igl::file_dialog_open();
+    if (fname.length() == 0)
+      return false;
+
+    ::igl::XMLSerializer serializer("Viewer");
+    serializer.Add(data,"Data");
+    serializer.Add(options,"Options");
+
+    if (plugin_manager)
+      for (unsigned int i = 0; i <plugin_manager->plugin_list.size(); ++i)
+        serializer.Add(*(plugin_manager->plugin_list[i]),plugin_manager->plugin_list[i]->plugin_name);
+
+    serializer.Load(fname.c_str());
+
+    #endif
+    return true;
+  }
+
+  void Viewer::align_camera_center()
+  {
+    get_scale_and_shift_to_fit_mesh(data.V,data.F,options.model_zoom,options.model_translation);
+    data.object_scale = (data.V.colwise().maxCoeff() - data.V.colwise().minCoeff()).norm();
+  }
+
+  void Viewer::resize(int w, int h)
+  {
+    width = w;
+    height = h;
+    viewport = Eigen::Vector4f(0,0,width,height);
+  }
+
+  void Viewer::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;
+    //Compute mesh centroid
+    Eigen::SparseMatrix<double> M;
+    igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M);
+    const auto & MV = M*V;
+    Eigen::RowVector3d centroid  = MV.colwise().sum()/M.diagonal().sum();
+    Eigen::RowVector3d min_point = V.colwise().minCoeff();
+    Eigen::RowVector3d max_point = V.colwise().maxCoeff();
+
+    shift = -centroid.cast<float>();
+    double x_scale = fabs(max_point[0] - min_point[0]);
+    double y_scale = fabs(max_point[1] - min_point[1]);
+    double z_scale = fabs(max_point[2] - min_point[2]);
+    zoom = 2.0/ std::max(z_scale,std::max(x_scale,y_scale));
+  }
+
+  void TW_CALL Viewer::snap_to_canonical_quaternion_cb(void *clientData)
+  {
+    Eigen::Vector4f snapq = static_cast<Viewer *>(clientData)->options.trackball_angle;
+    igl::snap_to_canonical_view_quat<float>(snapq.data(),1,static_cast<Viewer *>(clientData)->options.trackball_angle.data());
+  }
+  void TW_CALL Viewer::align_camera_center_cb(void *clientData)
+  {
+    static_cast<Viewer *>(clientData)->align_camera_center();
+  }
+
+  void TW_CALL Viewer::save_scene_cb(void *clientData)
+  {
+    static_cast<Viewer *>(clientData)->save_scene();
+  }
+
+  void TW_CALL Viewer::load_scene_cb(void *clientData)
+  {
+    static_cast<Viewer *>(clientData)->load_scene();
+  }
+
+  void TW_CALL Viewer::set_invert_normals_cb(const void *param, void *clientData)
+  {
+    Viewer *viewer = static_cast<Viewer *>(clientData);
+    viewer->data.dirty |= OpenGL_state::DIRTY_NORMAL;
+    viewer->options.invert_normals = *((bool *) param);
+  }
+
+  void TW_CALL Viewer::get_invert_normals_cb(void *param, void *clientData)
+  {
+    *((bool *) param) = static_cast<Viewer *>(clientData)->options.invert_normals;
+  }
+
+  void TW_CALL Viewer::set_face_based_cb(const void *param, void *clientData)
+  {
+    Viewer *viewer = static_cast<Viewer *>(clientData);
+    viewer->set_face_based(*((bool *) param));
+  }
+
+  void TW_CALL Viewer::get_face_based_cb(void *param, void *clientData)
+  {
+    *((bool *) param) = static_cast<Viewer *>(clientData)->options.face_based;
+  }
+
+  void TW_CALL Viewer::open_dialog_mesh(void *clientData)
+  {
+    std::string fname = igl::file_dialog_open();
+
+    if (fname.length() == 0)
+      return;
+
+    static_cast<Viewer *>(clientData)->load_mesh_from_file(fname.c_str());
+  }
+
+  // Serialization
+  void Viewer::Options::InitSerialization()
+  {
+    #ifdef ENABLE_XML_SERIALIZATION
+    xmlSerializer->Add(shininess, "shininess");
+    xmlSerializer->Add(background_color, "background_color");
+    xmlSerializer->Add(line_color, "line_color");
+    xmlSerializer->Add(light_position, "light_position");
+    xmlSerializer->Add(lighting_factor, "lighting_factor");
+    xmlSerializer->Add(trackball_angle, "trackball_angle");
+    xmlSerializer->Add(model_zoom, "model_zoom");
+    xmlSerializer->Add(model_translation, "model_translation");
+    xmlSerializer->Add(model_zoom_uv, "model_zoom_uv");
+    xmlSerializer->Add(model_translation_uv, "model_translation_uv");
+    xmlSerializer->Add(camera_zoom, "camera_zoom");
+    xmlSerializer->Add(orthographic, "orthographic");
+    xmlSerializer->Add(camera_eye, "camera_eye");
+    xmlSerializer->Add(camera_up, "camera_up");
+    xmlSerializer->Add(camera_center, "camera_center");
+    xmlSerializer->Add(camera_view_angle, "camera_view_angle");
+    xmlSerializer->Add(camera_dnear, "camera_dnear");
+    xmlSerializer->Add(camera_dfar, "camera_dfar");
+    xmlSerializer->Add(show_overlay, "show_overlay");
+    xmlSerializer->Add(show_overlay_depth, "show_overlay_depth");
+    xmlSerializer->Add(show_texture, "show_texture");
+    xmlSerializer->Add(show_faces, "show_faces");
+    xmlSerializer->Add(show_lines, "show_lines");
+    xmlSerializer->Add(show_vertid, "show_vertid");
+    xmlSerializer->Add(show_faceid, "show_faceid");
+    xmlSerializer->Add(point_size, "point_size");
+    xmlSerializer->Add(line_width, "line_width");
+    xmlSerializer->Add(invert_normals, "invert_normals");
+    xmlSerializer->Add(face_based, "face_based");
+    #endif
+  }
+
+  void Viewer::set_face_based(bool newvalue)
+  {
+    if (options.face_based != newvalue)
+    {
+      options.face_based = newvalue;
+      data.dirty = OpenGL_state::DIRTY_ALL;
+    }
+  }
+
+  // Helpers that draws the most common meshes
+  void Viewer::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 (data.V.rows() == 0 && data.F.rows() == 0)
+    {
+      clear_mesh();
+      data.V = V_temp;
+      data.F = F;
+
+      compute_normals();
+      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));
+
+      grid_texture();
+      align_camera_center();
+    }
+    else
+    {
+      if (data.V.rows() == V.rows() && data.F.rows() == F.rows())
+      {
+        data.V = V_temp;
+        data.F = F;
+        align_camera_center();
+      }
+      else
+        cerr << "ERROR (set_mesh): The new mesh has a different number of vertices/faces. Please clear the mesh before plotting.";
+    }
+    data.dirty |= OpenGL_state::DIRTY_FACE | OpenGL_state::DIRTY_POSITION;
+  }
+
+  void Viewer::set_vertices(const Eigen::MatrixXd& V)
+  {
+    data.V = V;
+    assert(data.F.size() == 0 || data.F.maxCoeff() < data.V.rows());
+    data.dirty |= OpenGL_state::DIRTY_POSITION;
+  }
+
+  void Viewer::set_normals(const Eigen::MatrixXd& N)
+  {
+    using namespace std;
+    if (N.rows() == data.V.rows())
+    {
+      set_face_based(false);
+      data.V_normals = N;
+    }
+    else if (N.rows() == data.F.rows() || N.rows() == data.F.rows()*3)
+    {
+      set_face_based(true);
+      data.F_normals = N;
+    }
+    else
+      cerr << "ERROR (set_normals): Please provide a normal per face, per corner or per vertex.";
+    data.dirty |= OpenGL_state::DIRTY_NORMAL;
+  }
+
+  void Viewer::set_colors(const Eigen::MatrixXd &C)
+  {
+    using namespace std;
+    using namespace Eigen;
+    // Ambient color should be darker color
+    const auto ambient = [](const MatrixXd & C)->MatrixXd
+    {
+      return 0.1*C;
+    };
+    // Specular color should be a less saturated and darker color: dampened
+    // highlights
+    const auto specular = [](const MatrixXd & C)->MatrixXd
+    {
+      const double grey = 0.3;
+      return grey+0.1*(C.array()-grey);
+    };
+    if (C.rows() == 1)
+    {
+      for (unsigned i=0;i<data.V_material_diffuse.rows();++i)
+      {
+        data.V_material_diffuse.row(i) = C.row(0);
+      }
+      data.V_material_ambient = ambient(data.V_material_diffuse);
+      data.V_material_specular = specular(data.V_material_diffuse);
+
+      for (unsigned i=0;i<data.F_material_diffuse.rows();++i)
+      {
+        data.F_material_diffuse.row(i) = C.row(0);
+      }
+      data.F_material_ambient = ambient(data.F_material_diffuse);
+      data.F_material_specular = specular(data.F_material_diffuse);
+    }
+    else if (C.rows() == data.V.rows())
+    {
+      set_face_based(false);
+      data.V_material_diffuse = C;
+      data.V_material_ambient = ambient(data.V_material_diffuse);
+      data.V_material_specular = specular(data.V_material_diffuse);
+    }
+    else if (C.rows() == data.F.rows())
+    {
+      set_face_based(true);
+      data.F_material_diffuse = C;
+      data.F_material_ambient = ambient(data.F_material_diffuse);
+      data.F_material_specular = specular(data.F_material_diffuse);
+    }
+    else
+      cerr << "ERROR (set_colors): Please provide a single color, or a color per face or per vertex.";
+    data.dirty |= OpenGL_state::DIRTY_DIFFUSE;
+
+  }
+
+  void Viewer::set_uv(const Eigen::MatrixXd& UV)
+  {
+    using namespace std;
+    if (UV.rows() == data.V.rows())
+    {
+      set_face_based(false);
+      data.V_uv = UV;
+    }
+    else
+      cerr << "ERROR (set_UV): Please provide uv per vertex.";
+    data.dirty |= OpenGL_state::DIRTY_UV;
+  }
+
+  void Viewer::set_uv(const Eigen::MatrixXd& UV_V, const Eigen::MatrixXi& UV_F)
+  {
+    set_face_based(true);
+    data.V_uv = UV_V;
+    data.F_uv = UV_F;
+    data.dirty |= OpenGL_state::DIRTY_UV;
+  }
+
+
+  void Viewer::set_texture(
+    const Eigen::Matrix<char,Eigen::Dynamic,Eigen::Dynamic>& R,
+    const Eigen::Matrix<char,Eigen::Dynamic,Eigen::Dynamic>& G,
+    const Eigen::Matrix<char,Eigen::Dynamic,Eigen::Dynamic>& B)
+  {
+    data.texture_R = R;
+    data.texture_G = G;
+    data.texture_B = B;
+    data.dirty |= OpenGL_state::DIRTY_TEXTURE;
+  }
+
+  void Viewer::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 = data.points.rows();
+    data.points.conservativeResize(data.points.rows() + P_temp.rows(),6);
+    for (unsigned i=0; i<P_temp.rows(); ++i)
+      data.points.row(lastid+i) << P_temp.row(i), i<C.rows() ? C.row(i) : C.row(C.rows()-1);
+
+    data.dirty |= OpenGL_state::DIRTY_OVERLAY_POINTS;
+  }
+
+  void Viewer::set_edges(
+    const Eigen::MatrixXd& P,
+    const Eigen::MatrixXi& E,
+    const Eigen::MatrixXd& C)
+  {
+    using namespace Eigen;
+    data.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);
+      }
+      data.lines.row(e)<< P.row(E(e,0)), P.row(E(e,1)), color;
+    }
+    data.dirty |= OpenGL_state::DIRTY_OVERLAY_LINES;
+  }
+
+  void Viewer::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 = data.lines.rows();
+    data.lines.conservativeResize(data.lines.rows() + P1_temp.rows(),9);
+    for (unsigned i=0; i<P1_temp.rows(); ++i)
+      data.lines.row(lastid+i) << P1_temp.row(i), P2_temp.row(i), i<C.rows() ? C.row(i) : C.row(C.rows()-1);
+
+    data.dirty |= OpenGL_state::DIRTY_OVERLAY_LINES;
+  }
+
+  void Viewer::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, 0;
+    }
+    else
+      P_temp = P;
+
+    int lastid = data.labels_positions.rows();
+    data.labels_positions.conservativeResize(lastid+1, 3);
+    data.labels_positions.row(lastid) = P_temp;
+    data.labels_strings.push_back(str);
+  }
+
+  int Viewer::launch(std::string filename)
+  {
+    GLFWwindow* window;
+
+    glfwSetErrorCallback(glfw_error_callback);
+    if (!glfwInit())
+      return EXIT_FAILURE;
+
+    glfwWindowHint(GLFW_SAMPLES, 16);
+    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+    #ifdef __APPLE__
+    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+    #endif
+    window = glfwCreateWindow(1280, 800, "IGL Viewer", NULL, NULL);
+    if (!window)
+    {
+      glfwTerminate();
+      return EXIT_FAILURE;
+    }
+
+	glfwMakeContextCurrent(window);
+
+#ifndef __APPLE__
+	glewExperimental = true;
+	GLenum err = glewInit();
+	if (GLEW_OK != err)
+	{
+		/* Problem: glewInit failed, something is seriously wrong. */
+		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
+	}
+	fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
+#endif
+
+    #ifdef DEBUG
+      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 recieved: %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 AntTweakBar
+    TwInit(TW_OPENGL_CORE, NULL);
+
+    // Initialize IGL viewer
+    init();
+    __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); glfwSetCharCallback(window, glfw_char_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();
+
+    // Load the mesh passed as input
+    if (filename.size() > 0)
+      load_mesh_from_file(filename.c_str());
+
+    // Rendering loop
+    while (!glfwWindowShouldClose(window))
+    {
+      double tic = get_seconds();
+      draw();
+
+      glfwSwapBuffers(window);
+      if(options.is_animating)
+      {
+        glfwPollEvents();
+        // In microseconds
+        double duration = 1000000.*(get_seconds()-tic);
+        const double min_duration = 1000000./options.animation_max_fps;
+        if(duration<min_duration)
+        {
+          // TODO: windows equivalent
+          usleep(min_duration-duration);
+        }
+      }
+      else
+      {
+        glfwWaitEvents();
+      }
+    }
+
+    opengl.free();
+    __font_renderer.Shut();
+
+    shutdown_plugins();
+
+    glfwDestroyWindow(window);
+    glfwTerminate();
+    return EXIT_SUCCESS;
+  }
+} // end namespace

+ 0 - 1
include/igl/viewer/Viewer.cpp.REMOVED.git-id

@@ -1 +0,0 @@
-b02c6fbf6cb464fdaf3bfb95194978587d5cbd50

+ 10 - 245
include/igl/viewer/Viewer.h

@@ -20,18 +20,21 @@
 #endif
 
 #include <Eigen/Core>
+#include <igl/viewer/OpenGL_shader.h>
+#include <igl/viewer/ViewerData.h>
+#include <igl/viewer/OpenGL_state.h>
 
 namespace igl
 {
-
-  class Plugin_manager;
+  // Forward declaration of the viewer_plugin class
+  class Viewer_plugin;
 
   class Viewer
   {
   public:
 
     int launch(std::string filename = "");
-    void init(Plugin_manager* pm);
+    void init();
 
     class Options
     #ifdef ENABLE_XML_SERIALIZATION
@@ -100,225 +103,17 @@ namespace igl
       double animation_max_fps;
     };
 
-    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
-    };
-
-    class Data
-    #ifdef ENABLE_XML_SERIALIZATION
-    : public ::igl::XMLSerialization
-    #endif
-    {
-    public:
-      Data()
-      #ifdef ENABLE_XML_SERIALIZATION
-      : XMLSerialization("Data"), dirty(DIRTY_ALL)
-      #endif
-      {};
-
-      void InitSerialization();
-
-      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<char,Eigen::Dynamic,Eigen::Dynamic> texture_R;
-      Eigen::Matrix<char,Eigen::Dynamic,Eigen::Dynamic> texture_G;
-      Eigen::Matrix<char,Eigen::Dynamic,Eigen::Dynamic> texture_B;
-
-      // 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;
-
-      // Caches the two-norm between the min/max point of the bounding box
-      float object_scale;
-      /*********************************/
-    };
-
-    class OpenGL_shader
-    {
-    public:
-      typedef unsigned int GLuint;
-      typedef int GLint;
-
-      GLuint vertex_shader;
-      GLuint fragment_shader;
-      GLuint geometry_shader;
-      GLuint program_shader;
-
-      OpenGL_shader() : vertex_shader(0), fragment_shader(0),
-        geometry_shader(0), program_shader(0) { }
-
-      // Create a new shader from the specified source strings
-      bool init(const std::string &vertex_shader_string,
-        const std::string &fragment_shader_string,
-        const std::string &fragment_data_name,
-        const std::string &geometry_shader_string = "",
-        int geometry_shader_max_vertices = 3);
-
-      // Create a new shader from the specified files on disk
-      bool init_from_files(const std::string &vertex_shader_filename,
-        const std::string &fragment_shader_filename,
-        const std::string &fragment_data_name,
-        const std::string &geometry_shader_filename = "",
-        int geometry_shader_max_vertices = 3);
-
-      // Select this shader for subsequent draw calls
-      void bind();
-
-      // Release all OpenGL objects
-      void free();
-
-      // Return the OpenGL handle of a named shader attribute (-1 if it does not exist)
-      GLint attrib(const std::string &name) const;
-
-      // Return the OpenGL handle of a uniform attribute (-1 if it does not exist)
-      GLint uniform(const std::string &name) const;
-
-      // Bind a per-vertex array attribute and refresh its contents from an Eigen amtrix
-      GLint bindVertexAttribArray(const std::string &name, GLuint bufferID,
-        const Eigen::MatrixXf &M, bool refresh) const;
-    };
-
-    class OpenGL_state
-    {
-    public:
-      typedef unsigned int GLuint;
-
-      GLuint vao_mesh;
-      GLuint vao_overlay_lines;
-      GLuint vao_overlay_points;
-      OpenGL_shader shader_mesh;
-      OpenGL_shader shader_overlay_lines;
-      OpenGL_shader 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
-      Eigen::MatrixXf V_vbo;
-      Eigen::MatrixXf V_normals_vbo;
-      Eigen::MatrixXf V_ambient_vbo;
-      Eigen::MatrixXf V_diffuse_vbo;
-      Eigen::MatrixXf V_specular_vbo;
-      Eigen::MatrixXf V_uv_vbo;
-      Eigen::MatrixXf lines_V_vbo;
-      Eigen::MatrixXf lines_V_colors_vbo;
-      Eigen::MatrixXf points_V_vbo;
-      Eigen::MatrixXf points_V_colors_vbo;
-
-      int tex_u;
-      int tex_v;
-      Eigen::Matrix<char,Eigen::Dynamic,1> tex;
-
-      Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic> F_vbo;
-      Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic> lines_F_vbo;
-      Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic> points_F_vbo;
-
-      // Marks dirty buffers that need to be uploaded to OpenGL
-      uint32_t dirty;
-
-      // Create a new set of OpenGL buffer objects
-      void init();
-
-      // Update contents from a 'Data' instance
-      void set_data(const Data &data, bool face_based, bool invert_normals);
-
-      // Bind the underlying OpenGL buffer objects for subsequent mesh draw calls
-      void bind_mesh();
-
-      /// Draw the currently buffered mesh (either solid or wireframe)
-      void draw_mesh(bool solid);
-
-      // Bind the underlying OpenGL buffer objects for subsequent line overlay draw calls
-      void bind_overlay_lines();
-
-      /// Draw the currently buffered line overlay
-      void draw_overlay_lines();
-
-      // Bind the underlying OpenGL buffer objects for subsequent point overlay draw calls
-      void bind_overlay_points();
-
-      /// Draw the currently buffered point overlay
-      void draw_overlay_points();
-
-      // Release the OpenGL buffer objects
-      void free();
-    };
-
     // Stores all the viewing options
     Options options;
 
     // Stores all the data that should be visualized
-    Data data;
+    igl::ViewerData data;
 
     // Stores the vbos indices and opengl related settings
-    OpenGL_state opengl;
+    igl::OpenGL_state opengl;
 
-    // Pointer to the plugin_manager (usually it will be a global variable)
-    Plugin_manager* plugin_manager;
+    // List of registered plugins
+    std::vector<Viewer_plugin*> plugins;
     void init_plugins();
     void shutdown_plugins();
 
@@ -423,11 +218,6 @@ namespace igl
       float & zoom,
       Eigen::Vector3f& shift);
 
-
-    // Init opengl shaders and VBOs
-    void init_opengl();
-    void free_opengl();
-
     // Draw everything
     void draw();
 
@@ -592,31 +382,6 @@ namespace igl
       EIGEN_MAKE_ALIGNED_OPERATOR_NEW
   };
 
-  // Keeps the lists of plugins
-  class Plugin_manager
-  {
-  public:
-
-    Plugin_manager() {}
-
-    /** Registers a new plugin. A call to this function should be
-     implemented in the constructor of all classes derived from PreviewPlugin. */
-    bool register_plugin(Viewer_plugin* p)
-    {
-      auto it = plugin_list.begin();
-      while(it != plugin_list.end() && (*it)->priority() < p->priority())
-        ++it;
-
-      plugin_list.insert(it,p);
-      return true;
-    }
-
-    std::vector<Viewer_plugin*> plugin_list;
-  public:
-      EIGEN_MAKE_ALIGNED_OPERATOR_NEW
-  };
-
-
 } // end namespace
 
 #ifndef IGL_STATIC_LIBRARY

+ 30 - 0
include/igl/viewer/ViewerData.cpp

@@ -0,0 +1,30 @@
+#include "ViewerData.h"
+
+IGL_INLINE void igl::ViewerData::InitSerialization()
+{
+  #ifdef ENABLE_XML_SERIALIZATION
+  xmlSerializer->Add(V,"V");
+  xmlSerializer->Add(F,"F");
+  xmlSerializer->Add(F_normals,"F_normals");
+
+  xmlSerializer->Add(F_material_ambient,"F_material_ambient");
+  xmlSerializer->Add(F_material_diffuse,"F_material_diffuse");
+  xmlSerializer->Add(F_material_specular,"F_material_specular");
+
+  xmlSerializer->Add(V_normals,"V_normals");
+  xmlSerializer->Add(V_material_ambient,"V_material_ambient");
+  xmlSerializer->Add(V_material_diffuse,"V_material_diffuse");
+  xmlSerializer->Add(V_material_specular,"V_material_specular");
+
+  xmlSerializer->Add(V_uv,"V_uv");
+  xmlSerializer->Add(F_uv,"F_uv");
+  xmlSerializer->Add(texture_R,"texture_R");
+  xmlSerializer->Add(texture_G,"texture_G");
+  xmlSerializer->Add(texture_B,"texture_B");
+  xmlSerializer->Add(lines,"lines");
+  xmlSerializer->Add(points,"points");
+
+  xmlSerializer->Add(labels_positions,"labels_positions");
+  xmlSerializer->Add(labels_strings,"labels_strings");
+  #endif
+}

+ 83 - 0
include/igl/viewer/ViewerData.h

@@ -0,0 +1,83 @@
+#ifndef IGL_VIEWER_DATA_H
+#define IGL_VIEWER_DATA_H
+
+#include <igl/igl_inline.h>
+#include <Eigen/Core>
+
+namespace igl
+{
+
+class ViewerData
+#ifdef ENABLE_XML_SERIALIZATION
+: public ::igl::XMLSerialization
+#endif
+{
+public:
+  ViewerData()
+  #ifdef ENABLE_XML_SERIALIZATION
+  : XMLSerialization("Data"), dirty(DIRTY_ALL)
+  #endif
+  {};
+
+  IGL_INLINE void InitSerialization();
+
+  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<char,Eigen::Dynamic,Eigen::Dynamic> texture_R;
+  Eigen::Matrix<char,Eigen::Dynamic,Eigen::Dynamic> texture_G;
+  Eigen::Matrix<char,Eigen::Dynamic,Eigen::Dynamic> texture_B;
+
+  // 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;
+
+  // Caches the two-norm between the min/max point of the bounding box
+  float object_scale;
+  /*********************************/
+};
+
+
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "ViewerData.cpp"
+#endif
+
+#endif

+ 0 - 235
tutorial/100_Introduction to libigl.md

@@ -1,235 +0,0 @@
-title: libigl Tutorial
-author: Daniele Panozzo, Alec Jacobson and others
-date: 20 June 2014
-css: style.css
-html header:   <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
-<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/default.min.css">
-<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script>
-<script>hljs.initHighlightingOnLoad();</script>
-
-* [Chapter 1: Introduction to libigl][100]
-    * [Mesh representation][101]
-    * [Plotting surfaces][102]
-    * [Interaction with keyboard and mouse][103]
-    * [Scalar field visualization][104]
-    * [Overlays][105]
-    * [Picking vertices and faces][106]
-    * [libigl design principles][107]
-
-# Chapter 1 [100]
-
-We introduce libIGL with a series of self-contained examples. The purpose of each example is to showcase a feature of libIGL while applying to a practical problem in geometry processing. In this chapter, we will showcase the basic concepts of libigl and introduce a simple mesh viewer that allows to easily visualize surface mesh and its attributes. All the examples are cross-platform and can be compiled on MacOSX, Linux and Windows.
-
-All dependencies for the compilation of these examples are contained in libigl (external folder), with the exception of Eigen, which should be downloaded and unpacked in the folder containing the libigl root folder.
-
-All examples depends on glfw, glew and anttweakbar. A copy
-of the sourcecode of each library is provided together with libigl
-and they can be precompiled using:
-```sh
-    sh compile_dependencies_macosx.sh (MACOSX)
-    sh compile_dependencies_linux.sh (LINUX)
-```
-Precompiled binaries are provided for Visual Studio 2014 64bit.
-
-Use the cmake file in the tutorial folder to build all the examples:
-```sh
-  cd tutorial
-  mkdir build
-  cd build
-  cmake ../
-  make
-```
-
-For a few examples in Chapter 5, the [CoMiSo solver](http://www.graphics.rwth-aachen.de/software/comiso) has to be downloaded and compiled separately.
-
-## Mesh representation [101]
-
-libIGL uses the [Eigen](http://eigen.tuxfamily.org/) library to encode vector and matrices. We will review in this tutorial many of the basic operations that Eigen supports: If you want to get an idea of what operations are supported you can take a look at the [dense](http://eigen.tuxfamily.org/dox/group__QuickRefPage.html) and [sparse](http://eigen.tuxfamily.org/dox/group__SparseQuickRefPage.html) quick reference guides.
-
-We encode a triangular mesh as a pair of matrices:
-```cpp
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-```
-**V** is a #N by 3 matrix which stores the coordinates of the vertices. Each row stores the coordinate of a vertex, with the x,y,z coordinates in the first, second and third column respectively. The matrix **F** stores the triangle connectivity: each line of **F** denotes a triangle whose 3 vertices are represented as indices pointing to vertex coordinates in **F**.
-
-![A simple mesh made of 2 triangles and 4 vertices.](images/VF.png)
-
-Note that the order of the vertex indices in F determines the orientation of the triangles and it should be consistent for the entire surface. As we will see later, additional properties of the mesh will be similarly stored as matrices. This simple representation has many advantages:
-
-* it is memory efficient and cache friendly
-* the use of indices instead of pointers greatly simplifies debuggind
-* the data can be trivially read/written on disk
-
-libIGL provides Input/Output functions to read and write common mesh formats.
-The reading/writing functions are named read\*.h and write\*.h, respectively.
-
-Reading a mesh from file requires a single igl function call:
-
-```cpp
-igl::readOFF("../shared/cube.off", V, F);
-```
-
-The functions read the mesh cube.off and fills the provided matrices V and F.
-Similarly, to write a mesh to file (in OBJ format):
-
-```cpp
-igl::writeOBJ("cube.obj",V,F);
-```
-
-See [Example 101](101_FileIO/main.cpp) for the source code of a simple mesh converter from OFF to OBJ format.
-
-## Plotting surfaces [102]
-
-libigl contains an OpenGL viewer that can visualize surface and their properties.
-
-The following code ([Example 102](102_DrawMesh/main.cpp)) is a basic skeleton that will be used over the entire tutorial. It is a standalone application that loads a mesh and visualize it.
-
-```cpp
-#include <igl/readOFF.h>
-#include <igl/viewer/Viewer.h>
-
-Eigen::MatrixXd V;
-Eigen::MatrixXi F;
-
-int main(int argc, char *argv[])
-{
-  // Load a mesh in OFF format
-  igl::readOFF("../shared/bunny.off", V, F);
-
-  // Plot the mesh
-  igl::Viewer viewer;
-  viewer.set_mesh(V, F);
-  viewer.launch();
-}
-```
-
-The function set_mesh assigns to the viewer the mesh that we want to plot, and the last line creates an opengl context and starts the draw loop. Additional properties can be plotted on the mesh, and it is also possible to extend the viewer with standard OpenGL code. Please see the documentation in  [Viewer.h](../include/igl/Viewer/Viewer.h) for more details.
-
-![([Example 102](102_DrawMesh/main.cpp)) loads and draws a mesh.](images/102_DrawMesh.png)
-
-## Interaction with keyboard and mouse [103]
-
-Keyboard and mouse events triggers callbacks that can be registered in the viewer. The viewer supports the following callbacks:
-
-```cpp
-bool (*callback_pre_draw)(Viewer& viewer);
-bool (*callback_post_draw)(Viewer& viewer);
-bool (*callback_mouse_down)(Viewer& viewer, int button, int modifier);
-bool (*callback_mouse_up)(Viewer& viewer, int button, int modifier);
-bool (*callback_mouse_move)(Viewer& viewer, int mouse_x, int mouse_y);
-bool (*callback_mouse_scroll)(Viewer& viewer, float delta_y);
-bool (*callback_key_down)(Viewer& viewer, unsigned char key, int modifiers);
-bool (*callback_key_up)(Viewer& viewer, unsigned char key, int modifiers);
-```
-
-A keyboard callback can be used to visualize multiple meshes or different stages of an algorithm, as demonstrated in [Example 103](103_Events/main.cpp). The keyboard callback changes the visualized mesh depending on the key pressed:
-
-```cpp
-bool key_down(igl::Viewer& viewer, unsigned char key, int modifier)
-{
-  if (key == '1')
-  {
-    viewer.clear_mesh();
-    viewer.set_mesh(V1, F1);
-  }
-  else if (key == '2')
-  {
-    viewer.clear_mesh();
-    viewer.set_mesh(V2, F2);
-  }
-  return false;
-}
-```
-and it is registered in the viewer as follows:
-
-```cpp
-viewer.callback_key_down = &key_down;
-```
-Note that the mesh is cleared before using set_mesh. This has to be called every time the number of vertices or faces of the plotted mesh changes. Every callback returns a boolean value that tells the viewer if the event has been handled by the plugin, or if the viewer should process it normally. This is useful, for example, to disable the default mouse event handling if you want to control the camera directly in your code.
-
-The viewer can be extended using plugins, which are classes that implements all the viewer's callbacks. See the class Viewer_plugin for more details.
-
-## Scalar field visualization [104]
-
-Colors and normals can be associated to both faces or normals using the set_colors function:
-```cpp
-viewer.set_colors(C);
-```
-**C** is a #C by 3 matrix with one RGB color per row, and as many rows as the number of faces **or** the number of vertices. Depending on the size of **C**, the viewer applies the color to faces or vertices.
-
-Colors are commonly used to visualize scalar functions defined on a surface using a transfer functions, that maps a scalar value between 0 and 1 to a color scale. A simple example of a scalar field defined on a surface is the z coordinate of each point. We can extract this information from our mesh by taking the first column of V (which contains the stacked z coordiantes of all the vertices), and map it to colors using the igl::jet function:
-
-```cpp
-Eigen::VectorXd x = V.col(2);
-igl::jet(x,true,C);
-```
-
-The first row extracts the third column from V and the second calls the libigl functions that converts a scalar field to colors. The second parameter of jet normalizes the scalar field to lie between 0 and 1 before applying the color scale.
-
-![([Example 104](104_Colors/main.cpp)) igl::jet converts a scalar field to a color field.](images/104_Colors.png)
-
-## Overlays [105]
-
-In addition to the surface, the viewer supports the visualization of points, lines and text label that can be very helful while developing geometric processing algorithms. These additional informations can be drawn using the following functions:
-
-```cpp
-viewer.add_points(P,Eigen::RowVector3d(r,g,b));
-```
-
-Draws a point of color r,g,b for each row of P at the coordinates specified in each row of P, which is a #P by 3 matrix.
-
-```cpp
-viewer.add_edges(P1,P2,Eigen::RowVector3d(r,g,b);
-```
-
-Draws a line for each line of P1 and P2, which connects the point in P1 to the point in P2.
-
-```cpp
-viewer.add_label(p,str);
-```
-
-Draws a label containing the string str at the position p.
-
-These functions are demonstrate in [Example 105](105_Overlays/main.cpp) where the bounding box of the mesh is plotted using lines and points. The bounding box of a mesh can be found using Eigen:
-
-```cpp
-Eigen::Vector3d m = V.colwise().minCoeff();
-Eigen::Vector3d M = V.colwise().maxCoeff();
-```
-
-![([Example 105](105_Overlays/main.cpp)) The bounding box of a mesh is shown using overlays.](images/105_Overlays.png)
-
-Using matrices to encode the mesh and its attributes allows to write short and efficient code for many operations, avoiding to write for loops.
-
-## Picking [106]
-
-Picking vertices and faces using the mouse is very common in geometry processing applications. While this might seem a simple operation, its implementation is quite involved. libigl contains a function that solves this problem using the [Embree](https://software.intel.com/en-us/articles/embree-photo-realistic-ray-tracing-kernels) raycaster. Its usage is demonstrated in [Example 106](106_Picking/main.cpp):
-```cpp
-bool hit = igl::unproject_in_mesh(
-  Vector2f(x,y),
-  F,
-  viewer.view * viewer.model,
-  viewer.proj,
-  viewer.viewport,
-  *ei,
-  fid,
-  vid);
-```
-
-This function casts a ray from the view plane in the view direction. x,y are the position of the mouse on screen; view,model,proj are the view, model and projection matrix respectively, viewport is the viewport in opengl format; ei contains a [Bounding Volume Hierarchy](http://en.wikipedia.org/wiki/Bounding_volume_hierarchy) constructed by Embree, and fid and vid are the picked face and vertex respectively.
-
-This function is a good example of the design principles in libigl: the function takes very simple types, mostly matrix or vectors, and can be easily reused for many different tasks.
-Not committing to heavy data structures, favors simplicity, ease of use and reusability.
-
-# libigl design choices [107]
-
-To conclude the introduction, we summarize the main design principles in libigl:
-
-* No complex data types. Mostly matrices and vectors. This greatly favors code reusability and forces the authors to expose all the parameters used by the algorithm.  
-
-* Minimal dependencies: we use external libraries only when necessary and we wrap them in a small set of functions.
-
-* Header-only: it is straighforward to use our library since it is only one additional include directory in your project. (if you are worried about compilation speed, it is also possible to build the library as a [static library](../build/))
-
-![([Example 106](106_Picking/main.cpp)) Picking via ray casting. The selected vertices are colored in red.](images/106_Picking.png)

+ 2 - 0
tutorial/403_BoundedBiharmonicWeights/CMakeLists.txt

@@ -3,6 +3,8 @@ project(403_BoundedBiharmonicWeights)
 
 include("../CMakeLists.shared")
 
+add_definitions(-DIGL_NO_MOSEK)
+
 set(SOURCES
 ${PROJECT_SOURCE_DIR}/main.cpp
 )

+ 0 - 278
tutorial/500_Parametrization.md

@@ -1,278 +0,0 @@
-title: libigl Tutorial
-author: Daniele Panozzo, Alec Jacobson and others
-date: 20 June 2014
-css: style.css
-html header:   <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
-<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/default.min.css">
-<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script>
-<script>hljs.initHighlightingOnLoad();</script>
-
-* [Chapter 5: Parametrization][500]
-    * [501 Harmonic parametrization][501]
-    * [502 Least-Square Conformal Maps][502]
-    * [503 As-Rigid-As-Possible][503]
-    * [504 N-Rotationally symmetric tangent fields][504]
-    * [505 Global, seamless integer-grid parametrization][505]
-    * [506 Anisotropic remeshing using frame fields][506]
-    * [507 N-PolyVector fields][507]
-    * [508 Conjugate vector fields][508]
-    * [509 Planarization][509]
-
-# Chapter 5: Parametrization [500]
-
-In computer graphics, we denote as parametrization of a surface a map from the surface to \\(\mathbf{R}^2\\). It is usually encoded by a new set of 2D coordinates for each vertex of the mesh and possibly also a new set of faces in one to one correspondence with the faces of the original surface. Note that this definition
-is the *inverse* of the classical differential geometry parametrization.
-
-A parametrization has many applications, ranging from texture mapping to surface remeshing. Many algorithms have been proposed, and they can be broadly characterized in four families:
-
-1. **Single patch, fixed boundary**: these algorithm can parametrize a disk-like part of the surface given fixed 2D positions for its boundary. These algorithms are efficient and simple, but usually produce high-distortion maps due to the strong cosntraints on the border.
-
-2. **Single patch, free border:** these algorithms allows the boundary to deform freely, reducing the distortion of the map. Care should be taken to prevent the border to self-intersect.
-
-3. **Global parametrization**: these algorithms works on meshes with arbitrary genus. They cut the mesh in multiple patches that are then possible to flatten in a 2D domain. Usually the map is discontinuous on the seams.
-
-4. **Global seamless parametrization**: similar to the global parametrization, but solved with a global solving strategy that hides the seams, making the parametrization "continuous", under specific assumptions that we will discuss later.
-
-## Harmonic parametrization [501]
-
-Harmonic parametrization is a single patch, fixed boundary parametrization algorithm that computes the 2D coordinates of the flattened mesh as two Harmonic functions.
-
-The algorithm is divided in 3 steps:
-
-* Detection of the boundary vertices
-
-```cpp
-Eigen::VectorXi bnd;
-igl::boundary_loop(V,F,bnd);
-```
-
-* Map the boundary vertices to a circle
-
-```cpp
-Eigen::MatrixXd bnd_uv;
-igl::map_vertices_to_circle(V,bnd,bnd_uv);
-```
-
-* Computation of harmonic functions for both the u and v coordinate on the plane, using the boundary positions as boundary constraints
-
-```cpp
-igl::harmonic(V,F,bnd,bnd_uv,1,V_uv);
-```
-
-bnd contains the indices of the boundary vertices, bnd_uv their position on the UV plane, and "1" denotes that we want to compute an harmonic function (2 will be for biharmonic, 3 for triharmonic, etc.). Note that each of the three functions is deisgned to be reusable in other parametrization algorithms.
-
-A UV parametrization can be visualized in the viewer using the method:
-
-```cpp
-viewer.set_uv(V_uv);
-```
-
-which uses the UV coordinates to apply a procedural checkerboard texture to the mesh ([Example 501](501_HarmonicParam/main.cpp)).
-
-![([Example 501](501_HarmonicParam/main.cpp)) Harmonic parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/501_HarmonicParam.png)
-
-###References:
-
-[Multiresolution Analysis of Arbitrary Meshes](http://research.microsoft.com/en-us/um/people/hoppe/mra.pdf),
-Matthias Eck, Tony DeRose, Tom Duchamp, Hugues Hoppe, Michael Lounsbery, Werner Stuetzle,
-SIGGRAPH 2005
-
-## Least-Square Conformal Maps [502]
-
-Least-square conformal maps parametrization minimizes the conformal (angular) distortion of the generated parametrization. If does not need to have a fixed boundary.
-
-LSCM minimizes the following energy:
-
-\\[ E_{LSCM}(\mathbf{u},\mathbf{v}) = \int_X \frac{1}{2}| \nabla \mathbf{u}^{\perp} - \nabla \mathbf{v} |^2 dA \\]
-
-which can be rewritten in matrix form as:
-
-\\[ E_{LSCM}(\mathbf{u},\mathbf{v}) = \frac{1}{2} [\mathbf{u},\mathbf{v}]^t (L_c - 2A) [\mathbf{u},\mathbf{v}] \\]
-
-where L_c is the cotangent laplacian matrix and A is a matrix such that \\( [\mathbf{u},\mathbf{v}]^t A  [\mathbf{u},\mathbf{v}] \\) is equal to the _vector area_ of the mesh.
-
-Using libigl, this matrix energy can be written using a few lines of codes. The cotangent matrix can be computed using igl::cotmatrix:
-
-```cpp
-SparseMatrix<double> L;
-igl::cotmatrix(V,F,L);
-```
-
-Note that we want to apply the laplacian matrix to the u and v coordinates at the same time, thus we need to extend the laplacian matrix taking the left Kronecker product with a 2x2 identity matrix:
-
-```cpp
-SparseMatrix<double> L_flat;
-repdiag(L,2,L_flat);
-```
-
-The area matrix is computed with igl::vector_area_matrix:
-
-```cpp
-SparseMatrix<double> A;
-igl::vector_area_matrix(F,A);
-```
-
-The final energy matrix is the sum of these two matrices. Note that in this case we don't need to fix the boundary, we only need to fix two arbitrary vertices to arbitrary positions to remove the null space of the energy and make the minimum unique. The full source code is provided in [Example 502](502_LSCMParam/main.cpp).
-
-
-![([Example 502](502_LSCMParam/main.cpp)) LSCM parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/502_LSCMParam.png)
-
-####References:
-
-[Least Squares Conformal Maps, for Automatic Texture Atlas Generation,](http://www.cs.jhu.edu/~misha/Fall09/Levy02.pdf)
-Bruno Lévy, Sylvain Petitjean, Nicolas Ray, Jérome Maillot,
-SIGGRAPH 2002
-
-[Spectral Conformal Parameterization](http://www.geometry.caltech.edu/pubs/MTAD08.pdf),
-Patrick Mullen, Yiying Tong, Pierre Alliez, Mathieu Desbrun,
-CGF 2008
-
-## As-Rigid-As-Possible parametrization [503]
-
-As-Rigid-As-Possible parametrizationis a powerful single-patch, non-linear algorithm to compute a parametrization that strives to preserve distances (and thus angles). The idea is very similar to ARAP surface deformation: each triangle is mapped to the plane trying to preserve its original shape, up to a rigid 2x2 rotation.
-
-The algorithm can be implemented reusing the functions discuss in the deformation chapter arap_precomputation and ara_solve. The only difference is that the optimization has to be done in 2D instead of 3D and that a starting point for the non-linear optimization is necessary. While for 3D deformation the original mesh is a perfect starting point, this is not the case for ARAP parametrization since the starting point must be a 2D mesh. In [Example 503](503_ARAPParam/main.cpp), we use Harmonic parametrization as a starting point for the ARAP parametrization: note that similarly to LSCM, the boundary is free to deform to minimize the distortion.
-
-![([Example 503](502_ARAPParam/main.cpp)) As-Rigid-As-Possible parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/503_ARAPParam.png)
-
-### References
-[A Local/Global Approach to Mesh Parameterization](http://cs.harvard.edu/~sjg/papers/arap.pdf)
-Ligang Liu, Lei Zhang, Yin Xu, Craig Gotsman, Steven J. Gortler
-SGP 2008
-
-## N-Rotationally symmetric tangent fields [504]
-
-The design of tangent fields is a basic tool used to design guidance fields for uniform quadrilateral and hexaedral remeshing. libigl contains an implementation of all the state- of-the-art to design algorithms for N-RoSy fields and their generalizations.
-
-In libigl, tangent unit-length vector fields are piece-wise constant on the faces of a triangle mesh, and described by one or more vectors per-face. The function
-
-```cpp
-igl::nrosy(V,F,b,bc,b_soft,b_soft_weight,bc_soft,N,0.5,
-           output_field,output_singularities);
-```
-
-creates a smooth vector field (N=1) starting from a sparse set of constrained faces, whose indices are listed in b and their constrained value is specified in bc. The functions supports soft_constraints (b_soft,b_soft_weight,bc_soft), and returns the interpolated field for each face of the triangle mesh (output_field) plus the singularities of the field (output_singularities).
-
-![Design of a unit-lenght vector field](images/504_vector_field.png)
-
-The singularities are vertices where the field vanishes, and they are highlighted in red. igl::nrosy can generate N-Rotation Symmetric fields, which are a generalization of vector fields where in every face the vector is defined up to a constant rotation of \\( 2\pi / N \\). As can be observed in the following figure, the singularities of fields generated with different N are in different positions and of a different kind.
-
-![Design of a 2-,4- and 9-RoSy field](images/504_nrosy_field.png)
-
-We demonstrate how to call and plot N-RoSy fields in [Example 504](504_NRosyDesign/main.cpp), where the degree of the field can be controlled by pressing the number keys.
-
-### References
-
-[N-Symmetry Direction Field Design](http://alice.loria.fr/publications/papers/2008/DGF/NSDFD-TOG.pdf),
-Nicolas Ray, Bruno Vallet, Wan Chiu Li, Bruno Lévy
-TOG 2008
-
-[Mixed-integer quadrangulation](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf),
-David Bommes, Henrik Zimmer, Leif Kobbelt
-SIGGRAPH 2009
-
-#Global, seamless integer grid parametrization
-
-The previous parametrization methods where focusing on generating parametrization of single patches, mainly aimed at texture mapping and baking of other surface properties like normals high-frequency details. Global, seamless parametrization aims at parametrizing complex shapes with a parametrization that is aligned with a given set of directions for the purpose of remeshing the surface. In libigl, we provide a reference  implementation of the pipeline of the  [MIQ](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf) paper.
-
-### Global, seamless integer-grid parametrization [505]
-
-The first step involves the design of a 4-RoSy field (sometimes called cross field) that describes how the edges of the final quad remeshing should align. The field constraints are usually manually specified or extracted from curvature. In this example, we simply fix one face in a random direction.
-
-![Initial cross field prescribing the edge alignment.](images/505_MIQ_1.png)
-
-### Combing and cutting
-
-Given the cross field, we now want to cut the surface so that it becomes homeorphic to a disk. While this can be done directly on the cross-field, we prefer to do this operation on its bisector field (a copy of the field rotated by 45 degrees) since it is more stable and generic.
-
-We thus rotate the field,
-
-![Bisector field.](images/505_MIQ_2.png)
-
-and we remove the rotation ambiguity by assigning to each face a u and v direction, computed by diffusing this alignment from a random face.
-
-![Combed bisector field.](images/505_MIQ_3.png)
-
-You can imagine this process as combing an hairy surface: you'll be able to comb part of it, but at some point you will not be able to comb consistently the full surface ([Hairy ball theorem](http://en.wikipedia.org/wiki/Hairy_ball_theorem)). The discontinuites in the combing defines the cut graph:
-
-![Cut graph.](images/505_MIQ_4.png)
-
-Finally, we rotate the combed field by 45 degrees to undo the initial 45 degrees rotation:
-
-![Combed cross field.](images/505_MIQ_5.png)
-
-This cross field can be seen as the ideal gradient of the parametrization that we want to compute.
-
-### Poisson parametrization
-
-The mesh can be then cut along the seams and a parametrization is computed trying to find two scalar functions whose gradient matches the combed cross field. This is a classical Poisson problem, that is solved minimizing the following quadratic energy:
-
-\\[ E(\mathbf{u},\mathbf{v}) = |\nabla \mathbf{u} - X_u|^2 + |\nabla \mathbf{v} - X_v|^2 \\]
-
-where \\( X_u \\) and \\( X_u \\) denotes the combed cross field. Solving this problem generates a parametrization whose u and v isolines are aligned with the input cross field.
-
-![Poisson parametrization.](images/505_MIQ_8.png)
-
-We hide the seams by adding a set of integer constraints to the Poisson problem that aligns the isolines on both sides of each seam.
-
-![Seamless Poisson parametrization.](images/505_MIQ_7.png)
-
-Note that this parametrization can only be used for remeshing purposes, since it contains many overlaps.
-
-![Seamless Poisson parametrization (in 2D).](images/505_MIQ_6.png)
-
-A quad mesh can be extracted from this parametrization using
-[libQEx](https://github.com/hcebke/libQEx) (not included in libigl).
-
-The full pipeline is demonstrated in [Example 505](505_MIQ/main.cpp).
-
-### References
-
-[Mixed-integer quadrangulation](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf),
-David Bommes, Henrik Zimmer, Leif Kobbelt
-SIGGRAPH 2009
-
-## Anisotropic remeshing [506]
-
-Anisotropic and non-uniform quad remeshing is important to concentrate the elements in the regions with more details. It is possible to extend the MIQ quad meshing framework to generate anisotropic quad meshes using a mesh deformation approach.
-
-The input of the remeshing algorithm is now a sparse set of constraints that defines the shape and scale of the desired quad remeshing. This can be encoded as a frame-field, which is a pair of non-orthogonal and non-unit lenght vectors. The frame field can be interpolated by decomposing it in a 4-RoSy field and a unique affine transformation. The two parts can then be interpolated separately, using igl::nrosy for the cross field, and an harmonic interpolant for the affine part.
-
-![Interpolation of a frame field. Colors on the vectors denote the desired scale. The red faces contains the frame field constraints.](images/506_FrameField_1.png)
-
-After the interpolation, the surface is warped to transform each frame into an orthogonal and unit lenght cross (i.e. removing the scaling and skewness from the frame). This deformation defines a new embedding (and a new metric) for the surface.
-
-![The surface is deformed to transform the frame field in a cross field.](images/506_FrameField_2.png)
-
-The deformed surface can the be isotropically remeshed using the MIQ algorithm that has been presented in the previous section.
-
-![The deformed surface is isotropically remeshed.](images/506_FrameField_3.png)
-
-The UV coordinates of the deformed surface can then be used to transport the parametrization to the original surface, where the isolines will trace a quad mesh whose elements are similar to the shape prescribed in the input frame field.
-
-![The global parametrization is lifted to the original surface to create the anisotropic quad meshing.](images/506_FrameField_4.png)
-
-Our implementation ([Example 506](506_FrameField/main.cpp)) uses MIQ to generate the UV parametrization, but other algorithms could be applied: the only desiderata is that the generated quad mesh will be as isotropic as possible.
-
-### References
-
-[Frame Fields: Anisotropic and Non-Orthogonal Cross Fields],
-Daniele Panozzo, Enrico Puppo, Marco Tarini, Olga Sorkine-Hornung,
-SIGGRAPH, 2014
-
-## N-PolyVector fields [507]
-
-* further generalization to arbitrary rosy, same interface
-
-* globally optimal and keenan optimal field are a subset of them
-
-## Conjugate vector fields [508]
-
-* they can be used to encode conjugate field -> planar meshing
-
-* global/local approach
-
-## Planarization [509]
-
-* given a mesh from conjugate directions, enforce planarity with a local/global approach
-* useful for architecture

+ 0 - 164
tutorial/600_External_libraries.md

@@ -1,164 +0,0 @@
-title: libigl Tutorial
-author: Daniele Panozzo, Alec Jacobson and others
-date: 20 June 2014
-css: style.css
-html header:   <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
-<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/default.min.css">
-<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script>
-<script>hljs.initHighlightingOnLoad();</script>
-
-* [Chapter 6: External libraries][600]
-    * [601 State serialization][601]
-    * [602 Mixing matlab code][602]
-    * [603 Calling igl functions from matlab][603]
-    * [604 Triangulation of closed polygons][604]
-    * [605 Tetrahedralization of closed surfaces][605]
-    * [606 Baking ambient occlusion][606]
-
-# Chapter 6: External libraries [600]
-
-An additional positive side effect of using matrices as basic types is that it is easy to exchange data between libigl and other softwares and libraries.
-
-## State serialization [601]
-
-Geometry processing applications often require a considerable amount of computational time and/or manual input. In order to make the development efficient it must be possible to serialize and deserialize the state of the application.
-
-Having a good serialization framework allows to quickly start debugging just before the crash happens, avoiding to wait for the precomputation to take place every time. It also makes it easier to define unit testing that can be used to find bugs in interactive applications: if the input is slightly different every time the algorithm is executed, it is very difficult to find bugs.
-
-Unfortunately, serialization is often not considered in geoemtry processing due to the extreme difficulty in serializing pointer-based data structures (like an helf-edge).
-
-In libigl, serialization is simpler, since the majority of the functions use basic types, and pointers are used in very rare cases (usually to interface with external libraries). libigl provides an extremely easy to use XML serialization framework, that drastically reduces the overhead required to add serialization to your applications.
-
-Assume that the state of your application is composed of a mesh and set of integer ids:
-``` cpp
-class State : public ::igl::XMLSerialization
-{
-public:
-  State() : XMLSerialization("dummy") {}
-
-  Eigen::MatrixXd V;
-  Eigen::MatrixXi F;
-  std::vector<int> ids;
-
-  void InitSerialization()
-  {
-    xmlSerializer->Add(V  , "V");
-    xmlSerializer->Add(F  , "F");
-    xmlSerializer->Add(ids, "ids");
-  }
-};
-```
-
-A class can be made serializable by inheriting from ::igl::XMLSerialization and trivially implementing the InitSerialization method. Note that you don't have to care the types, Add is able to serialize all basic stl types, all Eigen types and any class inheriting from ::igl::XMLSerialization.
-
-It is then possible to save the state to an xml file:
-
-``` cpp
-::igl::XMLSerializer serializer_save("601_Serialization");
-serializer_save.Add(state,"State");
-serializer_save.Save("temp.xml",true);
-```
-
-This code generates the following xml file (assuming V and F contains a simple mesh with two triangles, and ids contains the numbers 6 and 7):
-``` xml
-<:::601_Serialization>
-    <State>
-        <V rows="4" cols="3" matrix="
-0,0,0,
-1,0,0,
-1,1,1,
-2,1,0"/>
-        <F rows="2" cols="3" matrix="
-0,1,2,
-1,3,2"/>
-        <ids size="2" vector_int="
-6,7"/>
-    </State>
-</:::601_Serialization>
-```
-
-The xml file can then be loaded in a similar way:
-
-``` cpp
-State loaded_state;
-::igl::XMLSerializer serializer_load("601_Serialization");
-serializer_load.Add(loaded_state,"State");
-serializer_load.Load("temp.xml");
-```
-
-This can also be used as a convenient interface to provide parameters to command line applications, since the xml files can be directly edited with a standard text editor.
-
-We demonstrate the serialization framework in [Example 601](601_Serialization/main.cpp). We strongly suggest that you make the entire state of your application always serializable: this will save you a lot of troubles when you'll be making figures for a scientific publication. It is very common to have to do small changes to figures during the production of a paper, and being able to serialize the entire state just before you take screenshots will save you many painful hours before a submission deadline.
-
-## Mixing matlab code [602]
-
-libigl can be interfaced matlab, to offload some of the numerically heavy computation to a matlab script. This has the major advantage of allowing to develop efficient and complex UI in C++, while keeping the advantage of fast protototyping of matlab. In particular, using an external matlab script in a libigl application allows to change the algorithm in the matlab script without having to recompile the C++ part.
-
-We demonstrate how to integrate matlab in a libigl application in [Example 602](602_Matlab/main.cpp). The example uses matlab to compute the Eigenfunctions of the discrete Laplacian operator, relying on libigl for mesh IO, visualization and for computing the Laplacian operator.
-
-libigl can connect to an existing instance of matlab (or launching a new one on Linux/MacOSX) using:
-
-``` cpp
-igl::mlinit(&engine);
-```
-
-The cotangent laplacian is computed using igl::cotmatrix and uploaded to the matlab workspace:
-
-``` cpp
-igl::cotmatrix(V,F,L);
-igl::mlsetmatrix(&engine,"L",L);
-```
-
-It is now possible to use any matlab function on the data. For example, we can see the sparsity pattern of L using spy:
-
-``` cpp
-igl::mleval(&engine,"spy(L)");
-```
-
-![The matlab spy function is called from a libigl-based application.](images/602_Matlab_1.png)
-
-You can also do some computation and then return it back to the C++ application
-
-``` cpp
-igl::mleval(&engine,"[EV,~] = eigs(-L,10,'sm')");
-igl::mlgetmatrix(&engine,"EV",EV);
-```
-
-and then use libigl functions to plot the eigenfunctions.
-
-![4 Eigenfunctions of the Laplacian plotted in the libigl viewer.](images/602_Matlab_2.png)
-
-## Calling igl functions from matlab [603]
-
-It is also possible to call libigl functions from matlab, compiling them as MEX functions. This can be very useful to offload to C++ code the computationally intensive parts of a matlab application.
-
-We provide a wrapper for igl::readOBJ in [Example 603](603_MEX/compileMEX.m). We plan to provide wrappers for all our functions in the future, if you are interested in this feature (or if you want to help implementing it) please let us know.
-
-## Triangulation of closed polygons [604]
-
-
-
-* discretization are useful to solve PDE
-* to create a 2D triangulation we provide a convenient wrapper for triangle
-
-#Tetrahedralization of closed surfaces
-
-* the same in 3D, similar interface
-
-#Baking ambient occlusion
-
-* intro to ambinet occlusion
-* can be easily computed with raytracing
-* again a simple wrapper
-* then you multiply to the colors
-
-
-#Outlook for continuing development
-
-* better documentation
-* your contributions are welcome, using pull request
-* open things to do
-  * isotropic remeshing
-  * matlab wrappers
-  * mixed integer solvers
-  * fast spatial indices

+ 24 - 0
tutorial/CMakeLists.txt

@@ -2,6 +2,16 @@ cmake_minimum_required(VERSION 2.6)
 project(libigl_tutorials)
 
 SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../)
+SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
+
+## Check for CoMiSo, if not available skip the examples that depends on it
+find_package(LIBCOMISO QUIET)
+
+## Check for MATLAB, if not available skip the examples that depends on it
+find_package(LIBMATLAB QUIET)
+
+## Check for EMBREE, if not available skip the examples that depends on it
+find_package(LIBEMBREE QUIET)
 
 # Chapter 1
 add_subdirectory("101_FileIO")
@@ -9,7 +19,9 @@ add_subdirectory("102_DrawMesh")
 add_subdirectory("103_Events")
 add_subdirectory("104_Colors")
 add_subdirectory("105_Overlays")
+if(EMBREE_FOUND)
 add_subdirectory("106_Picking")
+endif(EMBREE_FOUND)
 
 # Chapter 2
 add_subdirectory("201_Normals")
@@ -25,17 +37,29 @@ add_subdirectory("303_LaplaceEquation")
 add_subdirectory("304_LinearEqualityConstraints")
 add_subdirectory("305_QuadraticProgramming")
 
+# Chapter 4
+add_subdirectory("401_BiharmonicDeformation")
+add_subdirectory("402_PolyharmonicDeformation")
+add_subdirectory("403_BoundedBiharmonicWeights")
+
 # Chapter 5
 add_subdirectory("501_HarmonicParam")
 add_subdirectory("502_LSCMParam")
 add_subdirectory("503_ARAPParam")
+
+if(LIBCOMISO_FOUND)
 add_subdirectory("504_NRosyDesign")
 add_subdirectory("505_MIQ")
 add_subdirectory("506_FrameField")
+endif(LIBCOMISO_FOUND)
 
 # Chapter 6
 add_subdirectory("601_Serialization")
+if(MATLAB_FOUND)
 add_subdirectory("602_Matlab")
+endif(MATLAB_FOUND)
 add_subdirectory("604_Triangle")
 add_subdirectory("605_Tetgen")
+if(EMBREE_FOUND)
 add_subdirectory("606_AmbientOcclusion")
+endif(EMBREE_FOUND)

+ 1 - 1
tutorial/cmake/FindEMBREE.cmake

@@ -39,5 +39,5 @@ IF (EMBREE_FOUND)
    )
    SET(EMBREE_INCLUDE_DIRS ${EMBREE_INCLUDE_DIR} ${EMBREE_INCLUDE_DIR}/embree)
 ELSE (EMBREE_FOUND)
-    message(FATAL_ERROR "could NOT find EMBREE")
+    message(STATUS "could NOT find EMBREE")
 ENDIF (EMBREE_FOUND)

+ 1 - 1
tutorial/cmake/FindLIBCOMISO.cmake

@@ -46,5 +46,5 @@ if(LIBCOMISO_INCLUDE_DIR AND LIBCOMISO_LIBRARY)
    message(STATUS "Found LIBCOMISO: ${LIBCOMISO_INCLUDE_DIR} ${LIBCOMISO_LIBRARY}")
    set(LIBCOMISO_FOUND TRUE)
 else(LIBCOMISO_INCLUDE_DIR)
-   message(FATAL_ERROR "could NOT find LIBCOMISO")
+   message(STATUS "could NOT find LIBCOMISO")
 endif(LIBCOMISO_INCLUDE_DIR AND LIBCOMISO_LIBRARY)

+ 1 - 0
tutorial/images/604_Triangle.png.REMOVED.git-id

@@ -0,0 +1 @@
+36007271400d1da84375594fce31ffa8a994beef

+ 1 - 0
tutorial/images/605_Tetgen.png.REMOVED.git-id

@@ -0,0 +1 @@
+9573b35d541d6f4fafd0f686be21f59162f54c1d

+ 1 - 0
tutorial/images/606_AmbientOcclusion.png.REMOVED.git-id

@@ -0,0 +1 @@
+24cf1f058d4c5a8b0a085f2189189b2f790734f8

+ 1684 - 0
tutorial/tutorial.md

@@ -0,0 +1,1684 @@
+title: libigl Tutorial
+author: Daniele Panozzo, Alec Jacobson and others
+date: 20 June 2014
+css: style.css
+html header:   <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
+<link rel="stylesheet" href="http://yandex.st/highlightjs/7.3/styles/default.min.css">
+<script src="http://yandex.st/highlightjs/7.3/highlight.min.js"></script>
+<script>hljs.initHighlightingOnLoad();</script>
+
+# libigl Tutorial notes
+Libigl is an open source C++ library for geometry processing research and development.  Dropping the heavy data structures of tradition geometry libraries, libigl is a simple header-only library of encapsulated functions. This combines the rapid prototyping familiar to Matlab or Python programmers with the performance and versatility of C++.  The tutorial is a self-contained, hands-on introduction to libigl.  Via live coding and interactive examples, we demonstrate how to accomplish various common geometry processing tasks such as computation of differential quantities and operators, real-time deformation, global parametrization, numerical optimization and mesh repair.  Each section of these lecture notes links to a cross-platform example application.
+
+# Table of Contents
+
+* [Chapter 1: Introduction to libigl][100]
+    * [101 Mesh representation][101]
+    * [102 Plotting surfaces][102]
+    * [103 Interaction with keyboard and mouse][103]
+    * [104 Scalar field visualization][104]
+    * [105 Overlays][105]
+    * [106 Picking vertices and faces][106]
+        * [libigl design principles][107]
+* [Chapter 2: Discrete Geometric Quantities and
+  Operators](#chapter2:discretegeometricquantitiesandoperators)
+    * [201 Normals](#normals)
+        * [Per-face](#per-face)
+        * [Per-vertex](#per-vertex)
+        * [Per-corner](#per-corner)
+    * [202 Gaussian Curvature](#gaussiancurvature)
+    * [203 Curvature Directions](#curvaturedirections)
+    * [204 Gradient](#gradient)
+    * [204 Laplacian](#laplacian)
+        * [Mass matrix](#massmatrix)
+        * [Alternative construction of
+          Laplacian](#alternativeconstructionoflaplacian)
+* [Chapter 3: Matrices and Linear Algebra](#chapter3:matricesandlinearalgebra)
+    * [301 Slice](#slice)
+    * [302 Sort](#sort)
+        * [Other Matlab-style functions](#othermatlab-stylefunctions)
+    * [303 Laplace Equation](#laplaceequation)
+        * [Quadratic energy minimization](#quadraticenergyminimization)
+    * [304 Linear Equality Constraints](#linearequalityconstraints)
+    * [305 Quadratic Programming](#quadraticprogramming)
+* [Chapter 4: Shape Deformation](#chapter4:shapedeformation)
+    * [401 Biharmonic Deformation](#biharmonicdeformation)
+    * [402 Polyharmonic Deformation](#polyharmonicdeformation)
+    * [403 Bounded Biharmonic Weights](#boundedbiharmonicweights)
+    * [404 Dual Quaternion Skinning](#dualquaternionskinning)
+    * [405 As-rigid-as-possible](#as-rigid-as-possible)
+    * [406 Fast automatic skinning
+      transformations](#fastautomaticskinningtransformations)
+
+* [Chapter 5: Parametrization][500]
+    * [501 Harmonic parametrization][501]
+    * [502 Least-Square Conformal Maps][502]
+    * [503 As-Rigid-As-Possible][503]
+    * [504 N-Rotationally symmetric tangent fields][504]
+    * [505 Global, seamless integer-grid parametrization][505]
+    * [506 Anisotropic remeshing using frame fields][506]
+    * [507 N-PolyVector fields][507]
+    * [508 Conjugate vector fields][508]
+    * [509 Planarization][509]
+
+* [Chapter 6: External libraries][600]
+    * [601 State serialization][601]
+    * [602 Mixing matlab code][602]
+    * [603 Calling igl functions from matlab][603]
+    * [604 Triangulation of closed polygons][604]
+    * [605 Tetrahedralization of closed surfaces][605]
+    * [606 Baking ambient occlusion][606]
+
+* [Chapter 7: Outlook for continuing development][future]
+
+# Chapter 1 [100]
+
+We introduce libIGL with a series of self-contained examples. The purpose of each example is to showcase a feature of libIGL while applying to a practical problem in geometry processing. In this chapter, we will showcase the basic concepts of libigl and introduce a simple mesh viewer that allows to easily visualize surface mesh and its attributes. All the examples are cross-platform and can be compiled on MacOSX, Linux and Windows.
+
+libigl can be downloaded from our [github repository](https://github.com/libigl/libigl) or cloned with git:
+``` sh
+git clone https://github.com/libigl/libigl.git
+```
+
+All examples depends on glfw, glew and anttweakbar. A copy
+of the sourcecode of each library is provided together with libigl
+and they can be precompiled using:
+
+```sh
+    sh compile_dependencies_macosx.sh (MACOSX)
+    sh compile_dependencies_linux.sh (LINUX)
+```
+while precompiled binaries are provided for Visual Studio 2014 64bit.
+
+You can use the CMakeLists.txt in the tutorial folder to build all the examples:
+
+```sh
+  cd tutorial
+  mkdir build
+  cd build
+  cmake ../
+  make
+```
+
+or you can use the CMakeLists.txt inside each example folder to build the examples independently.
+
+For a few examples in Chapter 5, the [CoMiSo solver](http://www.graphics.rwth-aachen.de/software/comiso) has to be downloaded and compiled separately.
+
+## Mesh representation [101]
+
+libIGL uses the [Eigen](http://eigen.tuxfamily.org/) library to encode vector and matrices. We will review in this tutorial many of the basic operations that Eigen supports: If you want to get an idea of what operations are supported you can take a look at the [dense](http://eigen.tuxfamily.org/dox/group__QuickRefPage.html) and [sparse](http://eigen.tuxfamily.org/dox/group__SparseQuickRefPage.html) quick reference guides.
+
+We encode a triangular mesh as a pair of matrices:
+
+```cpp
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+```
+
+**V** is a #N by 3 matrix which stores the coordinates of the vertices. Each row stores the coordinate of a vertex, with the x,y,z coordinates in the first, second and third column respectively. The matrix **F** stores the triangle connectivity: each line of **F** denotes a triangle whose 3 vertices are represented as indices pointing to vertex coordinates in **F**.
+
+![A simple mesh made of 2 triangles and 4 vertices.](images/VF.png)
+
+Note that the order of the vertex indices in F determines the orientation of the triangles and it should be consistent for the entire surface. As we will see later, additional properties of the mesh will be similarly stored as matrices. This simple representation has many advantages:
+
+* it is memory efficient and cache friendly
+* the use of indices instead of pointers greatly simplifies debuggind
+* the data can be trivially read/written on disk
+
+libIGL provides Input/Output functions to read and write common mesh formats.
+The reading/writing functions are named read\*.h and write\*.h, respectively.
+
+Reading a mesh from file requires a single igl function call:
+
+```cpp
+igl::readOFF("../shared/cube.off", V, F);
+```
+
+The functions read the mesh cube.off and fills the provided matrices V and F.
+Similarly, to write a mesh to file (in OBJ format):
+
+```cpp
+igl::writeOBJ("cube.obj",V,F);
+```
+
+See [Example 101](101_FileIO/main.cpp) for the source code of a simple mesh converter from OFF to OBJ format.
+
+## Plotting surfaces [102]
+
+libigl contains an OpenGL viewer that can visualize surface and their properties.
+
+The following code ([Example 102](102_DrawMesh/main.cpp)) is a basic skeleton that will be used over the entire tutorial. It is a standalone application that loads a mesh and visualize it.
+
+```cpp
+#include <igl/readOFF.h>
+#include <igl/viewer/Viewer.h>
+
+Eigen::MatrixXd V;
+Eigen::MatrixXi F;
+
+int main(int argc, char *argv[])
+{
+  // Load a mesh in OFF format
+  igl::readOFF("../shared/bunny.off", V, F);
+
+  // Plot the mesh
+  igl::Viewer viewer;
+  viewer.set_mesh(V, F);
+  viewer.launch();
+}
+```
+
+The function set_mesh assigns to the viewer the mesh that we want to plot, and the last line creates an opengl context and starts the draw loop. Additional properties can be plotted on the mesh, and it is also possible to extend the viewer with standard OpenGL code. Please see the documentation in  [Viewer.h](../include/igl/Viewer/Viewer.h) for more details.
+
+![([Example 102](102_DrawMesh/main.cpp)) loads and draws a mesh.](images/102_DrawMesh.png)
+
+## Interaction with keyboard and mouse [103]
+
+Keyboard and mouse events triggers callbacks that can be registered in the viewer. The viewer supports the following callbacks:
+
+```cpp
+bool (*callback_pre_draw)(Viewer& viewer);
+bool (*callback_post_draw)(Viewer& viewer);
+bool (*callback_mouse_down)(Viewer& viewer, int button, int modifier);
+bool (*callback_mouse_up)(Viewer& viewer, int button, int modifier);
+bool (*callback_mouse_move)(Viewer& viewer, int mouse_x, int mouse_y);
+bool (*callback_mouse_scroll)(Viewer& viewer, float delta_y);
+bool (*callback_key_down)(Viewer& viewer, unsigned char key, int modifiers);
+bool (*callback_key_up)(Viewer& viewer, unsigned char key, int modifiers);
+```
+
+A keyboard callback can be used to visualize multiple meshes or different stages of an algorithm, as demonstrated in [Example 103](103_Events/main.cpp). The keyboard callback changes the visualized mesh depending on the key pressed:
+
+```cpp
+bool key_down(igl::Viewer& viewer, unsigned char key, int modifier)
+{
+  if (key == '1')
+  {
+    viewer.clear_mesh();
+    viewer.set_mesh(V1, F1);
+  }
+  else if (key == '2')
+  {
+    viewer.clear_mesh();
+    viewer.set_mesh(V2, F2);
+  }
+  return false;
+}
+```
+and it is registered in the viewer as follows:
+
+```cpp
+viewer.callback_key_down = &key_down;
+```
+
+Note that the mesh is cleared before using set_mesh. This has to be called every time the number of vertices or faces of the plotted mesh changes. Every callback returns a boolean value that tells the viewer if the event has been handled by the plugin, or if the viewer should process it normally. This is useful, for example, to disable the default mouse event handling if you want to control the camera directly in your code.
+
+The viewer can be extended using plugins, which are classes that implements all the viewer's callbacks. See the class Viewer_plugin for more details.
+
+## Scalar field visualization [104]
+
+Colors and normals can be associated to both faces or normals using the set_colors function:
+
+```cpp
+viewer.set_colors(C);
+```
+**C** is a #C by 3 matrix with one RGB color per row, and as many rows as the number of faces **or** the number of vertices. Depending on the size of **C**, the viewer applies the color to faces or vertices.
+
+Colors are commonly used to visualize scalar functions defined on a surface using a transfer functions, that maps a scalar value between 0 and 1 to a color scale. A simple example of a scalar field defined on a surface is the z coordinate of each point. We can extract this information from our mesh by taking the first column of V (which contains the stacked z coordiantes of all the vertices), and map it to colors using the igl::jet function:
+
+```cpp
+Eigen::VectorXd x = V.col(2);
+igl::jet(x,true,C);
+```
+
+The first row extracts the third column from V and the second calls the libigl functions that converts a scalar field to colors. The second parameter of jet normalizes the scalar field to lie between 0 and 1 before applying the color scale.
+
+![([Example 104](104_Colors/main.cpp)) igl::jet converts a scalar field to a color field.](images/104_Colors.png)
+
+## Overlays [105]
+
+In addition to the surface, the viewer supports the visualization of points, lines and text label that can be very helful while developing geometric processing algorithms. These additional informations can be drawn using the following functions:
+
+```cpp
+viewer.add_points(P,Eigen::RowVector3d(r,g,b));
+```
+
+Draws a point of color r,g,b for each row of P at the coordinates specified in each row of P, which is a #P by 3 matrix.
+
+```cpp
+viewer.add_edges(P1,P2,Eigen::RowVector3d(r,g,b);
+```
+
+Draws a line for each line of P1 and P2, which connects the point in P1 to the point in P2.
+
+```cpp
+viewer.add_label(p,str);
+```
+
+Draws a label containing the string str at the position p.
+
+These functions are demonstrate in [Example 105](105_Overlays/main.cpp) where the bounding box of the mesh is plotted using lines and points. The bounding box of a mesh can be found using Eigen:
+
+```cpp
+Eigen::Vector3d m = V.colwise().minCoeff();
+Eigen::Vector3d M = V.colwise().maxCoeff();
+```
+
+![([Example 105](105_Overlays/main.cpp)) The bounding box of a mesh is shown using overlays.](images/105_Overlays.png)
+
+Using matrices to encode the mesh and its attributes allows to write short and efficient code for many operations, avoiding to write for loops.
+
+## Picking [106]
+
+Picking vertices and faces using the mouse is very common in geometry processing applications. While this might seem a simple operation, its implementation is quite involved. libigl contains a function that solves this problem using the [Embree](https://software.intel.com/en-us/articles/embree-photo-realistic-ray-tracing-kernels) raycaster. Its usage is demonstrated in [Example 106](106_Picking/main.cpp):
+
+```cpp
+bool hit = igl::unproject_in_mesh(
+  Vector2f(x,y),
+  F,
+  viewer.view * viewer.model,
+  viewer.proj,
+  viewer.viewport,
+  *ei,
+  fid,
+  vid);
+```
+
+This function casts a ray from the view plane in the view direction. x,y are the position of the mouse on screen; view,model,proj are the view, model and projection matrix respectively, viewport is the viewport in opengl format; ei contains a [Bounding Volume Hierarchy](http://en.wikipedia.org/wiki/Bounding_volume_hierarchy) constructed by Embree, and fid and vid are the picked face and vertex respectively.
+
+This function is a good example of the design principles in libigl: the function takes very simple types, mostly matrix or vectors, and can be easily reused for many different tasks.
+Not committing to heavy data structures, favors simplicity, ease of use and reusability.
+
+# libigl design choices [107]
+
+To conclude the introduction, we summarize the main design principles in libigl:
+
+* No complex data types. Mostly matrices and vectors. This greatly favors code reusability and forces the authors to expose all the parameters used by the algorithm.  
+
+* Minimal dependencies: we use external libraries only when necessary and we wrap them in a small set of functions.
+
+* Header-only: it is straighforward to use our library since it is only one additional include directory in your project. (if you are worried about compilation speed, it is also possible to build the library as a [static library](../build/))
+
+![([Example 106](106_Picking/main.cpp)) Picking via ray casting. The selected vertices are colored in red.](images/106_Picking.png)
+
+# Chapter 2: Discrete Geometric Quantities and Operators
+This chapter illustrates a few discrete quantities that libigl can compute on a
+mesh. This also provides an introduction to basic drawing and coloring routines
+in our example viewer. Finally, we construct popular discrete differential
+geometry operators.
+
+## Normals
+Surface normals are a basic quantity necessary for rendering a surface. There
+are a variety of ways to compute and store normals on a triangle mesh.
+
+### Per-face
+Normals are well defined on each triangle of a mesh as the vector orthogonal to
+triangle's plane. These piecewise constant normals produce piecewise-flat
+renderings: the surface appears non-smooth and reveals its underlying
+discretization.
+
+### Per-vertex
+Storing normals at vertices, Phong or Gouraud shading will interpolate shading
+inside mesh triangles to produce smooth(er) renderings. Most techniques for
+computing per-vertex normals take an average of incident face normals. The
+techniques vary with respect to their different weighting schemes. Uniform
+weighting is heavily biased by the discretization choice, where as area-based
+or angle-based weighting is more forgiving.
+
+The typical half-edge style computation of area-based weights might look
+something like this:
+
+```cpp
+N.setZero(V.rows(),3);
+for(int i : vertices)
+{
+  for(face : incident_faces(i))
+  {
+    N.row(i) += face.area * face.normal;
+  }
+}
+N.rowwise().normalize();
+```
+
+Without a half-edge data-structure it may seem at first glance that looping
+over incident faces---and thus constructing the per-vertex normals---would be
+inefficient. However, per-vertex normals may be _throwing_ each face normal to
+running sums on its corner vertices:
+
+```cpp
+N.setZero(V.rows(),3);
+for(int f = 0; f < F.rows();f++)
+{
+  for(int c = 0; c < 3;c++)
+  {
+    N.row(F(f,c)) += area(f) * face_normal.row(f);
+  }
+}
+N.rowwise().normalize();
+```
+
+### Per-corner
+
+Storing normals per-corner is an efficient an convenient way of supporting both
+smooth and sharp (e.g. creases and corners) rendering. This format is common to
+OpenGL and the .obj mesh file format. Often such normals are tuned by the mesh
+designer, but creases and corners can also be computed automatically. Libigl
+implements a simple scheme which computes corner normals as averages of
+normals of faces incident on the corresponding vertex which do not deviate by a
+specified dihedral angle (e.g. 20°).
+
+![The `Normals` example computes per-face (left), per-vertex (middle) and
+per-corner (right) normals](images/fandisk-normals.jpg)
+
+## Gaussian Curvature
+Gaussian curvature on a continuous surface is defined as the product of the
+principal curvatures:
+
+ $k_G = k_1 k_2.$
+
+As an _intrinsic_ measure, it depends on the metric and
+not the surface's embedding.
+
+Intuitively, Gaussian curvature tells how locally spherical or _elliptic_ the
+surface is ( $k_G>0$ ), how locally saddle-shaped or _hyperbolic_ the surface
+is ( $k_G<0$ ), or how locally cylindrical or _parabolic_ ( $k_G=0$ ) the
+surface is.
+
+In the discrete setting, one definition for a ``discrete Gaussian curvature''
+on a triangle mesh is via a vertex's _angular deficit_:
+
+ $k_G(v_i) = 2π - \sum\limits_{j\in N(i)}θ_{ij},$
+
+where $N(i)$ are the triangles incident on vertex $i$ and $θ_{ij}$ is the angle
+at vertex $i$ in triangle $j$ [][#meyer_2003].
+
+Just like the continuous analog, our discrete Gaussian curvature reveals
+elliptic, hyperbolic and parabolic vertices on the domain.
+
+![The `GaussianCurvature` example computes discrete Gaussian curvature and visualizes it in
+pseudocolor.](images/bumpy-gaussian-curvature.jpg)
+
+## Curvature Directions
+The two principal curvatures $(k_1,k_2)$ at a point on a surface measure how much the
+surface bends in different directions. The directions of maximum and minimum
+(signed) bending are call principal directions and are always
+orthogonal.
+
+Mean curvature is defined simply as the average of principal curvatures:
+
+ $H = \frac{1}{2}(k_1 + k_2).$
+
+One way to extract mean curvature is by examining the Laplace-Beltrami operator
+applied to the surface positions. The result is a so-called mean-curvature
+normal:
+
+  $-\Delta \mathbf{x} = H \mathbf{n}.$
+
+It is easy to compute this on a discrete triangle mesh in libigl using the cotangent
+Laplace-Beltrami operator [][#meyer_2003].
+
+```cpp
+#include <igl/cotmatrix.h>
+#include <igl/massmatrix.h>
+#include <igl/invert_diag.h>
+...
+MatrixXd HN;
+SparseMatrix<double> L,M,Minv;
+igl::cotmatrix(V,F,L);
+igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_VORONOI,M);
+igl::invert_diag(M,Minv);
+HN = -Minv*(L*V);
+H = (HN.rowwise().squaredNorm()).array().sqrt();
+```
+
+Combined with the angle defect definition of discrete Gaussian curvature, one
+can define principal curvatures and use least squares fitting to find
+directions [][#meyer_2003].
+
+Alternatively, a robust method for determining principal curvatures is via
+quadric fitting [][#panozzo_2010]. In the neighborhood
+around every vertex, a best-fit quadric is found and principal curvature values
+and directions are sampled from this quadric. With these in tow, one can
+compute mean curvature and Gaussian curvature as sums and products
+respectively.
+
+![The `CurvatureDirections` example computes principal curvatures via quadric
+fitting and visualizes mean curvature in pseudocolor and principal directions
+with a cross field.](images/fertility-principal-curvature.jpg)
+
+This is an example of syntax highlighted code:
+
+```cpp
+#include <foo.html>
+int main(int argc, char * argv[])
+{
+  return 0;
+}
+```
+
+## Gradient
+Scalar functions on a surface can be discretized as a piecewise linear function
+with values defined at each mesh vertex:
+
+ $f(\mathbf{x}) \approx \sum\limits_{i=1}^n \phi_i(\mathbf{x})\, f_i,$
+
+where $\phi_i$ is a piecewise linear hat function defined by the mesh so that
+for each triangle $\phi_i$ is _the_ linear function which is one only at
+vertex $i$ and zero at the other corners.
+
+![Hat function $\phi_i$ is one at vertex $i$, zero at all other vertices, and
+linear on incident triangles.](images/hat-function.jpg)
+
+Thus gradients of such piecewise linear functions are simply sums of gradients
+of the hat functions:
+
+ $\nabla f(\mathbf{x}) \approx
+ \nabla \sum\limits_{i=1}^n \nabla \phi_i(\mathbf{x})\, f_i =
+ \sum\limits_{i=1}^n \nabla \phi_i(\mathbf{x})\, f_i.$
+
+This reveals that the gradient is a linear function of the vector of $f_i$
+values. Because $\phi_i$ are linear in each triangle their gradient are
+_constant_ in each triangle. Thus our discrete gradient operator can be written
+as a matrix multiplication taking vertex values to triangle values:
+
+ $\nabla f \approx \mathbf{G}\,\mathbf{f},$
+
+where $\mathbf{f}$ is $n\times 1$ and $\mathbf{G}$ is an $md\times n$ sparse
+matrix. This matrix $\mathbf{G}$ can be derived geometrically, e.g.
+[ch. 2][#jacobson_thesis_2013].
+Libigl's `gradMat`**Alec: check name** function computes $\mathbf{G}$ for
+triangle and tetrahedral meshes:
+
+![The `Gradient` example computes gradients of an input function on a mesh and
+visualizes the vector field.](images/cheburashka-gradient.jpg)
+
+## Laplacian
+
+The discrete Laplacian is an essential geometry processing tool. Many
+interpretations and flavors of the Laplace and Laplace-Beltrami operator exist.
+
+In open Euclidean space, the _Laplace_ operator is the usual divergence of gradient
+(or equivalently the Laplacian of a function is the trace of its Hessian):
+
+ $\Delta f =
+ \frac{\partial^2 f}{\partial x^2} +
+ \frac{\partial^2 f}{\partial y^2} +
+ \frac{\partial^2 f}{\partial z^2}.$
+
+The _Laplace-Beltrami_ operator generalizes this to surfaces.
+
+When considering piecewise-linear functions on a triangle mesh, a discrete Laplacian may
+be derived in a variety of ways. The most popular in geometry processing is the
+so-called ``cotangent Laplacian'' $\mathbf{L}$, arising simultaneously from FEM, DEC and
+applying divergence theorem to vertex one-rings. As a linear operator taking
+vertex values to vertex values, the Laplacian $\mathbf{L}$ is a $n\times n$
+matrix with elements:
+
+$L_{ij} = \begin{cases}j \in N(i) &\cot \alpha_{ij} + \cot \beta_{ij},\\
+j \notin N(i) & 0,\\
+i = j & -\sum\limits_{k\neq i} L_{ik},
+\end{cases}$
+
+where $N(i)$ are the vertices adjacent to (neighboring) vertex $i$, and
+$\alpha_{ij},\beta_{ij}$ are the angles opposite edge ${ij}$.
+This oft
+produced formula leads to a typical half-edge style implementation for
+constructing $\mathbf{L}$:
+
+```cpp
+for(int i : vertices)
+{
+  for(int j : one_ring(i))
+  {
+    for(int k : triangle_on_edge(i,j))
+    {
+      L(i,j) = cot(angle(i,j,k));
+      L(i,i) -= cot(angle(i,j,k));
+    }
+  }
+}
+```
+
+Without a half-edge data-structure it may seem at first glance that looping
+over one-rings, and thus constructing the Laplacian would be inefficient.
+However, the Laplacian may be built by summing together contributions for each
+triangle, much in spirit with its FEM discretization of the Dirichlet energy
+(sum of squared gradients):
+
+```cpp
+for(triangle t : triangles)
+{
+  for(edge i,j : t)
+  {
+    L(i,j) += cot(angle(i,j,k));
+    L(j,i) += cot(angle(i,j,k));
+    L(i,i) -= cot(angle(i,j,k));
+    L(j,j) -= cot(angle(i,j,k));
+  }
+}
+```
+
+Libigl implements discrete "cotangent" Laplacians for triangles meshes and
+tetrahedral meshes, building both with fast geometric rules rather than "by the
+book" FEM construction which involves many (small) matrix inversions, cf.
+**Alec: cite Ariel reconstruction paper**.
+
+The operator applied to mesh vertex positions amounts to smoothing by _flowing_
+the surface along the mean curvature normal direction. This is equivalent to
+minimizing surface area.
+
+![The `Laplacian` example computes conformalized mean curvature flow using the
+cotangent Laplacian [#kazhdan_2012][].](images/cow-curvature-flow.jpg)
+
+### Mass matrix
+The mass matrix $\mathbf{M}$ is another $n \times n$ matrix which takes vertex
+values to vertex values. From an FEM point of view, it is a discretization of
+the inner-product: it accounts for the area around each vertex. Consequently,
+$\mathbf{M}$ is often a diagonal matrix, such that $M_{ii}$ is the barycentric
+or voronoi area around vertex $i$ in the mesh [#meyer_2003][]. The inverse of
+this matrix is also very useful as it transforms integrated quantities into
+point-wise quantities, e.g.:
+
+ $\nabla f \approx \mathbf{M}^{-1} \mathbf{L} \mathbf{f}.$
+
+In general, when encountering squared quantities integrated over the surface,
+the mass matrix will be used as the discretization of the inner product when
+sampling function values at vertices:
+
+ $\int_S x\, y\ dA \approx \mathbf{x}^T\mathbf{M}\,\mathbf{y}.$
+
+An alternative mass matrix $\mathbf{T}$ is a $md \times md$ matrix which takes
+triangle vector values to triangle vector values. This matrix represents an
+inner-product accounting for the area associated with each triangle (i.e. the
+triangles true area).
+
+### Alternative construction of Laplacian
+
+An alternative construction of the discrete cotangent Laplacian is by
+"squaring" the discrete gradient operator. This may be derived by applying
+Green's identity (ignoring boundary conditions for the moment):
+
+  $\int_S \|\nabla f\|^2 dA = \int_S f \Delta f dA$
+
+Or in matrix form which is immediately translatable to code:
+
+  $\mathbf{f}^T \mathbf{G}^T \mathbf{T} \mathbf{G} \mathbf{f} =
+  \mathbf{f}^T \mathbf{M} \mathbf{M}^{-1} \mathbf{L} \mathbf{f} =
+  \mathbf{f}^T \mathbf{L} \mathbf{f}.$
+
+So we have that $\mathbf{L} = \mathbf{G}^T \mathbf{T} \mathbf{G}$. This also
+hints that we may consider $\mathbf{G}^T$ as a discrete _divergence_ operator,
+since the Laplacian is the divergence of gradient. Naturally, $\mathbf{G}^T$ is
+$n \times md$ sparse matrix which takes vector values stored at triangle faces
+to scalar divergence values at vertices.
+
+# Chapter 3: Matrices and Linear Algebra
+Libigl relies heavily on the Eigen library for dense and sparse linear algebra
+routines. Besides geometry processing routines, libigl has a few linear algebra
+routines which bootstrap Eigen and make Eigen feel even more like a high-level
+algebra library like Matlab.
+
+## Slice
+A very familiar and powerful routine in Matlab is array slicing. This allows
+reading from or writing to a possibly non-contiguous sub-matrix. Let's consider
+the matlab code:
+
+```matlab
+B = A(R,C);
+```
+
+If `A` is a $m \times n$ matrix and `R` is a $j$-long list of row-indices
+(between 1 and $m$) and `C` is a $k$-long list of column-indices, then as a
+result `B` will be a $j \times k$ matrix drawing elements from `A` according to
+`R` and `C`. In libigl, the same functionality is provided by the `slice`
+function:
+
+```cpp
+VectorXi R,C;
+MatrixXd A,B;
+...
+igl::slice(A,R,C,B);
+```
+
+`A` and `B` could also be sparse matrices.
+
+Similarly, consider the matlab code:
+
+```matlab
+A(R,C) = B;
+```
+
+Now, the selection is on the left-hand side so the $j \times k$ matrix  `B` is
+being _written into_ the submatrix of `A` determined by `R` and `C`. This
+functionality is provided in libigl using `slice_into`:
+
+```cpp
+igl::slice_into(B,R,C,A);
+```
+
+![The example `Slice` shows how to use `igl::slice` to change the colors for triangles
+on a mesh.](images/decimated-knight-slice-color.jpg)
+
+## Sort
+
+Matlab and other higher-level languages make it very easy to extract indices of
+sorting and comparison routines. For example in Matlab, one can write:
+
+```matlab
+[Y,I] = sort(X,1,'ascend');
+```
+
+so if `X` is a $m \times n$ matrix then `Y` will also be an $m \times n$ matrix
+with entries sorted along dimension `1` in `'ascend'`ing order. The second
+output `I` is a $m \times n$ matrix of indices such that `Y(i,j) =
+X(I(i,j),j);`. That is, `I` reveals how `X` is sorted into `Y`.
+
+This same functionality is supported in libigl:
+
+```cpp
+igl::sort(X,1,true,Y,I);
+```
+
+Similarly, sorting entire rows can be accomplished in matlab using:
+
+```matlab
+[Y,I] = sortrows(X,'ascend');
+```
+
+where now `I` is a $m$ vector of indices such that `Y = X(I,:)`.
+
+In libigl, this is supported with
+
+```cpp
+igl::sortrows(X,true,Y,I);
+```
+where again `I` reveals the index of sort so that it can be reproduced with
+`igl::slice(X,I,1,Y)`.
+
+Analogous functions are available in libigl for: `max`, `min`, and `unique`.
+
+![The example `Sort` shows how to use `igl::sortrows` to
+pseudocolor triangles according to their barycenters' sorted
+order.](images/decimated-knight-sort-color.jpg)
+
+
+### Other Matlab-style functions
+Libigl implements a variety of other routines with the same api and
+functionality as common matlab functions.
+
+- `igl::any_of` Whether any elements are non-zero (true)
+- `igl::cat` Concatenate two matrices (especially useful for dealing with Eigen
+  sparse matrices)
+- `igl::ceil` Round entries up to nearest integer
+- `igl::cumsum` Cumulative sum of matrix elements
+- `igl::colon` Act like Matlab's `:`, similar to Eigen's `LinSpaced`
+- `igl::cross` Cross product per-row
+- `igl::dot` dot product per-row
+- `igl::find` Find subscripts of non-zero entries
+- `igl::floot` Round entries down to nearest integer
+- `igl::histc` Counting occurrences for building a histogram
+- `igl::hsv_to_rgb` Convert HSV colors to RGB (cf. Matlab's `hsv2rgb`)
+- `igl::intersect` Set intersection of matrix elements.
+- `igl::jet` Quantized colors along the rainbow.
+- `igl::kronecker_product` Compare to Matlab's `kronprod`
+- `igl::median` Compute the median per column
+- `igl::mode` Compute the mode per column
+- `igl::orth` Orthogonalization of a basis
+- `igl::setdiff` Set difference of matrix elements
+- `igl::speye` Identity as sparse matrix
+
+## Laplace Equation
+A common linear system in geometry processing is the Laplace equation:
+
+ $∆z = 0$
+
+subject to some boundary conditions, for example Dirichlet boundary conditions
+(fixed value):
+
+ $\left.z\right|_{\partial{S}} = z_{bc}$
+
+In the discrete setting, this begins with the linear system:
+
+ $\mathbf{L} \mathbf{z} = \mathbf{0}$
+
+where $\mathbf{L}$ is the $n \times n$ discrete Laplacian and $\mathbf{z}$ is a
+vector of per-vertex values. Most of $\mathbf{z}$ correspond to interior
+vertices and are unknown, but some of $\mathbf{z}$ represent values at boundary
+vertices. Their values are known so we may move their corresponding terms to
+the right-hand side.
+
+Conceptually, this is very easy if we have sorted $\mathbf{z}$ so that interior
+vertices come first and then boundary vertices:
+
+ $$\left(\begin{array}{cc}
+ \mathbf{L}_{in,in} & \mathbf{L}_{in,b}\\
+ \mathbf{L}_{b,in} & \mathbf{L}_{b,b}\end{array}\right)
+ \left(\begin{array}{c}
+ \mathbf{z}_{in}\\
+ \mathbf{L}_{b}\end{array}\right) =
+ \left(\begin{array}{c}
+ \mathbf{0}_{in}\\
+ \mathbf{*}_{b}\end{array}\right)$$
+
+The bottom block of equations is no longer meaningful so we'll only consider
+the top block:
+
+ $$\left(\begin{array}{cc}
+ \mathbf{L}_{in,in} & \mathbf{L}_{in,b}\end{array}\right)
+ \left(\begin{array}{c}
+ \mathbf{z}_{in}\\
+ \mathbf{z}_{b}\end{array}\right) =
+ \mathbf{0}_{in}$$
+
+Where now we can move known values to the right-hand side:
+
+ $$\mathbf{L}_{in,in}
+ \mathbf{z}_{in} = -
+ \mathbf{L}_{in,b}
+ \mathbf{z}_{b}$$
+
+Finally we can solve this equation for the unknown values at interior vertices
+$\mathbf{z}_{in}$.
+
+However, probably our vertices are not sorted. One option would be to sort `V`,
+then proceed as above and then _unsort_ the solution `Z` to match `V`. However,
+this solution is not very general.
+
+With array slicing no explicit sort is needed. Instead we can _slice-out_
+submatrix blocks ($\mathbf{L}_{in,in}$, $\mathbf{L}_{in,b}$, etc.) and follow
+the linear algebra above directly. Then we can slice the solution _into_ the
+rows of `Z` corresponding to the interior vertices.
+
+![The `LaplaceEquation` example solves a Laplace equation with Dirichlet
+boundary conditions.](images/camelhead-laplace-equation.jpg)
+
+### Quadratic energy minimization
+
+The same Laplace equation may be equivalently derived by minimizing Dirichlet
+energy subject to the same boundary conditions:
+
+ $\mathop{\text{minimize }}_z \frac{1}{2}\int\limits_S \|\nabla z\|^2 dA$
+
+On our discrete mesh, recall that this becomes
+
+ $\mathop{\text{minimize }}_\mathbf{z}  \frac{1}{2}\mathbf{z}^T \mathbf{G}^T \mathbf{D}
+ \mathbf{G} \mathbf{z} \rightarrow \mathop{\text{minimize }}_\mathbf{z} \mathbf{z}^T \mathbf{L} \mathbf{z}$
+
+The general problem of minimizing some energy over a mesh subject to fixed
+value boundary conditions is so wide spread that libigl has a dedicated api for
+solving such systems.
+
+Let's consider a general quadratic minimization problem subject to different
+common constraints:
+
+ $$\mathop{\text{minimize }}_\mathbf{z}  \frac{1}{2}\mathbf{z}^T \mathbf{Q} \mathbf{z} +
+ \mathbf{z}^T \mathbf{B} + \text{constant},$$
+
+ subject to
+
+ $$\mathbf{z}_b = \mathbf{z}_{bc} \text{ and } \mathbf{A}_{eq} \mathbf{z} =
+ \mathbf{B}_{eq},$$
+
+where
+
+  - $\mathbf{Q}$ is a (usually sparse) $n \times n$ positive semi-definite
+    matrix of quadratic coefficients (Hessian),
+  - $\mathbf{B}$ is a $n \times 1$ vector of linear coefficients,
+  - $\mathbf{z}_b$ is a $|b| \times 1$ portion of
+$\mathbf{z}$ corresponding to boundary or _fixed_ vertices,
+  - $\mathbf{z}_{bc}$ is a $|b| \times 1$ vector of known values corresponding to
+    $\mathbf{z}_b$,
+  - $\mathbf{A}_{eq}$ is a (usually sparse) $m \times n$ matrix of linear
+    equality constraint coefficients (one row per constraint), and
+  - $\mathbf{B}_{eq}$ is a $m \times 1$ vector of linear equality constraint
+    right-hand side values.
+
+This specification is overly general as we could write $\mathbf{z}_b =
+\mathbf{z}_{bc}$ as rows of $\mathbf{A}_{eq} \mathbf{z} =
+\mathbf{B}_{eq}$, but these fixed value constraints appear so often that they
+merit a dedicated place in the API.
+
+In libigl, solving such quadratic optimization problems is split into two
+routines: precomputation and solve. Precomputation only depends on the
+quadratic coefficients, known value indices and linear constraint coefficients:
+
+```cpp
+igl::min_quad_with_fixed_data mqwf;
+igl::min_quad_with_fixed_precompute(Q,b,Aeq,true,mqwf);
+```
+
+The output is a struct `mqwf` which contains the system matrix factorization
+and is used during solving with arbitrary linear terms, known values, and
+constraint right-hand sides:
+
+```cpp
+igl::min_quad_with_fixed_solve(mqwf,B,bc,Beq,Z);
+```
+
+The output `Z` is a $n \times 1$ vector of solutions with fixed values
+correctly placed to match the mesh vertices `V`.
+
+## Linear Equality Constraints
+We saw above that `min_quad_with_fixed_*` in libigl provides a compact way to
+solve general quadratic programs. Let's consider another example, this time
+with active linear equality constraints. Specifically let's solve the
+`bi-Laplace equation` or equivalently minimize the Laplace energy:
+
+ $$\Delta^2 z = 0 \leftrightarrow \mathop{\text{minimize }}\limits_z \frac{1}{2}
+ \int\limits_S (\Delta z)^2 dA$$
+
+subject to fixed value constraints and a linear equality constraint:
+
+ $z_{a} = 1, z_{b} = -1$ and $z_{c} = z_{d}$.
+
+Notice that we can rewrite the last constraint in the familiar form from above:
+
+ $z_{c} - z_{d} = 0.$
+
+Now we can assembly `Aeq` as a $1 \times n$ sparse matrix with a coefficient
+$1$
+in the column corresponding to vertex $c$ and a $-1$ at $d$. The right-hand side
+`Beq` is simply zero.
+
+Internally, `min_quad_with_fixed_*` solves using the Lagrange Multiplier
+method. This method adds additional variables for each linear constraint (in
+general a $m \times 1$ vector of variables $\lambda$) and then solves the
+saddle problem:
+
+ $$\mathop{\text{find saddle }}_{\mathbf{z},\lambda}\, \frac{1}{2}\mathbf{z}^T \mathbf{Q} \mathbf{z} +
+  \mathbf{z}^T \mathbf{B} + \text{constant} + \lambda^T\left(\mathbf{A}_{eq}
+ \mathbf{z} - \mathbf{B}_{eq}\right)$$
+
+This can be rewritten in a more familiar form by stacking $\mathbf{z}$ and
+$\lambda$ into one $(m+n) \times 1$ vector of unknowns:
+
+ $$\mathop{\text{find saddle }}_{\mathbf{z},\lambda}\,
+ \frac{1}{2}
+ \left(
+  \mathbf{z}^T
+  \lambda^T
+ \right)
+ \left(
+  \begin{array}{cc}
+  \mathbf{Q}      & \mathbf{A}_{eq}^T\\
+  \mathbf{A}_{eq} & 0
+  \end{array}
+ \right)
+ \left(
+  \begin{array}{c}
+  \mathbf{z}\\
+  \lambda
+  \end{array}
+ \right) +
+ \left(
+  \mathbf{z}^T
+  \lambda^T
+ \right)
+ \left(
+  \begin{array}{c}
+  \mathbf{B}\\
+  -\mathbf{B}_{eq}
+  \end{array}
+  \right)
+  + \text{constant}$$
+
+Differentiating with respect to $\left( \mathbf{z}^T \lambda^T \right)$ reveals
+a linear system and we can solve for $\mathbf{z}$ and $\lambda$. The only
+difference from
+the straight quadratic
+_minimization_ system, is that
+this saddle problem system will not be positive definite. Thus, we must use a
+different factorization technique (LDLT rather than LLT). Luckily, libigl's
+`min_quad_with_fixed_precompute` automatically chooses the correct solver in
+the presence of linear equality constraints.
+
+![The example `LinearEqualityConstraints` first solves with just fixed value
+constraints (left: 1 and -1 on the left hand and foot respectively), then
+solves with an additional linear equality constraint (right: points on right
+hand and foot constrained to be equal).](images/cheburashka-biharmonic-leq.jpg)
+
+## Quadratic Programming
+
+We can generalize the quadratic optimization in the previous section even more
+by allowing inequality constraints. Specifically box constraints (lower and
+upper bounds):
+
+ $\mathbf{l} \le \mathbf{z} \le \mathbf{u},$
+
+where $\mathbf{l},\mathbf{u}$ are $n \times 1$ vectors of lower and upper
+bounds
+and general linear inequality constraints:
+
+ $\mathbf{A}_{ieq} \mathbf{z} \le \mathbf{B}_{ieq},$
+
+where $\mathbf{A}_{ieq}$ is a $k \times n$ matrix of linear coefficients and
+$\mathbf{B}_{ieq}$ is a $k \times 1$ matrix of constraint right-hand sides.
+
+Again, we are overly general as the box constraints could be written as
+rows of the linear inequality constraints, but bounds appear frequently enough
+to merit a dedicated api.
+
+Libigl implements its own active set routine for solving _quadratric programs_
+(QPs). This algorithm works by iteratively "activating" violated inequality
+constraints by enforcing them as equalities and "deactivating" constraints
+which are no longer needed.
+
+After deciding which constraints are active each iteration simple reduces to a
+quadratic minimization subject to linear _equality_ constraints, and the method
+from the previous section is invoked. This is repeated until convergence.
+
+Currently the implementation is efficient for box constraints and sparse
+non-overlapping linear inequality constraints.
+
+Unlike alternative interior-point methods, the active set method benefits from
+a warm-start (initial guess for the solution vector $\mathbf{z}$).
+
+```cpp
+igl::active_set_params as;
+// Z is optional initial guess and output
+igl::active_set(Q,B,b,bc,Aeq,Beq,Aieq,Bieq,lx,ux,as,Z);
+```
+
+![The example `QuadraticProgramming` uses an active set solver to optimize
+discrete biharmonic kernels at multiple scales [#rustamov_2011][].](images/cheburashka-multiscale-biharmonic-kernels.jpg)
+
+# Chapter 4: Shape Deformation
+Modern mesh-based shape deformation methods satisfy user deformation
+constraints at handles (selected vertices or regions on the mesh) and propagate
+these handle deformations to the rest of shape _smoothly_ and _without removing
+or distorting details_. Libigl provides implementations of a variety of
+state-of-the-art deformation techniques, ranging from quadratic mesh-based
+energy minimizers, to skinning methods, to non-linear elasticity-inspired
+techniques.
+
+## Biharmonic Deformation
+The period of research between 2000 and 2010 produced a collection of
+techniques that cast the problem of handle-based shape deformation as a
+quadratic energy minimization problem or equivalently the solution to a linear
+partial differential equation.
+
+There are many flavors of these techniques, but
+a prototypical subset are those that consider solutions to the bi-Laplace
+equation, that is biharmonic functions [#botsch_2004][]. This fourth-order PDE provides
+sufficient flexibility in boundary conditions to ensure $C^1$ continuity at
+handle constraints (in the limit under refinement) [#jacobson_mixed_2010][].
+
+### Biharmonic surfaces
+Let us first begin our discussion of biharmonic _deformation_, by considering
+biharmonic _surfaces_. We will casually define biharmonic surfaces as surface
+whose _position functions_ are biharmonic with respect to some initial
+parameterization:
+
+ $\Delta \mathbf{x}' = 0$
+
+and subject to some handle constraints, conceptualized as "boundary
+conditions":
+
+ $\mathbf{x}'_{b} = \mathbf{x}_{bc}.$
+
+where $\mathbf{x}'$ is the unknown 3D position of a point on the surface. So we are
+asking that the bi-Laplace of each of spatial coordinate functions to be zero.
+
+In libigl, one can solve a biharmonic problem like this with `igl::harmonic`
+and setting $k=2$ (_bi_-harmonic):
+
+```cpp
+// U_bc contains deformation of boundary vertices b
+igl::harmonic(V,F,b,U_bc,2,U);
+```
+
+This produces smooth surfaces that interpolate the handle constraints, but all
+original details on the surface will be _smoothed away_. Most obviously, if the
+original surface is not already biharmonic, then giving all handles the identity
+deformation (keeping them at their rest positions) will **not** reproduce the
+original surface. Rather, the result will be the biharmonic surface that does
+interpolate those handle positions.
+
+Thus, we may conclude that this is not an intuitive technique for shape
+deformation.
+
+### Biharmonic deformation fields
+Now we know that one useful property for a deformation technique is "rest pose
+reproduction": applying no deformation to the handles should apply no
+deformation to the shape.
+
+To guarantee this by construction we can work with _deformation fields_ (ie.
+displacements)
+$\mathbf{d}$ rather
+than directly with positions $\mathbf{x}. Then the deformed positions can be
+recovered as
+
+ $\mathbf{x}' = \mathbf{x}+\mathbf{d}.$
+
+A smooth deformation field $\mathbf{d}$ which interpolates the deformation
+fields of the handle constraints will impose a smooth deformed shape
+$\mathbf{x}'$. Naturally, we consider _biharmonic deformation fields_:
+
+ $\Delta \mathbf{d} = 0$
+
+subject to the same handle constraints, but rewritten in terms of their implied
+deformation field at the boundary (handles):
+
+ $\mathbf{d}_b = \mathbf{x}_{bc} - \mathbf{x}_b.$
+
+Again we can use `igl::harmonic` with $k=2$, but this time solve for the
+deformation field and then recover the deformed positions:
+
+```cpp
+// U_bc contains deformation of boundary vertices b
+D_bc = U_bc - igl::slice(V,b,1);
+igl::harmonic(V,F,b,D_bc,2,D);
+U = V+D;
+```
+
+![The `BiharmonicDeformation` example deforms a statue's head as a _biharmonic
+surface_ (top) and using a _biharmonic displacements_ (bottom).](images/max-biharmonic.jpg)
+
+#### Relationship to "differential coordinates" and Laplacian surface editing
+Biharmonic functions (whether positions or displacements) are solutions to the
+bi-Laplace equation, but also minimizers of the "Laplacian energy". For
+example, for displacements $\mathbf{d}$, the energy reads
+
+ $\int\limits_S \|\Delta \mathbf{d}\|^2 dA.$
+
+By linearity of the Laplace(-Beltrami) operator we can reexpress this energy in
+terms of the original positions $\mathbf{x}$ and the unknown positions
+$\mathbf{x}' = \mathbf{x} - \mathbf{d}$:
+
+ $\int\limits_S \|\Delta (\mathbf{x}' - \mathbf{x})\|^2 dA = \int\limits_S \|\Delta \mathbf{x}' - \Delta \mathbf{x})\|^2 dA.$
+
+In the early work of Sorkine et al., the quantities $\Delta \mathbf{x}'$ and
+$\Delta \mathbf{x}$ were dubbed "differential coordinates" [#sorkine_2004][].
+Their deformations (without linearized rotations) is thus equivalent to
+biharmonic deformation fields.
+
+## Polyharmonic deformation
+We can generalize biharmonic deformation by considering different powers of
+the Laplacian, resulting in a series of PDEs of the form:
+
+ $\Delta^k \mathbf{d} = 0.$
+
+with $k\in{1,2,3,\dots}$. The choice of $k$ determines the level of continuity
+at the handles. In particular, $k=1$ implies $C^0$ at the boundary, $k=2$
+implies $C^1$, $k=3$ implies $C^2$ and in general $k$ implies $C^{k-1}$.
+
+```cpp
+int k = 2;// or 1,3,4,...
+igl::harmonic(V,F,b,bc,k,Z);
+```
+
+![The `PolyharmonicDeformation` example deforms a flat domain (left) into a bump as a
+solution to various $k$-harmonic PDEs.](images/bump-k-harmonic.jpg)
+
+## Bounded Biharmonic Weights
+In computer animation, shape deformation is often referred to as "skinning".
+Constraints are posed as relative rotations of internal rigid "bones" inside a
+character. The deformation method, or skinning method, determines how the
+surface of the character (i.e. its skin) should move as a function of the bone
+rotations.
+
+The most popular technique is linear blend skinning. Each point on the shape
+computes its new location as a linear combination of bone transformations:
+
+ $\mathbf{x}' = \sum\limits_{i = 1}^m w_i(\mathbf{x}) \mathbf{T}_i
+ \left(\begin{array}{c}\mathbf{x}_i\\1\end{array}\right),$
+
+where $w_i(\mathbf{x})$ is the scalar _weight function_ of the ith bone evaluated at
+$\mathbf{x}$ and $\mathbf{T}_i$ is the bone transformation as a $4 \times 3$
+matrix.
+
+This formula is embarassingly parallel (computation at one point does not
+depend on shared data need by computation at another point). It is often
+implemented as a vertex shader. The weights and rest positions for each vertex
+are sent as vertex shader _attribtues_ and bone transformations are sent as
+_uniforms_. Then vertices are transformed within the vertex shader, just in
+time for rendering.
+
+As the skinning formula is linear (hence its name), we can write it as matrix
+multiplication:
+
+ $\mathbf{X}' = \mathbf{M} \mathbf{T},$
+
+where $\mathbf{X}'$ is $n \times 3$ stack of deformed positions as row
+vectors, $\mathbf{M}$ is a $n \times m\cdot dim$ matrix containing weights and
+rest positions and $\mathbf{T}$ is a $m\cdot (dim+1) \times dim$ stack of
+transposed bone transformations.
+
+Traditionally, the weight functions $w_j$ are painted manually by skilled
+rigging professionals. Modern techniques now exist to compute weight functions
+automatically given the shape and a description of the skeleton (or in general
+any handle structure such as a cage, collection of points, selected regions,
+etc.).
+
+Bounded biharmonic weights are one such technique that casts weight computation
+as a constrained optimization problem [#jacobson_2011][]. The weights enforce
+smoothness by minimizing a smoothness energy: the familiar Laplacian energy:
+
+ $\sum\limits_{i = 1}^m \int_S (\Delta w_i)^2 dA$
+  
+subject to constraints which enforce interpolation of handle constraints:
+
+ $w_i(\mathbf{x}) = \begin{cases} 1 & \text{ if } \mathbf{x} \in H_i\\ 0 & \text{ otherwise }
+ \end{cases},$
+
+where $H_i$ is the ith handle, and constraints which enforce non-negativity,
+parition of unity and encourage sparsity:
+
+ $0\le w_i \le 1$ and $\sum\limits_{i=1}^m w_i = 1.$
+
+This is a quadratic programming problem and libigl solves it using its active
+set solver or by calling out to Mosek.
+
+![The example `BoundedBiharmonicWeights` computes weights for a tetrahedral
+mesh given a skeleton (top) and then animates a linear blend skinning
+deformation (bottom).](images/hand-bbw.jpg)
+
+## Dual Quaternion Skinning
+Even with high quality weights, linear blend skinning is limited. In
+particular, it suffers from known artifacts stemming from blending rotations as
+as matrices: a weight combination of rotation matrices is not necessarily a
+rotation. Consider an equal blend between rotating by $-pi/2$ and by $pi/2$
+about the $z$-axis. Intuitively one might expect to get the identity matrix,
+but instead the blend is a degenerate matrix scaling the $x$ and $y$
+coordinates by zero:
+
+ $0.5\left(\begin{array}{ccc}0&-1&0\\1&0&0\\0&0&1\end{array}\right)+
+ 0.5\left(\begin{array}{ccc}0&1&0\\-1&0&0\\0&0&1\end{array}\right)=
+ \left(\begin{array}{ccc}0&0&0\\0&0&0\\0&0&1\end{array}\right)$
+
+In practice, this means the shape shrinks and collapses in regions where bone
+weights overlap: near joints.
+
+Dual quaternion skinning presents a solution [#kavan_2008]. This method
+represents rigid transformations as a pair of unit quaternions,
+$\hat{\mathbf{q}}$. The linear blend skinning formula is replaced with a
+linear blend of dual quaternions:
+
+ $\mathbf{x}' =
+ \cfrac{\sum\limits_{i=1}^m w_i(\mathbf{x})\hat{\mathbf{q}_i}}
+ {\left\|\sum\limits_{i=1}^m w_i(\mathbf{x})\hat{\mathbf{q}_i}\right\|}
+ \mathbf{x},$
+
+where $\hat{\mathbf{q}_i}$ is the dual quaternion representation of the rigid
+transformation of bone $i$. The normalization forces the result of the linear blending
+to again be a unit dual quaternion and thus also a rigid transformation.
+
+Like linear blend skinning, dual quaternion skinning is best performed in the
+vertex shader. The only difference being that bone transformations are sent as
+dual quaternions rather than affine transformation matrices.  Libigl supports
+CPU-side dual quaternion skinning with the `igl::dqs` function, which takes a
+more traditional representation of rigid transformations as input and
+internally converts to the dual quaternion representation before blending:
+
+```cpp
+// vQ is a list of rotations as quaternions
+// vT is a list of translations
+igl::dqs(V,W,vQ,vT,U);
+```
+
+
+# Chapter 5: Parametrization [500]
+
+In computer graphics, we denote as parametrization of a surface a map from the surface to \\(\mathbf{R}^2\\). It is usually encoded by a new set of 2D coordinates for each vertex of the mesh and possibly also a new set of faces in one to one correspondence with the faces of the original surface. Note that this definition
+is the *inverse* of the classical differential geometry parametrization.
+
+A parametrization has many applications, ranging from texture mapping to surface remeshing. Many algorithms have been proposed, and they can be broadly characterized in four families:
+
+1. **Single patch, fixed boundary**: these algorithm can parametrize a disk-like part of the surface given fixed 2D positions for its boundary. These algorithms are efficient and simple, but usually produce high-distortion maps due to the strong cosntraints on the border.
+
+2. **Single patch, free border:** these algorithms allows the boundary to deform freely, reducing the distortion of the map. Care should be taken to prevent the border to self-intersect.
+
+3. **Global parametrization**: these algorithms works on meshes with arbitrary genus. They cut the mesh in multiple patches that are then possible to flatten in a 2D domain. Usually the map is discontinuous on the seams.
+
+4. **Global seamless parametrization**: similar to the global parametrization, but solved with a global solving strategy that hides the seams, making the parametrization "continuous", under specific assumptions that we will discuss later.
+
+## Harmonic parametrization [501]
+
+Harmonic parametrization is a single patch, fixed boundary parametrization algorithm that computes the 2D coordinates of the flattened mesh as two Harmonic functions.
+
+The algorithm is divided in 3 steps:
+
+* Detection of the boundary vertices
+
+```cpp
+Eigen::VectorXi bnd;
+igl::boundary_loop(V,F,bnd);
+```
+
+* Map the boundary vertices to a circle
+
+```cpp
+Eigen::MatrixXd bnd_uv;
+igl::map_vertices_to_circle(V,bnd,bnd_uv);
+```
+
+* Computation of harmonic functions for both the u and v coordinate on the plane, using the boundary positions as boundary constraints
+
+```cpp
+igl::harmonic(V,F,bnd,bnd_uv,1,V_uv);
+```
+
+bnd contains the indices of the boundary vertices, bnd_uv their position on the UV plane, and "1" denotes that we want to compute an harmonic function (2 will be for biharmonic, 3 for triharmonic, etc.). Note that each of the three functions is deisgned to be reusable in other parametrization algorithms.
+
+A UV parametrization can be visualized in the viewer using the method:
+
+```cpp
+viewer.set_uv(V_uv);
+```
+
+which uses the UV coordinates to apply a procedural checkerboard texture to the mesh ([Example 501](501_HarmonicParam/main.cpp)).
+
+![([Example 501](501_HarmonicParam/main.cpp)) Harmonic parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/501_HarmonicParam.png)
+
+###References:
+
+[Multiresolution Analysis of Arbitrary Meshes](http://research.microsoft.com/en-us/um/people/hoppe/mra.pdf),
+Matthias Eck, Tony DeRose, Tom Duchamp, Hugues Hoppe, Michael Lounsbery, Werner Stuetzle,
+SIGGRAPH 2005
+
+## Least-Square Conformal Maps [502]
+
+Least-square conformal maps parametrization minimizes the conformal (angular) distortion of the generated parametrization. If does not need to have a fixed boundary.
+
+LSCM minimizes the following energy:
+
+\\[ E_{LSCM}(\mathbf{u},\mathbf{v}) = \int_X \frac{1}{2}| \nabla \mathbf{u}^{\perp} - \nabla \mathbf{v} |^2 dA \\]
+
+which can be rewritten in matrix form as:
+
+\\[ E_{LSCM}(\mathbf{u},\mathbf{v}) = \frac{1}{2} [\mathbf{u},\mathbf{v}]^t (L_c - 2A) [\mathbf{u},\mathbf{v}] \\]
+
+where L_c is the cotangent laplacian matrix and A is a matrix such that \\( [\mathbf{u},\mathbf{v}]^t A  [\mathbf{u},\mathbf{v}] \\) is equal to the _vector area_ of the mesh.
+
+Using libigl, this matrix energy can be written using a few lines of codes. The cotangent matrix can be computed using igl::cotmatrix:
+
+```cpp
+SparseMatrix<double> L;
+igl::cotmatrix(V,F,L);
+```
+
+Note that we want to apply the laplacian matrix to the u and v coordinates at the same time, thus we need to extend the laplacian matrix taking the left Kronecker product with a 2x2 identity matrix:
+
+```cpp
+SparseMatrix<double> L_flat;
+repdiag(L,2,L_flat);
+```
+
+The area matrix is computed with igl::vector_area_matrix:
+
+```cpp
+SparseMatrix<double> A;
+igl::vector_area_matrix(F,A);
+```
+
+The final energy matrix is the sum of these two matrices. Note that in this case we don't need to fix the boundary, we only need to fix two arbitrary vertices to arbitrary positions to remove the null space of the energy and make the minimum unique. The full source code is provided in [Example 502](502_LSCMParam/main.cpp).
+
+
+![([Example 502](502_LSCMParam/main.cpp)) LSCM parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/502_LSCMParam.png)
+
+####References:
+
+[Least Squares Conformal Maps, for Automatic Texture Atlas Generation,](http://www.cs.jhu.edu/~misha/Fall09/Levy02.pdf)
+Bruno Lévy, Sylvain Petitjean, Nicolas Ray, Jérome Maillot,
+SIGGRAPH 2002
+
+[Spectral Conformal Parameterization](http://www.geometry.caltech.edu/pubs/MTAD08.pdf),
+Patrick Mullen, Yiying Tong, Pierre Alliez, Mathieu Desbrun,
+CGF 2008
+
+## As-Rigid-As-Possible parametrization [503]
+
+As-Rigid-As-Possible parametrizationis a powerful single-patch, non-linear algorithm to compute a parametrization that strives to preserve distances (and thus angles). The idea is very similar to ARAP surface deformation: each triangle is mapped to the plane trying to preserve its original shape, up to a rigid 2x2 rotation.
+
+The algorithm can be implemented reusing the functions discuss in the deformation chapter arap_precomputation and ara_solve. The only difference is that the optimization has to be done in 2D instead of 3D and that a starting point for the non-linear optimization is necessary. While for 3D deformation the original mesh is a perfect starting point, this is not the case for ARAP parametrization since the starting point must be a 2D mesh. In [Example 503](503_ARAPParam/main.cpp), we use Harmonic parametrization as a starting point for the ARAP parametrization: note that similarly to LSCM, the boundary is free to deform to minimize the distortion.
+
+![([Example 503](502_ARAPParam/main.cpp)) As-Rigid-As-Possible parametrization. (left) mesh with texture, (right) UV parametrization with texture](images/503_ARAPParam.png)
+
+### References
+[A Local/Global Approach to Mesh Parameterization](http://cs.harvard.edu/~sjg/papers/arap.pdf)
+Ligang Liu, Lei Zhang, Yin Xu, Craig Gotsman, Steven J. Gortler
+SGP 2008
+
+## N-Rotationally symmetric tangent fields [504]
+
+The design of tangent fields is a basic tool used to design guidance fields for uniform quadrilateral and hexaedral remeshing. libigl contains an implementation of all the state- of-the-art to design algorithms for N-RoSy fields and their generalizations.
+
+In libigl, tangent unit-length vector fields are piece-wise constant on the faces of a triangle mesh, and described by one or more vectors per-face. The function
+
+```cpp
+igl::nrosy(V,F,b,bc,b_soft,b_soft_weight,bc_soft,N,0.5,
+           output_field,output_singularities);
+```
+
+creates a smooth vector field (N=1) starting from a sparse set of constrained faces, whose indices are listed in b and their constrained value is specified in bc. The functions supports soft_constraints (b_soft,b_soft_weight,bc_soft), and returns the interpolated field for each face of the triangle mesh (output_field) plus the singularities of the field (output_singularities).
+
+![Design of a unit-lenght vector field](images/504_vector_field.png)
+
+The singularities are vertices where the field vanishes, and they are highlighted in red. igl::nrosy can generate N-Rotation Symmetric fields, which are a generalization of vector fields where in every face the vector is defined up to a constant rotation of \\( 2\pi / N \\). As can be observed in the following figure, the singularities of fields generated with different N are in different positions and of a different kind.
+
+![Design of a 2-,4- and 9-RoSy field](images/504_nrosy_field.png)
+
+We demonstrate how to call and plot N-RoSy fields in [Example 504](504_NRosyDesign/main.cpp), where the degree of the field can be controlled by pressing the number keys.
+
+### References
+
+[N-Symmetry Direction Field Design](http://alice.loria.fr/publications/papers/2008/DGF/NSDFD-TOG.pdf),
+Nicolas Ray, Bruno Vallet, Wan Chiu Li, Bruno Lévy
+TOG 2008
+
+[Mixed-integer quadrangulation](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf),
+David Bommes, Henrik Zimmer, Leif Kobbelt
+SIGGRAPH 2009
+
+#Global, seamless integer grid parametrization
+
+The previous parametrization methods where focusing on generating parametrization of single patches, mainly aimed at texture mapping and baking of other surface properties like normals high-frequency details. Global, seamless parametrization aims at parametrizing complex shapes with a parametrization that is aligned with a given set of directions for the purpose of remeshing the surface. In libigl, we provide a reference  implementation of the pipeline of the  [MIQ](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf) paper.
+
+### Global, seamless integer-grid parametrization [505]
+
+The first step involves the design of a 4-RoSy field (sometimes called cross field) that describes how the edges of the final quad remeshing should align. The field constraints are usually manually specified or extracted from curvature. In this example, we simply fix one face in a random direction.
+
+![Initial cross field prescribing the edge alignment.](images/505_MIQ_1.png)
+
+### Combing and cutting
+
+Given the cross field, we now want to cut the surface so that it becomes homeorphic to a disk. While this can be done directly on the cross-field, we prefer to do this operation on its bisector field (a copy of the field rotated by 45 degrees) since it is more stable and generic.
+
+We thus rotate the field,
+
+![Bisector field.](images/505_MIQ_2.png)
+
+and we remove the rotation ambiguity by assigning to each face a u and v direction, computed by diffusing this alignment from a random face.
+
+![Combed bisector field.](images/505_MIQ_3.png)
+
+You can imagine this process as combing an hairy surface: you'll be able to comb part of it, but at some point you will not be able to comb consistently the full surface ([Hairy ball theorem](http://en.wikipedia.org/wiki/Hairy_ball_theorem)). The discontinuites in the combing defines the cut graph:
+
+![Cut graph.](images/505_MIQ_4.png)
+
+Finally, we rotate the combed field by 45 degrees to undo the initial 45 degrees rotation:
+
+![Combed cross field.](images/505_MIQ_5.png)
+
+This cross field can be seen as the ideal gradient of the parametrization that we want to compute.
+
+### Poisson parametrization
+
+The mesh can be then cut along the seams and a parametrization is computed trying to find two scalar functions whose gradient matches the combed cross field. This is a classical Poisson problem, that is solved minimizing the following quadratic energy:
+
+\\[ E(\mathbf{u},\mathbf{v}) = |\nabla \mathbf{u} - X_u|^2 + |\nabla \mathbf{v} - X_v|^2 \\]
+
+where \\( X_u \\) and \\( X_u \\) denotes the combed cross field. Solving this problem generates a parametrization whose u and v isolines are aligned with the input cross field.
+
+![Poisson parametrization.](images/505_MIQ_8.png)
+
+We hide the seams by adding a set of integer constraints to the Poisson problem that aligns the isolines on both sides of each seam.
+
+![Seamless Poisson parametrization.](images/505_MIQ_7.png)
+
+Note that this parametrization can only be used for remeshing purposes, since it contains many overlaps.
+
+![Seamless Poisson parametrization (in 2D).](images/505_MIQ_6.png)
+
+A quad mesh can be extracted from this parametrization using
+[libQEx](https://github.com/hcebke/libQEx) (not included in libigl).
+
+The full pipeline is demonstrated in [Example 505](505_MIQ/main.cpp).
+
+### References
+
+[Mixed-integer quadrangulation](http://www-sop.inria.fr/members/David.Bommes/publications/miq.pdf),
+David Bommes, Henrik Zimmer, Leif Kobbelt
+SIGGRAPH 2009
+
+## Anisotropic remeshing [506]
+
+Anisotropic and non-uniform quad remeshing is important to concentrate the elements in the regions with more details. It is possible to extend the MIQ quad meshing framework to generate anisotropic quad meshes using a mesh deformation approach.
+
+The input of the remeshing algorithm is now a sparse set of constraints that defines the shape and scale of the desired quad remeshing. This can be encoded as a frame-field, which is a pair of non-orthogonal and non-unit lenght vectors. The frame field can be interpolated by decomposing it in a 4-RoSy field and a unique affine transformation. The two parts can then be interpolated separately, using igl::nrosy for the cross field, and an harmonic interpolant for the affine part.
+
+![Interpolation of a frame field. Colors on the vectors denote the desired scale. The red faces contains the frame field constraints.](images/506_FrameField_1.png)
+
+After the interpolation, the surface is warped to transform each frame into an orthogonal and unit lenght cross (i.e. removing the scaling and skewness from the frame). This deformation defines a new embedding (and a new metric) for the surface.
+
+![The surface is deformed to transform the frame field in a cross field.](images/506_FrameField_2.png)
+
+The deformed surface can the be isotropically remeshed using the MIQ algorithm that has been presented in the previous section.
+
+![The deformed surface is isotropically remeshed.](images/506_FrameField_3.png)
+
+The UV coordinates of the deformed surface can then be used to transport the parametrization to the original surface, where the isolines will trace a quad mesh whose elements are similar to the shape prescribed in the input frame field.
+
+![The global parametrization is lifted to the original surface to create the anisotropic quad meshing.](images/506_FrameField_4.png)
+
+Our implementation ([Example 506](506_FrameField/main.cpp)) uses MIQ to generate the UV parametrization, but other algorithms could be applied: the only desiderata is that the generated quad mesh will be as isotropic as possible.
+
+### References
+
+[Frame Fields: Anisotropic and Non-Orthogonal Cross Fields],
+Daniele Panozzo, Enrico Puppo, Marco Tarini, Olga Sorkine-Hornung,
+SIGGRAPH, 2014
+
+## N-PolyVector fields [507]
+
+* further generalization to arbitrary rosy, same interface
+
+* globally optimal and keenan optimal field are a subset of them
+
+## Conjugate vector fields [508]
+
+* they can be used to encode conjugate field -> planar meshing
+
+* global/local approach
+
+## Planarization [509]
+
+* given a mesh from conjugate directions, enforce planarity with a local/global approach
+* useful for architecture
+
+# Chapter 6: External libraries [600]
+
+An additional positive side effect of using matrices as basic types is that it is easy to exchange data between libigl and other softwares and libraries.
+
+## State serialization [601]
+
+Geometry processing applications often require a considerable amount of computational time and/or manual input. In order to make the development efficient it must be possible to serialize and deserialize the state of the application.
+
+Having a good serialization framework allows to quickly start debugging just before the crash happens, avoiding to wait for the precomputation to take place every time. It also makes it easier to define unit testing that can be used to find bugs in interactive applications: if the input is slightly different every time the algorithm is executed, it is very difficult to find bugs.
+
+Unfortunately, serialization is often not considered in geoemtry processing due to the extreme difficulty in serializing pointer-based data structures (like an helf-edge).
+
+In libigl, serialization is simpler, since the majority of the functions use basic types, and pointers are used in very rare cases (usually to interface with external libraries). libigl provides an extremely easy to use XML serialization framework, that drastically reduces the overhead required to add serialization to your applications.
+
+Assume that the state of your application is composed of a mesh and set of integer ids:
+
+``` cpp
+class State : public ::igl::XMLSerialization
+{
+public:
+  State() : XMLSerialization("dummy") {}
+
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+  std::vector<int> ids;
+
+  void InitSerialization()
+  {
+    xmlSerializer->Add(V  , "V");
+    xmlSerializer->Add(F  , "F");
+    xmlSerializer->Add(ids, "ids");
+  }
+};
+```
+
+A class can be made serializable by inheriting from ::igl::XMLSerialization and trivially implementing the InitSerialization method. Note that you don't have to care the types, Add is able to serialize all basic stl types, all Eigen types and any class inheriting from ::igl::XMLSerialization.
+
+It is then possible to save the state to an xml file:
+
+``` cpp
+::igl::XMLSerializer serializer_save("601_Serialization");
+serializer_save.Add(state,"State");
+serializer_save.Save("temp.xml",true);
+```
+
+This code generates the following xml file (assuming V and F contains a simple mesh with two triangles, and ids contains the numbers 6 and 7):
+
+``` xml
+<:::601_Serialization>
+    <State>
+        <V rows="4" cols="3" matrix="
+0,0,0,
+1,0,0,
+1,1,1,
+2,1,0"/>
+        <F rows="2" cols="3" matrix="
+0,1,2,
+1,3,2"/>
+        <ids size="2" vector_int="
+6,7"/>
+    </State>
+</:::601_Serialization>
+```
+
+The xml file can then be loaded in a similar way:
+
+``` cpp
+State loaded_state;
+::igl::XMLSerializer serializer_load("601_Serialization");
+serializer_load.Add(loaded_state,"State");
+serializer_load.Load("temp.xml");
+```
+
+This can also be used as a convenient interface to provide parameters to command line applications, since the xml files can be directly edited with a standard text editor.
+
+We demonstrate the serialization framework in [Example 601](601_Serialization/main.cpp). We strongly suggest that you make the entire state of your application always serializable: this will save you a lot of troubles when you'll be making figures for a scientific publication. It is very common to have to do small changes to figures during the production of a paper, and being able to serialize the entire state just before you take screenshots will save you many painful hours before a submission deadline.
+
+## Mixing matlab code [602]
+
+libigl can be interfaced matlab, to offload some of the numerically heavy computation to a matlab script. This has the major advantage of allowing to develop efficient and complex UI in C++, while keeping the advantage of fast protototyping of matlab. In particular, using an external matlab script in a libigl application allows to change the algorithm in the matlab script without having to recompile the C++ part.
+
+We demonstrate how to integrate matlab in a libigl application in [Example 602](602_Matlab/main.cpp). The example uses matlab to compute the Eigenfunctions of the discrete Laplacian operator, relying on libigl for mesh IO, visualization and for computing the Laplacian operator.
+
+libigl can connect to an existing instance of matlab (or launching a new one on Linux/MacOSX) using:
+
+``` cpp
+igl::mlinit(&engine);
+```
+
+The cotangent laplacian is computed using igl::cotmatrix and uploaded to the matlab workspace:
+
+``` cpp
+igl::cotmatrix(V,F,L);
+igl::mlsetmatrix(&engine,"L",L);
+```
+
+It is now possible to use any matlab function on the data. For example, we can see the sparsity pattern of L using spy:
+
+``` cpp
+igl::mleval(&engine,"spy(L)");
+```
+
+![The matlab spy function is called from a libigl-based application.](images/602_Matlab_1.png)
+
+You can also do some computation and then return it back to the C++ application
+
+``` cpp
+igl::mleval(&engine,"[EV,~] = eigs(-L,10,'sm')");
+igl::mlgetmatrix(&engine,"EV",EV);
+```
+
+and then use libigl functions to plot the eigenfunctions.
+
+![4 Eigenfunctions of the Laplacian plotted in the libigl viewer.](images/602_Matlab_2.png)
+
+## Calling igl functions from matlab [603]
+
+It is also possible to call libigl functions from matlab, compiling them as MEX functions. This can be very useful to offload to C++ code the computationally intensive parts of a matlab application.
+
+We provide a wrapper for igl::readOBJ in [Example 603](603_MEX/compileMEX.m). We plan to provide wrappers for all our functions in the future, if you are interested in this feature (or if you want to help implementing it) please let us know.
+
+## Triangulation of closed polygons [604]
+
+The generation of high-quality triangle and tetrahedral meshes is a very common task in geometry processing. We provide wrappers in libigl to triangle and tetegen.
+
+A triangle mesh canb e cerated starting from a set of boundary edges using igl::triangulate.
+
+``` cpp
+igl::triangulate(V,E,H,V2,F2,"a0.005q");
+```
+
+where E is a set of boundary edges, H a set of 2D positions of points contained in holes of the triangulation and (V2,F2) is the generate triangulation. Additional parameters can be given to triangles, to control the quality: "a0.005q" puts a bound on the maximal area of the triangles and a minimal angle of 20 degrees. In Example [Example 604](604_Triangle/main.m), the interior of a square (excluded a smaller square in its interior) is triangulated.
+
+![Triangulation of the interior of a polygon.](images/604_Triangle.png)
+
+## Tetrahedralization of closed surfaces [605]
+
+Similarly, the interior of a closed manifold surface can be tetrahedralized using the function igl::tetrahedralize which wraps the tetgen library ([Example 605](605_Tetgen/main.c)):
+
+``` cpp
+igl::tetrahedralize(V,F,"pq1.414", TV,TT,TF);
+```
+
+![Tetrahedralization of the interior of a surface mesh.](images/605_Tetgen.png)
+
+## Baking ambient occlusion [606]
+
+[Ambient occlusion](http://en.wikipedia.org/wiki/Ambient_occlusion) is a rendering technique used to calculate the exposure of each point in a surface to ambient lighting. It is usually encoded as a scalar (normalized between 0 and 1) associated with the vertice of a mesh.
+
+Formally, ambient occlusion is defined as:
+
+\\[ A_p = \frac{1}{\pi} \int_\omega V_{p,\omega}(n \cdot \omega) d\omega \\]
+
+where \\( V_{p,\omega} \\) is the visibility function at  p, defined to be zero if p is occluded in the direction \\( \omega \\) and one otherwise, and \\( d\omega \\) is the infinitesimal solid angle step of the integration variable \\( \omega \\).
+
+The integral is usually approximate by casting rays in random directions around each vertex. This approximation can be computed using the function:
+
+``` cpp
+igl::ambient_occlusion(V,F,V_samples,N_samples,500,AO);
+```
+
+that given a scene described in V,F, computes the ambient occlusion of the points in V_samples whose associated normals are N_samples. The number of casted rays can be controlled (usually at least 400-500 rays are required to get a smooth result) and the result is return in AO, as a single scalar for each sample.
+
+Ambient occlusion can be used to darken the surface colors, as shown in [Example 606](606_AmbientOcclusion/main.c)
+
+![A mesh rendered without (left) and with (right) ambient occlusion.](images/606_AmbientOcclusion.png)
+
+# Outlook for continuing development [future]
+
+libigl is in active development, and we plan to focus on the following features in the next months:
+
+* A better and more consistent documentation for all functions, plus exteding this tutorial to cover more features of libigl
+
+* Include a robust, adaptive triangular remeshing algorithm. Currently, the only remeshing functions available are only able to create quadrilateral remeshings
+
+* Generate matlab and python wrappers for all libigl functions
+
+* Implement a mixed-integer solver which only uses Eigen to remove the dependency on CoMiSo
+
+* Add a standalone BVH and a simple ray casting engine to make the dependency on Embree optional
+
+We encourage you to contribute to the library and to report problems and bugs that you encounter while using it. The best way of contributing new feature or patches is to fork the libigl repository and to open a [pull request](https://help.github.com/articles/using-pull-requests) on [our github repository](https://github.com/libigl/libigl).
+
+
+
+[#botsch_2004]: Matrio Botsch and Leif Kobbelt. "An Intuitive Framework for
+Real-Time Freeform Modeling," 2004.
+[#jacobson_thesis_2013]: Alec Jacobson,
+_Algorithms and Interfaces for Real-Time Deformation of 2D and 3D Shapes_,
+2013.
+[#jacobson_2011]: Alec Jacobson, Ilya Baran, Jovan Popović, and Olga Sorkin.
+"Bounded Biharmonic Weights for Real-Time Deformation," 2011.
+[#jacobson_mixed_2010]: Alec Jacobson, Elif Tosun, Olga Sorkine, and Denis
+Zorin. "Mixed Finite Elements for Variational Surface Modeling," 2010.
+[#kavan_2008]: Ladislav Kavan, Steven Collins, Jiri Zara, and Carol O'Sullivan.
+"Geometric Skinning with Approximate Dual Quaternion Blending," 2008.
+[#kazhdan_2012]: Michael Kazhdan, Jake Solomon, Mirela Ben-Chen,
+"Can Mean-Curvature Flow Be Made Non-Singular," 2012.
+[#meyer_2003]: Mark Meyer, Mathieu Desbrun, Peter Schröder and Alan H.  Barr,
+"Discrete Differential-Geometry Operators for Triangulated
+2-Manifolds," 2003.
+[#panozzo_2010]: Daniele Panozzo, Enrico Puppo, Luigi Rocca,
+"Efficient Multi-scale Curvature and Crease Estimation," 2010.
+[#rustamov_2011]: Raid M. Rustamov, "Multiscale Biharmonic Kernels", 2011.
+[#sorkine_2004]: Olga Sorkine, Yaron Lipman, Daniel Cohen-Or, Marc Alexa,
+Christian Rössl and Hans-Peter Seidel. "Laplacian Surface Editing," 2004.