A good sample to look at to see how patches are created is the patch grid plugin. The source code is in\MAXSDK\SAMPLES\OBJECTS\PATCHGRD.CPP.
The code below is a portion of the source of this plugin. 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) { intix,iy,np,kv; intwsegs,lsegs,nv; Point3 v,p; floatl, 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 ); intlv = lsegs + 1; intwv = wsegs + 1; intnverts = lv * wv; intnpatches = lsegs * wsegs; intnexteriors = npatches * 4 + lsegs * 2 + wsegs * 2; intninteriors = npatches * 4; intnvecs = 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; floatdx = w/wsegs; floatdy = l/lsegs; floatfws = (float)wsegs; floatfls = (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); intrv = iy * vecColInc; // Row vector start intcv = 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; }