void RelaxMod::ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *node) { // Get our personal validity interval... Interval valid = GetValidity(t); // and intersect it with the channels we use as input (see ChannelsUsed) valid &= os->obj->ChannelValidity (t, GEOM_CHAN_NUM); valid &= os->obj->ChannelValidity (t, TOPO_CHAN_NUM); valid &= os->obj->ChannelValidity (t, SUBSEL_TYPE_CHAN_NUM); valid &= os->obj->ChannelValidity (t, SELECT_CHAN_NUM); Matrix3 modmat,minv; if (mc.localData == NULL) mc.localData = new RelaxModData; RelaxModData *rd = (RelaxModData *) mc.localData; TriObject *triObj = NULL; PatchObject *patchObj = NULL; PolyObject *polyObj = NULL; BOOL converted = FALSE; // For version 4 and later, we process patch or poly meshes as they are and pass them on. // Earlier versions converted to TriMeshes (done below). // For adding other new types of objects, add them here! #ifndef NO_PATCHES if(version >= RELAXMOD_VER4 && os->obj->IsSubClassOf(patchObjectClassID)) { patchObj = (PatchObject *)os->obj; } else // If it's a TriObject, process it #endif // NO_PATCHES if(os->obj->IsSubClassOf(triObjectClassID)) { triObj = (TriObject *)os->obj; } else if (os->obj->IsSubClassOf (polyObjectClassID)) { polyObj = (PolyObject *) os->obj; } else // If it can convert to a TriObject, do it if(os->obj->CanConvertToType(triObjectClassID)) { triObj = (TriObject *)os->obj->ConvertToType(t, triObjectClassID); converted = TRUE; } else return; // We can't deal with it! Mesh *mesh = triObj ? &(triObj->GetMesh()) : NULL; #ifndef NO_PATCHES PatchMesh &pmesh = patchObj ? patchObj->GetPatchMesh(t) : *((PatchMesh *)NULL); #else PatchMesh &pmesh = *((PatchMesh *)NULL); #endif // NO_PATCHES float relax, wtdRelax; // mjm - 4.8.99 int iter; BOOL boundary, saddle; pblock->GetValue (PB_RELAX, t, relax, FOREVER); pblock->GetValue (PB_ITER, t, iter, FOREVER); pblock->GetValue (PB_BOUNDARY, t, boundary, FOREVER); pblock->GetValue (PB_SADDLE, t, saddle, FOREVER); LimitValue (relax, MIN_RELAX, MAX_RELAX); LimitValue (iter, MIN_ITER, MAX_ITER); if(triObj) { int i, j, max; DWORD selLevel = mesh->selLevel; // mjm - 4.8.99 - add support for soft selection // sca - 4.29.99 - extended soft selection support to cover EDGE and FACE selection levels. float *vsw = (selLevel!=MESH_OBJECT) ? mesh->getVSelectionWeights() : NULL; if (rd->ivalid.InInterval(t) && (mesh->numVerts != rd->vnum)) { // Shouldn't happen, but does with Loft bug and may with other bugs. rd->ivalid.SetEmpty (); } if (!rd->ivalid.InInterval(t)) { rd->SetVNum (mesh->numVerts); for (i=0; i<rd->vnum; i++) { rd->fnum[i]=0; rd->nbor[i].ZeroCount(); } rd->sel.ClearAll (); DWORD *v; int k1, k2, origmax; for (i=0; i<mesh->numFaces; i++) { v = mesh->faces[i].v; for (j=0; j<3; j++) { if ((selLevel==MESH_FACE) && mesh->faceSel[i]) rd->sel.Set(v[j]); if ((selLevel==MESH_EDGE) && mesh->edgeSel[i*3+j]) rd->sel.Set(v[j]); if ((selLevel==MESH_EDGE) && mesh->edgeSel[i*3+(j+2)%3]) rd->sel.Set(v[j]); origmax = max = rd->nbor[v[j]].Count(); rd->fnum[v[j]]++; for (k1=0; k1<max; k1++) if (rd->nbor[v[j]][k1] == v[(j+1)%3]) break; if (k1==max) { rd->nbor[v[j]].Append (1, v+(j+1)%3, 1); max++; } for (k2=0; k2<max; k2++) if (rd->nbor[v[j]][k2] == v[(j+2)%3]) break; if (k2==max) { rd->nbor[v[j]].Append (1, v+(j+2)%3, 1); max++; } if (max>origmax) rd->vis[v[j]].SetSize (max, TRUE); if (mesh->faces[i].getEdgeVis (j)) rd->vis[v[j]].Set (k1); else if (k1>=origmax) rd->vis[v[j]].Clear (k1); if (mesh->faces[i].getEdgeVis ((j+2)%3)) rd->vis[v[j]].Set (k2); else if (k2>= origmax) rd->vis[v[j]].Clear (k2); } } // mjm - begin - 4.8.99 // if (selLevel==MESH_VERTEX) rd->sel = mesh->vertSel; if (selLevel==MESH_VERTEX) rd->sel = mesh->vertSel; else if (selLevel==MESH_OBJECT) rd->sel.SetAll (); // mjm - end rd->ivalid = os->obj->ChannelValidity (t, TOPO_CHAN_NUM); rd->ivalid &= os->obj->ChannelValidity (t, SUBSEL_TYPE_CHAN_NUM); rd->ivalid &= os->obj->ChannelValidity (t, SELECT_CHAN_NUM); } Tab<float> vangles; if (saddle) vangles.SetCount (rd->vnum); Point3 *hold = new Point3[rd->vnum]; int act; for (int k=0; k<iter; k++) { for (i=0; i<rd->vnum; i++) hold[i] = triObj->GetPoint(i); if (saddle) mesh->FindVertexAngles (vangles.Addr(0)); for (i=0; i<rd->vnum; i++) { // mjm - begin - 4.8.99 // if ((selLevel!=MESH_OBJECT) && (!rd->sel[i])) continue; if ( (!rd->sel[i] ) && (!vsw || vsw[i] == 0) ) continue; // mjm - end if (saddle && (vangles[i] <= 2*PI*.99999f)) continue; max = rd->nbor[i].Count(); if (boundary && (rd->fnum[i] < max)) continue; if (max<1) continue; Point3 avg(0.0f, 0.0f, 0.0f); for (j=0,act=0; j<max; j++) { if (!rd->vis[i][j]) continue; act++; avg += hold[rd->nbor[i][j]]; } if (act<1) continue; // mjm - begin - 4.8.99 wtdRelax = (!rd->sel[i]) ? relax * vsw[i] : relax; triObj->SetPoint (i, hold[i]*(1-wtdRelax) + avg*wtdRelax/((float)act)); // triObj->SetPoint (i, hold[i]*(1-relax) + avg*relax/((float)act)); // mjm - end } } delete [] hold; } if (polyObj) { int i, j, max; MNMesh & mm = polyObj->mm; float *vsw = (mm.selLevel!=MNM_SL_OBJECT) ? mm.getVSelectionWeights() : NULL; if (rd->ivalid.InInterval(t) && (mm.numv != rd->vnum)) { // Shouldn't happen, but does with Loft bug and may with other bugs. rd->ivalid.SetEmpty (); } if (!rd->ivalid.InInterval(t)) { rd->SetVNum (mm.numv); for (i=0; i<rd->vnum; i++) { rd->fnum[i]=0; rd->nbor[i].ZeroCount(); } rd->sel = mm.VertexTempSel (); int k1, k2, origmax; for (i=0; i<mm.numf; i++) { int deg = mm.f[i].deg; int *vtx = mm.f[i].vtx; for (j=0; j<deg; j++) { Tab<DWORD> & nbor = rd->nbor[vtx[j]]; origmax = max = nbor.Count(); rd->fnum[vtx[j]]++; DWORD va = vtx[(j+1)%deg]; DWORD vb = vtx[(j+deg-1)%deg]; for (k1=0; k1<max; k1++) if (nbor[k1] == va) break; if (k1==max) { nbor.Append (1, &va, 1); max++; } for (k2=0; k2<max; k2++) if (nbor[k2] == vb) break; if (k2==max) { nbor.Append (1, &vb, 1); max++; } } } rd->ivalid = os->obj->ChannelValidity (t, TOPO_CHAN_NUM); rd->ivalid &= os->obj->ChannelValidity (t, SUBSEL_TYPE_CHAN_NUM); rd->ivalid &= os->obj->ChannelValidity (t, SELECT_CHAN_NUM); } Tab<float> vangles; if (saddle) vangles.SetCount (rd->vnum); Tab<Point3> hold; hold.SetCount (rd->vnum); int act; for (int k=0; k<iter; k++) { for (i=0; i<rd->vnum; i++) hold[i] = mm.P(i); if (saddle) FindVertexAngles (mm, vangles.Addr(0)); for (i=0; i<rd->vnum; i++) { if ((!rd->sel[i]) && (!vsw || vsw[i] == 0) ) continue; if (saddle && (vangles[i] <= 2*PI*.99999f)) continue; max = rd->nbor[i].Count(); if (boundary && (rd->fnum[i] < max)) continue; if (max<1) continue; Point3 avg(0.0f, 0.0f, 0.0f); for (j=0,act=0; j<max; j++) { act++; avg += hold[rd->nbor[i][j]]; } if (act<1) continue; wtdRelax = (!rd->sel[i]) ? relax * vsw[i] : relax; polyObj->SetPoint (i, hold[i]*(1-wtdRelax) + avg*wtdRelax/((float)act)); } } } #ifndef NO_PATCHES else if(patchObj) { int i, j, max; DWORD selLevel = pmesh.selLevel; // mjm - 4.8.99 - add support for soft selection // sca - 4.29.99 - extended soft selection support to cover EDGE and FACE selection levels. float *vsw = (selLevel!=PATCH_OBJECT) ? pmesh.GetVSelectionWeights() : NULL; if (rd->ivalid.InInterval(t) && (pmesh.numVerts != rd->vnum)) { // Shouldn't happen, but does with Loft bug and may with other bugs. rd->ivalid.SetEmpty (); } if (!rd->ivalid.InInterval(t)) { int vecBase = pmesh.numVerts; rd->SetVNum (pmesh.numVerts + pmesh.numVecs); for (i=0; i<rd->vnum; i++) { rd->fnum[i]=1; // For patches, this means it's not a boundary rd->nbor[i].ZeroCount(); } rd->sel.ClearAll (); for (i=0; i<pmesh.numPatches; i++) { Patch &p = pmesh.patches[i]; int vecLimit = p.type * 2; for (j=0; j<p.type; j++) { PatchEdge &e = pmesh.edges[p.edge[j]]; BOOL isBoundary = (e.patches.Count() < 2) ? TRUE : FALSE; int theVert = p.v[j]; int nextVert = p.v[(j+1)%p.type]; int nextVec = p.vec[j*2] + vecBase; int nextVec2 = p.vec[j*2+1] + vecBase; int prevEdge = (j+p.type-1)%p.type; int prevVec = p.vec[prevEdge*2+1] + vecBase; int prevVec2 = p.vec[prevEdge*2] + vecBase; int theInterior = p.interior[j] + vecBase; // Establish selection bits if ((selLevel==PATCH_PATCH) && pmesh.patchSel[i]) { rd->sel.Set(theVert); rd->sel.Set(nextVec); rd->sel.Set(prevVec); rd->sel.Set(theInterior); } else if ((selLevel==PATCH_EDGE) && pmesh.edgeSel[p.edge[j]]) { rd->sel.Set(e.v1); rd->sel.Set(e.vec12 + vecBase); rd->sel.Set(e.vec21 + vecBase); rd->sel.Set(e.v2); } else if ((selLevel==PATCH_VERTEX) && pmesh.vertSel[theVert]) { rd->sel.Set(theVert); rd->sel.Set(nextVec); rd->sel.Set(prevVec); rd->sel.Set(theInterior); } // Set boundary flags if necessary if(isBoundary) { rd->fnum[theVert] = 0; rd->fnum[nextVec] = 0; rd->fnum[nextVec2] = 0; rd->fnum[nextVert] = 0; } // First process the verts int work = theVert; max = rd->nbor[work].Count(); // Append the neighboring vectors rd->MaybeAppendNeighbor(work, nextVec, max); rd->MaybeAppendNeighbor(work, prevVec, max); rd->MaybeAppendNeighbor(work, theInterior, max); // Now process the edge vectors work = nextVec; max = rd->nbor[work].Count(); // Append the neighboring points rd->MaybeAppendNeighbor(work, theVert, max); rd->MaybeAppendNeighbor(work, theInterior, max); rd->MaybeAppendNeighbor(work, prevVec, max); rd->MaybeAppendNeighbor(work, nextVec2, max); rd->MaybeAppendNeighbor(work, p.interior[(j+1)%p.type] + vecBase, max); work = prevVec; max = rd->nbor[work].Count(); // Append the neighboring points rd->MaybeAppendNeighbor(work, theVert, max); rd->MaybeAppendNeighbor(work, theInterior, max); rd->MaybeAppendNeighbor(work, nextVec, max); rd->MaybeAppendNeighbor(work, prevVec2, max); rd->MaybeAppendNeighbor(work, p.interior[(j+p.type-1)%p.type] + vecBase, max); // Now append the interior, if not auto if(!p.IsAuto()) { work = theInterior; max = rd->nbor[work].Count(); // Append the neighboring points rd->MaybeAppendNeighbor(work, p.v[j], max); rd->MaybeAppendNeighbor(work, nextVec, max); rd->MaybeAppendNeighbor(work, nextVec2, max); rd->MaybeAppendNeighbor(work, prevVec, max); rd->MaybeAppendNeighbor(work, prevVec2, max); for(int k = 1; k < p.type; ++k) rd->MaybeAppendNeighbor(work, p.interior[(j+k)%p.type] + vecBase, max); } } } // mjm - begin - 4.8.99 if (selLevel==PATCH_VERTEX) { for (int i=0; i<pmesh.numVerts; ++i) { if (pmesh.vertSel[i]) rd->sel.Set(i); } } else if (selLevel==PATCH_OBJECT) rd->sel.SetAll(); // mjm - end rd->ivalid = os->obj->ChannelValidity (t, TOPO_CHAN_NUM); rd->ivalid &= os->obj->ChannelValidity (t, SUBSEL_TYPE_CHAN_NUM); rd->ivalid &= os->obj->ChannelValidity (t, SELECT_CHAN_NUM); } Tab<float> vangles; if (saddle) vangles.SetCount (rd->vnum); Point3 *hold = new Point3[rd->vnum]; int act; for (int k=0; k<iter; k++) { for (i=0; i<rd->vnum; i++) hold[i] = patchObj->GetPoint(i); if (saddle) FindVertexAngles(pmesh, vangles.Addr(0)); for (i=0; i<rd->vnum; i++) { // mjm - begin - 4.8.99 // if ((selLevel!=MESH_OBJECT) && (!rd->sel[i])) continue; if ( (!rd->sel[i] ) && (!vsw || vsw[i] == 0) ) continue; // mjm - end if (saddle && (i < pmesh.numVerts) && (vangles[i] <= 2*PI*.99999f)) continue; max = rd->nbor[i].Count(); if (boundary && !rd->fnum[i]) continue; if (max<1) continue; Point3 avg(0.0f, 0.0f, 0.0f); for (j=0,act=0; j<max; j++) { act++; avg += hold[rd->nbor[i][j]]; } if (act<1) continue; // mjm - begin - 4.8.99 wtdRelax = (!rd->sel[i]) ? relax * vsw[i] : relax; patchObj->SetPoint (i, hold[i]*(1-wtdRelax) + avg*wtdRelax/((float)act)); // patchObj->SetPoint (i, hold[i]*(1-relax) + avg*relax/((float)act)); // mjm - end } } delete [] hold; patchObj->patch.computeInteriors(); patchObj->patch.ApplyConstraints(); } #endif // NO_PATCHES if(!converted) { os->obj->SetChannelValidity(GEOM_CHAN_NUM, valid); } else { // Stuff converted object into the pipeline! triObj->SetChannelValidity(TOPO_CHAN_NUM, valid); triObj->SetChannelValidity(GEOM_CHAN_NUM, valid); triObj->SetChannelValidity(TEXMAP_CHAN_NUM, valid); triObj->SetChannelValidity(MTL_CHAN_NUM, valid); triObj->SetChannelValidity(SELECT_CHAN_NUM, valid); triObj->SetChannelValidity(SUBSEL_TYPE_CHAN_NUM, valid); triObj->SetChannelValidity(DISP_ATTRIB_CHAN_NUM, valid); os->obj = triObj; } }