*/}}
Browse Source

Basic loop cut.

YimingWu 1 year ago
parent
commit
e20adbf10f
4 changed files with 66 additions and 15 deletions
  1. 2 0
      la_tns.h
  2. 6 1
      la_tns_mesh.c
  3. 56 14
      resources/la_modelling.c
  4. 2 0
      resources/la_widgets_viewers.c

+ 2 - 0
la_tns.h

@@ -1185,6 +1185,7 @@ int tnsMMeshCalculateNormal(tnsMeshObject* mo);
 void tnsMMeshEdgeAssignVerts(tnsMEdge* me,tnsMVert* mv1,tnsMVert* mv2);
 tnsMEdge* tnsMMeshVertShareEdge(tnsMVert* mv0, tnsMVert* mv1);
 int tnsMMeshEdgeHasVert(tnsMEdge* me, tnsMVert* MV);
+tnsMFace* tnsMMeshEdgeShareFace(tnsMEdge* me0, tnsMEdge* me1);
 tnsMVert* tnsMMeshEdgeShareVert(tnsMEdge* me0, tnsMEdge* me1);
 tnsMVert* tnsMMeshEdgeAnotherVert(tnsMEdge* me, tnsVert* v);
 tnsMVert* tnsMMeshEdgeStartingVert(tnsMEdge* me0, tnsMEdge* me1);
@@ -1215,6 +1216,7 @@ void tnsMMeshSelectEdge(tnsMeshObject* mo, tnsMEdge* me, int select, int toggle)
 void tnsMMeshEnsureSelectionFromVerts(tnsMeshObject* mo);
 void tnsMMeshEnsureSelectionFromEdges(tnsMeshObject* mo);
 void tnsMMeshEnsureSelection(tnsMeshObject* mo, int SelectMode);
+void tnsMMeshExpandBandList(tnsMeshObject* mo, tnsMEdge* me, laListHandle* lst);
 int tnsMMeshSelectRingBandFrom(tnsMeshObject* mo, tnsMEdge* me, int ring_band, int select, int toggle);
 
 void tnsInvalidateMeshBatch(tnsMeshObject* mo);

+ 6 - 1
la_tns_mesh.c

@@ -363,6 +363,11 @@ tnsMEdge* tnsMMeshVertShareEdge(tnsMVert* mv0, tnsMVert* mv1){
 int tnsMMeshEdgeHasVert(tnsMEdge* me, tnsMVert* mv){
     if(me->vl==mv || me->vr==mv) return 1; return 0;
 }
+tnsMFace* tnsMMeshEdgeShareFace(tnsMEdge* me0, tnsMEdge* me1){
+    if(me0->fl==me1->fl || me0->fl==me1->fr) return me0->fl;
+    if(me0->fr==me1->fl || me0->fr==me1->fr) return me0->fr;
+    return 0;
+}
 tnsMVert* tnsMMeshEdgeShareVert(tnsMEdge* me0, tnsMEdge* me1){
     if(me0->vl==me1->vl || me0->vl==me1->vr) return me0->vl;
     if(me0->vr==me1->vl || me0->vr==me1->vr) return me0->vr;
@@ -678,7 +683,7 @@ void tnsMMeshExpandRingList(tnsMeshObject* mo, tnsMEdge* me, laListHandle* lst){
     if(er=tnsMMeshGetRingEdge(me,1)){ lstAppendPointer(lst,er); tnsMMeshExpandRingList(mo,er,lst); }
 }
 tnsMEdge* tnsMMeshGetBandEdge(tnsMEdge* me, int from_right){
-    tnsMFace* FL=from_right?me->fl:me->fr;
+    tnsMFace* FL=from_right?me->fl:me->fr; if(!FL) return 0;
     if(FL->looplen!=4){ return 0;}
     for(laListItemPointer* lip=FL->l.pFirst;lip;lip=lip->pNext){
         tnsMEdge* te=lip->p; if((!(te->flags&TNS_MESH_FLAG_PICKED)) && (!tnsMMeshEdgeShareVert(te,me))){

+ 56 - 14
resources/la_modelling.c

@@ -278,6 +278,7 @@ STRUCTURE(MSelectExtra){
     
     laListHandle KnifeElements;
     void* PendingElem; int PendingElemType, BatchDirty;
+    int IsLoop;
 };
 
 int OPINV_Select(laOperator *a, laEvent *e){
@@ -1328,10 +1329,10 @@ void la_KnifeUpdateToolBatch(MSelectExtra* se,tnsObject* o){
     c=tnsCreateCommand(batch, "hovering_point", 1, 3, GL_POINTS, &elem, 0);
     tnsCommandUseUniformColor(c,laAccentColor(LA_BT_SVERTEX));
     if(count){
-        c=tnsCreateCommand(batch, "edges", count+1, 3, GL_LINE_STRIP, 0, 0);
+        c=tnsCreateCommand(batch, "edges", count+(se->IsLoop?0:1), 3, se->IsLoop?GL_LINE_LOOP:GL_LINE_STRIP, 0, 0);
         tnsCommandUseUniformColor(c,laAccentColor(LA_BT_NORMAL));
         tnsCommandUseWidth(c, 2);
-        c=tnsCreateCommand(batch, "points", count+1, 3, GL_POINTS, 0, 0);
+        c=tnsCreateCommand(batch, "points", count, 3, GL_POINTS, 0, 0);
         tnsCommandUseUniformColor(c,laAccentColor(LA_BT_NORMAL));
         tnsCommandUseWidth(c, 6);
     }
@@ -1348,18 +1349,20 @@ void la_KnifeAppendCut(MSelectExtra* se){
     MKnifeElement* ke=lstAppendPointerSized(&se->KnifeElements,se->PendingElem,sizeof(MKnifeElement));
     ke->Type=se->PendingElemType;
 }
-int la_KnifeRegisterCuts(MSelectExtra* se, tnsMeshObject* mo){
+int la_KnifeRegisterCuts(MSelectExtra* se, tnsMeshObject* mo, int TryClose){
     if(!mo || mo->Base.Type!=TNS_OBJECT_MESH || mo->Mode!=TNS_MESH_EDIT_MODE) return 0;
     if(!se->KnifeElements.pFirst) return 0;
-    tnsMVert* lastv=0,*newv=0; tnsMEdge* newme=0; int changed=0;
+    tnsMVert* lastv=0,*newv=0,*firstv=0; tnsMEdge* newme=0; int changed=0; tnsMFace* mf=0;
     for(MKnifeElement* ke=se->KnifeElements.pFirst;ke;ke=ke->Item.pNext){
         if(ke->Type==TNS_MMESH_EDGE_BIT){
             newv=tnsMMeshEdgeInsertVertAt(mo,ke->p,0.5,0,0,0); changed=1;
         }
         else{ newv=ke->p; }
         if(lastv){ if(tnsMMeshVertsShareFace(lastv,newv)) newme=tnsMMeshMakeEdge(mo, lastv, newv); changed=1; }
-        lastv=newv; newv->flags|=TNS_MESH_FLAG_SELECTED; if(newme){ newme->flags|=TNS_MESH_FLAG_SELECTED; }
+        lastv=newv; if(!firstv) firstv=newv; newv->flags|=TNS_MESH_FLAG_SELECTED;
+        if(newme){ newme->flags|=TNS_MESH_FLAG_SELECTED; }
     }
+    if(TryClose){ if((mf=tnsMMeshVertsShareFace(lastv,firstv))&&mf->looplen==6) newme=tnsMMeshMakeEdge(mo, lastv, firstv); changed=1; }
     if(changed){ tnsMMeshRefreshIndex(mo); tnsMMeshCalculateNormal(mo); }
     return changed;
 }
@@ -1368,6 +1371,31 @@ void la_KnifeFinish(MSelectExtra* se, tnsObject*o){
     while(lstPopPointer(&se->KnifeElements));
     la_FreeSelectData(se->sd); memFree(se);
 }
+void la_KnifeRefreshLoopCuts(MSelectExtra* se, tnsObject* o,tnsMEdge* me){
+    if(!me) return;
+    tnsMMeshClearExtraFlags(o);
+    laListHandle lst={0}; lstAppendPointer(&lst,me); me->flags|=TNS_MESH_FLAG_PICKED;
+    tnsMMeshExpandBandList(o, me, &lst);
+    if(lst.pFirst==lst.pLast){ while(lstPopPointer(&lst)); return; }
+    tnsMEdge* mme; while((mme=lstPopPointer(&lst))){
+        MKnifeElement* ke=lstAppendPointerSized(&se->KnifeElements,mme,sizeof(MKnifeElement));
+        ke->Type=TNS_MMESH_EDGE_BIT;
+    }
+}
+void la_KnifeSortLoopCuts(MSelectExtra* se){
+    laListHandle lst={0};MKnifeElement* ke=se->KnifeElements.pFirst,*NextKe;
+    if(!ke) return;
+    lstRemoveItem(&se->KnifeElements,ke); lstAppendItem(&lst,ke);
+    for(ke=se->KnifeElements.pFirst;ke;ke=NextKe){
+        NextKe=ke->Item.pNext; tnsMEdge* me=ke->p,*firste=((MKnifeElement*)lst.pFirst)->p,*laste=((MKnifeElement*)lst.pLast)->p;
+        if(tnsMMeshEdgeShareFace(me,firste) && (!tnsMMeshEdgeShareVert(me,firste))){
+            lstRemoveItem(&se->KnifeElements,ke); lstPushItem(&lst,ke); ke=se->KnifeElements.pFirst; continue; }
+        if(tnsMMeshEdgeShareFace(me,laste) && (!tnsMMeshEdgeShareVert(me,laste))){
+            lstRemoveItem(&se->KnifeElements,ke); lstAppendItem(&lst,ke); ke=se->KnifeElements.pFirst; continue; }
+    }
+    if(se->KnifeElements.pFirst){ /* something went wrong */ while(lstPopPointer(&se->KnifeElements)); while(lstPopPointer(&lst)); }
+    se->KnifeElements.pFirst=lst.pFirst; se->KnifeElements.pLast=lst.pLast; 
+}
 int OPINV_Knife(laOperator *a, laEvent *e){
     if(!a->This || !a->This->EndInstance){ return 0; }
     laCanvasExtra* ex=a->This->EndInstance; tnsCamera*c=ex->ViewingCamera; laUiItem* ui=ex->ParentUi;
@@ -1379,9 +1407,14 @@ int OPINV_Knife(laOperator *a, laEvent *e){
     MSelectExtra* se=memAcquire(sizeof(MSelectExtra));
     MSelectData* sd=la_InitSelectData(ex->OffScr->pColor[0]->Width, ex->OffScr->pColor[0]->Height, c);
     a->CustomData=se; se->sd=sd; se->root=root;
-    la_PopulateSelectDataPrimitives(sd,mo,c,LA_CANVAS_SELECT_MODE_KNIFE);
 
-    strSafeSet(&a->RuntimeHint, "◧ Set cut  ◨ Cancel  ⮨ Confirm");
+    int SelectMode=LA_CANVAS_SELECT_MODE_KNIFE;
+    if(strSame(strGetArgumentString(a->ExtraInstructionsP, "mode"), "loop_cut")){
+        se->IsLoop=1; SelectMode=LA_CANVAS_SELECT_MODE_EDGES; }
+    la_PopulateSelectDataPrimitives(sd,mo,c,SelectMode);
+
+    if(se->IsLoop){ strSafePrint(&a->RuntimeHint,"◧ Cut  ◨ Cancel"); }
+    else{ strSafePrint(&a->RuntimeHint,"◧ Place Cut  ◨ Cancel  ⮨ Confirm"); }
 
     return LA_RUNNING;
 }
@@ -1399,15 +1432,24 @@ int OPMOD_Knife(laOperator *a, laEvent *e){
 
     if(e->Type&LA_MOUSE_EVENT){
         int elemtype,id=la_SelectGetClosest(se->sd, e->x-ui->L, e->y-ui->U, LA_RH,&elemtype)-1;
-        void* p=la_SelectGetRef(se->sd,id,elemtype); if(la_KnifeIsDuplicated(se,p)) p=0;
-        if(se->PendingElem!=p){ changed=1; }
-        se->PendingElem=p; se->PendingElemType=elemtype;
-        if(e->Type==LA_L_MOUSE_DOWN && p){ la_KnifeAppendCut(se); changed=1; }
+        void* p=la_SelectGetRef(se->sd,id,elemtype);
+        if(se->IsLoop){
+            if(se->PendingElem!=p){ changed=1; while(lstPopPointer(&se->KnifeElements));
+                se->PendingElem=p; la_KnifeRefreshLoopCuts(se, mo, se->PendingElem);
+                la_KnifeSortLoopCuts(se);
+            }
+        }else{
+            if(la_KnifeIsDuplicated(se,p)) p=0;
+            if(se->PendingElem!=p){ changed=1; }
+            se->PendingElem=p; se->PendingElemType=elemtype;
+            if(e->Type==LA_L_MOUSE_DOWN && p){ la_KnifeAppendCut(se); changed=1; }
+        }
     }
 
-    if(e->Type==LA_KEY_DOWN && e->key==LA_KEY_ENTER){
-        if(la_KnifeRegisterCuts(se,mo)){  tnsMMeshEnsureSelection(mo,ex->SelectMode);
-            tnsInvalidateMeshBatch(mo); laRecordAndPush(0,"tns.world","Knife Cut",TNS_HINT_GEOMETRY);laNotifyUsers("tns.world");
+    if((e->Type==LA_KEY_DOWN && e->key==LA_KEY_ENTER) || (se->IsLoop && e->Type==LA_L_MOUSE_DOWN)){
+        if(la_KnifeRegisterCuts(se,mo,se->IsLoop)){ tnsMMeshEnsureSelection(mo,ex->SelectMode);
+            tnsInvalidateMeshBatch(mo); laNotifyUsers("tns.world");
+            laRecordAndPush(0,"tns.world",se->IsLoop?"Loop Cut":"Knife Cut",TNS_HINT_GEOMETRY);
         }
         la_KnifeFinish(se,root); return LA_FINISHED;
     }

+ 2 - 0
resources/la_widgets_viewers.c

@@ -497,6 +497,7 @@ void laDefault3DViewOverlay(laUiItem *ui){
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_extrude");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_make");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_knife");
+        laShowItemFull(gu,gc,&ui->ExtraPP,"_this_M_knife",0,"mode=loop_cut;text=Loop Cut",0,0);
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_subdiv");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_separate");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_combine");
@@ -1106,4 +1107,5 @@ void la_RegisterUiTypesViewerWidgets(){
     laAssignNewKey(km, 0, "M_duplicate", LA_KM_SEL_UI_EXTRA, LA_KEY_SHIFT, LA_KEY_DOWN, 'd', 0);
     laAssignNewKey(km, 0, "M_recalculate_normals", LA_KM_SEL_UI_EXTRA, LA_KEY_CTRL, LA_KEY_DOWN, 'n', 0);
     laAssignNewKey(km, 0, "M_knife", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 'k', 0);
+    laAssignNewKey(km, 0, "M_knife", LA_KM_SEL_UI_EXTRA, LA_KEY_CTRL, LA_KEY_DOWN, 'r', "mode=loop_cut");
 }