Browse Source

Text Rendering Using GLSL in ImGui Menu (#1549)

* This is the code that renders the letter a for each vertex on the mesh paired with  a libigl-example-project

* Code for rendering just the number 12 on the 12th vertex

* Rendering vertices via glsl works, with geometry shader

* Added dynamic font scaling so accomodate ortho and perspective projections.

* Got faceids rendering

* Code cleanup

* This allows user to add custom text at some location rendered with the text shaders

* Dedicated separate VBOs for face and vertid labels

* Added in extra label rendering

* Moved texture binding to its own function and other code cleanup

* More conflict resolution when fetching upstream changes

* Resolved the relative path to font atlas

* Change scope variable for font atlas macro

* Removed PNG texture loaded from disk, and encoded font atlas into byte array. Other minor changes for VBO iterators and removed uneeded dependencies from previous commits

* Encapsulated text VBOs into struct to remove code duplication, and fixed bug for both overlay and text shaders being used at same time.

* Remove whitespace

* Tentative for Windows build fix not initializing TextGL structs in header

* Removed uniform that only belongs for text labels

* Added init and free methods for label buffers initialization and passing some args by reference on label methods

* Compressed font atlas that is under 50kB reduced from 330kB with decompression function

* Converting type from string literal to char array

* Broke up string literal

* Cleanup font atlas.

* Linux compile fix.

* Remove deprecated text pipeline.

Co-authored-by: Jérémie Dumas <[email protected]>
michelle 5 years ago
parent
commit
b8ee8b99cf

+ 159 - 0
include/igl/opengl/MeshGL.cpp

@@ -10,6 +10,7 @@
 #include "bind_vertex_attrib_array.h"
 #include "bind_vertex_attrib_array.h"
 #include "create_shader_program.h"
 #include "create_shader_program.h"
 #include "destroy_shader_program.h"
 #include "destroy_shader_program.h"
+#include "verasansmono_compressed.h"
 #include <iostream>
 #include <iostream>
 
 
 IGL_INLINE igl::opengl::MeshGL::MeshGL():
 IGL_INLINE igl::opengl::MeshGL::MeshGL():
@@ -31,6 +32,7 @@ IGL_INLINE void igl::opengl::MeshGL::init_buffers()
   glGenBuffers(1, &vbo_V_uv);
   glGenBuffers(1, &vbo_V_uv);
   glGenBuffers(1, &vbo_F);
   glGenBuffers(1, &vbo_F);
   glGenTextures(1, &vbo_tex);
   glGenTextures(1, &vbo_tex);
+  glGenTextures(1, &font_atlas);
 
 
   // Line overlay
   // Line overlay
   glGenVertexArrays(1, &vao_overlay_lines);
   glGenVertexArrays(1, &vao_overlay_lines);
@@ -46,6 +48,11 @@ IGL_INLINE void igl::opengl::MeshGL::init_buffers()
   glGenBuffers(1, &vbo_points_V);
   glGenBuffers(1, &vbo_points_V);
   glGenBuffers(1, &vbo_points_V_colors);
   glGenBuffers(1, &vbo_points_V_colors);
 
 
+  // Text Labels
+  vertex_labels.init_buffers();
+  face_labels.init_buffers();
+  custom_labels.init_buffers();
+
   dirty = MeshGL::DIRTY_ALL;
   dirty = MeshGL::DIRTY_ALL;
 }
 }
 
 
@@ -71,10 +78,34 @@ IGL_INLINE void igl::opengl::MeshGL::free_buffers()
     glDeleteBuffers(1, &vbo_points_V);
     glDeleteBuffers(1, &vbo_points_V);
     glDeleteBuffers(1, &vbo_points_V_colors);
     glDeleteBuffers(1, &vbo_points_V_colors);
 
 
+    // Text Labels
+    vertex_labels.free_buffers();
+    face_labels.free_buffers();
+    custom_labels.free_buffers();
+
     glDeleteTextures(1, &vbo_tex);
     glDeleteTextures(1, &vbo_tex);
+    glDeleteTextures(1, &font_atlas);
   }
   }
 }
 }
 
 
+IGL_INLINE void igl::opengl::MeshGL::TextGL::init_buffers()
+{
+  glGenVertexArrays(1, &vao_labels);
+  glBindVertexArray(vao_labels);
+  glGenBuffers(1, &vbo_labels_pos);
+  glGenBuffers(1, &vbo_labels_characters);
+  glGenBuffers(1, &vbo_labels_offset);
+  glGenBuffers(1, &vbo_labels_indices);
+}
+
+IGL_INLINE void igl::opengl::MeshGL::TextGL::free_buffers()
+{
+  glDeleteBuffers(1, &vbo_labels_pos);
+  glDeleteBuffers(1, &vbo_labels_characters);
+  glDeleteBuffers(1, &vbo_labels_offset);
+  glDeleteBuffers(1, &vbo_labels_indices);
+}
+
 IGL_INLINE void igl::opengl::MeshGL::bind_mesh()
 IGL_INLINE void igl::opengl::MeshGL::bind_mesh()
 {
 {
   glBindVertexArray(vao_mesh);
   glBindVertexArray(vao_mesh);
@@ -116,7 +147,9 @@ IGL_INLINE void igl::opengl::MeshGL::bind_overlay_lines()
 
 
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_lines_F);
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_lines_F);
   if (is_dirty)
   if (is_dirty)
+  {
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*lines_F_vbo.size(), lines_F_vbo.data(), GL_DYNAMIC_DRAW);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*lines_F_vbo.size(), lines_F_vbo.data(), GL_DYNAMIC_DRAW);
+  }
 
 
   dirty &= ~MeshGL::DIRTY_OVERLAY_LINES;
   dirty &= ~MeshGL::DIRTY_OVERLAY_LINES;
 }
 }
@@ -132,11 +165,50 @@ IGL_INLINE void igl::opengl::MeshGL::bind_overlay_points()
 
 
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_points_F);
   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_points_F);
   if (is_dirty)
   if (is_dirty)
+  {
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*points_F_vbo.size(), points_F_vbo.data(), GL_DYNAMIC_DRAW);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*points_F_vbo.size(), points_F_vbo.data(), GL_DYNAMIC_DRAW);
+  }
 
 
   dirty &= ~MeshGL::DIRTY_OVERLAY_POINTS;
   dirty &= ~MeshGL::DIRTY_OVERLAY_POINTS;
 }
 }
 
 
+IGL_INLINE void igl::opengl::MeshGL::init_text_rendering()
+{
+  // Decompress the png of the font atlas
+  unsigned char verasansmono_font_atlas[256*256];
+  decompress_verasansmono_atlas(verasansmono_font_atlas);
+
+  // Bind atlas
+  glBindTexture(GL_TEXTURE_2D, font_atlas);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 256, 256, 0, GL_RED, GL_UNSIGNED_BYTE, verasansmono_font_atlas);
+
+  // TextGL initialization
+  vertex_labels.dirty_flag = MeshGL::DIRTY_VERTEX_LABELS;
+  face_labels.dirty_flag = MeshGL::DIRTY_FACE_LABELS;
+  custom_labels.dirty_flag = MeshGL::DIRTY_CUSTOM_LABELS;
+}
+
+IGL_INLINE void igl::opengl::MeshGL::bind_labels(const TextGL& labels)
+{
+  bool is_dirty = dirty & labels.dirty_flag;
+  glBindTexture(GL_TEXTURE_2D, font_atlas);
+  glBindVertexArray(labels.vao_labels);
+  glUseProgram(shader_text);
+  bind_vertex_attrib_array(shader_text, "position" , labels.vbo_labels_pos       , labels.label_pos_vbo   , is_dirty);
+  bind_vertex_attrib_array(shader_text, "character", labels.vbo_labels_characters, labels.label_char_vbo  , is_dirty);
+  bind_vertex_attrib_array(shader_text, "offset"   , labels.vbo_labels_offset    , labels.label_offset_vbo, is_dirty);
+  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, labels.vbo_labels_indices);
+  if (is_dirty)
+  {
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned)*labels.label_indices_vbo.size(), labels.label_indices_vbo.data(), GL_DYNAMIC_DRAW);
+  }
+  dirty &= ~labels.dirty_flag;
+}
+
 IGL_INLINE void igl::opengl::MeshGL::draw_mesh(bool solid)
 IGL_INLINE void igl::opengl::MeshGL::draw_mesh(bool solid)
 {
 {
   glPolygonMode(GL_FRONT_AND_BACK, solid ? GL_FILL : GL_LINE);
   glPolygonMode(GL_FRONT_AND_BACK, solid ? GL_FILL : GL_LINE);
@@ -163,6 +235,11 @@ IGL_INLINE void igl::opengl::MeshGL::draw_overlay_points()
   glDrawElements(GL_POINTS, points_F_vbo.rows(), GL_UNSIGNED_INT, 0);
   glDrawElements(GL_POINTS, points_F_vbo.rows(), GL_UNSIGNED_INT, 0);
 }
 }
 
 
+IGL_INLINE void igl::opengl::MeshGL::draw_labels(const TextGL& labels)
+{
+  glDrawElements(GL_POINTS, labels.label_indices_vbo.rows(), GL_UNSIGNED_INT, 0);
+}
+
 IGL_INLINE void igl::opengl::MeshGL::init()
 IGL_INLINE void igl::opengl::MeshGL::init()
 {
 {
   if(is_initialized)
   if(is_initialized)
@@ -289,7 +366,82 @@ R"(#version 150
   }
   }
 )";
 )";
 
 
+  std::string text_vert_shader =
+R"(#version 330
+    in vec3 position;
+    in float character;
+    in float offset;
+    uniform mat4 view;
+    uniform mat4 proj;
+    out int vPosition;
+    out int vCharacter;
+    out float vOffset;
+    void main()
+    {
+      vCharacter = int(character);
+      vOffset = offset;
+      vPosition = gl_VertexID;
+      gl_Position = proj * view * vec4(position, 1.0);
+    }
+)";
+
+  std::string text_geom_shader =
+R"(#version 150 core
+    layout(points) in;
+    layout(triangle_strip, max_vertices = 4) out;
+    out vec2 gTexCoord;
+    uniform mat4 view;
+    uniform mat4 proj;
+    uniform vec2 CellSize;
+    uniform vec2 CellOffset;
+    uniform vec2 RenderSize;
+    uniform vec2 RenderOrigin;
+    uniform float TextShiftFactor;
+    in int vPosition[1];
+    in int vCharacter[1];
+    in float vOffset[1];
+    void main()
+    {
+      // Code taken from https://prideout.net/strings-inside-vertex-buffers
+      // Determine the final quad's position and size:
+      vec4 P = gl_in[0].gl_Position + vec4( vOffset[0]*TextShiftFactor, 0.0, 0.0, 0.0 ); // 0.04
+      vec4 U = vec4(1, 0, 0, 0) * RenderSize.x; // 1.0
+      vec4 V = vec4(0, 1, 0, 0) * RenderSize.y; // 1.0
+
+      // Determine the texture coordinates:
+      int letter = vCharacter[0]; // used to be the character
+      letter = clamp(letter - 32, 0, 96);
+      int row = letter / 16 + 1;
+      int col = letter % 16;
+      float S0 = CellOffset.x + CellSize.x * col;
+      float T0 = CellOffset.y + 1 - CellSize.y * row;
+      float S1 = S0 + CellSize.x - CellOffset.x;
+      float T1 = T0 + CellSize.y;
+
+      // Output the quad's vertices:
+      gTexCoord = vec2(S0, T1); gl_Position = P - U - V; EmitVertex();
+      gTexCoord = vec2(S1, T1); gl_Position = P + U - V; EmitVertex();
+      gTexCoord = vec2(S0, T0); gl_Position = P - U + V; EmitVertex();
+      gTexCoord = vec2(S1, T0); gl_Position = P + U + V; EmitVertex();
+      EndPrimitive();
+    }
+)";
+
+  std::string text_frag_shader =
+R"(#version 330
+    out vec4 outColor;
+    in vec2 gTexCoord;
+    uniform sampler2D font_atlas;
+    uniform vec3 TextColor;
+    void main()
+    {
+      float A = texture(font_atlas, gTexCoord).r;
+      outColor = vec4(TextColor, A);
+    }
+)";
+
   init_buffers();
   init_buffers();
+  init_text_rendering();
   create_shader_program(
   create_shader_program(
     mesh_vertex_shader_string,
     mesh_vertex_shader_string,
     mesh_fragment_shader_string,
     mesh_fragment_shader_string,
@@ -305,6 +457,12 @@ R"(#version 150
     overlay_point_fragment_shader_string,
     overlay_point_fragment_shader_string,
     {},
     {},
     shader_overlay_points);
     shader_overlay_points);
+  create_shader_program(
+    text_geom_shader,
+    text_vert_shader,
+    text_frag_shader,
+    {},
+    shader_text);
 }
 }
 
 
 IGL_INLINE void igl::opengl::MeshGL::free()
 IGL_INLINE void igl::opengl::MeshGL::free()
@@ -323,6 +481,7 @@ IGL_INLINE void igl::opengl::MeshGL::free()
     free(shader_mesh);
     free(shader_mesh);
     free(shader_overlay_lines);
     free(shader_overlay_lines);
     free(shader_overlay_points);
     free(shader_overlay_points);
+    free(shader_text);
     free_buffers();
     free_buffers();
   }
   }
 }
 }

+ 31 - 1
include/igl/opengl/MeshGL.h

@@ -40,7 +40,10 @@ public:
     DIRTY_MESH           = 0x00FF,
     DIRTY_MESH           = 0x00FF,
     DIRTY_OVERLAY_LINES  = 0x0100,
     DIRTY_OVERLAY_LINES  = 0x0100,
     DIRTY_OVERLAY_POINTS = 0x0200,
     DIRTY_OVERLAY_POINTS = 0x0200,
-    DIRTY_ALL            = 0x03FF
+    DIRTY_VERTEX_LABELS  = 0x0400,
+    DIRTY_FACE_LABELS    = 0x0800,
+    DIRTY_CUSTOM_LABELS  = 0x1000,
+    DIRTY_ALL            = 0xFFFF
   };
   };
 
 
   bool is_initialized = false;
   bool is_initialized = false;
@@ -50,6 +53,7 @@ public:
   GLuint shader_mesh;
   GLuint shader_mesh;
   GLuint shader_overlay_lines;
   GLuint shader_overlay_lines;
   GLuint shader_overlay_points;
   GLuint shader_overlay_points;
+  GLuint shader_text;
 
 
   GLuint vbo_V; // Vertices of the current mesh (#V x 3)
   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_uv; // UV coordinates for the current mesh (#V x 2)
@@ -81,6 +85,27 @@ public:
   RowMatrixXf points_V_vbo;
   RowMatrixXf points_V_vbo;
   RowMatrixXf points_V_colors_vbo;
   RowMatrixXf points_V_colors_vbo;
 
 
+  // Text Rendering
+  struct TextGL
+  { 
+    uint32_t dirty_flag;
+    GLuint vao_labels;
+    GLuint vbo_labels_pos;
+    GLuint vbo_labels_characters;
+    GLuint vbo_labels_offset; 
+    GLuint vbo_labels_indices;
+    RowMatrixXf label_pos_vbo;
+    RowMatrixXf label_char_vbo;
+    RowMatrixXf label_offset_vbo;
+    Eigen::Matrix<unsigned, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> label_indices_vbo;
+    void init_buffers();
+    void free_buffers();
+  };
+  TextGL vertex_labels;
+  TextGL face_labels;  
+  TextGL custom_labels;
+  GLuint font_atlas;
+
   int tex_u;
   int tex_u;
   int tex_v;
   int tex_v;
   GLint tex_filter;
   GLint tex_filter;
@@ -123,6 +148,11 @@ public:
   /// Draw the currently buffered point overlay
   /// Draw the currently buffered point overlay
   IGL_INLINE void draw_overlay_points();
   IGL_INLINE void draw_overlay_points();
 
 
+  // Text Binding and Draw functions
+  IGL_INLINE void init_text_rendering();
+  IGL_INLINE void bind_labels(const TextGL& labels);
+  IGL_INLINE void draw_labels(const TextGL& labels);
+
   // Release the OpenGL buffer objects
   // Release the OpenGL buffer objects
   IGL_INLINE void free_buffers();
   IGL_INLINE void free_buffers();
 
 

+ 34 - 2
include/igl/opengl/ViewerCore.cpp

@@ -238,13 +238,17 @@ IGL_INLINE void igl::opengl::ViewerCore::draw(
       glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data());
       glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data());
       glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data());
       glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data());
       glPointSize(data.point_size);
       glPointSize(data.point_size);
-
       data.meshgl.draw_overlay_points();
       data.meshgl.draw_overlay_points();
     }
     }
-
     glEnable(GL_DEPTH_TEST);
     glEnable(GL_DEPTH_TEST);
   }
   }
 
 
+  if(is_set(data.show_vertex_labels)&&data.vertex_labels_positions.rows()>0) 
+    draw_labels(data, data.meshgl.vertex_labels);
+  if(is_set(data.show_face_labels)&&data.face_labels_positions.rows()>0) 
+    draw_labels(data, data.meshgl.face_labels);
+  if(is_set(data.show_custom_labels)&&data.labels_positions.rows()>0) 
+    draw_labels(data, data.meshgl.custom_labels);
 }
 }
 
 
 IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(ViewerData& data,
 IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(ViewerData& data,
@@ -344,6 +348,34 @@ IGL_INLINE void igl::opengl::ViewerCore::draw_buffer(ViewerData& data,
   free(pixels);
   free(pixels);
 }
 }
 
 
+// Define uniforms for text labels
+IGL_INLINE void igl::opengl::ViewerCore::draw_labels(
+  ViewerData& data,
+  const igl::opengl::MeshGL::TextGL& labels
+){
+  glDisable(GL_LINE_SMOOTH); // Clear settings if overlay is activated
+  data.meshgl.bind_labels(labels);
+  GLint viewi = glGetUniformLocation(data.meshgl.shader_text,"view");
+  GLint proji = glGetUniformLocation(data.meshgl.shader_text,"proj");
+  glUniformMatrix4fv(viewi, 1, GL_FALSE, view.data());
+  glUniformMatrix4fv(proji, 1, GL_FALSE, proj.data());
+  // Parameters for mapping characters from font atlass
+  float width  = viewport(2);
+  float height = viewport(3);
+  float text_shift_scale_factor = orthographic ? 0.01 : 0.03;
+  float render_scale = orthographic ? 0.6 : 1.7;
+  glUniform1f(glGetUniformLocation(data.meshgl.shader_text, "TextShiftFactor"), text_shift_scale_factor);
+  glUniform3f(glGetUniformLocation(data.meshgl.shader_text, "TextColor"), 0, 0, 0);
+  glUniform2f(glGetUniformLocation(data.meshgl.shader_text, "CellSize"), 1.0f / 16, (300.0f / 384) / 6);
+  glUniform2f(glGetUniformLocation(data.meshgl.shader_text, "CellOffset"), 0.5 / 256.0, 0.5 / 256.0);
+  glUniform2f(glGetUniformLocation(data.meshgl.shader_text, "RenderSize"), 
+                                    render_scale * 0.75 * 16 / (width), 
+                                    render_scale * 0.75 * 33.33 / (height));
+  glUniform2f(glGetUniformLocation(data.meshgl.shader_text, "RenderOrigin"), -2, 2);
+  data.meshgl.draw_labels(labels);
+  glEnable(GL_DEPTH_TEST);
+}
+
 IGL_INLINE void igl::opengl::ViewerCore::set_rotation_type(
 IGL_INLINE void igl::opengl::ViewerCore::set_rotation_type(
   const igl::opengl::ViewerCore::RotationType & value)
   const igl::opengl::ViewerCore::RotationType & value)
 {
 {

+ 4 - 0
include/igl/opengl/ViewerCore.h

@@ -80,6 +80,10 @@ public:
     Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
     Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& G,
     Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B,
     Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& B,
     Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A);
     Eigen::Matrix<unsigned char,Eigen::Dynamic,Eigen::Dynamic>& A);
+  IGL_INLINE void draw_labels(
+    ViewerData& data,
+    const igl::opengl::MeshGL::TextGL& labels
+  );
 
 
   // Trackball angle (quaternion)
   // Trackball angle (quaternion)
   enum RotationType
   enum RotationType

+ 102 - 4
include/igl/opengl/ViewerData.cpp

@@ -28,9 +28,9 @@ IGL_INLINE igl::opengl::ViewerData::ViewerData()
   invert_normals    (false),
   invert_normals    (false),
   show_overlay      (~unsigned(0)),
   show_overlay      (~unsigned(0)),
   show_overlay_depth(~unsigned(0)),
   show_overlay_depth(~unsigned(0)),
-  show_vertid       (false),
-  show_faceid       (false),
-  show_labels       (false),
+  show_vertex_labels(0),
+  show_face_labels  (0),
+  show_custom_labels(0),
   show_texture      (false),
   show_texture      (false),
   use_matcap        (false),
   use_matcap        (false),
   point_size(30),
   point_size(30),
@@ -191,7 +191,7 @@ IGL_INLINE void igl::opengl::ViewerData::set_colors(const Eigen::MatrixXd &C)
   }
   }
   else if(C.rows() == V.rows() || C.rows() == F.rows())
   else if(C.rows() == V.rows() || C.rows() == F.rows())
   {
   {
-    // face based colors? 
+    // face based colors?
     if((C.rows()==F.rows()) && (C.rows() != V.rows() || face_based))
     if((C.rows()==F.rows()) && (C.rows() != V.rows() || face_based))
     {
     {
       set_face_based(true);
       set_face_based(true);
@@ -417,6 +417,8 @@ IGL_INLINE void igl::opengl::ViewerData::add_label(const Eigen::VectorXd& P,  co
   labels_positions.conservativeResize(lastid+1, 3);
   labels_positions.conservativeResize(lastid+1, 3);
   labels_positions.row(lastid) = P_temp;
   labels_positions.row(lastid) = P_temp;
   labels_strings.push_back(str);
   labels_strings.push_back(str);
+
+  dirty |= MeshGL::DIRTY_CUSTOM_LABELS;
 }
 }
 
 
 IGL_INLINE void igl::opengl::ViewerData::set_labels(const Eigen::MatrixXd& P, const std::vector<std::string>& str)
 IGL_INLINE void igl::opengl::ViewerData::set_labels(const Eigen::MatrixXd& P, const std::vector<std::string>& str)
@@ -454,7 +456,12 @@ IGL_INLINE void igl::opengl::ViewerData::clear()
 
 
   lines                   = Eigen::MatrixXd (0,9);
   lines                   = Eigen::MatrixXd (0,9);
   points                  = Eigen::MatrixXd (0,6);
   points                  = Eigen::MatrixXd (0,6);
+
+  vertex_labels_positions = Eigen::MatrixXd (0,3);
+  face_labels_positions   = Eigen::MatrixXd (0,3);
   labels_positions        = Eigen::MatrixXd (0,3);
   labels_positions        = Eigen::MatrixXd (0,3);
+  vertex_labels_strings.clear();
+  face_labels_strings.clear();
   labels_strings.clear();
   labels_strings.clear();
 
 
   face_based = false;
   face_based = false;
@@ -562,6 +569,41 @@ IGL_INLINE void igl::opengl::ViewerData::grid_texture()
   dirty |= MeshGL::DIRTY_TEXTURE;
   dirty |= MeshGL::DIRTY_TEXTURE;
 }
 }
 
 
+// Populate VBOs of a particular label stype (Vert, Face, Custom)
+IGL_INLINE void igl::opengl::ViewerData::update_labels(
+  igl::opengl::MeshGL& meshgl,
+  igl::opengl::MeshGL::TextGL& GL_labels,
+  const Eigen::MatrixXd& positions,
+  const std::vector<std::string>& strings
+){
+  if (positions.rows()>0)
+  {
+    int numCharsToRender = 0;
+    for(size_t p=0; p<positions.rows(); p++)
+    {
+      numCharsToRender += strings.at(p).length();
+    }
+    GL_labels.label_pos_vbo.resize(numCharsToRender, 3);
+    GL_labels.label_char_vbo.resize(numCharsToRender, 1);
+    GL_labels.label_offset_vbo.resize(numCharsToRender, 1);
+    GL_labels.label_indices_vbo.resize(numCharsToRender, 1);
+    int idx=0;
+    assert(strings.size() == positions.rows());
+    for(size_t s=0; s<strings.size(); s++)
+    {
+      const auto & label = strings.at(s);
+      for(size_t c=0; c<label.length(); c++)
+      {
+        GL_labels.label_pos_vbo.row(idx) = positions.row(s).cast<float>();
+        GL_labels.label_char_vbo(idx) = (float)(label.at(c));
+        GL_labels.label_offset_vbo(idx) = c;
+        GL_labels.label_indices_vbo(idx) = idx;
+        idx++;
+      }
+    }
+  }
+}
+
 IGL_INLINE void igl::opengl::ViewerData::updateGL(
 IGL_INLINE void igl::opengl::ViewerData::updateGL(
   const igl::opengl::ViewerData& data,
   const igl::opengl::ViewerData& data,
   const bool invert_normals,
   const bool invert_normals,
@@ -795,4 +837,60 @@ IGL_INLINE void igl::opengl::ViewerData::updateGL(
       meshgl.points_F_vbo(i) = i;
       meshgl.points_F_vbo(i) = i;
     }
     }
   }
   }
+
+  if (meshgl.dirty & MeshGL::DIRTY_FACE_LABELS)
+  {
+    if(face_labels_positions.rows()==0)
+    {
+      face_labels_positions.conservativeResize(F.rows(), 3);
+      Eigen::MatrixXd faceNormals = F_normals.normalized();
+      for (int f=0; f<F.rows();++f)
+      {
+        std::string faceName = std::to_string(f);
+        face_labels_positions.row(f) = V.row(F.row(f)(0));
+        face_labels_positions.row(f) += V.row(F.row(f)(1));
+        face_labels_positions.row(f) += V.row(F.row(f)(2));
+        face_labels_positions.row(f) /= 3.;
+        face_labels_positions.row(f) = (faceNormals*0.05).row(f) + face_labels_positions.row(f);
+        face_labels_strings.push_back(faceName);
+      }
+    }
+    update_labels(
+      meshgl,
+      meshgl.face_labels,
+      face_labels_positions,
+      face_labels_strings
+    );
+  }
+
+  if (meshgl.dirty & MeshGL::DIRTY_VERTEX_LABELS)
+  {
+    if(vertex_labels_positions.rows()==0)
+    {
+      vertex_labels_positions.conservativeResize(V.rows(), 3);
+      Eigen::MatrixXd normalized = V_normals.normalized();
+      for (int v=0; v<V.rows();++v)
+      {
+        std::string vertName = std::to_string(v);
+        vertex_labels_positions.row(v) = (normalized*0.1).row(v) + V.row(v);
+        vertex_labels_strings.push_back(vertName);
+      }
+    }
+    update_labels(
+      meshgl,
+      meshgl.vertex_labels,
+      vertex_labels_positions,
+      vertex_labels_strings
+    );
+  }
+
+  if (meshgl.dirty & MeshGL::DIRTY_CUSTOM_LABELS)
+  {
+    update_labels(
+      meshgl,
+      meshgl.custom_labels,
+      labels_positions,
+      labels_strings
+    );
+  }
 }
 }

+ 17 - 7
include/igl/opengl/ViewerData.h

@@ -234,7 +234,11 @@ public:
   // Text labels plotted over the scene
   // 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
   // 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
   // Texts contains in the i-th position the text of the i-th label
+  Eigen::MatrixXd           vertex_labels_positions;
+  Eigen::MatrixXd           face_labels_positions;
   Eigen::MatrixXd           labels_positions;
   Eigen::MatrixXd           labels_positions;
+  std::vector<std::string>  vertex_labels_strings;
+  std::vector<std::string>  face_labels_strings;
   std::vector<std::string>  labels_strings;
   std::vector<std::string>  labels_strings;
 
 
   // Marks dirty buffers that need to be uploaded to OpenGL
   // Marks dirty buffers that need to be uploaded to OpenGL
@@ -259,9 +263,9 @@ public:
   unsigned int use_matcap;
   unsigned int use_matcap;
   unsigned int show_faces;
   unsigned int show_faces;
   unsigned int show_lines;
   unsigned int show_lines;
-  bool show_vertid; // shared across viewports for now
-  bool show_faceid; // shared across viewports for now
-  bool show_labels; // shared across viewports for now
+  unsigned int show_vertex_labels;
+  unsigned int show_face_labels;
+  unsigned int show_custom_labels;
 
 
   // Point size / line width
   // Point size / line width
   float point_size;
   float point_size;
@@ -279,11 +283,17 @@ public:
   igl::opengl::MeshGL meshgl;
   igl::opengl::MeshGL meshgl;
 
 
   // Update contents from a 'Data' instance
   // Update contents from a 'Data' instance
+  IGL_INLINE void update_labels(
+    igl::opengl::MeshGL& meshgl,
+    igl::opengl::MeshGL::TextGL& GL_labels,
+    const Eigen::MatrixXd& positions,
+    const std::vector<std::string>& strings
+  );
   IGL_INLINE void updateGL(
   IGL_INLINE void updateGL(
     const igl::opengl::ViewerData& data,
     const igl::opengl::ViewerData& data,
     const bool invert_normals,
     const bool invert_normals,
     igl::opengl::MeshGL& meshgl);
     igl::opengl::MeshGL& meshgl);
-};
+  };
 
 
 } // namespace opengl
 } // namespace opengl
 } // namespace igl
 } // namespace igl
@@ -324,9 +334,9 @@ namespace igl
       SERIALIZE_MEMBER(invert_normals);
       SERIALIZE_MEMBER(invert_normals);
       SERIALIZE_MEMBER(show_overlay);
       SERIALIZE_MEMBER(show_overlay);
       SERIALIZE_MEMBER(show_overlay_depth);
       SERIALIZE_MEMBER(show_overlay_depth);
-      SERIALIZE_MEMBER(show_vertid);
-      SERIALIZE_MEMBER(show_faceid);
-      SERIALIZE_MEMBER(show_labels);
+      SERIALIZE_MEMBER(show_vertex_labels);
+      SERIALIZE_MEMBER(show_face_labels);
+      SERIALIZE_MEMBER(show_custom_labels);
       SERIALIZE_MEMBER(show_texture);
       SERIALIZE_MEMBER(show_texture);
       SERIALIZE_MEMBER(double_sided);
       SERIALIZE_MEMBER(double_sided);
       SERIALIZE_MEMBER(point_size);
       SERIALIZE_MEMBER(point_size);

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

@@ -592,10 +592,10 @@ namespace glfw
         return true;
         return true;
       }
       }
       case ';':
       case ';':
-        data().show_vertid = !data().show_vertid;
+        data().show_vertex_labels = !data().show_vertex_labels;
         return true;
         return true;
       case ':':
       case ':':
-        data().show_faceid = !data().show_faceid;
+        data().show_face_labels = !data().show_face_labels;
         return true;
         return true;
       default: break;//do nothing
       default: break;//do nothing
     }
     }

+ 3 - 101
include/igl/opengl/glfw/imgui/ImGuiMenu.cpp

@@ -152,9 +152,6 @@ IGL_INLINE bool ImGuiMenu::key_up(int key, int modifiers)
 // Draw menu
 // Draw menu
 IGL_INLINE void ImGuiMenu::draw_menu()
 IGL_INLINE void ImGuiMenu::draw_menu()
 {
 {
-  // Text labels
-  draw_labels_window();
-
   // Viewer settings
   // Viewer settings
   if (callback_draw_viewer_window) { callback_draw_viewer_window(); }
   if (callback_draw_viewer_window) { callback_draw_viewer_window(); }
   else { draw_viewer_window(); }
   else { draw_viewer_window(); }
@@ -301,105 +298,10 @@ IGL_INLINE void ImGuiMenu::draw_viewer_menu()
   {
   {
     make_checkbox("Wireframe", viewer->data().show_lines);
     make_checkbox("Wireframe", viewer->data().show_lines);
     make_checkbox("Fill", viewer->data().show_faces);
     make_checkbox("Fill", viewer->data().show_faces);
-    ImGui::Checkbox("Show vertex labels", &(viewer->data().show_vertid));
-    ImGui::Checkbox("Show faces labels", &(viewer->data().show_faceid));
-    ImGui::Checkbox("Show extra labels", &(viewer->data().show_labels));
-  }
-}
-
-IGL_INLINE void ImGuiMenu::draw_labels_window()
-{
-  // Text labels
-  ImGui::SetNextWindowPos(ImVec2(0,0), ImGuiCond_Always);
-  ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize, ImGuiCond_Always);
-  bool visible = true;
-  ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0,0,0,0));
-  ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
-  ImGui::Begin("ViewerLabels", &visible,
-      ImGuiWindowFlags_NoTitleBar
-      | ImGuiWindowFlags_NoResize
-      | ImGuiWindowFlags_NoMove
-      | ImGuiWindowFlags_NoScrollbar
-      | ImGuiWindowFlags_NoScrollWithMouse
-      | ImGuiWindowFlags_NoCollapse
-      | ImGuiWindowFlags_NoSavedSettings
-      | ImGuiWindowFlags_NoInputs);
-  for (const auto & data : viewer->data_list)
-  {
-    draw_labels(data);
-  }
-  ImGui::End();
-  ImGui::PopStyleColor();
-  ImGui::PopStyleVar();
-}
-
-IGL_INLINE void ImGuiMenu::draw_labels(const igl::opengl::ViewerData &data)
-{
-  // Alec: How can we get these to respect (optionally) the depth of the scene?
-  if (data.show_vertid)
-  {
-    for (int i = 0; i < data.V.rows(); ++i)
-    {
-      draw_text(
-        data.V.row(i), 
-        data.V_normals.row(i), 
-        std::to_string(i),
-        data.label_color);
-    }
+    make_checkbox("Show vertex labels", viewer->data().show_vertex_labels);
+    make_checkbox("Show faces labels", viewer->data().show_face_labels);
+    make_checkbox("Show extra labels", viewer->data().show_custom_labels);
   }
   }
-
-  if (data.show_faceid)
-  {
-    for (int i = 0; i < data.F.rows(); ++i)
-    {
-      Eigen::RowVector3d p = Eigen::RowVector3d::Zero();
-      for (int j = 0; j < data.F.cols(); ++j)
-      {
-        p += data.V.row(data.F(i,j));
-      }
-      p /= (double) data.F.cols();
-
-      draw_text(
-        p, 
-        data.F_normals.row(i), 
-        std::to_string(i),
-        data.label_color);
-    }
-  }
-
-  if (data.show_labels)
-  {
-    for (int i = 0; i < data.labels_positions.rows(); ++i)
-    {
-      draw_text(
-        data.labels_positions.row(i), 
-        Eigen::Vector3d(0.0,0.0,0.0),
-        data.labels_strings[i],
-        data.label_color);
-    }
-  }
-}
-
-IGL_INLINE void ImGuiMenu::draw_text(
-  Eigen::Vector3d pos, 
-  Eigen::Vector3d normal, 
-  const std::string &text,
-  const Eigen::Vector4f color)
-{
-  pos += normal * 0.005f * viewer->core().object_scale;
-  Eigen::Vector3f coord = igl::project(Eigen::Vector3f(pos.cast<float>()),
-    viewer->core().view, viewer->core().proj, viewer->core().viewport);
-
-  // Draw text labels slightly bigger than normal text
-  ImDrawList* drawList = ImGui::GetWindowDrawList();
-  drawList->AddText(ImGui::GetFont(), ImGui::GetFontSize() * 1.2,
-      ImVec2(coord[0]/pixel_ratio_, (viewer->core().viewport[3] - coord[1])/pixel_ratio_),
-      ImGui::GetColorU32(ImVec4(
-        color(0),
-        color(1),
-        color(2),
-        color(3))),
-      &text[0], &text[0] + text.size());
 }
 }
 
 
 IGL_INLINE float ImGuiMenu::pixel_ratio()
 IGL_INLINE float ImGuiMenu::pixel_ratio()

+ 2 - 6
include/igl/opengl/glfw/imgui/ImGuiMenu.h

@@ -86,13 +86,9 @@ public:
   std::function<void(void)> callback_draw_viewer_menu;
   std::function<void(void)> callback_draw_viewer_menu;
   std::function<void(void)> callback_draw_custom_window;
   std::function<void(void)> callback_draw_custom_window;
 
 
-  IGL_INLINE void draw_labels_window();
-
-  IGL_INLINE void draw_labels(const igl::opengl::ViewerData &data);
-
   IGL_INLINE void draw_text(
   IGL_INLINE void draw_text(
-    Eigen::Vector3d pos, 
-    Eigen::Vector3d normal, 
+    Eigen::Vector3d pos,
+    Eigen::Vector3d normal,
     const std::string &text,
     const std::string &text,
     const Eigen::Vector4f color = Eigen::Vector4f(0,0,0.04,1)); // old default color
     const Eigen::Vector4f color = Eigen::Vector4f(0,0,0.04,1)); // old default color
 
 

File diff suppressed because it is too large
+ 17 - 0
include/igl/opengl/verasansmono_compressed.cpp


+ 21 - 0
include/igl/opengl/verasansmono_compressed.h

@@ -0,0 +1,21 @@
+#ifndef IGL_OPENGL_VERASANSMONO_COMPRESSED_H
+#define IGL_OPENGL_VERASANSMONO_COMPRESSED_H
+
+#include <igl/opengl/../igl_inline.h>
+
+namespace igl
+{
+namespace opengl
+{
+
+IGL_INLINE void decompress_verasansmono_atlas(unsigned char* _fontatlas);
+
+}
+
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "verasansmono_compressed.cpp"
+#endif
+
+#endif

+ 3 - 3
tutorial/105_Overlays/main.cpp

@@ -63,12 +63,12 @@ int main(int argc, char *argv[])
   // Plot labels with the coordinates of bounding box vertices
   // Plot labels with the coordinates of bounding box vertices
   std::stringstream l1;
   std::stringstream l1;
   l1 << m(0) << ", " << m(1) << ", " << m(2);
   l1 << m(0) << ", " << m(1) << ", " << m(2);
-  viewer.data().add_label(m,l1.str());
+  viewer.data().add_label(m+Eigen::Vector3d(-0.007, 0, 0),l1.str());
   std::stringstream l2;
   std::stringstream l2;
   l2 << M(0) << ", " << M(1) << ", " << M(2);
   l2 << M(0) << ", " << M(1) << ", " << M(2);
-  viewer.data().add_label(M,l2.str());
+  viewer.data().add_label(M+Eigen::Vector3d(0.007, 0, 0),l2.str());
   // activate label rendering
   // activate label rendering
-  viewer.data().show_labels = true;
+  viewer.data().show_custom_labels = true;
 
 
   // Rendering of text labels is handled by ImGui, so we need to enable the ImGui
   // Rendering of text labels is handled by ImGui, so we need to enable the ImGui
   // plugin to show text labels.
   // plugin to show text labels.

+ 1 - 0
tutorial/106_ViewerMenu/main.cpp

@@ -99,5 +99,6 @@ int main(int argc, char *argv[])
 
 
   // Plot the mesh
   // Plot the mesh
   viewer.data().set_mesh(V, F);
   viewer.data().set_mesh(V, F);
+  viewer.data().add_label(viewer.data().V.row(0) + viewer.data().V_normals.row(0).normalized()*0.005, "Hello World!");
   viewer.launch();
   viewer.launch();
 }
 }

Some files were not shown because too many files changed in this diff