// A module for reading simple obj files with groups.
// Authors: Jens Schwaiger and John Bowman
//
// Here simple means that :
//
// 1) all vertex statements should come before the face statements;
//
// 2) face informations with respect to texture and/or normal vectors are
// ignored;
//
// 3) face statements only contain positive numbers(no relative positions).
//
// The reading process only takes into account lines starting with "v" or
// "f" or "g"(group).

import three;

struct obj {
 surface s;
 material[] surfacepen;
 pen[] meshpen;

 path3[][] read(string datafile, bool verbose=false) {
   file in=input(datafile).word().line();
   triple[] vert;
   path3[][] g;
   g[0]=new path3[] ;
   string[] G;
   void Vertex(real x,real y ,real z) {vert.push((x,y,z));}
   void Face(int[] vertnr, int groupnr) {
     guide3 gh;
     for(int i=0; i < vertnr.length; ++i)
       gh=gh--vert[vertnr[i]-1];
     gh=gh--cycle;
     g[groupnr].push(gh);
   }
   if(verbose) write("Reading data from "+datafile+".");
   int groupnr;
   while(true) {
     string[] str=in;
     if(str.length == 0) break;
     str=sequence(new string(int i) {return split(str[i],"/")[0];},str.length);
     if(str[0] == "g" && str.length > 1) {
       int tst=find(G == str[1]);
       if(tst == -1) {
         G.push(str[1]);
         groupnr=G.length-1;
         g[groupnr]=new path3[] ;
       }
       if(tst > -1) groupnr=tst;
     }
     if(str[0] == "v") Vertex((real) str[1],(real) str[2],(real) str[3]);
     if(str[0] == "f") {
       int[] vertnr;
       for(int i=1; i < str.length; ++i) vertnr[i-1]=(int) str[i];
       Face(vertnr,groupnr);
     }
     if(eof(in)) break;
   }
   close(in);
   if(verbose) {
     write("Number of groups: ",G.length);
     write("Groups and their names:");
     write(G);
     write("Reading done.");
     write("Number of faces contained in the groups: ");
     for(int j=0; j < G.length; ++j)
       write(G[j],": ",(string) g[j].length);
   }
   return g;
 }

 void operator init(path3[][] g, material[] surfacepen, pen[] meshpen) {
   for(int i=0; i < g.length; ++i) {
     path3[] gi=g[i];
     for(int j=0; j < gi.length; ++j) {
       // Treat all faces as planar to avoid subdivision cracks.
       surface sij=surface(gi[j],planar=true);
       s.append(sij);
       this.surfacepen.append(array(sij.s.length,surfacepen[i]));
       this.meshpen.append(array(sij.s.length,meshpen[i]));
     }
   }
 }

 void operator init(string datafile, bool verbose=false,
                    material[] surfacepen, pen[] meshpen=nullpens) {
   operator init(read(datafile,verbose),surfacepen,meshpen);
 }

 void operator init(string datafile, bool verbose=false,
                    material surfacepen, pen meshpen=nullpen) {
   material[] surfacepen={surfacepen};
   pen[] meshpen={meshpen};
   surfacepen.cyclic=true;
   meshpen.cyclic=true;
   operator init(read(datafile,verbose),surfacepen,meshpen);
 }
}

obj operator * (transform3 T, obj o)
{
 obj ot;
 ot.s=T*o.s;
 ot.surfacepen=copy(o.surfacepen);
 ot.meshpen=copy(o.meshpen);
 return ot;
}

void draw(picture pic=currentpicture, obj o, light light=currentlight)
{
 draw(pic,o.s,o.surfacepen,o.meshpen,light);
}