From e8e448bb9bc6a9c6391200a4d0ab7cdf9f7819ce Mon Sep 17 00:00:00 2001
From: Jean-Marie Favreau <J-Marie.Favreau@udamail.fr>
Date: Mon, 11 Feb 2013 12:11:46 +0100
Subject: [PATCH] Implement the common tool to save PLY

---
 src/PlyLoader.cpp                    |   5 -
 src/PlyLoader.h                      |  18 ---
 src/core/Mesh.cpp                    | 170 +-----------------------
 src/core/PLY.cpp                     | 156 ++++++++++++++++++++++
 src/core/PLY.h                       | 102 +++++++++++++++
 src/parameterization/Mapping2D3D.cpp | 185 +--------------------------
 src/parameterization/Mapping2D3D.h   |   4 -
 src/utils/MeshMap.h                  |  15 +++
 8 files changed, 286 insertions(+), 369 deletions(-)
 delete mode 100644 src/PlyLoader.cpp
 delete mode 100644 src/PlyLoader.h
 create mode 100644 src/core/PLY.cpp
 create mode 100644 src/core/PLY.h

diff --git a/src/PlyLoader.cpp b/src/PlyLoader.cpp
deleted file mode 100644
index 6fd30f7..0000000
--- a/src/PlyLoader.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "PlyLoader.h"
-
-PLYLoader::PLYLoader()
-{
-}
diff --git a/src/PlyLoader.h b/src/PlyLoader.h
deleted file mode 100644
index 8bbcdab..0000000
--- a/src/PlyLoader.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef PLYLOADER_H
-#define PLYLOADER_H
-
-#include "FileManipulator.h"
-#include "Mesh.h"
-#include "Mapping2D3D.h"
-#include "MeshMap.h"
-
-namespace Taglut {
-
-class PLYLoader : FileManipulator {
-public:
-    PLYLoader();
-};
-
-}
-
-#endif // PLYLOADER_H
diff --git a/src/core/Mesh.cpp b/src/core/Mesh.cpp
index ff3a368..8f2976c 100644
--- a/src/core/Mesh.cpp
+++ b/src/core/Mesh.cpp
@@ -40,6 +40,7 @@
 #include "NLoop.h"
 #include "PointOnEdge.h"
 #include "PLPath.h"
+#include "PLY.h"
 
 using namespace Taglut;
 
@@ -2851,23 +2852,8 @@ void Mesh::loadASC(const std::string & fileName, const std::string & object) {
 }
 
 void Mesh::loadPLY(const std::string & fileName) {
-  points.clear();
-  triangles.clear();
-
-  std::ifstream infile(fileName.c_str());
-
-  if (!infile.is_open())
-    throw ExceptionFileNotFound();
-
-  if (!checkFormatPLY(infile))
-    throw ExceptionUnknownFormat();
-
-  if (!loadPLY(infile))
-    throw ExceptionErrorDuringReadingFile();
-
-  infile.close();
-
-  initIsBoundaryFlag();
+    PLYLoader plyLoader(fileName);
+    plyLoader.loadMeshFromPLY(*this);
 }
 
 void Mesh::loadOFF(const std::string & fileName) {
@@ -3561,127 +3547,6 @@ bool Mesh::loadOFF(std::ifstream & file) {
   return true;
 }
 
-bool Mesh::checkFormatPLY(std::ifstream & file) {
-  std::string firstLine, secondLine;
-  getline(file, firstLine);
-  getline(file, secondLine);
-  return (((firstLine == "ply") || (firstLine == "ply\r")) &&
-	  ((secondLine == "format ascii 1.0") || (secondLine == "format ascii 1.0\r")));
-}
-
-
-bool Mesh::loadPLY(std::ifstream & file) {
-  std::string buf;
-  VertexID nbPt = 0;
-  unsigned int nbProp = 0;
-  TriangleID nbPrimitives = 0;
-
-
-  /* find definition of vertices in the header */
-  while(!file.eof()) {
-    file >> buf;
-    if (buf == "element") {
-      file >> buf;
-      if (buf != "vertex")
-	return false;
-      file >> nbPt;
-      break;
-    }
-  }
-  if (file.eof())
-    return false;
-  if (nbPt < 3)
-    return false;
-
-  /* compute number of properties for points */
-  while(!file.eof()) {
-    file >> buf;
-    if (buf == "property") { // the point properties are not really checked, assuming that the 3 first properties are the coordinates
-      file >> buf;
-      if (buf == "list")
-	file >> buf;
-      file >> buf;
-      ++nbProp;
-    }
-    else
-      break;
-  }
-  if (file.eof())
-    return false;
-
-  if (buf != "end_header") {
-    while(buf != "element") {
-      file >> buf;
-      if (file.eof())
-	return false;
-    }
-    file >> buf;
-    if (buf != "face")
-      return false;
-    /* read the definition of primitives in the header */
-    file >> nbPrimitives;
-
-    /* go to the end of the header */
-    while(!file.eof()) {
-      file >> buf;
-      if (buf == "end_header")
-	break;
-    }
-    if (file.eof())
-      return false;
-  }
-  // then read points
-  for(VertexID i = 0; i < nbPt; ++i) {
-    double x, y , z;
-    file >> x >> y >> z;
-    for (unsigned int s = 0; s < nbProp - 3; ++s)
-      file >> buf; // skip the other point's informations
-    points.push_back(Point3D(x, y, z, nbPoints++));
-  }
-
-  // and read faces
-  bool start1 = false;
-  for(TriangleID i = 0; i < nbPrimitives; ++i) {
-    VertexID nbP;
-    file >> nbP;
-
-    if((nbP != 3) && (nbP != 4))
-      return false;
-
-    VertexID x, y, z;
-    file >> x >> y >> z;
-    if (nbP == 3) {
-      if ((x == nbPoints) ||
-	  (y == nbPoints) ||
-	  (z == nbPoints))
-	start1 = true;
-
-      addTriangle(Triangle(x, y, z));
-    }
-    else {
-      unsigned int t;
-      file >> t;
-      if ((x == nbPoints) ||
-	  (y == nbPoints) ||
-	  (z == nbPoints) ||
-	  (t == nbPoints))
-	start1 = true;
-
-      addTriangle(Triangle(x, y, z));
-      addTriangle(Triangle(x, z, t));
-
-    }
-  }
-
-  if (start1) {
-    for(std::vector<Triangle>::iterator t = triangles.begin(); t != triangles.end(); ++t)
-      (*t).incIds(-1, 0);
-  }
-
-  updateNeighboursAndTriangles();
-
-  return true;
-}
 
 void Mesh::loadXYZ(const std::string & fileName) {
   Point3D point_l;
@@ -3788,33 +3653,10 @@ void Mesh::saveAltFormat(const std::string & fileName, const std::string & forma
     throw Exception("Unknown format");
 }
 
-void Mesh::savePLY(const std::string & fileName, const std::string & objectName) const {
-  std::ofstream outfile(fileName.c_str(), std::ios::out);
-
-  if (!outfile.is_open())
-    throw ExceptionCannotOpenOutputFile();
-
-  outfile << "ply" << std::endl;
-  outfile << "format ascii 1.0" << std::endl;
-  outfile << "comment   Generated by savePLY" << std::endl;
-  if (objectName != "")
-    outfile << "comment    " << objectName << std::endl;
-
-  outfile << "element vertex " << nbPoints << std::endl;
-  outfile << "property double x" << std::endl;
-  outfile << "property double y" << std::endl;
-  outfile << "property double z" << std::endl;
-  outfile << "element face " << nbTriangles << std::endl;
-  outfile << "property list uchar integer vertex_index" << std::endl;
-  outfile << "end_header" << std::endl;
+void Mesh::savePLY(const std::string & fileName, const std::string &) const {
+    PLYSaver plysaver;
 
-  for(Mesh::const_point_iterator p = point_begin(); p != point_end(); ++p)
-    outfile << (*p).getX() << " " << (*p).getY() << " " << (*p).getZ() << std::endl;
-
-  for(Mesh::const_triangle_iterator t = triangle_begin(); t != triangle_end(); ++t)
-    outfile << " 3 " << (*t).getP1() << " " << (*t).getP2() << " " << (*t).getP3() << std::endl;
-
-  outfile.close();
+    plysaver.savePLY(*this, fileName);
 }
 
 void Mesh::saveOFF(const std::string & fileName, const std::string & objectName) const {
diff --git a/src/core/PLY.cpp b/src/core/PLY.cpp
new file mode 100644
index 0000000..17ff4a3
--- /dev/null
+++ b/src/core/PLY.cpp
@@ -0,0 +1,156 @@
+#include "PLY.h"
+#include "Mesh.h"
+#include "Mapping2D3D.h"
+#include "MeshMap.h"
+
+using namespace Taglut;
+
+void PLY::reset() {
+    properties.clear();
+    vertices.clear();
+    triangles.clear();
+}
+
+PLYLoader::PLYLoader() {
+    addFormats();
+}
+
+void PLYLoader::addFormats() {
+    addLoadFormat("ply");
+}
+
+
+PLYLoader::PLYLoader(const std::string & filename, const std::string & objectName) {
+    addFormats();
+    load(filename);
+}
+
+void PLYLoader::loadAltFormat(const std::string & fileName, const std::string & altFormat, const std::string &) {
+  if (altFormat == "ply")
+    loadPLY(fileName);
+  else
+    throw Exception("Unknown format");
+}
+
+void PLYLoader::loadPLY(const std::string & fileName) {
+    // TODO
+}
+
+void PLYLoader::loadMeshFromPLY(Mesh & mesh) const {
+    // TODO
+}
+
+void PLYLoader::loadMapping2D3DFromPLY(Mapping2D3D & mapping) const {
+    // TODO
+}
+
+void PLYLoader::loadMeshMapFromPLY(const MeshMap & mmap) const {
+    // TODO
+}
+
+/** PLY saver */
+
+PLYSaver::PLYSaver() {
+
+}
+
+void PLYSaver::addFormats() {
+    addSaveFormat("ply");
+}
+
+void PLYSaver::initDataFromMesh(const Mesh & mesh) {
+    triangles.clear();
+    vertices.clear();
+
+    vertices.insert(vertices.begin(), mesh.getPoints().begin(), mesh.getPoints().end());
+    triangles.insert(triangles.begin(), mesh.getTriangles().begin(), mesh.getTriangles().end());
+}
+
+void PLYSaver::initDataFromMapping(const Mapping2D3D & mapping) {
+    properties["s"] = std::list<double>();
+    properties["t"] = std::list<double>();
+    for(Mapping2D3D::const_iterator p = mapping.begin(); p != mapping.end(); ++p) {
+        properties["s"].push_back((*p).get2DX());
+        properties["t"].push_back((*p).get2DY());
+    }
+}
+
+void PLYSaver::initDataFromMap(const MeshMap & mmap) {
+    properties["intensity"] = std::list<double>();
+    for(VertexID id = 0; id != mmap.getNbValues(); ++id)
+        properties["intensity"].push_back(mmap.getValue(id));
+}
+
+void PLYSaver::savePLY(const std::string & fileName) const {
+  if (vertices.empty())
+    return;
+  std::ofstream outfile(fileName.c_str(), std::ios::out);
+
+  if (!outfile.is_open())
+      throw ExceptionCannotOpenOutputFile();
+  outfile << "ply" << std::endl;
+  outfile << "format ascii 1.0" << std::endl;
+  outfile << "comment   Generated by PLYSaver" << std::endl;
+
+  outfile << "element vertex " << vertices.size() << std::endl;
+  outfile << "property float x" << std::endl;
+  outfile << "property float y" << std::endl;
+  outfile << "property float z" << std::endl;
+  std::list<std::list<double>::const_iterator> itProps;
+  for(std::map<std::string, std::list<double> >::const_iterator p = properties.begin(); p != properties.end(); ++p) {
+      outfile << "property double " << (*p).first << std::endl;
+      assert((*p).second.size() == vertices.size());
+      itProps.push_back((*p).second.begin());
+  }
+
+  outfile << "element face " << triangles.size() << std::endl;
+  outfile << "property list uchar int vertex_indices" << std::endl;
+  outfile << "end_header" << std::endl;
+
+  for(std::list<Point3D>::const_iterator p = vertices.begin(); p != vertices.end(); ++p) {
+      outfile << (*p).getX() << " " << (*p).getY() << " " << (*p).getZ();
+      for(std::list<std::list<double>::const_iterator>::iterator pr = itProps.begin(); pr != itProps.end(); ++pr) {
+          outfile << " " << **pr;
+          ++(*pr);
+      }
+      outfile << std::endl;
+  }
+
+  for(std::list<Triangle>::const_iterator t = triangles.begin(); t != triangles.end(); ++t)
+      outfile << " 3 " << (*t).getP1() << " " << (*t).getP2() << " " << (*t).getP3() << std::endl;
+
+  outfile.close();
+}
+
+void PLYSaver::saveAltFormat(const std::string & fileName, const std::string &,
+                             const std::string &) const {
+    savePLY(fileName);
+}
+
+void PLYSaver::savePLY(const Mesh & mesh, const std::string & fileName) {
+    initDataFromMesh(mesh);
+    savePLY(fileName);
+}
+
+void PLYSaver::savePLY(const MeshMap & mmap, const std::string & fileName) {
+    reset();
+    initDataFromMesh(mmap.getMesh());
+    initDataFromMap(mmap);
+    savePLY(fileName);
+}
+
+void PLYSaver::savePLY(const Mapping2D3D & mapping, const std::string & fileName) {
+    reset();
+    initDataFromMapping(mapping);
+    initDataFromMesh(mapping.getMesh());
+    savePLY(fileName);
+}
+
+void PLYSaver::savePLY(const Mapping2D3D & mapping, const MeshMap & mmap, const std::string & fileName) {
+    reset();
+    initDataFromMapping(mapping);
+    initDataFromMesh(mmap.getMesh());
+    initDataFromMap(mmap);
+    savePLY(fileName);
+}
+
diff --git a/src/core/PLY.h b/src/core/PLY.h
new file mode 100644
index 0000000..a55b991
--- /dev/null
+++ b/src/core/PLY.h
@@ -0,0 +1,102 @@
+#include "MeshMap.h"
+
+#ifndef PLYLOADER_H
+#define PLYLOADER_H
+
+#include "FileManipulator.h"
+
+#include <string>
+#include <map>
+#include <list>
+
+#include "Point3D.h"
+
+namespace Taglut {
+class Mesh;
+class Mapping2D3D;
+class Triangle;
+
+class PLY {
+protected:
+    std::map<std::string, std::list<double> > properties;
+    std::list<Point3D> vertices;
+    std::list<Triangle> triangles;
+    void reset();
+};
+
+class PLYLoader : public FileManipulator, public PLY {
+private:
+    bool loadPLY(std::ifstream & file);
+
+    void addFormats();
+public:
+    PLYLoader();
+    PLYLoader(const std::string & filename, const std::string & objectName = "");
+
+    /** no save function */
+    virtual void saveAltFormat(const std::string &, const std::string & = "",
+                               const std::string &  = "") const {
+    }
+
+    virtual void loadAltFormat(const std::string & fileName, const std::string & altFormat = "", const std::string & object = "");
+
+    /**
+       Load Mesh from file (PLY format)
+    */
+    void loadPLY(const std::string & fileName);
+
+    /** given a mesh, it modifies it according to the data of the PLY file */
+    void loadMeshFromPLY(Mesh & mesh) const;
+
+    /** given a mapping2D3D, it modifies it according to the data of the PLY file */
+    void loadMapping2D3DFromPLY(Mapping2D3D & mapping) const;
+
+    /** given a mesh map, it modifies it according to the data of the PLY file */
+    void loadMeshMapFromPLY(const MeshMap & mmap) const;
+
+};
+
+class PLYSaver : public FileManipulator, public PLY {
+private:
+    void initDataFromMesh(const Mesh & mesh);
+    void initDataFromMapping(const Mapping2D3D & mapping);
+    void initDataFromMap(const MeshMap & mmap);
+    void savePLY(const std::string & fileName) const;
+    void addFormats();
+public:
+    PLYSaver();
+
+    /** no load function */
+    virtual void loadAltFormat(const std::string &, const std::string & = "", const std::string & = "") {
+    }
+
+    /**
+       Save Mesh to file
+    */
+    virtual void saveAltFormat(const std::string & fileName, const std::string & format = "",
+                               const std::string & objectName  = "") const;
+    /**
+       Save Mesh to file (PLY format)
+    */
+    void savePLY(const Mesh & mesh, const std::string & fileName);
+
+    /**
+       Save Mesh and meshmap to file (PLY format)
+    */
+    void savePLY(const MeshMap & mmap, const std::string & fileName);
+
+    /**
+       Save Mesh and Mapping2D3D to file (PLY format)
+    */
+    void savePLY(const Mapping2D3D & mapping, const std::string & fileName);
+
+    /**
+       Save Mesh, Mapping2D3D and meshmap to file (PLY format)
+    */
+    void savePLY(const Mapping2D3D & mapping, const MeshMap & mmap, const std::string & fileName);
+
+};
+
+}
+
+#endif // PLYLOADER_H
diff --git a/src/parameterization/Mapping2D3D.cpp b/src/parameterization/Mapping2D3D.cpp
index 3d3823f..49b71ea 100644
--- a/src/parameterization/Mapping2D3D.cpp
+++ b/src/parameterization/Mapping2D3D.cpp
@@ -34,6 +34,7 @@
 #include "PlaneSpliter.h"
 #include "StringManipulation.h"
 #include "PointOnEdge.h"
+#include "PLY.h"
 
 #ifdef USE_LIBXML
 #include <libxml/xmlreader.h>
@@ -233,164 +234,12 @@ void Mapping2D3D::loadAltFormat(const std::string & fileName, const std::string
     throw Exception("Unknow format");
 }
 
-bool Mapping2D3D::loadPLY(std::ifstream & file) {
-  std::string buf;
-  VertexID nbPt = 0;
-  unsigned int nbProp = 0;
-  TriangleID nbPrimitives = 0;
-  VertexID nbPoints = 0;
-  std::deque<Point3D> pts3d;
-  std::deque<Triangle> triangles;
-
-  (*mesh).clear();
-  clear();
-
-  /* find definition of vertices in the header */
-  while(!file.eof()) {
-    file >> buf;
-    if (buf == "element") {
-      file >> buf;
-      if (buf != "vertex")
-	return false;
-      file >> nbPt;
-      break;
-    }
-  }
-  if (file.eof())
-    return false;
-  if (nbPt < 3)
-    return false;
-
-  /* compute number of properties for points */
-  while(!file.eof()) {
-    file >> buf;
-    if (buf == "property") { // the point properties are not really checked, assuming that the 3 first properties are the coordinates
-      file >> buf;
-      if (buf == "list")
-	file >> buf;
-      file >> buf;
-      ++nbProp;
-    }
-    else if (buf != "element")
-      return false;
-    else
-      break;
-  }
-  if (file.eof())
-    return false;
-
-  /* read the definition of primitives in the header */
-  file >> buf;
-  file >> nbPrimitives;
-  if (nbPrimitives == 0)
-    return false;
-
-  /* go to the end of the header */
-  while(!file.eof()) {
-    file >> buf;
-    if (buf == "end_header")
-      break;
-  }
-  if (file.eof())
-    return false;
-
-  // then read points
-  for(VertexID i = 0; i < nbPt; ++i) {
-    double x, y, z;
-    file >> x >> y >> z;
-    unsigned int nbRead = 3;
-    if (nbProp >= 5) {
-      Point2D p2D;
-      double x2, y2;
-      file >> x2 >> y2;
-      nbRead += 2;
-
-      // set point
-      p2D.set2DX(x2);
-      p2D.set2DY(y2);
-      p2D.setId(size());
-      update2DExtrema(p2D);
-
-      // add point to the mapping
-      push_back(p2D);
-    }
-    for (unsigned int s = 0; s < nbProp - nbRead; ++s)
-      file >> buf; // skip the other point's informations
-    pts3d.push_back(Point3D(x, y, z, nbPoints++));
-  }
-
-  // and read faces
-  bool start1 = false;
-  for(TriangleID i = 0; i < nbPrimitives; ++i) {
-    VertexID nbP;
-    file >> nbP;
-
-    if((nbP != 3) && (nbP != 4))
-      return false;
-
-    VertexID x, y, z;
-
-    file >> x >> y >> z;
-    if (nbP == 3) {
-      if ((x == nbPoints) ||
-	  (y == nbPoints) ||
-	  (z == nbPoints))
-	start1 = true;
-
-      triangles.push_back(Triangle(x, y, z, triangles.size()));
-    }
-    else {
-      unsigned int t;
-      file >> t;
-      if ((x == nbPoints) ||
-	  (y == nbPoints) ||
-	  (z == nbPoints) ||
-	  (t == nbPoints))
-	start1 = true;
-
-      triangles.push_back(Triangle(x, y, z, triangles.size()));
-      triangles.push_back(Triangle(x, z, t, triangles.size()));
-
-    }
-  }
-
-  if (start1) {
-    std::cout << "-1" << std::endl;
-    for(std::deque<Triangle>::iterator t = triangles.begin(); t != triangles.end(); ++t)
-      (*t).incIds(-1, 0);
-  }
-
-  (*mesh).setPointsAndbuildMesh(pts3d, triangles);
-
-
-  if (size() == (*mesh).getNbPoints())
-    flatten = true;
-  else {
-    if(size() == 0)
-      std::cout << "Warning: no UV data available" << std::endl;
-    else {
-      std::cout << "Warning: UV data not fully available, cleaning." << std::endl;
-      clear();
-    }
-    reinit();
-  }
-  return true;
-}
-
 
 void Mapping2D3D::loadPLY(const std::string & fileName) {
-  std::ifstream infile(fileName.c_str());
-
-  if (!infile.is_open())
-    throw ExceptionFileNotFound();
-
-  if (!checkFormatPLY(infile))
-    throw ExceptionUnknownFormat();
-
-  if (!loadPLY(infile))
-    throw ExceptionErrorDuringReadingFile();
-
-  infile.close();
+    PLYLoader plyLoader(fileName);
+    plyLoader.loadMapping2D3DFromPLY(*this);
+    if (mesh != NULL)
+        plyLoader.loadMeshFromPLY(*mesh);
 }
 
 bool Mapping2D3D::checkFormatPLY(std::ifstream & file) {
@@ -585,29 +434,9 @@ void Mapping2D3D::saveAltFormat(const std::string & fileName, const std::string
       outFile << "f " << ((*t).getP1() + 1) << "/" << ((*t).getP1() + 1) << " " << ((*t).getP2() + 1) << "/" << ((*t).getP2() + 1) << " " << ((*t).getP3() + 1) << "/" << ((*t).getP3() + 1) << std::endl;
   }
   else if (format == "ply") {
-    outFile << "ply" << std::endl;
-    outFile << "format ascii 1.0" << std::endl;
-    outFile << "comment   Generated by savePLY" << std::endl;
-    if (objectName != "")
-      outFile << "comment    " << objectName << std::endl;
-
-    outFile << "element vertex " << (*mesh).getNbPoints() << std::endl;
-    outFile << "property double x" << std::endl;
-    outFile << "property double y" << std::endl;
-    outFile << "property double z" << std::endl;
-    outFile << "property double s" << std::endl;
-    outFile << "property double t" << std::endl;
-    outFile << "element face " << (*mesh).getNbTriangles() << std::endl;
-    outFile << "property list uchar integer vertex_index" << std::endl;
-    outFile << "end_header" << std::endl;
-
-    Mapping2D3D::const_iterator pm = begin();
-    for(Mesh::const_point_iterator p = (*mesh).point_begin(); p != (*mesh).point_end(); ++p, ++pm)
-      outFile << (*p).getX() << " " << (*p).getY() << " " << (*p).getZ() << " " << (*pm).get2DX() << " " << (*pm).get2DY() << std::endl;
-
-    for(Mesh::const_triangle_iterator t = (*mesh).triangle_begin(); t != (*mesh).triangle_end(); ++t)
-      outFile << " 3 " << (*t).getP1() << " " << (*t).getP2() << " " << (*t).getP3() << std::endl;
+      PLYSaver plysaver;
 
+      plysaver.savePLY(*this, fileName);
   }
   else if ((format == "xml") || (format == "map"))
     outFile <<  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl << toStringXML();
diff --git a/src/parameterization/Mapping2D3D.h b/src/parameterization/Mapping2D3D.h
index 169b4a0..c146cd4 100644
--- a/src/parameterization/Mapping2D3D.h
+++ b/src/parameterization/Mapping2D3D.h
@@ -124,10 +124,6 @@ namespace Taglut {
     */
     bool loadOBJ(std::ifstream & file);
 
-    /**
-       Load the mapping from a ply file
-    */
-    bool loadPLY(std::ifstream & file);
 
     /**
        Check the ply format, reading the header
diff --git a/src/utils/MeshMap.h b/src/utils/MeshMap.h
index ad2af01..556d009 100644
--- a/src/utils/MeshMap.h
+++ b/src/utils/MeshMap.h
@@ -26,6 +26,7 @@
 #include "FileManipulator.h"
 #include "IDTranslator.h"
 
+
 namespace Taglut {
   class Mesh;
   class MeshManipulator;
@@ -517,6 +518,7 @@ namespace Taglut {
 #include "FileExceptions.h"
 #include "PointOnEdge.h"
 #include "PLPath.h"
+#include "PLY.h"
 
 namespace Taglut {
 
@@ -1808,6 +1810,19 @@ namespace Taglut {
     }
   }
 
+  template <typename T>
+  void MeshMapT<T>::savePLY(const std::string & fileName) const {
+      PLYSaver plysaver;
+      plysaver.savePLY(*this, fileName);
+  }
+
+  template <typename T>
+  void MeshMapT<T>::loadPLY(const std::string & fileName) {
+      PLYLoader plyLoader(fileName);
+      plyLoader.loadMapping2D3DFromPLY(*this);
+      if (mesh != NULL)
+          plyLoader.loadMeshFromPLY(*mesh);
+  }
 }
 
 
-- 
GitLab