Working with Patches

3DS Max Plug-In SDK

Working with Patches

See Also: Class Patch, Class PatchMesh, Class PatchObject, Class PatchVec, Class PatchVert, Class PatchEdge, Class TVPatch.

Overview

This section provides an overview of how to work with Patches. It covers the main classes used when working with patches, and an example of creating a patch mesh.

The SDK provides a set of classes for creating and working with patches. A Patch is a three or four sided surface. Each edge of a patch is a bezier curve. Each edge has a vertex at the end and a vector attached to each of those ends. Inside a four sided patch are four interior vertices used to generate the surface inside. Inside a three sided patch are three interior vertices. Normally these vertices are set to be automatically calculated by the program to maintain continuity at the corners. They can however be set to be user-definable interior points.

Overview of the Principal Classes

The SDK provides a set of classes for creating patch objects. These classes are listed below.

Class PatchObject

This is the base class for the creation of Patch Objects in MAX. The PatchObject is the object that flows down the 3ds max geometry pipeline. This object stores a PatchMesh as well as a Mesh cache. This is similar to the way a TriObject has a Mesh. The TriObject provides methods to manage the operations associated with flowing down the pipeline, while the Mesh stores the vertex, edge and face information. In the PatchObject case, the PatchObject provides methods for working with the pipeline while the Mesh cache is used for display in the viewports.

Class PatchMesh

A PatchMesh can be made up of any number of Patches. Each of these patches can be three or four sided. The topology of these patches are not quite as freeform as a regular TriObject mesh. For example, in a normal triangle mesh you can have an edge that is used by any number of faces. However with patches, in order for the system to figure out how the topology is connected together, each edge can only be used by either one patch (which makes it an open edge), or two patches (which makes it a transitional edge between the two).

Class Patch

A PatchMesh is made up of a series of Patch objects derived from this class. This is similar to the way faces relate to a mesh, i.e. a Patch is like the face, and the PatchMesh is like the Mesh. Each Patch can be three or four sided. The Patch contains the vertices (three or four), and a set of vectors (six or eight) that go between the vertices (two per edge). There are also a set of vertices in the interior of the patch (three or four of these).

Class PatchVec

This class represents a patch vector. This can be either an interior vector or an edge vector.

Class PatchVert

This class stores the information associated with a patch vertex and provides methods to access this data. This information includes the vertex location, a table of vectors attached to the patch, and a table of patches using the vertex.

Class PatchEdge

This class describes a patch edge using the vertices at the edge ends, and the indices of the patches sharing the edge.

Class TVPatch

This class is a texture vertex patch structure. This is similar to the TVFace class used with a Mesh.

Main Methods in Creating a Patch

A good sample to look at to see how patches are created is the patch grid plug-in. The source code is in \MAXSDK\SAMPLES\OBJECTS\PATCHGRD.CPP.

 

The code below is a portion of the source of this plug-in. This section discusses some of the key methods used in the creation of the patch.

// Quad patch layout

//

// A---> ad ----- da <---D

// | |

// | |

// v v

// ab i1 i4 dc

//

// | |

// | |

//

// ba i2 i3 cd

// ^ ^

// | |

// | |

// B---> bc ----- cb <---C

//

// Vertices ( a b c d ) are in counter clockwise order when viewed from

// outside the surface

void QuadPatchObject::BuildPatch(TimeValue t,PatchMesh& amesh)

 {

 int ix,iy,np,kv;

 int wsegs,lsegs,nv;

 Point3 v,p;

 float l, w;

 // Start the validity interval at forever and widdle it down.

 ivalid = FOREVER;

 pblock->GetValue( PB_LENGTH, t, l, ivalid );

 pblock->GetValue( PB_WIDTH, t, w, ivalid );

 pblock->GetValue( PB_LSEGS, t, lsegs, ivalid );

 pblock->GetValue( PB_WSEGS, t, wsegs, ivalid );

 int lv = lsegs + 1;

 int wv = wsegs + 1;

 int nverts = lv * wv;

 int npatches = lsegs * wsegs;

 int nexteriors = npatches * 4 + lsegs * 2 + wsegs * 2;

 int ninteriors = npatches * 4;

 int nvecs = ninteriors + nexteriors;

Here the number of vertices, texture vertices, vectors, patches and texture patches are established in the PatchMesh.

 amesh.setNumVerts(nverts);

 amesh.setNumTVerts(textured ? nverts : 0);

 amesh.setNumVecs(nvecs);

 amesh.setNumPatches(npatches);

 amesh.setNumTVPatches(textured ? npatches : 0);

 v = Point3(-w, -l, 0.0f) / 2.0f;

 float dx = w/wsegs;

 float dy = l/lsegs;

 float fws = (float)wsegs;

 float fls = (float)lsegs;

Next the vertices are created. The setVert() method of the PatchMesh is used to do this.

 // Create the vertices.

 nv = 0;

 p.z = v.z;

 p.y = v.y;

 for(iy=0; iy<=lsegs; iy++) {

  p.x = v.x;

  for (ix=0; ix<=wsegs; ix++) {

   if(textured)

    amesh.tVerts[nv] = UVVert((float)ix /

     fws, (float)iy / fls, 0.0f);

   amesh.verts[nv].flags = PVERT_COPLANAR;

   amesh.setVert(nv++, p);

   p.x += dx;

   }

  p.y += dy;

  }

Next the patches are created. The PatchMesh method MakeQuadPatch() is used once the vertices, vectors, and interiors are set up.

 // Create patches.

 np = 0;

 int interior = nexteriors;

 int vecRowInc = lsegs * 2;

 int vecColInc = wsegs * 2;

 for(iy=0; iy<lsegs; iy++) {

  kv = iy*(wsegs+1);

  int rv = iy * vecColInc; // Row vector start

  int cv = vecColInc * lv + iy * 2; // column vector start

  for (ix=0; ix<wsegs; ix++,++np) {

   Patch &p = amesh.patches[np];

   int a = kv, b = kv+1, c = kv+wsegs+2, d = kv + wsegs + 1;

   int ab = rv, ba = rv+1;

   int bc = cv+vecRowInc, cb = cv + vecRowInc + 1;

   int cd = rv+vecColInc+1, dc = rv+vecColInc;

   int da = cv + 1, ad = cv;

   amesh.MakeQuadPatch(np, a, ab, ba, b, bc, cb, c, cd, dc,

    d, da, ad, interior, interior+1, interior+2,

    interior+3, 1);

If textures are being used, then setTVerts() is called.

   if(textured) {

    TVPatch &tp = amesh.tvPatches[np];

    tp.setTVerts(a,b,c,d);

    }

Next the vectors are set using setVec(). One third of the distance from vertex a to vertex b is vector ab, and one third of the distance from vertex b to vertex a is ba.

   // Create the default vectors

   Point3 pa = amesh.getVert(a).p;

   Point3 pb = amesh.getVert(b).p;

   Point3 pc = amesh.getVert(c).p;

   Point3 pd = amesh.getVert(d).p;

   amesh.setVec(ab, pa + (pb - pa) / 3.0f);

   amesh.setVec(ba, pb - (pb - pa) / 3.0f);

   amesh.setVec(bc, pb + (pc - pb) / 3.0f);

   amesh.setVec(cb, pc - (pc - pb) / 3.0f);

   amesh.setVec(cd, pc + (pd - pc) / 3.0f);

   amesh.setVec(dc, pd - (pd - pc) / 3.0f);

   amesh.setVec(da, pd + (pa - pd) / 3.0f);

   amesh.setVec(ad, pa - (pa - pd) / 3.0f);

   kv++;

   cv += vecRowInc;

   rv += 2;

   interior += 4;

   }

  }

 // Verify that we have the right number of parts!

 assert(np==npatches);

 assert(nv==nverts);

Next the PatchMesh method buildLinkages() is called. This makes sure that everything is connected correctly and there are not any edges that are used by more than two patches. It will return FALSE if these conditions are not met.

 // Finish up patch internal linkages (and bail out if it fails!)

 assert(amesh.buildLinkages());

Next the method computeInteriors() is called. For any automatic patches, the interior vertices will be computed.

 // Calculate the interior bezier points on the PatchMesh's patches

 amesh.computeInteriors();

Then the geometry cache is cleared to make sure that any cache that might have been in the PatchMesh is emptied.

 amesh.InvalidateGeomCache();

Finally the validity of the Mesh cache is set to FALSE to indicate the PatchMesh has been changed.

 // Tell the PatchObject its mesh just got changed

 meshValid = FALSE;

 }

Patch Interpolation

The patch system supports any combination of quadrilateral and triangular patches, as long as they are set up so that a single edge is only shared by at most two patches.

Adjacent patches share control points and vectors on their common edges. This minimizes the amount of data and makes book keeping easier. It also guarantees matching patch edges.

The only problem with this system is that the 3ds max quad patches are bicubic (degree 3), and if they are connected to degree 3 triangular patches, discontinuities will result at the boundaries. The solution used in 3ds max is to store the patch information and allow users to manipulate it in a degree-3 manner (4 points on the side of a patch). When we go to interpolate it, however, we convert all the triangular patches' control points to degree 4, where they can be completely compatible with their quad neighbors.

Below is the source code for the interpolators for both the quad and tri patches.

Note: Interested developers should see the "Patch Tesselation" upload in the private Sparks Developer section on the webboard. Here developers can get a working example on how to tesselate a PatchMesh for use in a game engine. This modifier sample is available in the Sparks Developer section of the webboard at http://sparks.discreet.com

Also Note: The UpdateHooks() method shown below is modified from the original version in PATCH.CPP.

// Triangular patch interpolator

Point3 Patch::interp(PatchMesh *pMesh,float pu, float pv, float pw) {

 // It had better be a triangular patch!

 assert(type == PATCH_TRI);

 Point3 p;

 PatchVert *vp = pMesh->verts;

 PatchVec *vecp = pMesh->vecs;

 float pu2 = pu * pu;

 float pu3 = pu2 * pu;

 float pu4 = pu3 * pu;

 float pv2 = pv * pv;

 float pv3 = pv2 * pv;

 float pv4 = pv3 * pv;

 float pw2 = pw * pw;

 float pw3 = pw2 * pw;

 float pw4 = pw3 * pw;

 // Hold on to your hats -- Here it comes!

 p = vp[v[0]].p * pw4 +

aux[0] * 4.0f * pu * pw3 +

aux[1] * 6.0f * pu2 * pw2 +

aux[2] * 4.0f * pu3 * pw +

vp[v[1]].p * pu4 +

aux[3] * 4.0f * pv * pu3 +

aux[4] * 6.0f * pu2 * pv2 +

aux[5] * 4.0f * pv3 * pu +

  vp[v[2]].p * pv4 +

aux[6] * 4.0f * pw * pv3 +

aux[7] * 6.0f * pv2 * pw2 +

aux[8] * 4.0f * pw3 * pv +

  vecp[interior[0]].p * 12.0f * pu * pv * pw2 +

  vecp[interior[1]].p * 12.0f * pu2 * pv * pw +

  vecp[interior[2]].p * 12.0f * pu * pv2 * pw;

 return p;

 }

// Quadrilateral patch interpolator

Point3 Patch::interp(PatchMesh *pMesh,float pu, float pv) {

 // It had better be a quad patch!

 assert(type == PATCH_QUAD);

 Point3 p;

 PatchVert *vp = pMesh->verts;

 PatchVec *vecp = pMesh->vecs;

 float pu2 = pu * pu;

 float pu1 = 1.0f - pu;

 float pu12 = pu1 * pu1;

 float u0 = pu12 * pu1;

 float u1 = 3.0f * pu * pu12;

 float u2 = 3.0f * pu2 * pu1;

 float u3 = pu2 * pu;

 float pv2 = pv * pv;

 float pv1 = 1.0f - pv;

 float pv12 = pv1 * pv1;

 float v0 = pv12 * pv1;

 float v1 = 3.0f * pv * pv12;

 float v2 = 3.0f * pv2 * pv1;

 float v3 = pv2 * pv;

 // Hold on to your hats -- Here it comes!

 p =  vp[v[0]].p * u0 * v0 +

vecp[vec[7]].p * u1 * v0 +

vecp[vec[6]].p * u2 * v0 +

vp[v[3]].p * u3 * v0 +

  vecp[vec[0]].p * u0 * v1 +

vecp[interior[0]].p * u1 * v1 +

vecp[interior[3]].p * u2 * v1 +

vecp[vec[5]].p * u3 * v1 +

  vecp[vec[1]].p * u0 * v2 +

vecp[interior[1]].p * u1 * v2 +

vecp[interior[2]].p * u2 * v2 +

vecp[vec[4]].p * u3 * v2 +

  vp[v[1]].p * u0 * v3 +

vecp[vec[2]].p * u1 * v3 +

vecp[vec[3]].p * u2 * v3 +

vp[v[2]].p * u3 * v3;

 return p;

 }

Building the vertices are just a matter of looping through your patches and then running through each patches UV space to get the corresponding point on the surface. Below is sample showing the basics.

 float fpd = (float)patchDivs;

 for(px = 0; px < numPatches; ++px) {

  Patch &p = patches[px];

  switch(p.type) {

   case PATCH_TRI: {

    for(int ax = patchDivs - 1; ax > 0; --ax) {

     for(int bx = 1; bx < ax; ++bx) {

      float u = (float)bx / fpd;

      float v = (float)(patchDivs - ax) / fpd;

      float w = 1.0f - u - v; // Barycentric validity guaranteed!

      mesh.setVert(vert++, p.interp(this, u, v, w));

      }

     }

    }

    break;

   case PATCH_QUAD: {

    for(int u = 1; u < patchDivs; ++u) {

     float fu = (float)u / fpd;

     for(int v = 1; v < patchDivs; ++v) {

      float fv = (float)v / fpd;

      mesh.setVert(vert++, p.interp(this, fu, fv));

      }

     }

    }

    break;

   }

  }

Below is the sample code that puts it all together. It shows the interpolation of the surface points, the book keeping code for shared edges, the construction of the mesh faces, the handling of texture vertices and faces, and how bind patches are handled.

// vertices ( a b c d ) are in counter clockwise order when viewed from

// outside the surface

//watje 12-8-98

static void MakeQuad(int nVerts, Face *f, int a, int b , int c , int d, DWORD sm, MtlID m,

      int e1a, int e1b, int e1c,

      int e2a, int e2b, int e2c,

      int hide

      ) {

//DebugPrint("Make quad %d %d %d %d \n",a,b,c,d);

 assert(a<nVerts);

 assert(b<nVerts);

 assert(c<nVerts);

 assert(d<nVerts);

 f[0].setVerts( a, b, c);

 f[0].setSmGroup(sm);

 f[0].setEdgeVisFlags(e1a,e1b,e1c);

 f[0].setMatID(m);

 f[0].SetHide(hide);

 f[1].setVerts( c, d, a);

 f[1].setSmGroup(sm);

 f[1].setEdgeVisFlags(e2a,e2b,e2c);

 f[1].setMatID(m);

 f[1].SetHide(hide);

 }

//watje 12-8-98

#define MAKE_QUAD(na,nb,nc,nd,sm,m,e1a,e1b,e1c,e2a,e2b,e2c,hide) { MakeQuad(nVerts,&(mesh.faces[face]),na, nb, nc, nd, sm, m,e1a,e1b,e1c,e2a,e2b,e2c,hide); face+=2; }

// Make texture face quad

static void MakeTQuad(int chan, int nTVerts, TVFace *f, int a, int b , int c , int d) {

 assert(a<nTVerts);

 assert(b<nTVerts);

 assert(c<nTVerts);

 assert(d<nTVerts);

 f[0].setTVerts( a, b, c);

 f[1].setTVerts( c, d, a);

 }

#define MAKE_TQUAD(ch,na,nb,nc,nd) { MakeTQuad(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),na, nb, nc, nd); tface+=2; }

// vertices ( a b c d ) are in counter clockwise order when viewed from

// outside the surface

//watje 12-8-98

static void MakeTri(int nVerts, Face *f, int a, int b, int c, DWORD sm, MtlID m,

     int ea, int eb, int ec, int hide

     ) {

 assert(a<nVerts);

 assert(b<nVerts);

 assert(c<nVerts);

//DebugPrint("Making tri %d %d %d \n",a,b,c);

 f->setVerts( a, b, c);

 f->setSmGroup(sm);

 f->setEdgeVisFlags(ea,eb,ec);

 f->setMatID(m);

 f->SetHide(hide);

 }

//watje 12-8-98

#define MAKE_TRI(na,nb,nc,sm,mat,ea,eb,ec,hide) { MakeTri(nVerts,&(mesh.faces[face]),na, nb, nc, sm, mat,ea,eb,ec,hide); face++; }

#define MAKE_QUAD_SPECIAL(na,nm,nb,nc,sm,mat,ea,eb,ec,ed,hide)    \

{ MakeTri(nVerts,&(mesh.faces[face]),na, nm, nc, sm, mat,ea,0,ed,hide); face++; \

  MakeTri(nVerts,&(mesh.faces[face]),nm, nb, nc, sm, mat,eb,ec,0,hide); face++; }

#define MAKE_PENTA_EDGE1(na,nm,nb,nc,nd,sm,mat,ea,eb,ec,ed,hide) {      \

MakeTri(nVerts,&(mesh.faces[face]),na, nm, nc, sm, mat,ea,0,0,hide); face++; \

  MakeTri(nVerts,&(mesh.faces[face]),nm, nb, nc, sm, mat,ea,eb,0,hide); face++; \

  MakeTri(nVerts,&(mesh.faces[face]),nc, nd, na, sm, mat,ec,ed,0,hide); face++; \

  }

#define MAKE_PENTA_EDGE2(na,nb,nm,nc,nd,sm,mat,ea,eb,ec,ed,hide) {      \

  MakeTri(nVerts,&(mesh.faces[face]),na,nb,nm, sm, mat,ea,eb,0,hide); face++;  \

  MakeTri(nVerts,&(mesh.faces[face]),na, nm, nc, sm, mat,0,eb,0,hide); face++; \

  MakeTri(nVerts,&(mesh.faces[face]),nc, nd, na, sm, mat,ec,ed,0,hide); face++; \

  }                    

#define MAKE_PENTA_EDGE3(na,nb,nc,nm,nd,sm,mat,ea,eb,ec,ed,hide) {      \

  MakeTri(nVerts,&(mesh.faces[face]),na,nb,nc, sm, mat,ea,eb,0,hide); face++; \

  MakeTri(nVerts,&(mesh.faces[face]),nc, nm, na, sm, mat,ec,0,0,hide); face++; \

  MakeTri(nVerts,&(mesh.faces[face]),nm, nd, na, sm, mat,ec,ed,0,hide); face++; \

  }                    

#define MAKE_PENTA_EDGE4(na,nb,nc,nd,nm,sm,mat,ea,eb,ec,ed,hide) {      \

  MakeTri(nVerts,&(mesh.faces[face]),na,nb,nc, sm, mat,ea,eb,0,hide); face++; \

  MakeTri(nVerts,&(mesh.faces[face]),nc, nd, nm, sm, mat,ec,ed,0,hide); face++; \

  MakeTri(nVerts,&(mesh.faces[face]),nc, nm, na, sm, mat,0,ed,0,hide); face++; \

  }                    

// Make texture face triangle

static void MakeTTri(int chan, int nTVerts, TVFace *f, int a, int b, int c) {

 assert(a<nTVerts);

 assert(b<nTVerts);

 assert(c<nTVerts);

 f->setTVerts( a, b, c);

 }

#define MAKE_TTRI(ch,na,nb,nc) { MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),na, nb, nc); tface++; }

#define MAKE_TQUAD_SPECIAL(ch,na,nm,nb,nc) \

{ MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),na, nm, nc); tface++; \

 MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),nm, nb, nc); tface++; \

tvertHookStart++; }

#define MAKE_TPENTA_EDGE1(ch,na,nm,nb,nc,nd) \

{          \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),na, nm, nc); tface++; \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),nm, nb, nc); tface++; \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),nc, nd, na); tface++; \

tvertHookStart++;   }

#define MAKE_TPENTA_EDGE2(ch,na,nb,nm,nc,nd) \

{          \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),na, nb, nm); tface++; \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),na, nm, nc); tface++; \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),nc, nd, na); tface++; \

tvertHookStart++;   }

#define MAKE_TPENTA_EDGE3(ch,na,nb,nc,nm,nd) \

{          \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),na, nb, nc); tface++; \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),nc, nm, na); tface++; \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),nm, nd, na); tface++; \

tvertHookStart++;   }

#define MAKE_TPENTA_EDGE4(ch,na,nb,nc,nd,nm) \

{          \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),na, nb, nc); tface++; \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),nc, nd, nm); tface++; \

MakeTTri(ch, nTVerts[ch], &(mesh.mapFaces(ch)[tface]),nc, nm, na); tface++; \

tvertHookStart++;   }

void PatchMesh::PrepareMesh() {

 // If mesh is already there, just return it!

 if(meshValid && cacheSteps == GetMeshSteps() && cacheAdaptive == GetAdaptive())

  return;

 // TH 3/23/99 -- Added this because something in the pipeline is updating the

 // vert/vector coords without calling computeInteriors()!

 computeAux();

 // Keep a record of the settings used for the cache

 cacheSteps = GetMeshSteps();

 cacheAdaptive = GetAdaptive();

 int patchDivs = cacheAdaptive ? 11 : cacheSteps + 1 ;

 float fpd = (float)patchDivs;

 int workSteps = patchDivs - 1;

 int px;

 int nFaces = 0;

 int nVerts = 0;

 // Calc the number of verts and faces in the mesh

 nVerts += numVerts;       // Corner verts

 nVerts += (getNumEdges() * (patchDivs - 1));  // Edge verts

// watje compute the hook verts additions

 nVerts += hooks.Count() * (patchDivs) - hooks.Count() - (hooks.Count() *2 * (patchDivs - 1));

 // Precalculate interior vertex counts

 int quadint = (patchDivs - 1) * (patchDivs - 1);

 int triint = (patchDivs + 1) * (patchDivs + 2) / 2 - patchDivs * 3;

 // Precalculate number of texture verts

 int quadTVerts = (patchDivs + 1) * (patchDivs + 1);

 int triTVerts = (patchDivs + 1) * (patchDivs + 2) / 2;

 // Now the interior verts

 for(px = 0; px < numPatches; ++px) {

  switch(patches[px].type) {

   case PATCH_TRI:

    nFaces += (patchDivs * patchDivs);

    nVerts += triint;

    break;

   case PATCH_QUAD:

    nFaces += (patchDivs * patchDivs * 2);

    nVerts += quadint;

    break;

   }

  }

// watje compute the hook face additions

 nFaces += hooks.Count() * (patchDivs);

// watje compute the hook texture verts additions

 // Build the vertices!

 mesh.setNumVerts(nVerts);

 // The first vertices in the Mesh match those in the PatchMesh

 for(int i = 0; i < numVerts; ++i)

  mesh.setVert(i, verts[i].p);

 int edgeBase = numVerts;

 int vert = edgeBase;

 // Now we put in the edge vertices

 // Set up a spline object we'll use for ease of edge interpolation

 Spline3D work;

 work.AddKnot(SplineKnot(KTYPE_BEZIER, LTYPE_CURVE, zeroPoint, zeroPoint, zeroPoint));

 work.AddKnot(SplineKnot(KTYPE_BEZIER, LTYPE_CURVE, zeroPoint, zeroPoint, zeroPoint));

 Tab<int> hookEdges;

 hookEdges.SetCount(getNumEdges());

 Tab<int> hookEdgeList;

 hookEdgeList.SetCount(hooks.Count() * (patchDivs*2-1));

 BitArray eEdges;

 eEdges.SetSize(getNumEdges());

 eEdges.ClearAll();

 for(i = 0; i < getNumEdges(); i++) {

  hookEdges[i] = -1;

  }

 for(i = 0; i < hooks.Count(); i++)

  { 

  hookEdges[hooks[i].hookEdge] = i;

  int a = hooks[i].upperEdge;

  int b = hooks[i].lowerEdge;

  eEdges.Set(a);

  eEdges.Set(b);

  }

 Tab<int> edgeStart;

 Tab<int> edgeEnd;

 Tab<int> hookEdgeStart;

 Tab<int> hookEdgeEnd;

 edgeStart.SetCount(getNumEdges());

 edgeEnd.SetCount(getNumEdges());

 hookEdgeStart.SetCount(hooks.Count());

 hookEdgeEnd.SetCount(hooks.Count());

 int ct = 0;

 for(i = 0; i < getNumEdges(); ++i) {

  PatchEdge &edge = edges[i];

  work.SetKnotPoint(0, verts[edge.v1].p);

  work.SetOutVec(0, vecs[edge.vec12].p);

  work.SetInVec(1, vecs[edge.vec21].p);

  work.SetKnotPoint(1, verts[edge.v2].p);

//need to check if edge is hooked if so double the number ofpoints - 1

  if (hookEdges[i]!=-1)

   {

//need to take out middle point since this the corner of another patch

   edgeStart[i] = vert;

   int index = hookEdges[i];

   hookEdgeStart[index]=ct;

   edgeStart[hooks[index].upperEdge] = vert;

   int mid = (patchDivs+patchDivs)/2;

   for(int terp = 1; terp < (patchDivs+patchDivs); ++terp)

    {

//add ehook edges upper start

    if (terp != mid)

     {

     hookEdgeList[ct++] = vert;

     mesh.setVert(vert++, work.InterpCurve3D((float)terp / (fpd+fpd)));

     }

    else

     {

//add ehook edges upper end

     hookEdgeList[ct++] = hooks[index].hookPoint;

     edgeEnd[hooks[index].upperEdge] = vert-1;

//add ehook edges lower start

     edgeStart[hooks[index].lowerEdge] = vert;

     }

    }

//add ehook edges lower end

   hookEdgeEnd[index]=ct-1;

   edgeEnd[hooks[index].lowerEdge] = vert-1;

//check if these need to be flipped

   int u,l;//,h;

   u = edge.v1;

   l = edge.v2;

   PatchEdge eu = edges[hooks[index].upperEdge];

   PatchEdge el = edges[hooks[index].lowerEdge];

   if (u != eu.v1)

    {

    int t = edgeEnd[hooks[index].upperEdge];

    edgeEnd[hooks[index].upperEdge] = edgeStart[hooks[index].upperEdge];

    edgeStart[hooks[index].upperEdge] = t;

    }

   if (l != el.v2)

    {

    int t = edgeEnd[hooks[index].lowerEdge];

    edgeEnd[hooks[index].lowerEdge] = edgeStart[hooks[index].lowerEdge];

    edgeStart[hooks[index].lowerEdge] = t;

    }

   edgeEnd[i] = vert-1;

   }

  else

   {

//look for edges that are share with a hook patch and don't use since we already

   if ( !eEdges[i] )

    {

    edgeStart[i] = vert;

    for(int terp = 1; terp < patchDivs; ++terp)

     {

     mesh.setVert(vert++, work.InterpCurve3D((float)terp / fpd));

     }

    edgeEnd[i] = vert-1;

    }

   }

  }

 // Now generate the patch interior points

 int interiorBase = vert;

 IntTab patchVOff;

 for(px = 0; px < numPatches; ++px) {

  patchVOff.Append(1, &vert);  // Keep a record of this patch's starting interior vertex

  Patch &p = patches[px];

  switch(p.type) {

   case PATCH_TRI: {

    for(int ax = patchDivs - 1; ax > 0; --ax) {

     for(int bx = 1; bx < ax; ++bx) {

      float u = (float)bx / fpd;

      float v = (float)(patchDivs - ax) / fpd;

      float w = 1.0f - u - v;  // Barycentric validity guaranteed!

      mesh.setVert(vert++, p.interp(this, u, v, w));

      }

     }

    }

    break;

   case PATCH_QUAD: {

    for(int u = 1; u < patchDivs; ++u) {

     float fu = (float)u / fpd;

     for(int v = 1; v < patchDivs; ++v) {

      float fv = (float)v / fpd;

      mesh.setVert(vert++, p.interp(this, fu, fv));

      }

     }

    }

    break;

   }

  }

 assert(vert == nVerts);

  

 // Build the faces!

 // This is a bit tricky because we're sharing vertices at patch edges

 mesh.setNumFaces(nFaces);

 int face = 0;

//watje 12-10-98

 BOOL hidden;

//build hook patch db so we can quicky determine which edge is hooked

 Tab<int> hookPatches;

 Tab<int> hookPoints;

 hookPatches.SetCount(numPatches);

 hookPoints.SetCount(numPatches);

 for(px = 0; px < numPatches; px++)

  {

  hookPatches[px] = 0;

  hookPoints[px] = -1;

  }

 for(px = 0; px < hooks.Count(); px++)

  {

  int a= hooks[px].hookPatch;

//find which edge is the hook

  int ea,eb;

  ea = hooks[px].upperPoint;

  eb = hooks[px].lowerPoint;

  Patch p = patches[a];

  int pa,pb,pc,pd;

  pa = p.v[0];

  pb = p.v[1];

  pc = p.v[2];

  if (p.type == PATCH_TRI)

   pd = -1;

  else pd = p.v[3];

//check ab edge

  if ( ((pa==ea) && (pb==eb)) ||

    ((pa==eb) && (pb==ea)) )

   {

   hookPatches[a] |= 1;

   hookPoints[a] = px;

   }

//check bc edge

  else if ( ((pb==ea) && (pc==eb)) ||

    ((pb==eb) && (pc==ea)) )

   {

   hookPatches[a] |= 2;

   hookPoints[a] = px;

   }

//check if tri

  else if (pd == -1)

   {

 //check ca edge

   if ( ((pc==ea) && (pa==eb)) ||

    ((pc==eb) && (pa==ea)) )

    {

    hookPatches[a] |= 4;

    hookPoints[a] = px;

    }

   }

  else

   {

 //check cd edge

   if ( ((pc==ea) && (pd==eb)) ||

    ((pc==eb) && (pd==ea)) )

    {

    hookPatches[a] |= 4;

    hookPoints[a] = px;

    }

 //check da edge

   else if ( ((pd==ea) && (pa==eb)) ||

      ((pd==eb) && (pa==ea)) )

    {

    hookPatches[a] |= 8;

    hookPoints[a] = px;

    }

   }

  }

 // Table used to store texture vertex base for each patch

 Tab<IntTab *> patchTVOff;

 Tab<int> nTVerts;

 patchTVOff.SetCount (getNumMaps());

 for (int chan=0; chan<getNumMaps(); chan++) patchTVOff[chan] = NULL;

 nTVerts.SetCount (getNumMaps());

 int tvertHookStart;

 // Build texture verts if necessary

 for(chan = 0; chan < getNumMaps(); ++chan) {

  nTVerts[chan] = 0;

  if(tvPatches[chan]) {

   patchTVOff[chan] = new IntTab;

   for(px = 0; px < numPatches; ++px) {

    Patch &p = patches[px];

    patchTVOff[chan]->Append(1, &nTVerts[chan]);

    switch(p.type) {

     case PATCH_TRI:

      nTVerts[chan] += (patchDivs + 1) * (patchDivs + 2) / 2;

      break;

     case PATCH_QUAD:

      nTVerts[chan] += (patchDivs + 1) * (patchDivs + 1);

      break;

     }

    }

//add in additional vertices for hooks

   int hv = hooks.Count() * (patchDivs);

   tvertHookStart = nTVerts[chan];

   nTVerts[chan] += hv;

   mesh.setMapSupport (chan);

   mesh.setNumMapVerts (chan, nTVerts[chan]);

   // Generate the texture verts!

   for(px = 0; px < numPatches; ++px) {

    Patch &p = patches[px];

    TVPatch &tp = getMapPatch (chan,px);

    int tvert = (*patchTVOff[chan])[px];

    switch(p.type) {

     case PATCH_TRI: {

      UVVert &t0 = tVerts[chan][tp.tv[0]];

      UVVert &t1 = tVerts[chan][tp.tv[1]];

      UVVert &t2 = tVerts[chan][tp.tv[2]];

      for(int ax = patchDivs; ax >= 0; --ax) {

       for(int bx = 0; bx <= ax; ++bx) {

        float u = (float)bx / fpd;

        float v = (float)(patchDivs - ax) / fpd;

        float w = 1.0f - u - v;  // Barycentric validity guaranteed!

        Point3 vertLoc = t0 * w + t1 * u + t2 * v;

        mesh.setMapVert (chan, tvert++, vertLoc);

       }

       }

      }

      break;

     case PATCH_QUAD: {

      UVVert &t0 = tVerts[chan][tp.tv[0]];

      UVVert &t1 = tVerts[chan][tp.tv[1]];

      UVVert &t2 = tVerts[chan][tp.tv[2]];

      UVVert &t3 = tVerts[chan][tp.tv[3]];

      for(int v = 0; v <= patchDivs; ++v) {

       float fv = (float)v / fpd;

       UVVert iv1 = t0 + (t3 - t0) * fv;

       UVVert iv2 = t1 + (t2 - t1) * fv;

       for(int u = 0; u <= patchDivs; ++u) {

        float fu = (float)u / fpd;

        UVVert iu = iv1 + (iv2 - iv1) * fu;

        mesh.setMapVert (chan, tvert++, iu);

        }

       }

      }

      break;

     }

    }

//now add additional hook points

   int tvIndex = tvertHookStart;

   for(px = 0; px < numPatches; ++px)

    {

    Patch &p = patches[px];

    TVPatch &tp = getMapPatch (chan,px);

    UVVert t0;

    UVVert t1;

    if (hookPatches[px]&1)

     {

     t0 = tVerts[chan][tp.tv[0]];

     t1 = tVerts[chan][tp.tv[1]];

     }

    else if (hookPatches[px]&2)

     {

     t0 = tVerts[chan][tp.tv[1]];

     t1 = tVerts[chan][tp.tv[2]];

     }

    else if (hookPatches[px]&4)

     {

     if (p.type == PATCH_TRI)

      {

      t0 = tVerts[chan][tp.tv[2]];

      t1 = tVerts[chan][tp.tv[0]];

      }

     else

      { 

      t0 = tVerts[chan][tp.tv[2]];

      t1 = tVerts[chan][tp.tv[3]];

      }

     }

    else if (hookPatches[px]&8)

     {

     t0 = tVerts[chan][tp.tv[3]];

     t1 = tVerts[chan][tp.tv[0]];

     }

    if (hookPatches[px]!=0)

     {

     Point3 vec = (t1-t0)/(fpd*2.0f);

//put first in TV for start corner

     t0 += vec;

     mesh.setMapVert (chan, tvIndex++, t0);

//put last in TV for end corner

     if (patchDivs > 1)

      {

      Point3 tEnd;

      tEnd = t0+ (vec * ((float)(patchDivs*2)-2.0f));

      mesh.setMapVert (chan, tvIndex++, tEnd);

//put middle in TV for edges

      t0 += vec;

      for(int v = 2; v <= ((patchDivs*2)-3); ++v) {

       if ((v%2) == 1) mesh.setMapVert (chan, tvIndex++, t0);

       t0 += vec;

       }

      }

     }

    }

   if (nTVerts[chan]!=tvIndex)

    {

DebugPrint("error\n");

    }

   }

  else {

   mesh.setMapSupport (chan, 0);

  }

 }

 for(px = 0; px < numPatches; ++px) {

  Patch &p = patches[px];

  int baseVert = patchVOff[px]; // Interior vertex base

  MtlID matid = p.getMatID();

  hidden = p.IsHidden();

  switch(p.type) {

   case PATCH_TRI: {

    int vbase = baseVert;

    // If only need to make 1 triangle, make it and blow!

    //no need to check if it is a hooked patch is so one edge or modre needs to be split

//watje 12-10-98

    if(workSteps == 0) {

     if (hookPatches[px]==0)

      {

      MAKE_TRI(p.v[0],p.v[1],p.v[2], p.smGroup, matid,1,1,1,hidden);

      }

     else

      {

      if (hookPatches[px]&1)

       {

       int ex = hooks[hookPoints[px]].hookPoint;

       MAKE_QUAD_SPECIAL(p.v[0],ex,p.v[1],p.v[2], p.smGroup, matid,1,1,1,1,hidden);

       }

      else if (hookPatches[px]&2)

       {

       int ex = hooks[hookPoints[px]].hookPoint;

       MAKE_QUAD_SPECIAL(p.v[1],ex,p.v[2],p.v[0], p.smGroup, matid,1,1,1,1,hidden);

       }

      else if (hookPatches[px]&4)

       {

       int ex = hooks[hookPoints[px]].hookPoint;

       MAKE_QUAD_SPECIAL(p.v[2],ex,p.v[0],p.v[1], p.smGroup, matid,1,1,1,1,hidden);

       }

      }

     break;

     }

    if(workSteps > 2) {

     // Make inside faces if necessary

     for(int ax = 0; ax < (workSteps - 2); ++ax) {

      int vix = vbase;

      for(int bx = 0; bx < (workSteps - 2 - ax); ++bx, ++vix)

//watje  12-10-98     

       {

       if (showInterior)

        {

        MAKE_TRI(vix, vix + 1, vix + workSteps - 1 - ax, p.smGroup, matid,1,1,1,hidden);

        }

       else{

        MAKE_TRI(vix, vix + 1, vix + workSteps - 1 - ax, p.smGroup, matid,0,0,0,hidden);

        }

       }

      vix = vbase;

//watje  12-10-98     

      for(bx = 1; bx < (workSteps - 2 - ax); ++bx, ++vix)

       {

//watje

       if (showInterior)

        {

        MAKE_TRI(vix + 1, vix + workSteps - ax, vix + workSteps - 1 - ax, p.smGroup, matid,1,1,1,hidden);

        }

       else{

        MAKE_TRI(vix + 1, vix + workSteps - ax, vix + workSteps - 1 - ax, p.smGroup, matid,0,0,0,hidden);

        }

       }

      vbase += (workSteps - 1 - ax);

      }

     }

    // Make corners

    int edge0 = p.edge[0];

    int edge1 = p.edge[1];

    int edge2 = p.edge[2];

    PatchEdge &e0 = edges[edge0];

    PatchEdge &e1 = edges[edge1];

    PatchEdge &e2 = edges[edge2];

    int e0start,e1start,e2start,e0end,e1end,e2end,e0dir,e1dir,e2dir;

    if(e0.v1 == p.v[0]) {

     e0start = edgeStart[edge0];

     e0end = edgeEnd[edge0];

     e0dir = 1;

     }

    else {

     e0start = edgeEnd[edge0];

     e0end = edgeStart[edge0];

     e0dir = -1;

     }

    if(e1.v1 == p.v[1]) {

     e1start = edgeStart[edge1];

     e1end = edgeEnd[edge1];

     e1dir = 1;

     }

    else {

     e1start = edgeEnd[edge1];

     e1end = edgeStart[edge1];

     e1dir = -1;

     }

    if(e2.v1 == p.v[2]) {

     e2start = edgeStart[edge2];

     e2end = edgeEnd[edge2];

     e2dir = 1;

     }

    else {

     e2start = edgeEnd[edge2];

     e2end = edgeStart[edge2];

     e2dir = -1;

     }

    if (e2start > e2end) e2dir = -1;

     else e2dir = 1;

    if (e1start > e1end) e1dir = -1;

     else e1dir = 1;

    if (e0start > e0end) e0dir = -1;

     else e0dir = 1;

    // Create the corners

    int i0 = baseVert;

    int i1 = baseVert + (workSteps - 2);

    int i2 = baseVert + triint - 1;

    if (showInterior)

     {

     if (hookPatches[px]&1)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_QUAD_SPECIAL(p.v[0],s1,s2,e2end, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_QUAD_SPECIAL(e1,e2,p.v[1],e1start, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_TRI(p.v[2], e2start, e1end, p.smGroup, matid,1,1,1,hidden);

      }

     else if (hookPatches[px]&2)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_TRI(p.v[0], e0start, e2end, p.smGroup, matid,1,1,1,hidden);

      MAKE_QUAD_SPECIAL(p.v[1],s1,s2,e0end, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_QUAD_SPECIAL(e1,e2,p.v[2],e2start, p.smGroup, matid,1,1,1,1,hidden);

      }

     else if (hookPatches[px]&4)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_TRI(p.v[1], e1start, e0end, p.smGroup, matid,1,1,1,hidden);

      MAKE_QUAD_SPECIAL(p.v[2],s1,s2,e1end, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_QUAD_SPECIAL(e1,e2,p.v[0],e0start, p.smGroup, matid,1,1,1,1,hidden);

      }

     else

      {

      MAKE_TRI(p.v[0], e0start, e2end, p.smGroup, matid,1,1,1,hidden);

      MAKE_TRI(p.v[1], e1start, e0end, p.smGroup, matid,1,1,1,hidden);

      MAKE_TRI(p.v[2], e2start, e1end, p.smGroup, matid,1,1,1,hidden);

      }

     }

    else

     {

     if (hookPatches[px]&1)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_QUAD_SPECIAL(p.v[0],s1,s2,e2end, p.smGroup, matid,1,1,0,1,hidden);

      MAKE_QUAD_SPECIAL(e1,e2,p.v[1],e1start, p.smGroup, matid,1,1,1,0,hidden);

      MAKE_TRI(p.v[2], e2start, e1end, p.smGroup, matid,1,0,1,hidden);

      }

     else if (hookPatches[px]&2)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_TRI(p.v[0], e0start, e2end, p.smGroup, matid,1,0,1,hidden);

      MAKE_QUAD_SPECIAL(p.v[1],s1,s2,e0end, p.smGroup, matid,1,1,0,1,hidden);

      MAKE_QUAD_SPECIAL(e1,e2,p.v[2],e2start, p.smGroup, matid,1,1,1,0,hidden);

      }

     else if (hookPatches[px]&4)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      

      MAKE_TRI(p.v[1], e1start,e0end, p.smGroup, matid,1,0,1,hidden);

      MAKE_QUAD_SPECIAL(p.v[2],s1,s2,e1end, p.smGroup, matid,1,1,0,1,hidden);

      MAKE_QUAD_SPECIAL(e1,e2,p.v[0],e0start, p.smGroup, matid,1,1,1,0,hidden);

      }

     else

      {

      MAKE_TRI(p.v[0], e0start, e2end, p.smGroup, matid,1,0,1,hidden);

      MAKE_TRI(p.v[1], e1start, e0end, p.smGroup, matid,1,0,1,hidden);

      MAKE_TRI(p.v[2], e2start, e1end, p.smGroup, matid,1,0,1,hidden);

      }

     }

    // Create the edges, if necessary

    if(workSteps > 1) {

     // Corner-to-interiors

//watje 12=10=98

     if (showInterior)

      {

      if (hookPatches[px]&1)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int end = hookEdgeEnd[index];

       int s1 = hookEdgeList[st];

       int s2 = hookEdgeList[st+1];

       int e1 = hookEdgeList[end-1];

       int e2 = hookEdgeList[end];

       MAKE_TRI(s2, i0, e2end, p.smGroup, matid,1,1,1,hidden);

       MAKE_TRI(e1start, i1, e1, p.smGroup, matid,1,1,1,hidden);

       MAKE_TRI(e2start, i2, e1end, p.smGroup, matid,1,1,1,hidden);

       }

      else if (hookPatches[px]&2)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int end = hookEdgeEnd[index];

       int s1 = hookEdgeList[st];

       int s2 = hookEdgeList[st+1];

       int e1 = hookEdgeList[end-1];

       int e2 = hookEdgeList[end];

       MAKE_TRI(e0start, i0, e2end, p.smGroup, matid,1,1,1,hidden);

       MAKE_TRI(s2, i1, e0end, p.smGroup, matid,1,1,1,hidden);

       MAKE_TRI(e2start, i2, e1, p.smGroup, matid,1,1,1,hidden);

       }

      else if (hookPatches[px]&4)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int end = hookEdgeEnd[index];

       int s1 = hookEdgeList[st];

       int s2 = hookEdgeList[st+1];

       int e1 = hookEdgeList[end-1];

       int e2 = hookEdgeList[end];

       MAKE_TRI(e0start, i0, e1, p.smGroup, matid,1,1,1,hidden);

       MAKE_TRI(e1start, i1, e0end, p.smGroup, matid,1,1,1,hidden);

       MAKE_TRI(s2, i2, e1end, p.smGroup, matid,1,1,1,hidden);

       }

      else

       {

       MAKE_TRI(e0start, i0, e2end, p.smGroup, matid,1,1,1,hidden);

       MAKE_TRI(e1start, i1, e0end, p.smGroup, matid,1,1,1,hidden);

       MAKE_TRI(e2start, i2, e1end, p.smGroup, matid,1,1,1,hidden);

       }

      }

     else

      {

      if (hookPatches[px]&1)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int end = hookEdgeEnd[index];

       int s1 = hookEdgeList[st];

       int s2 = hookEdgeList[st+1];

       int e1 = hookEdgeList[end-1];

       int e2 = hookEdgeList[end];

       MAKE_TRI(s2, i0, e2end, p.smGroup, matid,0,0,0,hidden);

       MAKE_TRI(e1start, i1, e1, p.smGroup, matid,0,0,0,hidden);

       MAKE_TRI(e2start, i2, e1end, p.smGroup, matid,0,0,0,hidden);

       }

      else if (hookPatches[px]&2)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int end = hookEdgeEnd[index];

       int s1 = hookEdgeList[st];

       int s2 = hookEdgeList[st+1];

       int e1 = hookEdgeList[end-1];

       int e2 = hookEdgeList[end];

       MAKE_TRI(e0start, i0, e2end, p.smGroup, matid,0,0,0,hidden);

       MAKE_TRI(s2, i1, e0end, p.smGroup, matid,0,0,0,hidden);

       MAKE_TRI(e2start, i2, e1, p.smGroup, matid,0,0,0,hidden);

       }

      else if (hookPatches[px]&4)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int end = hookEdgeEnd[index];

       int s1 = hookEdgeList[st];

       int s2 = hookEdgeList[st+1];

       int e1 = hookEdgeList[end-1];

       int e2 = hookEdgeList[end];

       MAKE_TRI(e0start, i0, e1, p.smGroup, matid,0,0,0,hidden);

       MAKE_TRI(e1start, i1, e0end, p.smGroup, matid,0,0,0,hidden);

       MAKE_TRI(s2, i2, e1end, p.smGroup, matid,0,0,0,hidden);

       }

      else

       {

       MAKE_TRI(e0start, i0, e2end, p.smGroup, matid,0,0,0,hidden);

       MAKE_TRI(e1start, i1, e0end, p.smGroup, matid,0,0,0,hidden);

       MAKE_TRI(e2start, i2, e1end, p.smGroup, matid,0,0,0,hidden);

       }

      }

     // Now the edges

     int e0ix = e0start, e1ix = e1start, e2ix = e2start;

     int i0ix = i0, i1ix = i1, i2ix = i2;

     for(int i = 0; i < (workSteps - 1); ++i,e0ix+=e0dir,e1ix+=e1dir,e2ix+=e2dir) {

      if (showInterior)

       {

       if (hookPatches[px]&1)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_QUAD_SPECIAL(s1,s2,s3,i0ix, p.smGroup, matid,1,1,1,1,hidden);

        MAKE_TRI(e1ix, e1ix+e1dir, i1ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e2ix, e2ix+e2dir, i2ix, p.smGroup, matid,1,1,1,hidden);

        }

       else if (hookPatches[px]&2)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_TRI(e0ix, e0ix+e0dir, i0ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_QUAD_SPECIAL(s1,s2,s3,i1ix, p.smGroup, matid,1,1,1,1,hidden);

        MAKE_TRI(e2ix, e2ix+e2dir, i2ix, p.smGroup, matid,1,1,1,hidden);

        }

       else if (hookPatches[px]&4)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_TRI(e0ix, e0ix+e0dir, i0ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e1ix, e1ix+e1dir, i1ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_QUAD_SPECIAL(s1,s2,s3,i2ix, p.smGroup, matid,1,1,1,1,hidden);

        }

       else

        {

        MAKE_TRI(e0ix, e0ix+e0dir, i0ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e1ix, e1ix+e1dir, i1ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e2ix, e2ix+e2dir, i2ix, p.smGroup, matid,1,1,1,hidden);

        }

      

       }

      else {

       if (hookPatches[px]&1)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_QUAD_SPECIAL(s1,s2,s3,i0ix, p.smGroup, matid,1,1,0,0,hidden);

        MAKE_TRI(e1ix, e1ix+e1dir, i1ix, p.smGroup, matid,1,0,0,hidden);

        MAKE_TRI(e2ix, e2ix+e2dir, i2ix, p.smGroup, matid,1,0,0,hidden);

        }

       else if (hookPatches[px]&2)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_TRI(e0ix, e0ix+e0dir, i0ix, p.smGroup, matid,1,0,0,hidden);

        MAKE_QUAD_SPECIAL(s1,s2,s3,i1ix, p.smGroup, matid,1,1,0,0,hidden);

        MAKE_TRI(e2ix, e2ix+e2dir, i2ix, p.smGroup, matid,1,0,0,hidden);

        }

       else if (hookPatches[px]&4)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_TRI(e0ix, e0ix+e0dir, i0ix, p.smGroup, matid,1,0,0,hidden);

        MAKE_TRI(e1ix, e1ix+e1dir, i1ix, p.smGroup, matid,1,0,0,hidden);

        MAKE_QUAD_SPECIAL(s1,s2,s3,i2ix, p.smGroup, matid,1,1,0,0,hidden);

        }

       else

        {

        MAKE_TRI(e0ix, e0ix+e0dir, i0ix, p.smGroup, matid,1,0,0,hidden);

        MAKE_TRI(e1ix, e1ix+e1dir, i1ix, p.smGroup, matid,1,0,0,hidden);

        MAKE_TRI(e2ix, e2ix+e2dir, i2ix, p.smGroup, matid,1,0,0,hidden);

        }

       }

      i0ix++;

      i1ix += (workSteps - i - 2);

      i2ix -= (i + 2);

      }

     // Make interleaving triangles

     e0ix = e0start + e0dir;

     e1ix = e1start + e1dir;

     e2ix = e2start + e2dir;

     i0ix = i0;

     i1ix = i1;

     i2ix = i2;

     for(i = 1; i < (workSteps - 1); ++i,e0ix+=e0dir,e1ix+=e1dir,e2ix+=e2dir) {

      if (showInterior)

       {

       if (hookPatches[px]&1)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        MAKE_TRI(s1, i0ix + 1, i0ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e1ix, i1ix + (workSteps - i - 1), i1ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e2ix, i2ix - (i + 1), i2ix, p.smGroup, matid,1,1,1,hidden);

        }

       else if (hookPatches[px]&2)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        MAKE_TRI(e0ix, i0ix + 1, i0ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(s1, i1ix + (workSteps - i - 1), i1ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e2ix, i2ix - (i + 1), i2ix, p.smGroup, matid,1,1,1,hidden);

        }

       else if (hookPatches[px]&4)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        MAKE_TRI(e0ix, i0ix + 1, i0ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e1ix, i1ix + (workSteps - i - 1), i1ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(s1, i2ix - (i + 1), i2ix, p.smGroup, matid,1,1,1,hidden);

        }

       else

        {

        MAKE_TRI(e0ix, i0ix + 1, i0ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e1ix, i1ix + (workSteps - i - 1), i1ix, p.smGroup, matid,1,1,1,hidden);

        MAKE_TRI(e2ix, i2ix - (i + 1), i2ix, p.smGroup, matid,1,1,1,hidden);

        }

       }

      else

       {

       if (hookPatches[px]&1)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        MAKE_TRI(s1, i0ix + 1, i0ix, p.smGroup, matid,0,0,0,hidden);

        MAKE_TRI(e1ix, i1ix + (workSteps - i - 1), i1ix, p.smGroup, matid,0,0,0,hidden);

        MAKE_TRI(e2ix, i2ix - (i + 1), i2ix, p.smGroup, matid,0,0,0,hidden);

        }

       else if (hookPatches[px]&2)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        MAKE_TRI(e0ix, i0ix + 1, i0ix, p.smGroup, matid,0,0,0,hidden);

        MAKE_TRI(s1, i1ix + (workSteps - i - 1), i1ix, p.smGroup, matid,0,0,0,hidden);

        MAKE_TRI(e2ix, i2ix - (i + 1), i2ix, p.smGroup, matid,0,0,0,hidden);

        }

       else if (hookPatches[px]&4)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        MAKE_TRI(e0ix, i0ix + 1, i0ix, p.smGroup, matid,0,0,0,hidden);

        MAKE_TRI(e1ix, i1ix + (workSteps - i - 1), i1ix, p.smGroup, matid,0,0,0,hidden);

        MAKE_TRI(s1, i2ix - (i + 1), i2ix, p.smGroup, matid,0,0,0,hidden);

        }

       else

        {

        MAKE_TRI(e0ix, i0ix + 1, i0ix, p.smGroup, matid,0,0,0,hidden);

        MAKE_TRI(e1ix, i1ix + (workSteps - i - 1), i1ix, p.smGroup, matid,0,0,0,hidden);

        MAKE_TRI(e2ix, i2ix - (i + 1), i2ix, p.smGroup, matid,0,0,0,hidden);

        }

       }

      i0ix++;

      i1ix += (workSteps - i - 1);

      i2ix -= (i + 1);

      }

     }

    else

     {

     if (showInterior)

      {

      if (hookPatches[px]&1)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int s2 = hookEdgeList[st+1];

       MAKE_TRI(s2, e1start, e2start, p.smGroup, matid,1,1,1,hidden);

       }

      else if (hookPatches[px]&2)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int s2 = hookEdgeList[st+1];

       MAKE_TRI(e0start, s2, e2start, p.smGroup, matid,1,1,1,hidden);

       }

      else if (hookPatches[px]&4)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int s2 = hookEdgeList[st+1];

       MAKE_TRI(e0start, e1start, s2, p.smGroup, matid,1,1,1,hidden);

       }

      else

       {

       MAKE_TRI(e0start, e1start, e2start, p.smGroup, matid,1,1,1,hidden);

       }

      }

     else

      {

      if (hookPatches[px]&1)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int s2 = hookEdgeList[st+1];

       MAKE_TRI(s2, e1start, e2start, p.smGroup, matid,0,0,0,hidden);

       }

      else if (hookPatches[px]&2)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int s2 = hookEdgeList[st+1];

       MAKE_TRI(e0start, s2, e2start, p.smGroup, matid,0,0,0,hidden);

       }

      else if (hookPatches[px]&4)

       {

       int index = hookPoints[px];

       int st = hookEdgeStart[index];

       int s2 = hookEdgeList[st+1];

       MAKE_TRI(e0start, e1start, s2, p.smGroup, matid,0,0,0,hidden);

       }

      else

       {

       MAKE_TRI(e0start, e1start, e2start, p.smGroup, matid,0,0,0,hidden);

       }

      }

     }

    }

    break;

   case PATCH_QUAD: {

    // If only need to make 1 quad, make it and blow!

    if(workSteps == 0) {

     if (hookPatches[px]==0)

      {

      MAKE_QUAD(p.v[0],p.v[1],p.v[2],p.v[3],p.smGroup, matid,1,1,0,1,1,0,hidden);

      }

     else

      {

//a edge

      if (hookPatches[px]&1)

       {

       int edge = p.edge[0];

       int ex = hooks[hookPoints[px]].hookPoint;

       MAKE_PENTA_EDGE1(p.v[0], ex, p.v[1], p.v[2], p.v[3], p.smGroup, matid,1,1,1,1,hidden);

       }

//b edge

      if (hookPatches[px]&2)

       {

       int edge = p.edge[1];

       int ex = hooks[hookPoints[px]].hookPoint;

       MAKE_PENTA_EDGE2(p.v[0], p.v[1], ex, p.v[2], p.v[3], p.smGroup, matid,1,1,1,1,hidden);       

       }

//c edge

      if (hookPatches[px]&4)

       {

       int edge = p.edge[2];

       int ex = hooks[hookPoints[px]].hookPoint;

       MAKE_PENTA_EDGE3(p.v[0], p.v[1], p.v[2],ex, p.v[3], p.smGroup, matid,1,1,1,1,hidden);       

       }

//d edge

      if (hookPatches[px]&8)

       {

       int edge = p.edge[3];

       int ex = hooks[hookPoints[px]].hookPoint;

       MAKE_PENTA_EDGE4(p.v[0], p.v[1], p.v[2], p.v[3], ex, p.smGroup, matid,1,1,1,1,hidden);       

       }

      }

     break;

     }

    if(workSteps > 1) {

     // Make the inside faces if necessary

     for(int u = 0; u < (workSteps-1); ++u) {

      int uix = baseVert + u * workSteps;

      for(int v = 0; v < (workSteps-1); ++v, ++uix) {

//watje 12-8-98

       if (showInterior)

        {

        MAKE_QUAD(uix, uix + 1, uix + workSteps + 1, uix + workSteps, p.smGroup, matid,1,1,0,1,1,0,hidden);

        }

       else

        {

        MAKE_QUAD(uix, uix + 1, uix + workSteps + 1, uix + workSteps, p.smGroup, matid,0,0,0,0,0,0,hidden);

        }

       }

      }

     }

    // Make the corner faces

    int edge0 = p.edge[0];

    int edge1 = p.edge[1];

    int edge2 = p.edge[2];

    int edge3 = p.edge[3];

    PatchEdge &e0 = edges[edge0];

    PatchEdge &e1 = edges[edge1];

    PatchEdge &e2 = edges[edge2];

    PatchEdge &e3 = edges[edge3];

    int e0start,e1start,e2start,e3start,e0end,e1end,e2end,e3end,e0dir,e1dir,e2dir,e3dir;

    if(e0.v1 == p.v[0]) {

     e0start = edgeStart[edge0];

     e0end = edgeEnd[edge0];

     e0dir = 1;

     }

    else {

     e0start = edgeEnd[edge0];

     e0end = edgeStart[edge0];

     e0dir = -1;

     }

    if(e1.v1 == p.v[1]) {

     e1start = edgeStart[edge1];

     e1end = edgeEnd[edge1];

     e1dir = 1;

     }

    else {

     e1start = edgeEnd[edge1];

     e1end = edgeStart[edge1];

     e1dir = -1;

     }

    if(e2.v1 == p.v[2]) {

     e2start = edgeStart[edge2];

     e2end = edgeEnd[edge2];

     e2dir = 1;

     }

    else {

     e2start = edgeEnd[edge2];

     e2end = edgeStart[edge2];

     e2dir = -1;

     }

    if(e3.v1 == p.v[3]) {

     e3start = edgeStart[edge3];

     e3end = edgeEnd[edge3];

     e3dir = 1;

     }

    else {

     e3start = edgeEnd[edge3];

     e3end = edgeStart[edge3];

     e3dir = -1;

     }

    if (e3start > e3end) e3dir = -1;

     else e3dir = 1;

    if (e2start > e2end) e2dir = -1;

     else e2dir = 1;

    if (e1start > e1end) e1dir = -1;

     else e1dir = 1;

    if (e0start > e0end) e0dir = -1;

     else e0dir = 1;

    // Create the corners

    int i0 = baseVert;

    int i1 = baseVert + (workSteps - 1);

    int i2 = baseVert + quadint - 1;

    int i3 = i2 - (workSteps - 1);

//watje 12-8-98

    if (showInterior)

     {

     if (hookPatches[px]&1)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_PENTA_EDGE1(p.v[0],s1,s2,i0,e3end, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_PENTA_EDGE1(e1,e2,p.v[1],e1start,i1, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_QUAD(p.v[2], e2start, i2, e1end, p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_QUAD(e3start, i3, e2end, p.v[3], p.smGroup, matid,1,1,0,1,1,0,hidden);

      } 

     else if (hookPatches[px]&2)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_QUAD(p.v[0], e0start, i0, e3end, p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_PENTA_EDGE2(e0end, p.v[1],s1,s2,i1, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_PENTA_EDGE2(i2,e1, e2, p.v[2],e2start, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_QUAD(e3start, i3, e2end, p.v[3], p.smGroup, matid,1,1,0,1,1,0,hidden);

      } 

     else if (hookPatches[px]&4)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_QUAD(p.v[0], e0start, i0, e3end, p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_QUAD(e1start, i1, e0end, p.v[1], p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_PENTA_EDGE3(i2,e1end,p.v[2],s1, s2, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_PENTA_EDGE3(e3start,i3,e1,e2,p.v[3], p.smGroup, matid,1,1,1,1,hidden);

      } 

     else if (hookPatches[px]&8)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_QUAD(e1start, i1, e0end, p.v[1], p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_QUAD(p.v[2], e2start, i2, e1end, p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_PENTA_EDGE4(s2,i3,e2end,p.v[3],s1, p.smGroup, matid,1,1,1,1,hidden);

      MAKE_PENTA_EDGE4(p.v[0],e0start,i0,e1,e2, p.smGroup, matid,1,1,1,1,hidden);

      } 

     else

      {

      MAKE_QUAD(p.v[0], e0start, i0, e3end, p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_QUAD(e1start, i1, e0end, p.v[1], p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_QUAD(p.v[2], e2start, i2, e1end, p.smGroup, matid,1,1,0,1,1,0,hidden);

      MAKE_QUAD(e3start, i3, e2end, p.v[3], p.smGroup, matid,1,1,0,1,1,0,hidden);

      }

     }

    else

     {

     if (hookPatches[px]&1)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_PENTA_EDGE1(p.v[0],s1,s2,i0,e3end, p.smGroup, matid,1,0,0,1,hidden);

      MAKE_PENTA_EDGE1(e1,e2,p.v[1],e1start,i1, p.smGroup, matid,1,1,0,0,hidden);

      MAKE_QUAD(p.v[2], e2start, i2, e1end, p.smGroup, matid,1,0,0,0,1,0,hidden);

      MAKE_QUAD(e3start, i3, e2end, p.v[3], p.smGroup, matid,0,0,0,1,1,0,hidden);

      } 

     else if (hookPatches[px]&2)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_QUAD(p.v[0], e0start, i0, e3end, p.smGroup, matid,1,0,0,0,1,0,hidden);

      MAKE_PENTA_EDGE2(e0end, p.v[1],s1,s2,i1, p.smGroup, matid,1,1,0,0,hidden);

      MAKE_PENTA_EDGE2(i2,e1, e2, p.v[2],e2start, p.smGroup, matid,0,1,1,0,hidden);

      MAKE_QUAD(e3start, i3, e2end, p.v[3], p.smGroup, matid,0,0,0,1,1,0,hidden);

      } 

     else if (hookPatches[px]&4)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_QUAD(p.v[0], e0start, i0, e3end, p.smGroup, matid,1,0,0,0,1,0,hidden);

      MAKE_QUAD(e1start, i1, e0end, p.v[1], p.smGroup, matid,0,0,0,1,1,0,hidden);

      MAKE_PENTA_EDGE3(i2,e1end,p.v[2],s1, s2, p.smGroup, matid,0,1,1,0,hidden);

      MAKE_PENTA_EDGE3(e3start,i3,e1,e2,p.v[3], p.smGroup, matid,0,0,1,1,hidden);

      } 

     else if (hookPatches[px]&8)

      {

      int index = hookPoints[px];

      int st = hookEdgeStart[index];

      int end = hookEdgeEnd[index];

      int s1 = hookEdgeList[st];

      int s2 = hookEdgeList[st+1];

      int e1 = hookEdgeList[end-1];

      int e2 = hookEdgeList[end];

      MAKE_QUAD(e1start, i1, e0end, p.v[1], p.smGroup, matid,0,0,0,1,1,0,hidden);

      MAKE_QUAD(p.v[2], e2start, i2, e1end, p.smGroup, matid,1,0,0,0,1,0,hidden);

      MAKE_PENTA_EDGE4(s2,i3,e2end,p.v[3],s1, p.smGroup, matid,0,0,1,1,hidden);

      MAKE_PENTA_EDGE4(p.v[0],e0start,i0,e1,e2, p.smGroup, matid,1,0,0,1,hidden);

      } 

     else

      {

      MAKE_QUAD(p.v[0], e0start, i0, e3end, p.smGroup, matid,1,0,0,0,1,0,hidden);

      MAKE_QUAD(e1start, i1, e0end, p.v[1], p.smGroup, matid,0,0,0,1,1,0,hidden);

      MAKE_QUAD(p.v[2], e2start, i2, e1end, p.smGroup, matid,1,0,0,0,1,0,hidden);

      MAKE_QUAD(e3start, i3, e2end, p.v[3], p.smGroup, matid,0,0,0,1,1,0,hidden);

      }

     }

    // Create the edges, if necessary

    if(workSteps > 1) {

     int e0ix = e0start, e1ix = e1start, e2ix = e2start, e3ix = e3start;

     for(int i = 0; i < (workSteps - 1); ++i,e0ix+=e0dir,e1ix+=e1dir,e2ix+=e2dir,e3ix+=e3dir) {

//watje 12-8-98

      if (showInterior)

       {

       if (hookPatches[px]&1)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_PENTA_EDGE1(s1,s2,s3, i0 + 1 + i, i0 + i, p.smGroup, matid,1,1,1,1,hidden);

        MAKE_QUAD(e1ix+e1dir, i1 + workSteps * (i + 1), i1 + workSteps * i, e1ix, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e2ix, e2ix+e2dir, i2 - 1 - i, i2 - i, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e3ix+e3dir, i3 - workSteps * (i + 1), i3 - workSteps * i, e3ix, p.smGroup, matid,1,1,0,1,1,0,hidden);

        } 

       else if (hookPatches[px]&2)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_QUAD(e0ix, e0ix+e0dir, i0 + 1 + i, i0 + i, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_PENTA_EDGE2(i1 + workSteps * i,s1,s2,s3, i1 + workSteps * (i + 1), p.smGroup, matid,1,1,1,1,hidden);

        MAKE_QUAD(e2ix, e2ix+e2dir, i2 - 1 - i, i2 - i, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e3ix+e3dir, i3 - workSteps * (i + 1), i3 - workSteps * i, e3ix, p.smGroup, matid,1,1,0,1,1,0,hidden);

        } 

       else if (hookPatches[px]&4)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_QUAD(e0ix, e0ix+e0dir, i0 + 1 + i, i0 + i, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e1ix+e1dir, i1 + workSteps * (i + 1), i1 + workSteps * i, e1ix, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_PENTA_EDGE3(i2 - 1 - i,i2 - i,s1,s2,s3, p.smGroup, matid,1,1,1,1,hidden);

        MAKE_QUAD(e3ix+e3dir, i3 - workSteps * (i + 1), i3 - workSteps * i, e3ix, p.smGroup, matid,1,1,0,1,1,0,hidden);

        } 

       else if (hookPatches[px]&8)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        MAKE_QUAD(e0ix, e0ix+e0dir, i0 + 1 + i, i0 + i, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e1ix+e1dir, i1 + workSteps * (i + 1), i1 + workSteps * i, e1ix, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e2ix, e2ix+e2dir, i2 - 1 - i, i2 - i, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_PENTA_EDGE4(s3,i3 - workSteps * (i + 1), i3 - workSteps * i,s1,s2, p.smGroup, matid,1,1,1,1,hidden);

        } 

       else

        {

        MAKE_QUAD(e0ix, e0ix+e0dir, i0 + 1 + i, i0 + i, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e1ix+e1dir, i1 + workSteps * (i + 1), i1 + workSteps * i, e1ix, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e2ix, e2ix+e2dir, i2 - 1 - i, i2 - i, p.smGroup, matid,1,1,0,1,1,0,hidden);

        MAKE_QUAD(e3ix+e3dir, i3 - workSteps * (i + 1), i3 - workSteps * i, e3ix, p.smGroup, matid,1,1,0,1,1,0,hidden);

        }

       }

      else

       {

       if (hookPatches[px]&1)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        

        MAKE_PENTA_EDGE1(s1,s2,s3, i0 + 1 + i, i0 + i, p.smGroup, matid,1,0,0,0,hidden);

        MAKE_QUAD(e1ix+e1dir, i1 + workSteps * (i + 1), i1 + workSteps * i, e1ix, p.smGroup, matid,0,0,0,0,1,0,hidden);

        MAKE_QUAD(e2ix, e2ix+e2dir, i2 - 1 - i, i2 - i, p.smGroup, matid,1,0,0,0,0,0,hidden);

        MAKE_QUAD(e3ix+e3dir, i3 - workSteps * (i + 1), i3 - workSteps * i, e3ix, p.smGroup, matid,0,0,0,0,1,0,hidden);

        }

       else if (hookPatches[px]&2)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        

        MAKE_QUAD(e0ix, e0ix+e0dir, i0 + 1 + i, i0 + i, p.smGroup, matid,1,0,0,0,0,0,hidden);

        MAKE_PENTA_EDGE2(i1 + workSteps * i,s1,s2,s3, i1 + workSteps * (i + 1), p.smGroup, matid,0,1,0,0,hidden);

        MAKE_QUAD(e2ix, e2ix+e2dir, i2 - 1 - i, i2 - i, p.smGroup, matid,1,0,0,0,0,0,hidden);

        MAKE_QUAD(e3ix+e3dir, i3 - workSteps * (i + 1), i3 - workSteps * i, e3ix, p.smGroup, matid,0,0,0,0,1,0,hidden);

        }

       else if (hookPatches[px]&4)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        

        MAKE_QUAD(e0ix, e0ix+e0dir, i0 + 1 + i, i0 + i, p.smGroup, matid,1,0,0,0,0,0,hidden);

        MAKE_QUAD(e1ix+e1dir, i1 + workSteps * (i + 1), i1 + workSteps * i, e1ix, p.smGroup, matid,0,0,0,0,1,0,hidden);

        MAKE_PENTA_EDGE3(i2 - 1 - i,i2 - i,s1,s2,s3, p.smGroup, matid,0,0,1,0,hidden);

        MAKE_QUAD(e3ix+e3dir, i3 - workSteps * (i + 1), i3 - workSteps * i, e3ix, p.smGroup, matid,0,0,0,0,1,0,hidden);

        }

       else if (hookPatches[px]&8)

        {

        int index = hookPoints[px];

        int st = hookEdgeStart[index];

        int s1 = hookEdgeList[st+i*2+1];

        int s2 = hookEdgeList[st+i*2+2];

        int s3 = hookEdgeList[st+i*2+3];

        

        MAKE_QUAD(e0ix, e0ix+e0dir, i0 + 1 + i, i0 + i, p.smGroup, matid,1,0,0,0,0,0,hidden);

        MAKE_QUAD(e1ix+e1dir, i1 + workSteps * (i + 1), i1 + workSteps * i, e1ix, p.smGroup, matid,0,0,0,0,1,0,hidden);

        MAKE_QUAD(e2ix, e2ix+e2dir, i2 - 1 - i, i2 - i, p.smGroup, matid,1,0,0,0,0,0,hidden);

        MAKE_PENTA_EDGE4(s3,i3 - workSteps * (i + 1), i3 - workSteps * i,s1,s2, p.smGroup, matid,0,0,0,1,hidden);

        }

       else

        {

        MAKE_QUAD(e0ix, e0ix+e0dir, i0 + 1 + i, i0 + i, p.smGroup, matid,1,0,0,0,0,0,hidden);

        MAKE_QUAD(e1ix+e1dir, i1 + workSteps * (i + 1), i1 + workSteps * i, e1ix, p.smGroup, matid,0,0,0,0,1,0,hidden);

        MAKE_QUAD(e2ix, e2ix+e2dir, i2 - 1 - i, i2 - i, p.smGroup, matid,1,0,0,0,0,0,hidden);

        MAKE_QUAD(e3ix+e3dir, i3 - workSteps * (i + 1), i3 - workSteps * i, e3ix, p.smGroup, matid,0,0,0,0,1,0,hidden);

        }

       }

      }

     }

    }

   }

  }

 assert(face == nFaces);

 // Build texture faces if necessary

 for(chan = 0; chan < getNumMaps(); ++chan) {

  

  if(getNumMapVerts (chan)) {

   mesh.setMapSupport (chan, TRUE);

   mesh.setNumMapFaces (chan, nFaces);

   int tface = 0;

   for(px = 0; px < numPatches; ++px) {

    Patch &p = patches[px];

    int baseTVert = (*patchTVOff[chan])[px]; // texture vertex base

    switch(p.type) {

     case PATCH_TRI: {

      int tvbase = baseTVert;

      // If only need to make 1 triangle, make it and blow!

      if(workSteps == 0) {

       if (hookPatches[px]==0)

        {

        MAKE_TTRI(chan, tvbase, tvbase+1, tvbase+2);

        }

       else

        { 

        if (hookPatches[px]&1)

         {

         MAKE_TQUAD_SPECIAL(chan, tvbase,tvertHookStart, tvbase+1,tvbase+2);

         }

        else if (hookPatches[px]&2)

         {

         MAKE_TQUAD_SPECIAL(chan, tvbase+1,tvertHookStart,tvbase+2,tvbase );

         }

        else if (hookPatches[px]&4)

         {

         MAKE_TQUAD_SPECIAL(chan, tvbase+2,tvertHookStart,tvbase,tvbase+1 );

         }

        }

       break;

       }

      if(workSteps > 2) {

       // Make inside faces if necessary

       int ivbase = baseTVert + workSteps + 3;

       for(int ax = 0; ax < (workSteps - 2); ++ax) {

        int vix = ivbase;

        for(int bx = 0; bx < (workSteps - 2 - ax); ++bx, ++vix)

         MAKE_TTRI(chan, vix, vix + 1, vix + workSteps + 1 - ax);

        vix = ivbase;

        for(bx = 1; bx < (workSteps - 2 - ax); ++bx, ++vix)

         MAKE_TTRI(chan, vix + 1, vix + workSteps + 2 - ax, vix + workSteps + 1 - ax);

        ivbase += (workSteps + 1 - ax);

        }

       }

      // Create the corners

      int i0 = baseTVert + patchDivs + 2;

      int i1 = i0 + workSteps - 2;

      int i2 = baseTVert + triTVerts - 5;

      int e0start = baseTVert + 1;

      int e1start = baseTVert + patchDivs * 2;

      int e2start = baseTVert + triTVerts - 3;

      int e0end = baseTVert + workSteps;

      int e1end = e2start + 1;

      int e2end = baseTVert + patchDivs + 1;

      if (hookPatches[px]&1)

       {

       MAKE_TQUAD_SPECIAL(chan, baseTVert, tvertHookStart, e0start, e2end);

       MAKE_TQUAD_SPECIAL(chan, e0end, tvertHookStart, baseTVert + patchDivs, e1start);

       MAKE_TTRI(chan, baseTVert + triTVerts-1, e2start, e1end);

       }

      else if (hookPatches[px]&2)

       {

       MAKE_TTRI(chan, baseTVert, e0start, e2end);

       MAKE_TQUAD_SPECIAL(chan, baseTVert + patchDivs, tvertHookStart, e1start, e0end);

       MAKE_TQUAD_SPECIAL(chan, e1end,tvertHookStart , baseTVert + triTVerts-1, e2start);

       }

      else if (hookPatches[px]&4)

       {

       MAKE_TTRI(chan, baseTVert + patchDivs, e1start, e0end);

       MAKE_TQUAD_SPECIAL(chan, baseTVert + triTVerts-1,tvertHookStart, e2start, e1end);

       MAKE_TQUAD_SPECIAL(chan, e2end,tvertHookStart, baseTVert, e0start);

       }

      else

       {

       MAKE_TTRI(chan, baseTVert, e0start, e2end);

       MAKE_TTRI(chan, baseTVert + patchDivs, e1start, e0end);

       MAKE_TTRI(chan, baseTVert + triTVerts-1, e2start, e1end);

       }

      // Create the edges, if necessary

      if(workSteps > 1) {

       // Corner-to-interiors

       MAKE_TTRI(chan, e0start, i0, e2end);

       MAKE_TTRI(chan, e1start, i1, e0end);

       MAKE_TTRI(chan, e2start, i2, e1end);

       // Now the edges

       int e0ix = e0start, e1ix = e1start, e2ix = e2start;

       int i0ix = i0, i1ix = i1, i2ix = i2;

       int holder = tvertHookStart;

       for(int i = 0; i < (workSteps - 1); ++i) {

        int e1dir = workSteps - i;

        int e2dir = -(3 + i);

        if (hookPatches[px]&1)

         {

         MAKE_TQUAD_SPECIAL(chan, e0ix, tvertHookStart, e0ix+1, i0ix);

         MAKE_TTRI(chan, e1ix, e1ix+e1dir, i1ix);

         MAKE_TTRI(chan, e2ix, e2ix+e2dir, i2ix);

         }

        else if (hookPatches[px]&2)

         {

         MAKE_TTRI(chan, e0ix, e0ix+1, i0ix);

         MAKE_TQUAD_SPECIAL(chan, e1ix, tvertHookStart, e1ix+e1dir, i1ix);

         MAKE_TTRI(chan, e2ix, e2ix+e2dir, i2ix);

         }

        else if (hookPatches[px]&4)

         {

         MAKE_TTRI(chan, e0ix, e0ix+1, i0ix);

         MAKE_TTRI(chan, e1ix, e1ix+e1dir, i1ix);

         MAKE_TQUAD_SPECIAL(chan, e2ix, tvertHookStart, e2ix+e2dir, i2ix);

         }

        else

         {

         MAKE_TTRI(chan, e0ix, e0ix+1, i0ix);

         MAKE_TTRI(chan, e1ix, e1ix+e1dir, i1ix);

         MAKE_TTRI(chan, e2ix, e2ix+e2dir, i2ix);

         }

        i0ix++;

        i1ix += e1dir;

        i2ix -= (4 + i);

        e0ix++;

        e1ix += e1dir;

        e2ix += e2dir;

        }

       // Make interleaving triangles

       e0ix = e0start + 1;

       e1ix = e1start + workSteps;

       e2ix = e2start - 3;

       i0ix = i0;

       i1ix = i1;

       i2ix = i2;

       for(i = 1; i < (workSteps - 1); ++i) {

        int e1dir = workSteps - i;

        int e2dir = -(3 + i);

        MAKE_TTRI(chan, e0ix, i0ix + 1, i0ix);

        MAKE_TTRI(chan, e1ix, e1ix - 1, i1ix);

        MAKE_TTRI(chan, e2ix, i2ix + e2dir, i2ix);

        i0ix++;

        i1ix = i1ix + e1dir + 1;

        i2ix += e2dir;

        e0ix++;

        e1ix += e1dir;

        e2ix += e2dir;

        }

       }

      else

       MAKE_TTRI(chan, e0start, e1start, e2start);

      }

      break;

     case PATCH_QUAD: {

      int tvbase = baseTVert;

      int qvstep = patchDivs + 1;

      // If only need to make 1 quad, make it and blow!

      if(workSteps == 0) {

       if (hookPatches[px]==0)

        {

        MAKE_TQUAD(chan, tvbase, tvbase+1, tvbase+3, tvbase+2);

        }

       else

        { 

        if (hookPatches[px]&1)

         {

         MAKE_TPENTA_EDGE1(chan, tvbase,tvertHookStart, tvbase+1, tvbase+3, tvbase+2);

         }

        else if (hookPatches[px]&2)

         {

         MAKE_TPENTA_EDGE2(chan, tvbase, tvbase+1,tvertHookStart, tvbase+3, tvbase+2);

         }

        else if (hookPatches[px]&4)

         {

         MAKE_TPENTA_EDGE3(chan, tvbase, tvbase+1, tvbase+3,tvertHookStart, tvbase+2);

         }

        else if (hookPatches[px]&8)

         {

         MAKE_TPENTA_EDGE4(chan, tvbase, tvbase+1, tvbase+3, tvbase+2,tvertHookStart);

         }

        }

       break;

       }

      if(workSteps > 1) {

       int ivbase = baseTVert + qvstep + 1;

       // Make the inside faces if necessary

       for(int v = 0; v < (workSteps-1); ++v) {

        int vix = ivbase + v * qvstep;

        for(int u = 0; u < (workSteps-1); ++u, ++vix) {

         MAKE_TQUAD(chan, vix, vix + 1, vix + qvstep + 1, vix + qvstep);

         }

        }

       }

      int e0start = baseTVert + 1;

      int e1start = baseTVert + qvstep * 2 - 1;

      int e2start = baseTVert + quadTVerts - 2;

      int e3start = baseTVert + qvstep * workSteps;

      int e0end = e0start + workSteps - 1;

      int e1end = e1start + qvstep * (workSteps - 1);

      int e2end = e2start - (workSteps - 1);

      int e3end = e3start - qvstep * (workSteps - 1);

      // Create the corners

      int i0 = baseTVert + qvstep + 1;

      int i1 = i0 + (workSteps - 1);

      int i2 = i1 + qvstep * (workSteps - 1);

      int i3 = i2 - (workSteps - 1);

      if (hookPatches[px]&1)

       {

       MAKE_TPENTA_EDGE1(chan, baseTVert,tvertHookStart, e0start, i0, e3end);

       MAKE_TPENTA_EDGE1(chan, e0end, tvertHookStart, baseTVert + patchDivs,e1start, i1);

       MAKE_TQUAD(chan, baseTVert + quadTVerts - 1, e2start, i2, e1end);

       MAKE_TQUAD(chan, e3start, i3, e2end, baseTVert + qvstep * patchDivs);

       }

      else if (hookPatches[px]&2)

       {

       MAKE_TQUAD(chan, baseTVert, e0start, i0, e3end);

       MAKE_TPENTA_EDGE2(chan, e0end, baseTVert + patchDivs,tvertHookStart,e1start, i1);

       MAKE_TPENTA_EDGE2(chan, i2, e1end,tvertHookStart, baseTVert + quadTVerts - 1, e2start);

       MAKE_TQUAD(chan, e3start, i3, e2end, baseTVert + qvstep * patchDivs);

       }

      else if (hookPatches[px]&4)

       {

       MAKE_TQUAD(chan, baseTVert, e0start, i0, e3end);

       MAKE_TQUAD(chan, e1start, i1, e0end, baseTVert + patchDivs);

       MAKE_TPENTA_EDGE3(chan, i2, e1end, baseTVert + quadTVerts - 1,tvertHookStart, e2start);

       MAKE_TPENTA_EDGE3(chan, e3start, i3, e2end,tvertHookStart, baseTVert + qvstep * patchDivs);

       }

      else if (hookPatches[px]&8)

       {

       MAKE_TQUAD(chan, e1start, i1, e0end, baseTVert + patchDivs);

       MAKE_TQUAD(chan, baseTVert + quadTVerts - 1, e2start, i2, e1end);

       MAKE_TPENTA_EDGE4(chan, e3start, i3, e2end, baseTVert + qvstep * patchDivs,tvertHookStart);

       MAKE_TPENTA_EDGE4(chan, baseTVert, e0start, i0, e3end,tvertHookStart);

       }

      else

       {

       MAKE_TQUAD(chan, baseTVert, e0start, i0, e3end);

       MAKE_TQUAD(chan, e1start, i1, e0end, baseTVert + patchDivs);

       MAKE_TQUAD(chan, baseTVert + quadTVerts - 1, e2start, i2, e1end);

       MAKE_TQUAD(chan, e3start, i3, e2end, baseTVert + qvstep * patchDivs);

       }

      // Create the edges, if necessary

      if(workSteps > 1) {

       int e0ix = e0start, e1ix = e1start, e2ix = e2start, e3ix = e3start;

       for(int i = 0; i < (workSteps - 1); ++i) {

        if (hookPatches[px]&1)

         {

         MAKE_TPENTA_EDGE1(chan, e0ix,tvertHookStart, e0ix+1, i0+1, i0);

         MAKE_TQUAD(chan, e1ix+qvstep, i1+qvstep, i1, e1ix);

         MAKE_TQUAD(chan, e2ix, e2ix-1, i2-1, i2);

         MAKE_TQUAD(chan, e3ix-qvstep, i3-qvstep, i3, e3ix);

         }

        else if (hookPatches[px]&2)

         {

         MAKE_TQUAD(chan, e0ix, e0ix+1, i0+1, i0);

         MAKE_TPENTA_EDGE2(chan, i1, e1ix, tvertHookStart, e1ix+qvstep, i1+qvstep);

         MAKE_TQUAD(chan, e2ix, e2ix-1, i2-1, i2);

         MAKE_TQUAD(chan, e3ix-qvstep, i3-qvstep, i3, e3ix);

         }

        else if (hookPatches[px]&4)

         {

         MAKE_TQUAD(chan, e0ix, e0ix+1, i0+1, i0);

         MAKE_TQUAD(chan, e1ix+qvstep, i1+qvstep, i1, e1ix);

         MAKE_TPENTA_EDGE3(chan, i2-1, i2,e2ix, tvertHookStart,e2ix-1);

         MAKE_TQUAD(chan, e3ix-qvstep, i3-qvstep, i3, e3ix);

         }

        else if (hookPatches[px]&8)

         {

         MAKE_TQUAD(chan, e0ix, e0ix+1, i0+1, i0);

         MAKE_TQUAD(chan, e1ix+qvstep, i1+qvstep, i1, e1ix);

         MAKE_TQUAD(chan, e2ix, e2ix-1, i2-1, i2);

         MAKE_TPENTA_EDGE4(chan, e3ix-qvstep, i3-qvstep, i3, e3ix,tvertHookStart);

         }

        else

         {

         MAKE_TQUAD(chan, e0ix, e0ix+1, i0+1, i0);

         MAKE_TQUAD(chan, e1ix+qvstep, i1+qvstep, i1, e1ix);

         MAKE_TQUAD(chan, e2ix, e2ix-1, i2-1, i2);

         MAKE_TQUAD(chan, e3ix-qvstep, i3-qvstep, i3, e3ix);

         }

        e0ix++;

        e1ix += qvstep;

        e2ix--;

        e3ix -= qvstep;

        i0++;

        i1 += qvstep;

        i2--;

        i3 -= qvstep;

        }

       }

      }

     }

    }

   assert(tface == nFaces);

   }

  }

  

 mesh.InvalidateGeomCache();

 mesh.EnableEdgeList(1);

 // The mesh is now valid!

 meshValid = TRUE;

 return;

 }

// Compute the degree-4 alias control points

void Patch::ComputeAux(PatchMesh *pMesh, int index) {

 Point3 p1 = pMesh->getVert(v[index]).p;

 Point3 p2 = pMesh->getVert(v[(index+1)%3]).p;

 int vecIndex = index * 2;

 Point3 v1 = pMesh->getVec(vec[vecIndex]).p;

 Point3 v2 = pMesh->getVec(vec[(vecIndex+1)%6]).p;

 int auxIndex = index * 3;

 aux[auxIndex++] = p1 + (v1 - p1) * 0.75f;

 aux[auxIndex++] = v1 + (v2 - v1) * 0.5f;

 aux[auxIndex++] = v2 + (p2 - v2) * 0.25f;

#ifdef CHECK_TRI_PATCH_AUX

 auxSource[index*3] = p1;

 auxSource[index*3+1] = v1;

 auxSource[index*3+2] = v2;

#endif //CHECK_TRI_PATCH_AUX

 }

// Compute interior vertices considering this patch only

void Patch::computeInteriors(PatchMesh *pMesh) {

 PatchVec *vecp = pMesh->vecs;

 switch(type) {

  case 3: {

   // Triangulars must also compute the degree 4 equivalent control points!

   // These get stored in the 'aux' array

   ComputeAux(pMesh, 0);

   ComputeAux(pMesh, 1);

   ComputeAux(pMesh, 2);

   

   // If the interior points are automatic, compute 'em!

   if(flags & PATCH_AUTO) {

    Point3 a = pMesh->getVert(v[0]).p;

    Point3 b = pMesh->getVert(v[1]).p;

    Point3 c = pMesh->getVert(v[2]).p;

    vecp[interior[0]].p = pMesh->getVec(vec[5]).p + (pMesh->getVec(vec[0]).p - a);

    vecp[interior[1]].p = pMesh->getVec(vec[1]).p + (pMesh->getVec(vec[2]).p - b);

    vecp[interior[2]].p = pMesh->getVec(vec[3]).p + (pMesh->getVec(vec[4]).p - c);

    }

   }

   break;

  case 4:

   if(flags & PATCH_AUTO) {

    Point3 a = pMesh->getVert(v[0]).p;

    Point3 b = pMesh->getVert(v[1]).p;

    Point3 c = pMesh->getVert(v[2]).p;

    Point3 d = pMesh->getVert(v[3]).p;

    vecp[interior[0]].p = pMesh->getVec(vec[7]).p + (pMesh->getVec(vec[0]).p - a);

    vecp[interior[1]].p = pMesh->getVec(vec[1]).p + (pMesh->getVec(vec[2]).p - b);

    vecp[interior[2]].p = pMesh->getVec(vec[3]).p + (pMesh->getVec(vec[4]).p - c);

    vecp[interior[3]].p = pMesh->getVec(vec[5]).p + (pMesh->getVec(vec[6]).p - d);

    }

   break;

  }

 }

int PatchMesh::UpdateHooks()

 {

//check to make sure all are valid hooks if not delete them

 for (int i = 0; i < hooks.Count();i++)

  {

  int j = hooks[i].hookEdge;

  int a = edges[j].v1;

  int b = edges[j].v2;

  

  if ( ((a == hooks[i].upperPoint) && (b == hooks[i].lowerPoint)) ||

    ((b == hooks[i].upperPoint) && (a == hooks[i].lowerPoint)) )

   {

     Point3 delta;

//build a spline based on that edge

     Spline3D work;

     work.AddKnot(SplineKnot(KTYPE_BEZIER, LTYPE_CURVE, zeroPoint, zeroPoint, zeroPoint));

     work.AddKnot(SplineKnot(KTYPE_BEZIER, LTYPE_CURVE, zeroPoint, zeroPoint, zeroPoint));

     PatchEdge &edge = edges[j];

     work.SetKnotPoint(0, verts[edge.v1].p);

     work.SetOutVec(0, vecs[edge.vec12].p);

     work.SetInVec(1, vecs[edge.vec21].p);

     work.SetKnotPoint(1, verts[edge.v2].p);

//refine from the spline refine

 // Get the knot points

     Point3 v00 = work.GetKnotPoint(0);

     Point3 v30 = work.GetKnotPoint(1);

     Point3 point = work.InterpBezier3D(0, 0.5f);

     Point3 v10 = work.GetOutVec(0);

     Point3 v20 = work.GetInVec(1);

     Point3 v01 = v00 + (v10 - v00) * 0.5f;

     Point3 v21 = v20 + (v30 - v20) * 0.5f;

     Point3 v11 = v10 + (v20 - v10) * 0.5f;

     Point3 v02 = v01 + (v11 - v01) * 0.5f;

     Point3 v12 = v11 + (v21 - v11) * 0.5f;

     Point3 v03 = v02 + (v12 - v02) * 0.5f;

     Point3 av,bv,cv,dv;

     av = v01;

     bv = v02;

     cv = v12;

     dv = v21;

     PatchVert patchvert = getVert(hooks[i].hookPoint);

     delta = point-patchvert.p;

     setVert(hooks[i].hookPoint,point);    

     setVec(hooks[i].upperVec,av);

     setVec(hooks[i].upperHookVec,bv);

     setVec(hooks[i].lowerHookVec,cv);

     setVec(hooks[i].lowerVec,dv);

     for (int ct = 0; ct <patchvert.vectors.Count();ct++)

      {

      int vecIndex = patchvert.vectors[ct];

      if ((vecIndex != hooks[i].upperHookVec) && (vecIndex != hooks[i].lowerHookVec))

       {

       PatchVec vp = getVec(vecIndex);

       vp.p += delta;

       setVec(vecIndex,vp.p);

       }

      }

   }

  else

   {

   hooks.Delete(i,1);

   i--;

   }

  }

 return 1;

 }