From 827c528708e67af15cccd3db56b825993ac22939 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gr=C3=A9goire=20Grzeczkowicz?= <gregoire@ggrz.fr>
Date: Fri, 11 Jun 2021 18:19:59 +0200
Subject: [PATCH] Read TextureFile header, face texcoord and texnumber for
 ASCII and binary PLY files
To: openmesh@lists.rwth-aachen.de

---
 src/OpenMesh/Core/IO/reader/PLYReader.cc      | 142 +++++++++++++-
 src/OpenMesh/Core/IO/reader/PLYReader.hh      |   5 +-
 .../meshlab-faceTexCoords-binary.ply          | Bin 0 -> 913 bytes
 .../TestFiles/meshlab-faceTexCoords.ply       |  34 ++++
 src/Unittests/unittests_read_write_PLY.cc     | 185 ++++++++++++++++++
 5 files changed, 360 insertions(+), 6 deletions(-)
 create mode 100644 src/Unittests/TestFiles/meshlab-faceTexCoords-binary.ply
 create mode 100644 src/Unittests/TestFiles/meshlab-faceTexCoords.ply

diff --git a/src/OpenMesh/Core/IO/reader/PLYReader.cc b/src/OpenMesh/Core/IO/reader/PLYReader.cc
index 84d9fd78..aa99214a 100644
--- a/src/OpenMesh/Core/IO/reader/PLYReader.cc
+++ b/src/OpenMesh/Core/IO/reader/PLYReader.cc
@@ -135,7 +135,11 @@ bool _PLYReader_::read(std::istream& _in, BaseImporter& _bi, Options& _opt) {
         return false;
     }
 
-
+    // Add TextureFiles
+    int texture_index = 0;
+    for(auto texture_file = texture_files_.begin(); texture_file != texture_files_.end(); texture_file++) {
+      _bi.add_texture_information(texture_index++, *texture_file);
+    }
 
     // filter relevant options for reading
     bool swap = _opt.check(Options::Swap);
@@ -157,6 +161,9 @@ bool _PLYReader_::read(std::istream& _in, BaseImporter& _bi, Options& _opt) {
     if (options_.face_has_color() && userOptions_.face_has_color()) {
         _opt += Options::FaceColor;
     }
+    if (options_.face_has_texcoord() && userOptions_.face_has_texcoord()) {
+        _opt += Options::FaceTexCoord;
+    }
     if (options_.is_binary()) {
         _opt += Options::Binary;
     }
@@ -282,12 +289,14 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options
 
     unsigned int i, j, k, l, idx;
     unsigned int nV;
-    OpenMesh::Vec3f v, n;
+    OpenMesh::Vec3f v, n, t3d;
     std::string trash;
     OpenMesh::Vec2f t;
     OpenMesh::Vec4i c;
     float tmp;
     BaseImporter::VHandles vhandles;
+    std::vector<Vec3f> face_texcoords3d;
+    std::vector<Vec2f> face_texcoords;
     VertexHandle vh;
 
     _bi.reserve(vertexCount_, 3* vertexCount_ , faceCount_);
@@ -304,7 +313,7 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options
 
 	for (std::vector<ElementInfo>::iterator e_it = elements_.begin(); e_it != elements_.end(); ++e_it)
 	{
-	        if (_in.eof()) {
+	  if (_in.eof()) {
 			if (err_enabled)
 				omerr().enable();
 
@@ -460,6 +469,61 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options
 							++complex_faces;
 						break;
 
+					case TEXCOORD:
+            // nV = number of texcoord for current face
+            _in >> nV;
+            
+            if (_opt.face_has_texcoord()) {
+
+              // need to know the number of vertex for this face
+              assert(!vhandles.empty());
+
+              if (nV == 2 * vhandles.size()) {
+
+                face_texcoords.clear();
+                face_texcoords.reserve(vhandles.size());
+
+                for (j = 0; j < vhandles.size(); j++) {
+                  _in >> t[0];
+                  _in >> t[1];
+
+                  face_texcoords.push_back(t);
+                }
+
+                _bi.add_face_texcoords(fh, vhandles[0], face_texcoords);
+
+              } else if (nV == 3 * vhandles.size()) {
+
+                face_texcoords3d.clear();
+                face_texcoords3d.reserve(vhandles.size());
+
+                for (j = 0; j < vhandles.size(); j++) {
+                  _in >> t3d[0];
+                  _in >> t3d[1];
+                  _in >> t3d[2];
+
+                  face_texcoords3d.push_back(t3d);
+                }
+
+                _bi.add_face_texcoords(fh, vhandles[0], face_texcoords3d);
+
+              } else {
+                for (j = 0; j < nV; j++) {
+                  _in >> trash;
+                }
+              }
+            } else {
+              for (j = 0; j < nV; j++) {
+                _in >> trash;
+              }
+            }
+						break;
+
+          case TEXNUMBER:
+            _in >> idx;
+            _bi.set_face_texindex(fh, idx);
+            break;
+
 					case COLORRED:
 					  if (prop.value == ValueTypeFLOAT32 || prop.value == ValueTypeFLOAT) {
 					    _in >> tmp;
@@ -504,7 +568,7 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options
 						break;
 					}
 				}
-				if (_opt.face_has_color())
+        if (_opt.face_has_color())
 					_bi.set_color(fh, Vec4uc(c));
 			}
 		}
@@ -539,9 +603,11 @@ bool _PLYReader_::read_ascii(std::istream& _in, BaseImporter& _bi, const Options
 
 bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap*/, const Options& _opt) const {
 
-    OpenMesh::Vec3f        v, n;  // Vertex
+    OpenMesh::Vec3f        v, n, t3d;  // Vertex
     OpenMesh::Vec2f        t;  // TexCoords
     BaseImporter::VHandles vhandles;
+    std::vector<Vec3f>     face_texcoords3d;
+    std::vector<Vec2f>     face_texcoords;
     VertexHandle           vh;
     OpenMesh::Vec4i        c;  // Color
     float                  tmp;
@@ -707,6 +773,56 @@ bool _PLYReader_::read_binary(std::istream& _in, BaseImporter& _bi, bool /*_swap
 						if (!fh.is_valid())
 							++complex_faces;
 						break;
+          case TEXCOORD:
+            // nV = number of texcoord for current face
+            readInteger(prop.listIndexType, _in, nV);
+            
+            if (_opt.face_has_texcoord()) {
+
+              // need to know the number of vertex for this face
+              assert(!vhandles.empty());
+
+              if (nV == 2 * vhandles.size()) {
+
+                face_texcoords.clear();
+                face_texcoords.reserve(vhandles.size());
+
+                for (std::size_t j = 0; j < vhandles.size(); j++) {
+                  readValue(prop.value, _in, t[0]);
+                  readValue(prop.value, _in, t[1]);
+
+                  face_texcoords.push_back(t);
+                }
+
+                _bi.add_face_texcoords(fh, vhandles[0], face_texcoords);
+
+              } else if (nV == 3 * vhandles.size()) {
+
+                face_texcoords3d.clear();
+                face_texcoords3d.reserve(vhandles.size());
+
+                for (std::size_t j = 0; j < vhandles.size(); j++) {
+                  readValue(prop.value, _in, t3d[0]);
+                  readValue(prop.value, _in, t3d[1]);
+                  readValue(prop.value, _in, t3d[2]);
+
+                  face_texcoords3d.push_back(t3d);
+                }
+
+                _bi.add_face_texcoords(fh, vhandles[0], face_texcoords3d);
+
+              } else {
+                consume_input(_in, nV * scalar_size_[prop.value]);
+              }
+            } else {
+              consume_input(_in, nV * scalar_size_[prop.value]);
+            }
+            break;
+          case TEXNUMBER:
+            unsigned int idx;
+            readInteger(prop.value, _in, idx);
+            _bi.set_face_texindex(fh, idx);
+            break;
 					case COLORRED:
 						if (prop.value == ValueTypeFLOAT32 ||
 							prop.value == ValueTypeFLOAT) {
@@ -1170,6 +1286,9 @@ bool _PLYReader_::can_u_read(std::istream& _is) const {
     // clear element list
     elements_.clear();
 
+    // clear texture list
+    texture_files_.clear();
+
     // read 1st line
     std::string line;
     std::getline(_is, line);
@@ -1231,6 +1350,11 @@ bool _PLYReader_::can_u_read(std::istream& _is) const {
 
       if (keyword == "comment") {
         std::getline(_is, line);
+        
+        // TextureFile
+        if (line.rfind(" TextureFile ", 0) == 0) {
+          texture_files_.push_back(line.substr(13));
+        }
       } else if (keyword == "element") {
         _is >> elementName;
         _is >> elementCount;
@@ -1300,6 +1424,11 @@ bool _PLYReader_::can_u_read(std::istream& _is) const {
                 omerr() << "Custom face Properties defined, before 'vertex_indices' property was defined. They will be skipped" << std::endl;
                 elements_.back().properties_.clear();
               }
+            } else if (propertyName == "texcoord")
+            {
+              property.property = TEXCOORD;
+              options_ += Options::FaceTexCoord;
+
             } else {
               options_ += Options::Custom;
             }
@@ -1408,6 +1537,9 @@ bool _PLYReader_::can_u_read(std::istream& _is) const {
               options_ += Options::ColorAlpha;
               if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
                 options_ += Options::ColorFloat;
+            } else if (propertyName == "texnumber") {
+              entry = PropertyInfo(TEXNUMBER, valueType);
+              options_ += Options::FaceTexCoord;
             }
           }
 
diff --git a/src/OpenMesh/Core/IO/reader/PLYReader.hh b/src/OpenMesh/Core/IO/reader/PLYReader.hh
index 44ba7a06..1821729a 100644
--- a/src/OpenMesh/Core/IO/reader/PLYReader.hh
+++ b/src/OpenMesh/Core/IO/reader/PLYReader.hh
@@ -61,6 +61,7 @@
 #include <string>
 #include <cstdio>
 #include <vector>
+#include <list>
 
 #include <OpenMesh/Core/System/config.h>
 #include <OpenMesh/Core/Utils/SingletonT.hh>
@@ -165,7 +166,7 @@ private:
     TEXX,TEXY,
     COLORRED,COLORGREEN,COLORBLUE,COLORALPHA,
     XNORM,YNORM,ZNORM, CUSTOM_PROP, VERTEX_INDICES,
-    UNSUPPORTED
+    TEXCOORD, TEXNUMBER, UNSUPPORTED
   };
 
   /// Stores sizes of property types
@@ -200,6 +201,8 @@ private:
 
   mutable std::vector< ElementInfo > elements_;
 
+  mutable std::list< std::string > texture_files_;
+
   template<typename T>
   inline void read(_PLYReader_::ValueType _type, std::istream& _in, T& _value, OpenMesh::GenProg::TrueType /*_binary*/) const
   {
diff --git a/src/Unittests/TestFiles/meshlab-faceTexCoords-binary.ply b/src/Unittests/TestFiles/meshlab-faceTexCoords-binary.ply
new file mode 100644
index 0000000000000000000000000000000000000000..e57b71329c9fdf5370ad365af47ca7e23a674030
GIT binary patch
diff --git a/src/Unittests/TestFiles/meshlab-faceTexCoords.ply b/src/Unittests/TestFiles/meshlab-faceTexCoords.ply
new file mode 100644
index 00000000..070b8952
--- /dev/null
+++ b/src/Unittests/TestFiles/meshlab-faceTexCoords.ply
@@ -0,0 +1,34 @@
+ply
+format ascii 1.0
+comment VCGLIB generated
+comment TextureFile tex_0.jpg
+comment TextureFile tex_1.jpg
+element vertex 8
+property float x
+property float y
+property float z
+element face 12
+property list uchar int vertex_indices
+property list uchar float texcoord
+property int texnumber
+end_header
+0.5 0.5 0.5 
+-0.5 0.5 0.5 
+0.5 -0.5 0.5 
+-0.5 -0.5 0.5 
+0.5 0.5 -0.5 
+-0.5 0.5 -0.5 
+0.5 -0.5 -0.5 
+-0.5 -0.5 -0.5 
+3 0 1 2 6 0 0 0.1 0.1 0.2 0.2 0 
+3 3 2 1 6 0 0 -0.1 -0.1 -0.2 -0.2 0 
+3 0 2 4 6 0 0 0.1 0.1 0.2 0.3 1 
+3 6 4 2 6 0 0 0.1 0.1 0.2 0.2 1 
+3 0 4 1 6 0 0 0.1 0.1 0.2 0.2 1 
+3 5 1 4 6 0 0 0.1 0.1 0.2 0.2 1 
+3 7 5 6 6 0 0 0.1 0.1 0.2 0.2 0 
+3 4 6 5 6 0 0 0.1 0.1 0.2 0.2 0 
+3 7 6 3 6 0 0 0.1 0.1 0.2 0.2 0 
+3 2 3 6 6 0 0 0.1 0.1 0.2 0.2 0 
+3 7 3 5 6 0 0 0.1 0.1 0.2 0.2 0 
+3 1 5 3 6 0 0 0.1 0.1 0.2 0.2 0 
diff --git a/src/Unittests/unittests_read_write_PLY.cc b/src/Unittests/unittests_read_write_PLY.cc
index 9cb614d7..85ae5c97 100644
--- a/src/Unittests/unittests_read_write_PLY.cc
+++ b/src/Unittests/unittests_read_write_PLY.cc
@@ -463,6 +463,191 @@ TEST_F(OpenMeshReadWritePLY, WriteAndReadBinaryPLYWithFloatVertexColors) {
     mesh_.release_vertex_colors();
 }
 
+/*
+ * Just load a ply file of a cube with face texcoord and face texture index
+ */
+TEST_F(OpenMeshReadWritePLY, LoadPLYFromMeshLabWithFaceTexCoord) {
+
+    mesh_.clear();
+
+    mesh_.request_halfedge_texcoords2D();
+    mesh_.request_face_texture_index();
+
+    OpenMesh::IO::Options options;
+    options += OpenMesh::IO::Options::FaceTexCoord;
+
+    std::string file_name = "meshlab-faceTexCoords.ply";
+
+    bool ok = OpenMesh::IO::read_mesh(mesh_, file_name, options);
+
+    EXPECT_TRUE(ok) << "Unable to load meshlab-faceTexCoords.ply";
+
+    EXPECT_EQ(8u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
+    EXPECT_EQ(18u , mesh_.n_edges())    << "The number of loaded edges is not correct!";
+    EXPECT_EQ(12u , mesh_.n_faces())    << "The number of loaded faces is not correct!";
+
+    EXPECT_FALSE(options.vertex_has_normal()) << "Wrong user options are returned!";
+    EXPECT_FALSE(options.vertex_has_texcoord()) << "Wrong user options are returned!";
+    EXPECT_TRUE(options.face_has_texcoord()) << "Wrong user options are returned!";
+
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(0))[0] ) << "Wrong texCoord at halfedge 0 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(0))[1] ) << "Wrong texCoord at halfedge 0 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(1))[0] ) << "Wrong texCoord at halfedge 1 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(1))[1] ) << "Wrong texCoord at halfedge 1 component 1";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(2))[0] ) << "Wrong texCoord at halfedge 2 component 0";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(2))[1] ) << "Wrong texCoord at halfedge 2 component 1";
+    EXPECT_FLOAT_EQ(-0.2, mesh_.texcoord2D(mesh_.halfedge_handle(3))[0] ) << "Wrong texCoord at halfedge 3 component 0";
+    EXPECT_FLOAT_EQ(-0.2, mesh_.texcoord2D(mesh_.halfedge_handle(3))[1] ) << "Wrong texCoord at halfedge 3 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(4))[0] ) << "Wrong texCoord at halfedge 4 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(4))[1] ) << "Wrong texCoord at halfedge 4 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(5))[0] ) << "Wrong texCoord at halfedge 5 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(5))[1] ) << "Wrong texCoord at halfedge 5 component 1";
+    EXPECT_FLOAT_EQ(-0.1, mesh_.texcoord2D(mesh_.halfedge_handle(6))[0] ) << "Wrong texCoord at halfedge 6 component 0";
+    EXPECT_FLOAT_EQ(-0.1, mesh_.texcoord2D(mesh_.halfedge_handle(6))[1] ) << "Wrong texCoord at halfedge 6 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(7))[0] ) << "Wrong texCoord at halfedge 7 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(7))[1] ) << "Wrong texCoord at halfedge 7 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(8))[0] ) << "Wrong texCoord at halfedge 8 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(8))[1] ) << "Wrong texCoord at halfedge 8 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(9))[0] ) << "Wrong texCoord at halfedge 9 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(9))[1] ) << "Wrong texCoord at halfedge 9 component 1";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(10))[0] ) << "Wrong texCoord at halfedge 10 component 0";
+    EXPECT_FLOAT_EQ(0.3, mesh_.texcoord2D(mesh_.halfedge_handle(10))[1] ) << "Wrong texCoord at halfedge 10 component 1";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(11))[0] ) << "Wrong texCoord at halfedge 11 component 0";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(11))[1] ) << "Wrong texCoord at halfedge 11 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(12))[0] ) << "Wrong texCoord at halfedge 12 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(12))[1] ) << "Wrong texCoord at halfedge 12 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(13))[0] ) << "Wrong texCoord at halfedge 13 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(13))[1] ) << "Wrong texCoord at halfedge 13 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(14))[0] ) << "Wrong texCoord at halfedge 14 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(14))[1] ) << "Wrong texCoord at halfedge 14 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(15))[0] ) << "Wrong texCoord at halfedge 15 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(15))[1] ) << "Wrong texCoord at halfedge 15 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(16))[0] ) << "Wrong texCoord at halfedge 16 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(16))[1] ) << "Wrong texCoord at halfedge 16 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(17))[0] ) << "Wrong texCoord at halfedge 17 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(17))[1] ) << "Wrong texCoord at halfedge 17 component 1";
+
+    //check texture mapping for the mesh
+    OpenMesh::MPropHandleT< std::map< int, std::string > > property;
+    mesh_.get_property_handle(property, "TextureMapping");
+    EXPECT_EQ(mesh_.property(property).size(), 2u) << "Wrong texture number";
+    EXPECT_EQ(mesh_.property(property).count(0), 1u) << "Could not find texture with id 0";
+    EXPECT_TRUE((mesh_.property(property)[0] == std::string("tex_0.jpg"))) << "Wrong texture name";
+    EXPECT_EQ(mesh_.property(property).count(1), 1u) << "Could not find texture with id 1";
+    EXPECT_TRUE((mesh_.property(property)[1] == std::string("tex_1.jpg"))) << "Wrong texture name";
+
+    //check texture mapping per face
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(0)), 0) << "Face 0 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(1)), 0) << "Face 1 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(2)), 1) << "Face 2 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(3)), 1) << "Face 3 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(4)), 1) << "Face 4 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(5)), 1) << "Face 5 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(6)), 0) << "Face 6 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(7)), 0) << "Face 7 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(8)), 0) << "Face 8 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(9)), 0) << "Face 9 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(10)), 0) << "Face 10 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(11)), 0) << "Face 11 texture index is not set correctly";
+        
+    mesh_.release_halfedge_texcoords2D();
+    mesh_.release_face_texture_index();
+
+}
+
+/*
+ * Just load a binary ply file of a cube with face texcoord and face texture index
+ */
+TEST_F(OpenMeshReadWritePLY, LoadBinaryPLYFromMeshLabWithFaceTexCoord) {
+
+    mesh_.clear();
+
+    mesh_.request_halfedge_texcoords2D();
+    mesh_.request_face_texture_index();
+
+    OpenMesh::IO::Options options;
+    options += OpenMesh::IO::Options::FaceTexCoord;
+    options += OpenMesh::IO::Options::Binary;
+
+    std::string file_name = "meshlab-faceTexCoords-binary.ply";
+
+    bool ok = OpenMesh::IO::read_mesh(mesh_, file_name, options);
+
+    EXPECT_TRUE(ok) << "Unable to load meshlab-faceTexCoords-binary.ply";
+
+    EXPECT_EQ(8u  , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
+    EXPECT_EQ(18u , mesh_.n_edges())    << "The number of loaded edges is not correct!";
+    EXPECT_EQ(12u , mesh_.n_faces())    << "The number of loaded faces is not correct!";
+
+    EXPECT_FALSE(options.vertex_has_normal()) << "Wrong user options are returned!";
+    EXPECT_FALSE(options.vertex_has_texcoord()) << "Wrong user options are returned!";
+    EXPECT_TRUE(options.face_has_texcoord()) << "Wrong user options are returned!";
+
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(0))[0] ) << "Wrong texCoord at halfedge 0 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(0))[1] ) << "Wrong texCoord at halfedge 0 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(1))[0] ) << "Wrong texCoord at halfedge 1 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(1))[1] ) << "Wrong texCoord at halfedge 1 component 1";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(2))[0] ) << "Wrong texCoord at halfedge 2 component 0";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(2))[1] ) << "Wrong texCoord at halfedge 2 component 1";
+    EXPECT_FLOAT_EQ(-0.2, mesh_.texcoord2D(mesh_.halfedge_handle(3))[0] ) << "Wrong texCoord at halfedge 3 component 0";
+    EXPECT_FLOAT_EQ(-0.2, mesh_.texcoord2D(mesh_.halfedge_handle(3))[1] ) << "Wrong texCoord at halfedge 3 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(4))[0] ) << "Wrong texCoord at halfedge 4 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(4))[1] ) << "Wrong texCoord at halfedge 4 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(5))[0] ) << "Wrong texCoord at halfedge 5 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(5))[1] ) << "Wrong texCoord at halfedge 5 component 1";
+    EXPECT_FLOAT_EQ(-0.1, mesh_.texcoord2D(mesh_.halfedge_handle(6))[0] ) << "Wrong texCoord at halfedge 6 component 0";
+    EXPECT_FLOAT_EQ(-0.1, mesh_.texcoord2D(mesh_.halfedge_handle(6))[1] ) << "Wrong texCoord at halfedge 6 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(7))[0] ) << "Wrong texCoord at halfedge 7 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(7))[1] ) << "Wrong texCoord at halfedge 7 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(8))[0] ) << "Wrong texCoord at halfedge 8 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(8))[1] ) << "Wrong texCoord at halfedge 8 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(9))[0] ) << "Wrong texCoord at halfedge 9 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(9))[1] ) << "Wrong texCoord at halfedge 9 component 1";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(10))[0] ) << "Wrong texCoord at halfedge 10 component 0";
+    EXPECT_FLOAT_EQ(0.3, mesh_.texcoord2D(mesh_.halfedge_handle(10))[1] ) << "Wrong texCoord at halfedge 10 component 1";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(11))[0] ) << "Wrong texCoord at halfedge 11 component 0";
+    EXPECT_FLOAT_EQ(0.2, mesh_.texcoord2D(mesh_.halfedge_handle(11))[1] ) << "Wrong texCoord at halfedge 11 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(12))[0] ) << "Wrong texCoord at halfedge 12 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(12))[1] ) << "Wrong texCoord at halfedge 12 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(13))[0] ) << "Wrong texCoord at halfedge 13 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(13))[1] ) << "Wrong texCoord at halfedge 13 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(14))[0] ) << "Wrong texCoord at halfedge 14 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(14))[1] ) << "Wrong texCoord at halfedge 14 component 1";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(15))[0] ) << "Wrong texCoord at halfedge 15 component 0";
+    EXPECT_FLOAT_EQ(0.1, mesh_.texcoord2D(mesh_.halfedge_handle(15))[1] ) << "Wrong texCoord at halfedge 15 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(16))[0] ) << "Wrong texCoord at halfedge 16 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(16))[1] ) << "Wrong texCoord at halfedge 16 component 1";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(17))[0] ) << "Wrong texCoord at halfedge 17 component 0";
+    EXPECT_FLOAT_EQ(0, mesh_.texcoord2D(mesh_.halfedge_handle(17))[1] ) << "Wrong texCoord at halfedge 17 component 1";
+
+    //check texture mapping for the mesh
+    OpenMesh::MPropHandleT< std::map< int, std::string > > property;
+    mesh_.get_property_handle(property, "TextureMapping");
+    EXPECT_EQ(mesh_.property(property).size(), 2u) << "Wrong texture number";
+    EXPECT_EQ(mesh_.property(property).count(0), 1u) << "Could not find texture with id 0";
+    EXPECT_TRUE((mesh_.property(property)[0] == std::string("tex_0.jpg"))) << "Wrong texture name";
+    EXPECT_EQ(mesh_.property(property).count(1), 1u) << "Could not find texture with id 1";
+    EXPECT_TRUE((mesh_.property(property)[1] == std::string("tex_1.jpg"))) << "Wrong texture name";
+
+    //check texture mapping per face
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(0)), 0) << "Face 0 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(1)), 0) << "Face 1 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(2)), 1) << "Face 2 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(3)), 1) << "Face 3 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(4)), 1) << "Face 4 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(5)), 1) << "Face 5 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(6)), 0) << "Face 6 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(7)), 0) << "Face 7 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(8)), 0) << "Face 8 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(9)), 0) << "Face 9 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(10)), 0) << "Face 10 texture index is not set correctly";
+    EXPECT_EQ(mesh_.property(mesh_.face_texture_index_pph(), mesh_.face_handle(11)), 0) << "Face 11 texture index is not set correctly";
+        
+    mesh_.release_halfedge_texcoords2D();
+    mesh_.release_face_texture_index();
+
+}
+
 /*
  * Just load a ply file of a cube with vertex texCoords
  */
-- 
2.20.1

