Browse Source

Bump to OpenGL version 4.1 (#2277)

* bump glad

* fix name changes

* remove compilation guards

* bump runtime to 4.1 too

* fix map_texture; template bind

* doc

* attempt to skip tests

* that didnt work, disable with cmake

* missing template

* rm __1::
Alec Jacobson 2 years ago
parent
commit
cb111f6838

+ 2 - 0
.github/workflows/continuous.yml

@@ -73,6 +73,7 @@ jobs:
             -DCMAKE_BUILD_TYPE=${{ matrix.config }} \
             -DLIBIGL_USE_STATIC_LIBRARY=${{ matrix.build-params.static }} \
             -DLIBIGL_BUILD_TUTORIALS=${{ matrix.build-params.tutorials }} \
+            -DLIBIGL_GLFW_TESTS=OFF \
             -DLIBIGL_BUILD_TESTS=${{ matrix.build-params.tests }} \
             -DLIBIGL_COPYLEFT_CGAL=ON
 
@@ -145,6 +146,7 @@ jobs:
             -DLIBIGL_COPYLEFT_CGAL=ON ^
             -DLIBIGL_BUILD_TUTORIALS=${{ matrix.build-params.tutorials }} ^
             -DLIBIGL_BUILD_TESTS=${{ matrix.build-params.tests }} ^
+            -DLIBIGL_GLFW_TESTS=OFF ^
             -DLIBIGL_TUTORIALS_CHAPTER1=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '1') && 'ON' || 'OFF' }} ^
             -DLIBIGL_TUTORIALS_CHAPTER2=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '2') && 'ON' || 'OFF' }} ^
             -DLIBIGL_TUTORIALS_CHAPTER3=${{ (matrix.build-params.selected_tutorial == 'NONE' || matrix.build-params.selected_tutorial == '3') && 'ON' || 'OFF' }} ^

+ 5 - 0
CMakeLists.txt

@@ -121,6 +121,11 @@ option(LIBIGL_RESTRICTED_MATLAB   "Build target igl_restricted::matlab"   ${LIBI
 option(LIBIGL_RESTRICTED_MOSEK    "Build target igl_restricted::mosek"    ${LIBIGL_DEFAULT_MOSEK})
 option(LIBIGL_RESTRICTED_TRIANGLE "Build target igl_restricted::triangle" ${LIBIGL_TOPLEVEL_PROJECT})
 
+# GLFW doesn't run on headless CI machines so don't run (or build them).
+# Unfortunately on headless mac machines glfw seems to hang rather than crash
+# making it hard to catch at runtime.
+option(LIBIGL_GLFW_TESTS       "Build igl::glfw tests"             ${LIBIGL_TOPLEVEL_PROJECT})
+
 option(LIBIGL_WARNINGS_AS_ERRORS "Turn on many warnings and treat as errors" OFF)
 
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

+ 6 - 0
cmake/igl/modules/glfw.cmake

@@ -21,3 +21,9 @@ target_link_libraries(igl_glfw ${IGL_SCOPE}
     igl::opengl
     glfw::glfw
 )
+
+# 5. Unit tests
+if(LIBIGL_GLFW_TESTS)
+  file(GLOB SRC_FILES "${libigl_SOURCE_DIR}/tests/include/igl/opengl/glfw/*.cpp")
+  igl_add_test(igl_glfw ${SRC_FILES})
+endif()

+ 1 - 1
cmake/recipes/external/glad.cmake

@@ -8,7 +8,7 @@ include(FetchContent)
 FetchContent_Declare(
     glad
     GIT_REPOSITORY https://github.com/libigl/libigl-glad.git
-    GIT_TAG        ceef55fcd08bdd16e985370a99cfb60e69623221
+    GIT_TAG        ead2d21fd1d9f566d8f9a9ce99ddf85829258c7a
 )
 
 FetchContent_MakeAvailable(glad)

+ 59 - 6
include/igl/opengl/bind_vertex_attrib_array.cpp

@@ -1,11 +1,54 @@
 #include "bind_vertex_attrib_array.h"
 
-IGL_INLINE GLint igl::opengl::bind_vertex_attrib_array(
+namespace igl{ namespace opengl{
+// This would be cleaner with C++17 if constexpr
+template <typename Scalar>
+IGL_INLINE void bind_vertex_attrib_array_helper(
+  const GLint id, 
+  const int size, 
+  const int num_cols, 
+  const Scalar * data, 
+  const bool refresh);
+
+template <>
+IGL_INLINE void bind_vertex_attrib_array_helper<float>(
+  const GLint id, 
+  const int size, 
+  const int num_cols, 
+  const float * data, 
+  const bool refresh)
+{
+  if (refresh)
+    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*size, data, GL_DYNAMIC_DRAW);
+  glVertexAttribPointer(id, num_cols, GL_FLOAT, GL_FALSE, 0, 0);
+};
+
+template <>
+IGL_INLINE void bind_vertex_attrib_array_helper<double>(
+  const GLint id, 
+  const int size, 
+  const int num_cols, 
+  const double * data, 
+  const bool refresh)
+{
+  // Are you really sure you want to use doubles? Are you going to change the
+  // `in vec3` etc. in your vertex shader to `in dvec3` ?
+  // Are you on a mac, where this will be emulated in software?
+  if (refresh)
+    glBufferData(GL_ARRAY_BUFFER, sizeof(double)*size, data, GL_DYNAMIC_DRAW);
+  glVertexAttribLPointer(id, num_cols, GL_DOUBLE, 0, 0);
+};
+}}
+
+
+namespace igl{ namespace opengl{
+template <typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
+IGL_INLINE GLint bind_vertex_attrib_array(
   const GLuint program_shader,
   const std::string &name, 
   GLuint bufferID, 
-  const Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> &M, 
-  bool refresh)
+  const Eigen::Matrix<Scalar,RowsAtCompileTime,ColsAtCompileTime,Eigen::RowMajor> &M,
+  const bool refresh)
 {
   GLint id = glGetAttribLocation(program_shader, name.c_str());
   if (id < 0)
@@ -16,9 +59,19 @@ IGL_INLINE GLint igl::opengl::bind_vertex_attrib_array(
     return id;
   }
   glBindBuffer(GL_ARRAY_BUFFER, bufferID);
-  if (refresh)
-    glBufferData(GL_ARRAY_BUFFER, sizeof(float)*M.size(), M.data(), GL_DYNAMIC_DRAW);
-  glVertexAttribPointer(id, M.cols(), GL_FLOAT, GL_FALSE, 0, 0);
+
+  bind_vertex_attrib_array_helper<Scalar>(
+    id, M.size(), M.cols(), M.data(), refresh);
+
   glEnableVertexAttribArray(id);
   return id;
 }
+}}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+// generated by autoexplicit.sh
+template int igl::opengl::bind_vertex_attrib_array<float, -1, -1>(unsigned int, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int, Eigen::Matrix<float, -1, -1, 1, -1, -1> const&, bool);
+// generated by autoexplicit.sh
+template int igl::opengl::bind_vertex_attrib_array<float, -1, 3>(unsigned int, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int, Eigen::Matrix<float, -1, 3, 1, -1, 3> const&, bool);
+#endif

+ 3 - 2
include/igl/opengl/bind_vertex_attrib_array.h

@@ -17,12 +17,13 @@ namespace igl
     /// @param[in] M  #V by dim matrix of per-vertex data
     /// @param[in] refresh  whether to actually call glBufferData or just bind the buffer
     /// @return id of named attribute in shader
+    template <typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
     IGL_INLINE GLint bind_vertex_attrib_array(
       const GLuint program_shader,
       const std::string &name, 
       GLuint bufferID, 
-      const Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> &M, 
-      bool refresh);
+      const Eigen::Matrix<Scalar,RowsAtCompileTime,ColsAtCompileTime,Eigen::RowMajor> &M,
+      const bool refresh);
   }
 }
 #ifndef IGL_STATIC_LIBRARY

+ 1 - 1
include/igl/opengl/gl.h

@@ -20,6 +20,6 @@
 //     #include <GL/gl.h>
 //
 
-#include <glad/gl.h>
+#include <glad/glad.h>
 
 #endif

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

@@ -146,8 +146,8 @@ namespace glfw
       return EXIT_FAILURE;
     }
     glfwWindowHint(GLFW_SAMPLES, 8);
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
     #ifdef __APPLE__
       glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
       glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
@@ -181,7 +181,7 @@ namespace glfw
     }
     glfwMakeContextCurrent(window);
     // Load OpenGL and its extensions
-    if (!gladLoadGL((GLADloadfunc) glfwGetProcAddress))
+    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
     {
       printf("Failed to load OpenGL and its extensions\n");
       return(-1);

+ 4 - 4
include/igl/opengl/glfw/background_window.cpp

@@ -7,9 +7,9 @@ IGL_INLINE bool igl::opengl::glfw::background_window(GLFWwindow* & window)
   if(!glfwInit()) return false;
   glfwSetErrorCallback([](int /*id*/,const char* m){std::cerr<<m<<std::endl;});
   glfwWindowHint(GLFW_SAMPLES, 4);
-  // Use 3.2 core profile
-  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+  // Use 4.1 core profile
+  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
   glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
   glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
   // Use background window
@@ -17,7 +17,7 @@ IGL_INLINE bool igl::opengl::glfw::background_window(GLFWwindow* & window)
   window = glfwCreateWindow(1, 1,"", NULL, NULL);
   if(!window) return false;
   glfwMakeContextCurrent(window);
-  if (!gladLoadGL((GLADloadfunc) glfwGetProcAddress))
+  if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
   {
     printf("Failed to load OpenGL and its extensions");
   }

+ 67 - 109
include/igl/opengl/glfw/map_texture.cpp

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

+ 3 - 6
include/igl/opengl/glfw/map_texture.h

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

+ 83 - 0
tests/include/igl/opengl/glfw/map_texture.cpp

@@ -0,0 +1,83 @@
+#include <test_common.h>
+#include <igl/opengl/glfw/map_texture.h>
+
+TEST_CASE("map_texture: identity","[igl/glfw]") 
+{
+  // 2 triangle quad
+  Eigen::MatrixXd V(4,3);
+  V<<0,0,0,
+     1,0,0,
+     1,1,0,
+     0,1,0;
+  Eigen::MatrixXi F(2,3);
+  F<<0,1,2,
+     0,2,3;
+  // Random RGBA texture
+  const int in_w = 16;
+  const int in_h = 16;
+  const int in_nc = 4;
+  using ArrayXuc = Eigen::Array<unsigned char,Eigen::Dynamic,1>;
+  ArrayXuc in_rgba = ArrayXuc::Random(in_h*in_w*in_nc,1);
+  std::vector<unsigned char> out_rgba;
+  int out_w, out_h, out_nc;
+  igl::opengl::glfw::map_texture(
+    V,F,V,
+    in_rgba.data(),
+    in_w,in_h,in_nc,
+    out_rgba,
+    out_w,out_h,out_nc);
+  REQUIRE(out_w == in_w);
+  REQUIRE(out_h == in_h);
+  REQUIRE(out_nc == in_nc);
+  {
+    // Map in_rgb
+    Eigen::Map<ArrayXuc> out_rgba_map(out_rgba.data(),out_w*out_h*out_nc,1);
+    REQUIRE(out_rgba_map.isApprox(in_rgba,0));
+  }
+}
+
+TEST_CASE("map_texture: transpose","[igl/glfw]") 
+{
+  // 2 triangle quad
+  Eigen::MatrixXd V(4,3);
+  V<<0,0,0,
+     1,0,0,
+     1,1,0,
+     0,1,0;
+  Eigen::MatrixXi F(2,3);
+  F<<0,1,2,
+     0,2,3;
+  // Random RGBA texture
+  const int in_w = 16;
+  const int in_h = 16;
+  const int in_nc = 4;
+  using ArrayXuc = Eigen::Array<unsigned char,Eigen::Dynamic,1>;
+  ArrayXuc in_rgba = ArrayXuc::Random(in_h*in_w*in_nc,1);
+  std::vector<unsigned char> out_rgba;
+  int out_w, out_h, out_nc;
+  Eigen::MatrixXd U(4,3);
+  U<< 
+    1,1,0,
+    1,0,0,
+    0,0,0,
+    0,1,0;
+  igl::opengl::glfw::map_texture(
+    V,F,U,
+    in_rgba.data(),
+    in_w,in_h,in_nc,
+    out_rgba,
+    out_w,out_h,out_nc);
+  REQUIRE(out_w == in_w);
+  REQUIRE(out_h == in_h);
+  REQUIRE(out_nc == in_nc);
+  {
+    // Treat each 4 unsigned chars as a single int32_t and then transpose
+    using FourChars = std::int32_t;
+    Eigen::Map<Eigen::Array<FourChars,Eigen::Dynamic,Eigen::Dynamic>>
+      pixel_map(reinterpret_cast<FourChars*>(out_rgba.data()), out_w,out_h);
+    pixel_map.transposeInPlace();
+    // Array of chars
+    Eigen::Map<ArrayXuc> out_rgba_map(out_rgba.data(),out_w*out_h*out_nc,1);
+    REQUIRE(out_rgba_map.isApprox(in_rgba,0));
+  }
+}