/********************************************************************** *< FILE: editpat.cpp DESCRIPTION: Edit Patch OSM CREATED BY: Tom Hudson, Dan Silva & Rolf Berteig HISTORY: created 23 June, 1995 IMPORTANT USAGE NOTE: When you do an operation in edit patch which will change the topology, the form of the code should look like this code, taken from the vertex deletion: ----- ip->GetModContexts(mcList, nodes); ClearPatchDataFlag(mcList, EPD_BEENDONE); theHold.Begin(); --> RecordTopologyTags(); for (int i = 0; i < mcList.Count(); i++) { int altered = 0; EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->GetFlag(EPD_BEENDONE)) continue; // If the mesh isn't yet cache, this will cause it to get cached. PatchMesh *patch = patchData->TempData(this)->GetPatch(t); if (!patch) continue; --> patchData->RecordTopologyTags(patch); // If this is the first edit, then the delta arrays will be allocated patchData->BeginEdit(t); // If any bits are set in the selection set, let's DO IT!! if (patch->vertSel.NumberSet()) { altered = holdNeeded = 1; if (theHold.Holding()) theHold.Put(new PatchRestore(patchData, this, patch, "DoVertDelete")); // Call the vertex delete function DeleteSelVerts(patch); --> patchData->UpdateChanges(patch); patchData->TempData(this)->Invalidate(PART_TOPO); } patchData->SetFlag(EPD_BEENDONE, TRUE); } if (holdNeeded) { --> ResolveTopoChanges(); theHold.Accept(GetString(IDS_TH_VERTDELETE)); } else { ip->DisplayTempPrompt(GetString(IDS_TH_NOVERTSSEL), PROMPT_TIME); theHold.End(); } nodes.DisposeTemporary(); ClearPatchDataFlag(mcList, EPD_BEENDONE); ----- The key elements in the "changed topology" case are the calls noted by arrows. These record special tags inside the object so that after the topology is changed by the modifier code, the UpdateChanges code can make a new mapping from the old object topology to the new. If the operation doesn't change the topology, then the three topology tag calls aren't needed and the UpdateChanges call becomes: patchData->UpdateChanges(patch, FALSE); This tells UpdateChanges not to bother remapping the topology. *> Copyright(c) 1994, All Rights Reserved. **********************************************************************/ #include "stdafx.h" #include "editpat.h" #include "../nel_patch_lib/vertex_neighborhood.h" #define DBGWELD_DUMPx #define DBGWELD_ACTIONx #define DBG_NAMEDSELSx // Uncomment this for vert mapper debugging //#define VMAP_DEBUG 1 // Forward references BOOL CALLBACK PatchSelectDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK PatchOpsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK PatchObjSurfDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK PatchSurfDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK PatchTileDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); BOOL CALLBACK PatchEdgeDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); void ResetVert (PatchMesh *patch); // A handy zero point Point3 zeroPoint(0, 0, 0); // Our temporary prompts last 2 seconds: #define PROMPT_TIME 2000 // in mods.cpp extern HINSTANCE hInstance; // Select by material parameters int sbmParams[4] = {1, 1, RPO_DEFAULT_TESSEL, RPO_DEFAULT_TESSEL}; // Select by smooth parameters DWORD sbsParams[3] = {1, 1, 0}; float weldThreshold = 0.1f; // Checkbox items for rollup pages int attachReorient = 0; // This is a special override value which allows us to hit-test on // any sub-part of a patch extern int patchHitOverride; // If zero, no override is done void SetPatchHitOverride(int value) { patchHitOverride = value; } void ClearPatchHitOverride() { patchHitOverride = 0; } PatchDeleteUser pDel; extern PatchRightMenu pMenu; /*-------------------------------------------------------------------*/ static EditPatchClassDesc editPatchDesc; extern ClassDesc* GetEditPatchModDesc() { return &editPatchDesc; } void EditPatchClassDesc::ResetClassParams(BOOL fileReset) { sbmParams[0] = 1; sbmParams[1] = 1; sbmParams[2] = RPO_DEFAULT_TESSEL; sbmParams[3] = RPO_DEFAULT_TESSEL; EditPatchMod::condenseMat = FALSE; EditPatchMod::attachMat = ATTACHMAT_IDTOMAT; } /*-------------------------------------------------------------------*/ int EditPatchMod::Display(TimeValue t, INode* inode, ViewExp *vpt, int flags, ModContext *mc) { return 0; } void EditPatchMod::GetWorldBoundBox(TimeValue t, INode* inode, ViewExp *vpt, Box3& box, ModContext *mc) { box.Init(); } //--------------------------------------------------------------------- // UI stuff void EditPatchMod::RecordTopologyTags() { ModContextList mcList; INodeTab nodes; TimeValue t = ip->GetTime(); ip->GetModContexts(mcList, nodes); ClearPatchDataFlag(mcList, EPD_BEENDONE); for (int i = 0; i < mcList.Count(); i++) { EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->GetFlag(EPD_BEENDONE)) continue; // If the mesh isn't yet cache, this will cause it to get cached. RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(t, rpatch); if (!patch) continue; patch->RecordTopologyTags(); patchData->SetFlag(EPD_BEENDONE, TRUE); } nodes.DisposeTemporary(); ClearPatchDataFlag(mcList, EPD_BEENDONE); } class ChangeNamedSetRestore : public RestoreObj { public: BitArray oldset, newset; int index; GenericNamedSelSetList *setList; ChangeNamedSetRestore(GenericNamedSelSetList *sl, int ix, BitArray *o) { setList = sl; index = ix; oldset = *o; } void Restore(int isUndo) { newset = *(setList->sets[index]); *(setList->sets[index]) = oldset; } void Redo() { *(setList->sets[index]) = newset; } TSTR Description() {return TSTR(_T("Change Named Sel Set"));} }; // Selection set, misc fixup utility function // This depends on PatchMesh::RecordTopologyTags being called prior to the topo changes void EditPatchMod::ResolveTopoChanges() { ModContextList mcList; INodeTab nodes; TimeValue t = ip->GetTime(); ip->GetModContexts(mcList, nodes); ClearPatchDataFlag(mcList, EPD_BEENDONE); for (int i = 0; i < mcList.Count(); i++) { EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->GetFlag(EPD_BEENDONE)) continue; // If the mesh isn't yet cache, this will cause it to get cached. RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(t, rpatch); if (!patch) continue; // First, the vertex selections int set; for (set = 0; set < patchData->vselSet.Count(); ++set) { BitArray *oldVS = &patchData->vselSet[set]; BitArray newVS; newVS.SetSize(patch->numVerts); for (int vert = 0; vert < patch->numVerts; ++vert) { // Get the knot's previous location, then copy that selection into the new set int tag = patch->verts[vert].aux1; if (tag >= 0) newVS.Set(vert, (*oldVS)[tag]); else newVS.Clear(vert); } if (theHold.Holding()) theHold.Put(new ChangeNamedSetRestore(&patchData->vselSet, set, oldVS)); patchData->vselSet[set] = newVS; } // Now the edge selections for (set = 0; set < patchData->eselSet.Count(); ++set) { BitArray *oldES = &patchData->eselSet[set]; BitArray newES; newES.SetSize(patch->numEdges); for (int edge = 0; edge < patch->numEdges; ++edge) { // Get the knot's previous location, then copy that selection into the new set int tag = patch->edges[edge].aux1; if (tag >= 0) newES.Set(edge, (*oldES)[tag]); else newES.Clear(edge); } if (theHold.Holding()) theHold.Put(new ChangeNamedSetRestore(&patchData->eselSet, set, oldES)); patchData->eselSet[set] = newES; } // Now the patch selections for (set = 0; set < patchData->pselSet.Count(); ++set) { BitArray *oldPS = &patchData->pselSet[set]; BitArray newPS; newPS.SetSize(patch->numPatches); for (int p = 0; p < patch->numPatches; ++p) { // Get the knot's previous location, then copy that selection into the new set int tag = patch->patches[p].aux1; if (tag >= 0) newPS.Set(p, (*oldPS)[tag]); else newPS.Clear(p); } if (theHold.Holding()) theHold.Put(new ChangeNamedSetRestore(&patchData->pselSet, set, oldPS)); patchData->pselSet[set] = newPS; } // watje 4-16-99 patch->HookFixTopology(); patchData->SetFlag(EPD_BEENDONE, TRUE); } nodes.DisposeTemporary(); ClearPatchDataFlag(mcList, EPD_BEENDONE); } class EPModContextEnumProc : public ModContextEnumProc { float f; public: EPModContextEnumProc(float f) { this->f = f; } BOOL proc(ModContext *mc); // Return FALSE to stop, TRUE to continue. }; BOOL EPModContextEnumProc::proc(ModContext *mc) { EditPatchData *patchData =(EditPatchData*)mc->localData; if (patchData) patchData->RescaleWorldUnits(f); return TRUE; } // World scaling void EditPatchMod::RescaleWorldUnits(float f) { if (TestAFlag(A_WORK1)) return; SetAFlag(A_WORK1); // rescale all our references for (int i = 0; i < NumRefs(); i++) { ReferenceMaker *srm = GetReference(i); if (srm) srm->RescaleWorldUnits(f); } // Now rescale stuff inside our data structures EPModContextEnumProc proc(f); EnumModContexts(&proc); NotifyDependents(FOREVER, PART_GEOM, REFMSG_CHANGE); } void EditPatchMod::InvalidateSurfaceUI() { if (hSurfPanel && selLevel == EP_PATCH) { InvalidateRect(hSurfPanel, NULL, FALSE); patchUIValid = FALSE; } } void EditPatchMod::InvalidateTileUI() { if (hTilePanel && selLevel == EP_TILE) { InvalidateRect(hTilePanel, NULL, FALSE); tileUIValid = FALSE; } } void EditPatchMod::InvalidateEdgeUI() { if (hEdgePanel && selLevel == EP_EDGE) { InvalidateRect(hEdgePanel, NULL, FALSE); edgeUIValid = FALSE; } } BitArray *EditPatchMod::GetLevelSelectionSet(PatchMesh *patch, RPatchMesh *rpatch) { switch (selLevel) { case EP_VERTEX: return &patch->vertSel; case EP_PATCH: return &patch->patchSel; case EP_EDGE: return &patch->edgeSel; case EP_TILE: return &rpatch->tileSel; } nlassert(0); return NULL; } void EditPatchMod::UpdateSelectDisplay() { TSTR buf; int num, j; if (!hSelectPanel) return; ModContextList mcList; INodeTab nodes; if (!ip) return; ip->GetModContexts(mcList, nodes); switch (GetSubobjectLevel()) { case EP_OBJECT: buf.printf(GetString(IDS_TH_OBJECT_SEL)); break; case EP_VERTEX: { num = 0; PatchMesh *thePatch = NULL; for (int i = 0; i < mcList.Count(); i++) { EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->tempData && patchData->TempData(this)->PatchCached(ip->GetTime())) { RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(ip->GetTime(), rpatch); if (!patch) continue; int thisNum = patch->vertSel.NumberSet(); if (thisNum) { num += thisNum; thePatch = patch; } } } if (num == 1) { for (j = 0; j < thePatch->vertSel.GetSize(); j++) if (thePatch->vertSel[j]) break; buf.printf(GetString(IDS_TH_NUMVERTSEL), j + 1); } else buf.printf(GetString(IDS_TH_NUMVERTSELP), num); } break; case EP_PATCH: { num = 0; PatchMesh *thePatch = NULL; for (int i = 0; i < mcList.Count(); i++) { EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->tempData && patchData->TempData(this)->PatchCached(ip->GetTime())) { RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(ip->GetTime(), rpatch); if (!patch) continue; int thisNum = patch->patchSel.NumberSet(); if (thisNum) { num += thisNum; thePatch = patch; } } } if (num == 1) { for (j = 0; j < thePatch->patchSel.GetSize(); j++) if (thePatch->patchSel[j]) break; buf.printf(GetString(IDS_TH_NUMPATCHSEL), j + 1); } else buf.printf(GetString(IDS_TH_NUMPATCHSELP), num); } break; case EP_EDGE: { num = 0; PatchMesh *thePatch = NULL; for (int i = 0; i < mcList.Count(); i++) { EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->tempData && patchData->TempData(this)->PatchCached(ip->GetTime())) { RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(ip->GetTime(), rpatch); if (!patch) continue; int thisNum = patch->edgeSel.NumberSet(); if (thisNum) { num += thisNum; thePatch = patch; } } } if (num == 1) { for (j = 0; j < thePatch->edgeSel.GetSize(); j++) if (thePatch->edgeSel[j]) break; buf.printf(GetString(IDS_TH_NUMEDGESEL), j + 1); } else buf.printf(GetString(IDS_TH_NUMEDGESELP), num); } break; case EP_TILE: { num = 0; RPatchMesh *thePatch = NULL; for (int i = 0; i < mcList.Count(); i++) { EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->tempData && patchData->TempData(this)->PatchCached(ip->GetTime())) { RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(ip->GetTime(), rpatch); if (!patch) continue; int thisNum = rpatch->tileSel.NumberSet(); if (thisNum) { num += thisNum; thePatch = rpatch; } } } if (num == 1) { for (j = 0; j < thePatch->tileSel.GetSize(); j++) if (thePatch->tileSel[j]) break; buf.printf("Tile %d Selected", j + 1); } else buf.printf("%d Tiles Selected", num); } break; } nodes.DisposeTemporary(); SetDlgItemText(hSelectPanel, IDC_NUMSEL_LABEL, buf); } void EditPatchMod::DoVertWeld() { ModContextList mcList; INodeTab nodes; TimeValue t = ip->GetTime(); int holdNeeded = 0; BOOL hadSel = FALSE; if (!ip) return; ip->GetModContexts(mcList, nodes); ClearPatchDataFlag(mcList, EPD_BEENDONE); theHold.Begin(); RecordTopologyTags(); for (int i = 0; i < mcList.Count(); i++) { BOOL altered = FALSE; EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->GetFlag(EPD_BEENDONE)) continue; // If the mesh isn't yet cache, this will cause it to get cached. RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(t, rpatch); if (!patch) continue; patchData->RecordTopologyTags(patch); // If this is the first edit, then the delta arrays will be allocated patchData->BeginEdit(t); // If any bits are set in the selection set, let's DO IT!! if (patch->vertSel.NumberSet() > 1) { hadSel = TRUE; if (theHold.Holding()) theHold.Put(new PatchRestore(patchData, this, patch, rpatch, "DoVertWeld")); // Call the patch weld function if (patch->Weld(weldThreshold)) { rpatch->Weld (patch); altered = holdNeeded = TRUE; patchData->UpdateChanges(patch, rpatch); patchData->TempData(this)->Invalidate(PART_TOPO); } } patchData->SetFlag(EPD_BEENDONE, TRUE); } if (holdNeeded) { ResolveTopoChanges(); theHold.Accept(GetString(IDS_TH_VERTWELD)); } else { if (!hadSel) ip->DisplayTempPrompt(GetString(IDS_TH_NOVERTSSEL), PROMPT_TIME); else ip->DisplayTempPrompt(GetString(IDS_TH_NOWELDPERFORMED), PROMPT_TIME); theHold.End(); } nodes.DisposeTemporary(); ClearPatchDataFlag(mcList, EPD_BEENDONE); NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE); ip->RedrawViews(ip->GetTime(), REDRAW_NORMAL); } void EditPatchMod::DoVertReset () { ModContextList mcList; INodeTab nodes; TimeValue t = ip->GetTime(); int holdNeeded = 0; BOOL hadSel = FALSE; if (!ip) return; ip->GetModContexts(mcList, nodes); ClearPatchDataFlag(mcList, EPD_BEENDONE); theHold.Begin(); RecordTopologyTags(); for (int i = 0; i < mcList.Count(); i++) { BOOL altered = FALSE; EditPatchData *patchData =(EditPatchData*)mcList[i]->localData; if (!patchData) continue; if (patchData->GetFlag(EPD_BEENDONE)) continue; // If the mesh isn't yet cache, this will cause it to get cached. RPatchMesh *rpatch; PatchMesh *patch = patchData->TempData(this)->GetPatch(t, rpatch); if (!patch) continue; patchData->RecordTopologyTags(patch); // If this is the first edit, then the delta arrays will be allocated patchData->BeginEdit(t); // If any bits are set in the selection set, let's DO IT!! if (patch->vertSel.NumberSet() > 0) { hadSel = TRUE; if (theHold.Holding()) theHold.Put(new PatchRestore(patchData, this, patch, rpatch, "DoVertReset")); // Call the patch weld function ResetVert (patch); patchData->UpdateChanges(patch, rpatch); patchData->TempData(this)->Invalidate(PART_GEOM); /*if (patch->Weld(weldThreshold)) { rpatch->Weld (patch); altered = holdNeeded = TRUE; patchData->UpdateChanges(patch, rpatch); patchData->TempData(this)->Invalidate(PART_TOPO); }*/ } patchData->SetFlag(EPD_BEENDONE, TRUE); } ResolveTopoChanges(); theHold.Accept("Reset Vertex"); /*if (holdNeeded) { ResolveTopoChanges(); theHold.Accept(GetString(IDS_TH_VERTWELD)); } else { if (!hadSel) ip->DisplayTempPrompt(GetString(IDS_TH_NOVERTSSEL), PROMPT_TIME); else ip->DisplayTempPrompt(GetString(IDS_TH_NOWELDPERFORMED), PROMPT_TIME); theHold.End(); }*/ nodes.DisposeTemporary(); ClearPatchDataFlag(mcList, EPD_BEENDONE); NotifyDependents(FOREVER, PART_GEOM, REFMSG_CHANGE); ip->RedrawViews(ip->GetTime(), REDRAW_NORMAL); } void EditPatchMod::PatchSelChanged() { SelectionChanged(); if (hSurfPanel && selLevel == EP_PATCH) InvalidateSurfaceUI(); if (hTilePanel && selLevel == EP_TILE) InvalidateTileUI(); if (hEdgePanel && selLevel == EP_EDGE) InvalidateEdgeUI(); } /* class AdvParams { public: TessSubdivStyle mStyle; int mMin, mMax; int mTris; }; */ void EditPatchMod::LocalDataChanged() { } int GetPointIndex (int nVertex, int nPatch, PatchMesh* patch) { for (int n=0; n<4; n++) { if (patch->patches[nPatch].v[n]==nVertex) return n; } nlassert (0); return 0; } Point3 GetInterior (int nPatch, int nInt, PatchMesh* patch) { return patch->vecs[patch->patches[nPatch].interior[nInt]].p; } void ResetVert (PatchMesh *patch) { // Make a edge table // Static table to avoid alloc prb CVertexNeighborhood& edgeTab=vertexNeighborhoodGlobal; edgeTab.build (*patch); // For each vertices for (int nV=0; nVnumVerts; nV++) { // Selected ? if (patch->vertSel[nV]) { Point3 vert=patch->verts[nV].p; Point3 normal (0,0,0); // Count of neigbor for vertex n uint listSize=edgeTab.getNeighborCount (nV); // List of neigbor const uint* pList=edgeTab.getNeighborList (nV); // For each neigbor uint nn; for (nn=0; nnedges[pList[nn]].patch1!=-1) normal+=patch->PatchNormal(patch->edges[pList[nn]].patch1); if (patch->edges[pList[nn]].patch2!=-1) normal+=patch->PatchNormal(patch->edges[pList[nn]].patch2); #else // (MAX_RELEASE <= 4000) // Compute average plane if (patch->edges[pList[nn]].patches[0]!=-1) normal+=patch->PatchNormal(patch->edges[pList[nn]].patches[0]); if (patch->edges[pList[nn]].patches[1]!=-1) normal+=patch->PatchNormal(patch->edges[pList[nn]].patches[1]); #endif // (MAX_RELEASE <= 4000) } // Normalize normal=normal.Normalize(); // Plane float fD=-DotProd(normal, vert); // Reset normales float fNorme=0.f; // For each neigbor for (nn=0; nnverts[(patch->edges[pList[nn]].v1==nV)?patch->edges[pList[nn]].v2:patch->edges[pList[nn]].v1].p; vect2-=vert; vect2/=3.f; Point3 tmp1=CrossProd (vect2, normal); tmp1=CrossProd (normal, tmp1); tmp1=Normalize(tmp1); int nTang=(patch->edges[pList[nn]].v1==nV)?patch->edges[pList[nn]].vec12:patch->edges[pList[nn]].vec21; patch->vecs[nTang].p=vert+tmp1*DotProd (tmp1,vect2); tmp1=patch->vecs[nTang].p; tmp1-=vert; fNorme+=tmp1.Length(); } // Renorme new normal /*fNorme/=(float)edgeTab[nV].size(); ite=edgeTab[nV].begin(); while (ite!=edgeTab[nV].end()) { int nTang=(patch->edges[pList[nn]].v1==nV)?patch->edges[pList[nn]].vec12:patch->edges[pList[nn]].vec21; patch->vecs[nTang].p=fNorme*(Normalize(patch->vecs[nTang].p-vert))+vert; ite++; }*/ } } patch->computeInteriors(); patch->InvalidateGeomCache (); }