diff --git a/Geo/GFace.cpp b/Geo/GFace.cpp
index deeb7fc2c6b4e62b6c6428c0f49ee80117f43ecf..ef6b3947183a92b92616f0fc2453e6f4d239c160 100644
--- a/Geo/GFace.cpp
+++ b/Geo/GFace.cpp
@@ -998,24 +998,118 @@ int GFace::genusGeom()
   return nSeams - single_seams.size();
 }
 
-void GFace::fillPointCloud(double maxDist, std::vector<SPoint3> *points,
+class IntSPoint2LessThan{
+ public:
+  bool operator()(const std::pair<int, SPoint2> &v1,
+                  const std::pair<int, SPoint2> &v2) const
+  {
+    return v1.second < v2.second;
+  }
+};
+
+static int addSTLVertex(std::set<std::pair<int, SPoint2>, IntSPoint2LessThan> &all,
+                        std::vector<SPoint2> &vertices, SPoint2 p)
+{
+  std::pair<int, SPoint2> tmp(0, p);
+  std::set<std::pair<int, SPoint2>, IntSPoint2LessThan>::iterator it = all.find(tmp);
+  if(it == all.end()){
+    vertices.push_back(p);
+    int index = vertices.size() - 1;
+    all.insert(std::pair<int, SPoint2>(index, p));
+    return index;
+  }
+  return it->first;
+}
+
+static void recurSTLTriangle(GFace *gf, double maxDist, 
+                             std::set<std::pair<int, SPoint2>, IntSPoint2LessThan> &all, 
+                             std::vector<SPoint2> &vertices,
+                             std::vector<int> &triangles, int v0, int v1, int v2)
+{
+  SPoint2 &p0(vertices[v0]); GPoint gp0 = gf->point(p0);
+  SPoint2 &p1(vertices[v1]); GPoint gp1 = gf->point(p1);
+  SPoint2 &p2(vertices[v2]); GPoint gp2 = gf->point(p2);
+  int v01 = addSTLVertex(all, vertices, (p0 + p1) * 0.5);
+  int v12 = addSTLVertex(all, vertices, (p1 + p2) * 0.5);
+  int v20 = addSTLVertex(all, vertices, (p2 + p0) * 0.5);
+  SPoint2 &p01(vertices[v01]); GPoint gp01 = gf->point(p01);
+  SPoint2 &p12(vertices[v12]); GPoint gp12 = gf->point(p12);
+  SPoint2 &p20(vertices[v20]); GPoint gp20 = gf->point(p20);
+  
+  if(gp0.distance(gp01) > maxDist || 
+     gp01.distance(gp20) > maxDist ||
+     gp20.distance(gp0) > maxDist){
+    recurSTLTriangle(gf, maxDist, all, vertices, triangles, v0, v01, v20);
+  }
+  else{
+    triangles.push_back(v0); triangles.push_back(v01); triangles.push_back(v20);
+  }
+
+  if(gp1.distance(gp12) > maxDist || 
+     gp12.distance(gp01) > maxDist ||
+     gp01.distance(gp1) > maxDist){
+    recurSTLTriangle(gf, maxDist, all, vertices, triangles, v1, v12, v01);
+  }
+  else{
+    triangles.push_back(v1); triangles.push_back(v12); triangles.push_back(v01);
+  }
+
+  if(gp2.distance(gp20) > maxDist || 
+     gp20.distance(gp12) > maxDist ||
+     gp12.distance(gp2) > maxDist){
+    recurSTLTriangle(gf, maxDist, all, vertices, triangles, v2, v20, v12);
+  }
+  else{
+    triangles.push_back(v2); triangles.push_back(v20); triangles.push_back(v12);
+  }
+
+  if(gp01.distance(gp12) > maxDist || 
+     gp12.distance(gp20) > maxDist ||
+     gp20.distance(gp01) > maxDist){
+    recurSTLTriangle(gf, maxDist, all, vertices, triangles, v01, v12, v20);
+  }
+  else{
+    triangles.push_back(v01); triangles.push_back(v12); triangles.push_back(v20);
+  }
+}
+
+bool GFace::fillPointCloud(double maxDist, std::vector<SPoint3> *points,
                            std::vector<SVector3> *normals)
 {
   if(!buildSTLTriangulation()){
     Msg::Error("No STL triangulation available to fill point cloud");
-    return;
+    return false;
   }
+  
+  if(!points) return false;
+
+  // FIXME: this is too complicated -- we should just split the
+  // triangles in one step (i.e., assume thay are mostly flat) and not
+  // worry about duplicate vertices on the triangle edges)
+
+  std::set<std::pair<int, SPoint2>, IntSPoint2LessThan> all_vertices;
+  for(unsigned int i = 0; i < stl_vertices.size(); i++)
+    all_vertices.insert(std::pair<int, SPoint2>(i, stl_vertices[i]));
+
+  std::vector<SPoint2> new_vertices;
+  new_vertices.insert(new_vertices.begin(), stl_vertices.begin(), 
+                      stl_vertices.end());
+
+  std::vector<int> new_triangles;
+  new_triangles.insert(new_triangles.begin(), stl_triangles.begin(), 
+                       stl_triangles.end());
 
-  bool computePoints = points ? true : false;
-  bool computeNormals = normals ? true : false;
-  if(!computePoints && !computeNormals) return;
-  /*
-  int N = va_geom_triangles->getNumVertices();
-  for (int i = 0; i < N; i += 3) {
-    SPoint3 p((va_geom_triangles->getVertexArray(3 * i))[0],
-              (va_geom_triangles->getVertexArray(3 * i))[1],
-              (va_geom_triangles->getVertexArray(3 * i))[2]);
+  for(unsigned int i = 0; i < stl_triangles.size(); i += 3)
+    recurSTLTriangle(this, maxDist, all_vertices, new_vertices, new_triangles,
+                     stl_triangles[i], stl_triangles[i + 1], stl_triangles[i + 2]);
+
+  for(unsigned int i = 0; i < new_vertices.size(); i++){
+    GPoint p(point(new_vertices[i]));
+    points->push_back(SPoint3(p.x(), p.y(), p.z()));
+  }
+
+  if(normals){
+    for(unsigned int i = 0; i < new_vertices.size(); i++)
+      normals->push_back(normal(new_vertices[i]));
   }
-  */
-  
 }
diff --git a/Geo/GFace.h b/Geo/GFace.h
index f6b033a93b7426e45c7dbc4e2ba14ded3ce6f16d..5bef52a9bf6bf2c20825e6c9a0eaed047122e915 100644
--- a/Geo/GFace.h
+++ b/Geo/GFace.h
@@ -232,8 +232,8 @@ class GFace : public GEntity
 
   // add points (and optionalluy normals) in vectors so that two
   // points are at most maxDist apart
-  void fillPointCloud(double maxDist, std::vector<SPoint3> *points,
-                      std::vector<SVector3> *normals);
+  bool fillPointCloud(double maxDist, std::vector<SPoint3> *points,
+                      std::vector<SVector3> *normals=0);
 
   struct {
     // do we recombine the triangles of the mesh?
@@ -267,7 +267,12 @@ class GFace : public GEntity
   // of start/end points
   std::vector<SPoint3> cross;
 
-  // a vertex array containing an STL representation of the surface
+  // the STL mesh
+  std::vector<SPoint2> stl_vertices;
+  std::vector<int> stl_triangles;
+
+  // a vertex array containing a geometrical representation of the
+  // surface
   VertexArray *va_geom_triangles;
 
   // a array for accessing the transfinite vertices using a pair of
diff --git a/Geo/OCCFace.cpp b/Geo/OCCFace.cpp
index 96cd08ffd6523c2508ced27e932f258affcf08ca..34dda7daf68eab635e69cd06d9366e3d491e93d9 100644
--- a/Geo/OCCFace.cpp
+++ b/Geo/OCCFace.cpp
@@ -312,62 +312,65 @@ bool OCCFace::buildSTLTriangulation(bool force)
       return true;
   }
 
-  TopLoc_Location loc;
-  int p1, p2, p3;
+  stl_vertices.clear();
+  stl_triangles.clear();
+  
   Bnd_Box aBox;
-  Standard_Boolean bWithShare = Standard_False;
-  Standard_Real aDiscret, aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
-  Standard_Real dX, dY, dZ, dMax, aCoeff;     
   BRepBndLib::Add(s, aBox);
+  Standard_Real aXmin, aYmin, aZmin, aXmax, aYmax, aZmax;
   aBox.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax);
-   
-  dX = aXmax-aXmin;
-  dY = aYmax-aYmin;
-  dZ = aZmax-aZmin;
-  dMax = dX;
-  if(dY > dMax) {
-    dMax = dY;
-  }
-  if(dZ > dMax) {
-    dMax = dZ;
-  }
-
-  aCoeff = 0.01;
-  aDiscret = aCoeff * dMax;
-  BRepMesh_FastDiscret aMesher(aDiscret, 0.5, aBox, bWithShare, Standard_True,
+  Standard_Real dX = aXmax - aXmin;
+  Standard_Real dY = aYmax - aYmin;
+  Standard_Real dZ = aZmax - aZmin;
+  Standard_Real dMax = dX;
+  if(dY > dMax) dMax = dY;
+  if(dZ > dMax) dMax = dZ;
+  Standard_Real aCoeff = 0.01;
+  Standard_Real aDiscret = aCoeff * dMax;
+  BRepMesh_FastDiscret aMesher(aDiscret, 0.5, aBox, Standard_False, Standard_True,
                                Standard_False, Standard_True);
   aMesher.Add(s);
+  TopLoc_Location loc;
   Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation(s, loc);
-  if (triangulation.IsNull()){
+  if(triangulation.IsNull()){
     Msg::Warning("OCC STL triangulation failed");
     return false;
   }
-  
-  int ntriangles = triangulation->NbTriangles();
-  if(!triangulation->HasUVNodes()) return false;
+  if(!triangulation->HasUVNodes()){
+    Msg::Warning("OCC STL triangulation has no u,v coordinates");
+    return false;
+  }
 
-  va_geom_triangles = new VertexArray(3, ntriangles);
-  
+  for(int i = 1; i <= triangulation->NbNodes(); i++){
+    gp_Pnt2d p = (triangulation->UVNodes())(i);
+    stl_vertices.push_back(SPoint2(p.X(), p.Y()));
+  }
+
+  for(int i = 1; i <= triangulation->NbTriangles(); i++){
+    Poly_Triangle triangle = (triangulation->Triangles())(i);
+    int p1, p2, p3;
+    triangle.Get(p1, p2, p3);
+    stl_triangles.push_back(p1 - 1);
+    stl_triangles.push_back(p2 - 1);
+    stl_triangles.push_back(p3 - 1);
+  }
+
+  va_geom_triangles = new VertexArray(3, stl_triangles.size() / 3);
   unsigned int c = CTX::instance()->color.geom.surface;
   unsigned int col[4] = {c, c, c, c};
-  for (int j = 1; j <= ntriangles; j++){
-    Poly_Triangle triangle = (triangulation->Triangles())(j);
-    triangle.Get(p1, p2, p3);
-    gp_Pnt2d x1 = (triangulation->UVNodes())(p1);
-    gp_Pnt2d x2 = (triangulation->UVNodes())(p2);
-    gp_Pnt2d x3 = (triangulation->UVNodes())(p3);
-    GPoint gp1 = point(x1.X(), x1.Y());
-    GPoint gp2 = point(x2.X(), x2.Y());
-    GPoint gp3 = point(x3.X(), x3.Y());
-    SVector3 n[3];
-    n[0] = normal(SPoint2(x1.X(), x1.Y()));
-    n[1] = normal(SPoint2(x2.X(), x2.Y()));
-    n[2] = normal(SPoint2(x3.X(), x3.Y()));
+  for (unsigned int i = 0; i < stl_triangles.size(); i += 3){
+    SPoint2 &p1(stl_vertices[stl_triangles[i]]);
+    SPoint2 &p2(stl_vertices[stl_triangles[i + 1]]);
+    SPoint2 &p3(stl_vertices[stl_triangles[i + 2]]);
+    GPoint gp1 = GFace::point(p1);
+    GPoint gp2 = GFace::point(p2);
+    GPoint gp3 = GFace::point(p3);
     double x[3] = {gp1.x(), gp2.x(), gp3.x()};
     double y[3] = {gp1.y(), gp2.y(), gp3.y()};
     double z[3] = {gp1.z(), gp2.z(), gp3.z()};
+    SVector3 n[3] = {normal(p1), normal(p2), normal(p3)};
     va_geom_triangles->add(x, y, z, n, col);
-  }  
+  }
   va_geom_triangles->finalize();
   return true;
 }
diff --git a/Geo/SPoint2.h b/Geo/SPoint2.h
index fae78d7c7a43a892c921a337a5c7bb1449a17506..9ca2c07e70d4cfa1ae3ec774922ef8c9cf600e96 100644
--- a/Geo/SPoint2.h
+++ b/Geo/SPoint2.h
@@ -30,6 +30,13 @@ class SPoint2 {
   void operator*=(double mult);
   SPoint2 operator*(double mult);
   operator double *() { return P; }
+  bool operator < (const SPoint2  &other) const
+  {
+    if(other.P[0] < P[0]) return true;
+    if(other.P[0] > P[0]) return false;
+    if(other.P[1] < P[1]) return true;
+    return false;
+  }
 };
 
 inline SPoint2 operator + (const SPoint2 &a, const SPoint2 &b)