/*****
 * drawsurface.h
 *
 * Stores a surface that has been added to a picture.
 *****/

#ifndef DRAWSURFACE_H
#define DRAWSURFACE_H

#include "drawelement.h"
#include "arrayop.h"
#include "path3.h"
#include "beziercurve.h"
#include "bezierpatch.h"

namespace run {
void inverse(double *a, size_t n);
}

const string need3pens="array of 3 pens required";

namespace camp {

#ifdef HAVE_LIBGLM
void storecolor(GLfloat *colors, int i, const vm::array &pens, int j);
#endif

class drawSurface : public drawElement {
protected:
  triple *controls;
  size_t ncontrols;
  triple center;
  bool straight; // True iff Bezier patch is planar and has straight edges.
  prc::RGBAColour diffuse;
  prc::RGBAColour emissive;
  prc::RGBAColour specular;
  prc::RGBAColour *colors;
  double opacity;
  double shininess;
  double metallic;
  double fresnel0;
  bool invisible;
  size_t centerIndex;
  Interaction interaction;
  bool billboard;

  triple Min,Max;
  int digits;
  bool primitive;

public:
#ifdef HAVE_GL
  BezierCurve C;
  bool transparent;
#endif

  string wrongsize() {
    return (ncontrols == 16 ? "4x4" : "triangular")+
      string(" array of triples and array of 4 pens required");
  }

  void init() {
    billboard=interaction == BILLBOARD;
    centerIndex=0;
  }

  drawSurface(const vm::array& g, size_t ncontrols, const triple& center,
              bool straight, const vm::array&p, double opacity,
              double shininess, double metallic, double fresnel0,
              const vm::array &pens, Interaction interaction, int digits,
              bool primitive=true, const string& key="") :
    drawElement(key), ncontrols(ncontrols), center(center), straight(straight),
    opacity(opacity), shininess(shininess), metallic(metallic),
    fresnel0(fresnel0), interaction(interaction), digits(digits),
    primitive(primitive) {
    init();
    if(checkArray(&g) != 4 || checkArray(&p) != 3)
      reportError(wrongsize());

    size_t k=0;
    controls=new(UseGC) triple[ncontrols];
    for(unsigned int i=0; i < 4; ++i) {
      vm::array *gi=vm::read<vm::array*>(g,i);
      size_t n=(ncontrols == 16 ? 4 : i+1);
      if(checkArray(gi) != n)
        reportError(wrongsize());
      for(unsigned int j=0; j < n; ++j)
        controls[k++]=vm::read<triple>(gi,j);
    }

    pen surfacepen=vm::read<camp::pen>(p,0);
    invisible=surfacepen.invisible();

    diffuse=rgba(surfacepen);
    emissive=rgba(vm::read<camp::pen>(p,1));
    specular=rgba(vm::read<camp::pen>(p,2));

    size_t nodes=(ncontrols == 16 ? 4 : 3);
    size_t size=checkArray(&pens);
    if(size > 0) {
      if(size != nodes) reportError("one vertex pen required per node");
      colors=new(UseGC) prc::RGBAColour[nodes];
      for(size_t i=0; i < nodes; ++i)
        colors[i]=rgba(vm::read<camp::pen>(pens,i));
    } else colors=NULL;
  }

  drawSurface(const double* t, const drawSurface *s) :
    drawElement(s->KEY), ncontrols(s->ncontrols), straight(s->straight),
    diffuse(s->diffuse), emissive(s->emissive), specular(s->specular),
    colors(s->colors), opacity(s->opacity), shininess(s->shininess),
    metallic(s->metallic), fresnel0(s->fresnel0), invisible(s->invisible),
    interaction(s->interaction), digits(s->digits), primitive(s->primitive) {
    init();
    if(s->controls) {
      controls=new(UseGC) triple[ncontrols];
      for(unsigned int i=0; i < ncontrols; ++i)
        controls[i]=t*s->controls[i];
    } else controls=NULL;

    center=t*s->center;
  }

  double renderResolution() {
    double prerender=settings::getSetting<double>("prerender");
    if(prerender <= 0.0) return 0.0;
    prerender=1.0/prerender;
    double perspective=gl::orthographic ? 0.0 : 1.0/gl::Zmax;
    double s=perspective ? Min.getz()*perspective : 1.0; // Move to glrender
    triple b(gl::Xmin,gl::Ymin,gl::Zmin);
    triple B(gl::Xmax,gl::Ymax,gl::Zmax);
    pair size3(s*(B.getx()-b.getx()),s*(B.gety()-b.gety()));
    pair size2(gl::fullWidth,gl::fullHeight);
    return prerender*size3.length()/size2.length();
  }

  virtual ~drawSurface() {}

  bool is3D() {return true;}
};

class drawBezierPatch : public drawSurface {
public:
#ifdef HAVE_LIBGLM
  BezierPatch S;
#endif

  drawBezierPatch(const vm::array& g, const triple& center, bool straight,
                  const vm::array&p, double opacity, double shininess,
                  double metallic, double fresnel0, const vm::array &pens,
                  Interaction interaction, int digits, bool primitive) :
    drawSurface(g,16,center,straight,p,opacity,shininess,metallic,fresnel0,
                pens,interaction,digits,primitive) {}

  drawBezierPatch(const double* t, const drawBezierPatch *s) :
    drawSurface(t,s) {}

  void bounds(const double* t, bbox3& b);

  void ratio(const double* t, pair &b, double (*m)(double, double),
             double fuzz, bool &first);

  void meshinit() {
    if(billboard)
      centerIndex=centerindex(center);
  }

  bool write(prcfile *out, unsigned int *, double, groupsmap&);
  bool write(abs3Doutfile *out);

  void render(double, const triple& b, const triple& B,
              double perspective, bool remesh);
  drawElement *transformed(const double* t);
};

class drawBezierTriangle : public drawSurface {
public:
#ifdef HAVE_LIBGLM
  BezierTriangle S;
#endif

  drawBezierTriangle(const vm::array& g, const triple& center, bool straight,
                     const vm::array&p, double opacity, double shininess,
                     double metallic, double fresnel0, const vm::array &pens,
                     Interaction interaction, int digits, bool primitive) :
    drawSurface(g,10,center,straight,p,opacity,shininess,metallic,fresnel0,
                pens,interaction,digits,primitive) {}

  drawBezierTriangle(const double* t, const drawBezierTriangle *s) :
    drawSurface(t,s) {}

  void bounds(const double* t, bbox3& b);

  void ratio(const double* t, pair &b, double (*m)(double, double),
             double fuzz, bool &first);

  void meshinit() {
    if(billboard)
      centerIndex=centerindex(center);
  }

  bool write(prcfile *out, unsigned int *, double, groupsmap&);
  bool write(abs3Doutfile *out);

  void render(double, const triple& b, const triple& B,
              double perspective, bool remesh);
  drawElement *transformed(const double* t);
};

class drawNurbs : public drawElement {
protected:
  size_t udegree,vdegree;
  size_t nu,nv;
  triple *controls;
  double *weights;
  double *uknots, *vknots;
  prc::RGBAColour diffuse;
  prc::RGBAColour emissive;
  prc::RGBAColour specular;
  double opacity;
  double shininess;
  double metallic;
  double fresnel0;
  triple normal;
  bool invisible;

  triple Min,Max;

#ifdef HAVE_LIBGLM
  GLfloat *colors;
  GLfloat *Controls;
  GLfloat *uKnots;
  GLfloat *vKnots;
#endif

public:
  drawNurbs(const vm::array& g, const vm::array* uknot, const vm::array* vknot,
            const vm::array* weight, const vm::array&p, double opacity,
            double shininess, double metallic, double fresnel0,
            const vm::array &pens, const string& key="")
    : drawElement(key), opacity(opacity), shininess(shininess),
      metallic(metallic), fresnel0(fresnel0) {
    size_t weightsize=checkArray(weight);

    const string wrongsize="Inconsistent NURBS data";
    nu=checkArray(&g);

    if(nu == 0 || (weightsize != 0 && weightsize != nu) || checkArray(&p) != 3)
      reportError(wrongsize);

    vm::array *g0=vm::read<vm::array*>(g,0);
    nv=checkArray(g0);

    size_t n=nu*nv;
    controls=new(UseGC) triple[n];

    size_t k=0;
    for(size_t i=0; i < nu; ++i) {
      vm::array *gi=vm::read<vm::array*>(g,i);
      if(checkArray(gi) != nv)
        reportError(wrongsize);
      for(size_t j=0; j < nv; ++j)
        controls[k++]=vm::read<triple>(gi,j);
    }

    if(weightsize > 0) {
      size_t k=0;
      weights=new(UseGC) double[n];
      for(size_t i=0; i < nu; ++i) {
        vm::array *weighti=vm::read<vm::array*>(weight,i);
        if(checkArray(weighti) != nv)
          reportError(wrongsize);
        for(size_t j=0; j < nv; ++j)
          weights[k++]=vm::read<double>(weighti,j);
      }
    } else weights=NULL;

    size_t nuknots=checkArray(uknot);
    size_t nvknots=checkArray(vknot);

    if(nuknots <= nu+1 || nuknots > 2*nu || nvknots <= nv+1 || nvknots > 2*nv)
      reportError(wrongsize);

    udegree=nuknots-nu-1;
    vdegree=nvknots-nv-1;

    run::copyArrayC(uknots,uknot,0,UseGC);
    run::copyArrayC(vknots,vknot,0,UseGC);

    pen surfacepen=vm::read<camp::pen>(p,0);
    invisible=surfacepen.invisible();

    diffuse=rgba(surfacepen);
    emissive=rgba(vm::read<camp::pen>(p,1));
    specular=rgba(vm::read<camp::pen>(p,2));

#ifdef HAVE_LIBGLM
    Controls=NULL;
    int size=checkArray(&pens);
    if(size > 0) {
      colors=new(UseGC) GLfloat[16];
      if(size != 4) reportError(wrongsize);
      storecolor(colors,0,pens,0);
      storecolor(colors,8,pens,1);
      storecolor(colors,12,pens,2);
      storecolor(colors,4,pens,3);
    } else colors=NULL;
#endif
  }

  drawNurbs(const double* t, const drawNurbs *s) :
    drawElement(s->KEY), udegree(s->udegree), vdegree(s->vdegree), nu(s->nu),
    nv(s->nv), weights(s->weights), uknots(s->uknots), vknots(s->vknots),
    diffuse(s->diffuse),
    emissive(s->emissive), specular(s->specular), opacity(s->opacity),
    shininess(s->shininess), invisible(s->invisible) {

    const size_t n=nu*nv;
    controls=new(UseGC) triple[n];
    for(unsigned int i=0; i < n; ++i)
      controls[i]=t*s->controls[i];

#ifdef HAVE_LIBGLM
    Controls=NULL;
    colors=s->colors;
#endif
  }

  bool is3D() {return true;}

  void bounds(const double* t, bbox3& b);

  virtual ~drawNurbs() {}

  bool write(prcfile *out, unsigned int *, double, groupsmap&);

  void displacement();
  void ratio(const double* t, pair &b, double (*m)(double, double), double,
             bool &first);

  void render(double size2, const triple& b, const triple& B,
              double perspective, bool remesh);

  drawElement *transformed(const double* t);
};

// Draw a transformed PRC object.
class drawPRC : public drawElementLC {
protected:
  prc::RGBAColour diffuse;
  prc::RGBAColour emissive;
  prc::RGBAColour specular;
  double opacity;
  double shininess;
  double metallic;
  double fresnel0;
  bool invisible;
public:
  void init(const vm::array&p) {
    if(checkArray(&p) != 3)
      reportError(need3pens);

    pen surfacepen=vm::read<camp::pen>(p,0);
    invisible=surfacepen.invisible();

    diffuse=rgba(surfacepen);
    emissive=rgba(vm::read<camp::pen>(p,1));
    specular=rgba(vm::read<camp::pen>(p,2));
  }

  drawPRC(const vm::array& t, const vm::array&p, double opacity,
          double shininess, double metallic, double fresnel0) :
    drawElementLC(t), opacity(opacity), shininess(shininess),
    metallic(metallic), fresnel0(fresnel0) {
    init(p);
  }

  drawPRC(const vm::array&p, double opacity,
          double shininess, double metallic, double fresnel0) :
    drawElementLC(NULL), opacity(opacity), shininess(shininess),
    metallic(metallic), fresnel0(fresnel0) {
    init(p);
  }

  drawPRC(const double* t, const drawPRC *s) :
    drawElementLC(t,s), diffuse(s->diffuse),
    emissive(s->emissive), specular(s->specular), opacity(s->opacity),
    shininess(s->shininess), metallic(s->metallic), fresnel0(s->fresnel0),
    invisible(s->invisible) {
  }

  virtual void P(triple& t, double x, double y, double z);

  virtual bool write(prcfile *out, unsigned int *, double, groupsmap&) override {
    return true;
  }

  virtual bool write(abs3Doutfile *out) override {return true;}

  virtual void transformedbounds(const double*, bbox3&) {}
  virtual void transformedratio(const double*, pair&,
                                double (*)(double, double), double, bool&) {}

};

// Output a unit sphere primitive.
class drawSphere : public drawPRC {
  bool half;
  int type;
public:
  drawSphere(const vm::array& t, bool half, const vm::array&p, double opacity,
             double shininess, double metallic, double fresnel0, int type) :
    drawPRC(t,p,opacity,shininess,metallic,fresnel0), half(half), type(type) {}

  drawSphere(const double* t, const drawSphere *s) :
    drawElement(s->KEY), drawPRC(t,s), half(s->half), type(s->type) {}

  void P(triple& t, double x, double y, double z);

  bool write(prcfile *out, unsigned int *, double, groupsmap&);
  bool write(abs3Doutfile *out);

  drawElement *transformed(const double* t) {
    return new drawSphere(t,this);
  }
};

// Output a unit cylinder primitive.
class drawCylinder : public drawPRC {
  bool core;
public:
  drawCylinder(const vm::array& t, const vm::array&p,
               double opacity, double shininess, double metallic,
               double fresnel0, bool core=false) :
    drawPRC(t,p,opacity,shininess,metallic,fresnel0), core(core) {}

  drawCylinder(const double* t, const drawCylinder *s) :
    drawPRC(t,s), core(s->core) {}

  bool write(prcfile *out, unsigned int *, double, groupsmap&) override;
  bool write(abs3Doutfile *out) override;

  drawElement *transformed(const double* t) override {
    return new drawCylinder(t,this);
  }
};

// Draw a unit disk.
class drawDisk : public drawPRC {
public:
  drawDisk(const vm::array& t, const vm::array&p, double opacity,
           double shininess, double metallic, double fresnel0) :
    drawPRC(t,p,opacity,shininess,metallic,fresnel0) {}

  drawDisk(const double* t, const drawDisk *s) :
    drawPRC(t,s) {}

  bool write(prcfile *out, unsigned int *, double, groupsmap&) override;
  bool write(abs3Doutfile *out) override;

  drawElement *transformed(const double* t) override {
    return new drawDisk(t,this);
  }
};

// Draw a tube.
class drawTube : public drawPRC {
protected:
  triple *g;
  double width;
  triple m,M;
  bool core;
public:
  drawTube(const vm::array&G, double width, const vm::array&p, double opacity,
           double shininess, double metallic, double fresnel0,
           const triple& m, const triple& M, bool core) :
    drawPRC(p,opacity,shininess,metallic,fresnel0), width(width), m(m), M(M),
    core(core) {
    if(vm::checkArray(&G) != 4)
      reportError("array of 4 triples required");

    g=new(UseGC) triple[4];
    for(size_t i=0; i < 4; ++i)
      g[i]=vm::read<triple>(G,i);
  }

  drawTube(const double* t, const drawTube *s) :
    drawElement(s->KEY), drawPRC(t,s), width(s->width), m(s->m), M(s->M),
    core(s->core) {
    g=new(UseGC) triple[4];
    for(size_t i=0; i < 4; ++i)
      g[i]=t*s->g[i];
  }

  bool write(abs3Doutfile *out) override;

  drawElement *transformed(const double* t) override {
    return new drawTube(t,this);
  }
};


class drawBaseTriangles : public drawElement {
protected:
#ifdef HAVE_LIBGLM
  Triangles R;
  bool transparent;
#endif

public:
  bool billboard;
  size_t nP;
  triple* P;
  triple center;
  size_t nN;
  triple* N;
  size_t nI;
  size_t Ni;
  uint32_t (*PI)[3];
  uint32_t (*NI)[3];
  size_t centerIndex;
  Interaction interaction;

  triple Min,Max;

  static const string wrongsize;
  static const string outofrange;

public:
  void init() {
    billboard=interaction == BILLBOARD;
    centerIndex=0;
  }

  drawBaseTriangles(const vm::array& v, const vm::array& vi,
                    const triple& center,
                    const vm::array& n, const vm::array& ni,
                    Interaction interaction) : center(center),
                                               interaction(interaction) {
    init();
    nP=checkArray(&v);
    P=new(UseGC) triple[nP];
    for(size_t i=0; i < nP; ++i)
      P[i]=vm::read<triple>(v,i);

    nI=checkArray(&vi);
    PI=new(UseGC) uint32_t[nI][3];
    for(size_t i=0; i < nI; ++i) {
      vm::array *vii=vm::read<vm::array*>(vi,i);
      if(checkArray(vii) != 3) reportError(wrongsize);
      uint32_t *PIi=PI[i];
      for(size_t j=0; j < 3; ++j) {
        size_t index=unsignedcast(vm::read<Int>(vii,j));
        if(index >= nP) reportError(outofrange);
        PIi[j]=index;
      }
    }

    nN=checkArray(&n);
    if(nN) {
      N=new(UseGC) triple[nN];
      for(size_t i=0; i < nN; ++i)
        N[i]=vm::read<triple>(n,i);

      Ni=checkArray(&ni);
      if(Ni == 0 && nN == nP)
        NI=PI;
      else {
        if(Ni != nI)
          reportError("Index arrays have different lengths");
        NI=new(UseGC) uint32_t[nI][3];
        for(size_t i=0; i < nI; ++i) {
          vm::array *nii=vm::read<vm::array*>(ni,i);
          if(checkArray(nii) != 3) reportError(wrongsize);
          uint32_t *NIi=NI[i];
          for(size_t j=0; j < 3; ++j) {
            size_t index=unsignedcast(vm::read<Int>(nii,j));
            if(index >= nN) reportError(outofrange);
            NIi[j]=index;
          }
        }
      }
    } else Ni=0;
  }

#ifdef HAVE_LIBGLM
  drawBaseTriangles(const vertexBuffer& vb, const triple& center,
                    Interaction interaction, bool isColor,
                    const triple& Min, const triple& Max) :
    transparent(false),
    nP(isColor ? vb.Vertices.size() : vb.vertices.size()), center(center),
    nN(nP), nI(vb.indices.size()/3), Ni(0),
    interaction(interaction), Min(Min), Max(Max) {
    init();
    assert(vb.indices.size() % 3 == 0);
    P=new(UseGC) triple[nP];
    N=new(UseGC) triple[nN];
    if(!isColor) {
      for (size_t i=0; i < vb.vertices.size(); ++i) {
        P[i]=triple(vb.vertices[i].position[0], vb.vertices[i].position[1], vb.vertices[i].position[2]);
        N[i]=triple(vb.vertices[i].normal[0], vb.vertices[i].normal[1], vb.vertices[i].normal[2]);
      }
    }
    else {
      for (size_t i=0; i < vb.Vertices.size(); ++i) {
        P[i]=triple(vb.Vertices[i].position[0], vb.Vertices[i].position[1], vb.Vertices[i].position[2]);
        N[i]=triple(vb.Vertices[i].normal[0], vb.Vertices[i].normal[1], vb.Vertices[i].normal[2]);
      }
    }

    PI=new(UseGC) uint32_t[nI][3];
    for (size_t i=0; i < nI; ++i) {
      PI[i][0]=vb.indices[3 * i];
      PI[i][1]=vb.indices[3 * i + 1];
      PI[i][2]=vb.indices[3 * i + 2];
    }
    NI=PI;
  }
#endif

  drawBaseTriangles(const double* t, const drawBaseTriangles *s) :
    drawElement(s->KEY),
    nP(s->nP), nN(s->nN), nI(s->nI), Ni(s->Ni), interaction(s->interaction) {
    init();
    P=new(UseGC) triple[nP];
    for(size_t i=0; i < nP; i++)
      P[i]=t*s->P[i];

    PI=new(UseGC) uint32_t[nI][3];
    for(size_t i=0; i < nI; ++i) {
      uint32_t *PIi=PI[i];
      uint32_t *sPIi=s->PI[i];
      for(size_t j=0; j < 3; ++j)
        PIi[j]=sPIi[j];
    }

    center=t*s->center;

    if(nN) {
      N=new(UseGC) triple[nN];
      if(t == NULL) {
        for(size_t i=0; i < nN; i++)
          N[i]=s->N[i];
      } else {
        double T[]={t[0],t[1],t[2],
                    t[4],t[5],t[6],
                    t[8],t[9],t[10]};
        run::inverse(T,3);
        for(size_t i=0; i < nN; i++)
          N[i]=unit(Transform3(s->N[i],T));
      }

      if(Ni == 0) {
        NI=PI;
      } else {
        NI=new(UseGC) uint32_t[nI][3];
        for(size_t i=0; i < nI; ++i) {
          uint32_t *NIi=NI[i];
          uint32_t *sNIi=s->NI[i];
          for(size_t j=0; j < 3; ++j)
            NIi[j]=sNIi[j];
        }
      }
    }
  }

  bool is3D() {return true;}

  void bounds(const double* t, bbox3& b);

  void ratio(const double* t, pair &b, double (*m)(double, double),
             double fuzz, bool &first);

  void meshinit() {
    if(billboard)
      centerIndex=centerindex(center);
  }

  virtual ~drawBaseTriangles() {}

  drawElement *transformed(const double* t) {
    return new drawBaseTriangles(t,this);
  }
};

class drawTriangles : public drawBaseTriangles {
  size_t nC;
  prc::RGBAColour*C;
  uint32_t (*CI)[3];
  size_t Ci;

  // Asymptote material data
  prc::RGBAColour diffuse;
  prc::RGBAColour emissive;
  prc::RGBAColour specular;
  double opacity;
  double shininess;
  double metallic;
  double fresnel0;
  bool invisible;

public:
  drawTriangles(const vm::array& v, const vm::array& vi, const triple& center,
                const vm::array& n, const vm::array& ni,
                const vm::array&p, double opacity, double shininess,
                double metallic, double fresnel0,
                const vm::array& c, const vm::array& ci,
                Interaction interaction) :
    drawBaseTriangles(v,vi,center,n,ni,interaction), opacity(opacity),
    shininess(shininess), metallic(metallic), fresnel0(fresnel0) {

    if(checkArray(&p) != 3)
      reportError(need3pens);

    const pen surfacepen=vm::read<camp::pen>(p,0);
    invisible=surfacepen.invisible();
    diffuse=rgba(surfacepen);

    nC=checkArray(&c);
    if(nC) {
      C=new(UseGC) prc::RGBAColour[nC];
      for(size_t i=0; i < nC; ++i)
        C[i]=rgba(vm::read<camp::pen>(c,i));

      size_t nI=checkArray(&vi);

      Ci=checkArray(&ci);
      if(Ci == 0 && nC == nP)
        CI=PI;
      else {
        if(Ci != nI)
          reportError("Index arrays have different lengths");
        CI=new(UseGC) uint32_t[nI][3];
        for(size_t i=0; i < nI; ++i) {
          vm::array *cii=vm::read<vm::array*>(ci,i);
          if(checkArray(cii) != 3) reportError(wrongsize);
          uint32_t *CIi=CI[i];
          for(size_t j=0; j < 3; ++j) {
            size_t index=unsignedcast(vm::read<Int>(cii,j));
            if(index >= nC) reportError(outofrange);
            CIi[j]=index;
          }
        }
      }
    } else {
      emissive=rgba(vm::read<camp::pen>(p,1));
    }
    specular=rgba(vm::read<camp::pen>(p,2));
  }

#ifdef HAVE_LIBGLM
  drawTriangles(vertexBuffer const& vb, const triple &center, bool isColor,
                prc::RGBAColour diffuse,
                prc::RGBAColour emissive,
                prc::RGBAColour specular,
                double opacity,
                double shininess,
                double metallic,
                double fresnel0, Interaction interaction,
                bool invisible,
                const triple& Min, const triple& Max) :
    drawBaseTriangles(vb,center,interaction,isColor,Min,Max),
    nC(isColor ? vb.Vertices.size() : 0), C(nullptr),
    CI(isColor ? PI : nullptr),
    Ci(isColor ? Ni : 0),
    diffuse(diffuse), emissive(emissive), specular(specular),
    opacity(opacity), shininess(shininess),
    metallic(metallic), fresnel0(fresnel0), invisible(invisible) {
    if(isColor) {
      C=new(UseGC) prc::RGBAColour[nC];
      for(size_t i=0; i < nC; ++i) {
        C[i].Set(vb.Vertices[i].color[0],
                 vb.Vertices[i].color[1],
                 vb.Vertices[i].color[2],
                 vb.Vertices[i].color[3]);
      }
    }
  }
#endif

  drawTriangles(const double* t, const drawTriangles *s) :
    drawBaseTriangles(t,s), nC(s->nC),
    diffuse(s->diffuse), emissive(s->emissive),
    specular(s->specular), opacity(s->opacity), shininess(s->shininess),
    metallic(s->metallic), fresnel0(s->fresnel0), invisible(s->invisible) {

    if(nC) {
      C=new(UseGC) prc::RGBAColour[nC];
      for(size_t i=0; i < nC; ++i)
        C[i]=s->C[i];

      CI=new(UseGC) uint32_t[nI][3];
      for(size_t i=0; i < nI; ++i) {
        uint32_t *CIi=CI[i];
        uint32_t *sCIi=s->CI[i];
        for(size_t j=0; j < 3; ++j)
          CIi[j]=sCIi[j];
      }
    }
  }

  virtual ~drawTriangles() {}

  void render(double size2, const triple& b, const triple& B,
              double perspective, bool remesh);

  bool write(prcfile *out, unsigned int *, double, groupsmap&);
  bool write(abs3Doutfile *out);

  drawElement *transformed(const double* t) {
    return new drawTriangles(t,this);
  }
};

}

#endif
