*/}}
Browse Source

F for faces okay I guess...

Yiming Wu 2 years ago
parent
commit
5e00e4ca70

+ 1 - 1
source/lagui/la_data.c

@@ -3744,7 +3744,7 @@ void la_FreeNewerDifferences(){
         PrevD=d->Item.pPrev;
         lstRemoveItem(&MAIN.Differences,d);
         la_FreeDifference(d);
-        if(MAIN.HeadDifference==d){ MAIN.HeadDifference=PrevD; laPushDifferenceOnly("desc",0); break; }
+        if(MAIN.HeadDifference==d){ MAIN.HeadDifference=PrevD; laPushDifferenceOnly(0,0); break; }
     }
 }
 void la_FreeAllDifferences(){

+ 7 - 1
source/lagui/la_tns.h

@@ -613,7 +613,8 @@ STRUCTURE(tnsGroup){
     int ExcludeFromCalculation;
 };
 
-#define TNS_MESH_FLAG_SELECTED 1
+#define TNS_MESH_FLAG_SELECTED (1<<0)
+#define TNS_MESH_FLAG_PICKED   (1<<1)
 
 STRUCTURE(tnsMVert){
     laListItem Item;
@@ -1060,13 +1061,17 @@ tnsMFace* tnsMMeshNewFace(tnsMeshObject* mo);
 tnsMEdge* tnsMMeshNewEdge(tnsMeshObject* mo);
 tnsMVert* tnsMMeshNewVert(tnsMeshObject* mo);
 void tnsMMeshEdgeAssignVerts(tnsMEdge* me,tnsMVert* mv1,tnsMVert* mv2);
+tnsMEdge* tnsMMeshVertShareEdge(tnsMVert* mv0, tnsMVert* mv1);
 tnsMVert* tnsMMeshEdgeShareVert(tnsMEdge* me0, tnsMEdge* me1);
 tnsMVert* tnsMMeshEdgeAnotherVert(tnsMEdge* me, tnsVert* v);
 tnsMVert* tnsMMeshEdgeStartingVert(tnsMEdge* me0, tnsMEdge* me1);
 int tnsMMeshSplitFace(tnsMeshObject* mo, tnsMFace* mf, tnsMEdge* me, tnsMFace** r_f1, tnsMFace** r_f2);
 void tnsMMeshFaceAddEdge(tnsMFace* mf, tnsMEdge* me);
 int tnsMMeshFaceMatches(tnsMFace* mf, int ecount, ...);
+tnsMFace* tnsMMeshMakeFaceN(tnsMeshObject* mo, int count, laListHandle* vip);
 tnsMFace* tnsMMeshMakeFace4v(tnsMeshObject* mo, tnsVert* v1,tnsVert* v2,tnsVert* v3,tnsVert* v4);
+int tnsMMeshLoopIsInverted(laListItemPointer* l);
+int tnsMMeshEdgeInsertVert(tnsMeshObject* mo, tnsMEdge* me, tnsMVert* mv, tnsMVert* ref_e1v_optional, tnsMEdge** r_e1, tnsMEdge** r_e2);
 void tnsMMeshRemoveFaceOnly(tnsMeshObject* mo, tnsMFace* mf);
 void tnsMMeshRemoveEdgeFace(tnsMeshObject* mo, tnsMEdge* me);
 void tnsMMeshRemoveVertEdgeFace(tnsMeshObject* mo, tnsMVert* mv);
@@ -1076,6 +1081,7 @@ void tnsMMeshRefreshIndex(tnsMeshObject* mo);
 void tnsMeshEnterEditMode(tnsMeshObject* mo);
 void tnsMeshLeaveEditMode(tnsMeshObject* mo);
 int tnsMMeshAnySelected(tnsMeshObject* mo);
+void tnsMMeshClearPickedFlags(tnsMeshObject* mo);
 void tnsMMeshDeselectAll(tnsMeshObject* mo);
 void tnsMMeshSelectAll(tnsMeshObject* mo);
 void tnsMMeshSelectVert(tnsMeshObject* mo, tnsMVert* mv, int select, int toggle);

+ 56 - 1
source/lagui/la_tns_mesh.c

@@ -178,7 +178,7 @@ void tnsRegenerateMeshBatch(tnsMeshObject* mo){
         tnsCommandOverrideColorArray(c, mo->Batch->NumVert, 3, idcolors);
         c= tnsCreateCommand(mo->Batch, "edges", mo->totme, 2, GL_LINES, eelems, 1);
         tnsCommandOverrideColorArray(c, mo->Batch->NumVert, 4, editcolors);
-        for(int i=0;i<mo->totme*2;i++){ printf("%d ",eelems[i]); } printf("\n");
+        //for(int i=0;i<mo->totme*2;i++){ printf("%d ",eelems[i]); } printf("\n");
     }
     if(idcolors) free(idcolors); if(editcolors) free(editcolors); if(eelems) free(eelems);
 }
@@ -222,6 +222,9 @@ void tnsMMeshEdgeAssignVerts(tnsMEdge* me,tnsMVert* mv1,tnsMVert* mv2){
     me->vl=mv1; me->vr=mv2;
     lstAppendPointer(&me->vl->elink,me); lstAppendPointer(&me->vr->elink,me);
 }
+tnsMEdge* tnsMMeshVertShareEdge(tnsMVert* mv0, tnsMVert* mv1){
+    for(laListItemPointer*lip=mv0->elink.pFirst;lip;lip=lip->pNext){ tnsMEdge* me=lip->p; if(tnsMMeshEdgeAnotherVert(me, mv0)==mv1) return me; } 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;
@@ -275,6 +278,17 @@ tnsMEdge* tnsMMeshMakeEdge(tnsMeshObject* mo, tnsMVert* v1, tnsMVert* v2){
     if(mf){ tnsMMeshSplitFace(mo, mf, me, 0,0); }
     return me;
 }
+int tnsMMeshFaceMatchesN(tnsMFace* mf, int ecount, laListHandle* eip){
+    if(!mf||mf->looplen!=ecount) return 0; laListItemPointer* lipe=eip->pFirst;
+    for(int i=0;i<ecount;i++){
+        tnsMEdge* me=lipe->p; int found=0; lipe=lipe->pNext;
+        for(laListItemPointer* lip=mf->l.pFirst;lip;lip=lip->pNext){
+            if(lip->p==me){ found=1; break; }
+        }
+        if(!found){ return 0; }
+    }
+    return 1;
+}
 int tnsMMeshFaceMatches(tnsMFace* mf, int ecount, ...){
     if(!mf||mf->looplen!=ecount) return 0;
     va_list list; va_start(list, ecount);
@@ -287,6 +301,20 @@ int tnsMMeshFaceMatches(tnsMFace* mf, int ecount, ...){
     }
     va_end(list); return 1;
 }
+tnsMFace* tnsMMeshMakeFaceN(tnsMeshObject* mo, int count, laListHandle* vip){
+    if(count<3) return; laListHandle el={0};
+    for(laListItemPointer*lip=vip->pFirst;lip;lip=lip->pNext){
+        tnsMVert* mv=lip->p; laListItemPointer*nlip=lip->pNext?((laListItemPointer*)lip->pNext):vip->pFirst; tnsMVert* nextmv=nlip->p;
+        lstAppendPointer(&el,tnsMMeshMakeEdge(mo,mv,nextmv));
+    }
+    tnsMEdge* e1=((laListItemPointer*)el.pFirst)->p;
+    if(tnsMMeshFaceMatchesN(e1->fl, count, &el))return e1->fl; if(tnsMMeshFaceMatchesN(e1->fr, count, &el))return e1->fr;
+    for(laListItemPointer* lip=el.pFirst;lip;lip=lip->pNext){ tnsMEdge*me=lip->p; if(me->fl&&me->fr) return 0; }
+    tnsMFace* mf=tnsMMeshNewFace(mo);
+    for(laListItemPointer* lip=el.pFirst;lip;lip=lip->pNext){ tnsMEdge*me=lip->p; tnsMMeshFaceAddEdge(mf, me); }
+    while(lstPopPointer(&el));
+    return mf;
+}
 tnsMFace* tnsMMeshMakeFace4v(tnsMeshObject* mo, tnsVert* v1,tnsVert* v2,tnsVert* v3,tnsVert* v4){
     tnsMEdge* e1=tnsMMeshMakeEdge(mo,v1,v2); tnsMEdge* e2=tnsMMeshMakeEdge(mo,v2,v3);
     tnsMEdge* e3=tnsMMeshMakeEdge(mo,v3,v4); tnsMEdge* e4=tnsMMeshMakeEdge(mo,v4,v1);
@@ -297,6 +325,28 @@ tnsMFace* tnsMMeshMakeFace4v(tnsMeshObject* mo, tnsVert* v1,tnsVert* v2,tnsVert*
     tnsMMeshFaceAddEdge(mf,e3); tnsMMeshFaceAddEdge(mf,e4);
     return mf;
 }
+int tnsMMeshLoopIsInverted(laListItemPointer* l){
+    tnsMEdge* me=l->p; if(l->pNext){ tnsMEdge*next=((laListItemPointer*)l->pNext)->p; if(me->vr==tnsMMeshEdgeShareVert(me,next))return 0; return 1; }
+    else{ tnsMEdge*prev=((laListItemPointer*)l->pPrev)->p; if(me->vl==tnsMMeshEdgeShareVert(prev,me))return 0; return 1;  }
+}
+int tnsMMeshEdgeInsertVert(tnsMeshObject* mo, tnsMEdge* me, tnsMVert* mv, tnsMVert* ref_e1v_optional, tnsMEdge** r_e1, tnsMEdge** r_e2){
+    if(mv->elink.pFirst||mv->elink.pLast||me->vl==mv||me->vl==mv) return 0;
+    tnsMEdge* me1=tnsMMeshNewEdge(mo),*me2=tnsMMeshNewEdge(mo);
+    me1->fl=me2->fl=me->fl; me1->fr=me2->fr=me->fr;
+    me1->vl=me->vl; me1->vr=mv; me2->vl=mv; me2->vr=me->vr;
+    laListItemPointer* lipa1=memAcquireSimple(sizeof(laListItemPointer)),*lipa2=memAcquireSimple(sizeof(laListItemPointer)); lipa1->p=me1; lipa2->p=me2;
+    laListItemPointer* lipb1=memAcquireSimple(sizeof(laListItemPointer)),*lipb2=memAcquireSimple(sizeof(laListItemPointer)); lipb1->p=me1; lipb2->p=me2;
+    for(laListItemPointer* lip=me->fl->l.pFirst;lip;lip=lip->pNext){
+        tnsMEdge*ie=lip->p; if(ie!=me) continue; if(tnsMMeshLoopIsInverted(lip)){ LA_SWAP(laListItemPointer*,lipa1,lipa2); }
+        lstInsertItemBefore(&me->fl->l,lipa1,lip); lstInsertItemBefore(&me->fl->l,lipa2,lip); lstRemoveItem(&me->fl->l, lip); memLeave(lip); me->fl->looplen++;
+    }
+    for(laListItemPointer* lip=me->fr->l.pFirst;lip;lip=lip->pNext){
+        tnsMEdge*ie=lip->p; if(ie!=me) continue; if(tnsMMeshLoopIsInverted(lip)){ LA_SWAP(laListItemPointer*,lipb1,lipb2); }
+        lstInsertItemBefore(&me->fr->l,lipb1,lip); lstInsertItemBefore(&me->fr->l,lipb2,lip); lstRemoveItem(&me->fr->l, lip); memLeave(lip); me->fr->looplen++;
+    }
+    if(tnsMMeshEdgeShareVert(me1, ref_e1v_optional)){ *r_e1=me1; *r_e2=me2; }else{ *r_e1=me2; *r_e2=me1; }
+    return 1;
+}
 void tnsMMeshRemoveFaceOnly(tnsMeshObject* mo, tnsMFace* mf){
     if(!mf) return; tnsMEdge* me;
     while(me=lstPopPointerLeave(&mf->l)){ if(me->fl==mf) me->fl=0; elif(me->fr==mf) me->fr=0; }
@@ -393,6 +443,11 @@ int tnsMMeshAnySelected(tnsMeshObject* mo){
     for(tnsMEdge* me=mo->me.pFirst;me;me=me->Item.pNext){ if(me->flags&TNS_MESH_FLAG_SELECTED) return 1; }
     for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ if(mf->flags&TNS_MESH_FLAG_SELECTED) return 1; } return 0;
 }
+void tnsMMeshClearPickedFlags(tnsMeshObject* mo){
+    for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ mv->flags&=(~TNS_MESH_FLAG_PICKED); }
+    for(tnsMEdge* me=mo->me.pFirst;me;me=me->Item.pNext){ me->flags&=(~TNS_MESH_FLAG_PICKED); }
+    for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ mf->flags&=(~TNS_MESH_FLAG_PICKED); }
+}
 void tnsMMeshDeselectAll(tnsMeshObject* mo){
     for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ mv->flags&=(~TNS_MESH_FLAG_SELECTED); }
     for(tnsMEdge* me=mo->me.pFirst;me;me=me->Item.pNext){ me->flags&=(~TNS_MESH_FLAG_SELECTED); }

+ 5 - 0
source/lagui/la_util.c

@@ -455,6 +455,11 @@ void *lstPushPointerSizedOnly(laListHandle *h, void *p, int size){
     return lip;
 }
 
+void lstReverse(laListHandle* h){
+    laListHandle l={0}; void* i; while(i=lstPopItem(h)){ lstPushItem(&l,i); }
+    memcpy(h,&l,sizeof(laListHandle));
+}
+
 int lstHasPointer(laListHandle* h, void *p){
     laListItemPointer *i; for (i = h->pFirst; i; i = i->pNext){
         if (i->p == p){return 1;}

+ 2 - 0
source/lagui/la_util.h

@@ -442,6 +442,8 @@ void* lstAppendPointerSizedOnly(laListHandle* h, void* p, int size);
 void* lstPushPointerOnly(laListHandle* h, void* p);
 void* lstPushPointerSizedOnly(laListHandle* h, void* p, int size);
 
+void lstReverse(laListHandle* h);
+
 int lstHasPointer(laListHandle* h, void *p);
 void* lstAppendPointer(laListHandle* h, void* p);
 void* lstAppendPointerSized(laListHandle* h, void* p, int size);

+ 139 - 9
source/lagui/resources/la_modelling.c

@@ -142,9 +142,9 @@ int la_SelectGetClosest(MSelectData* sd, int uix, int uiy, int radius){
         for(int j=0;j<w;j++){
             uint8_t* p=&buf[(i*w+j)*4]; int id=(p[0])|(p[1]<<8)|(p[2]<<16);
             if(id && (d=tnsDistIdv2(i,j, radius, radius))<MinD ){ MinD=d; MinID=id; }
-            printf("%d ",buf[(i*w+j)*4]);
+            //printf("%d ",buf[(i*w+j)*4]);
         }
-        printf("\n");
+        //printf("\n");
     }
     free(buf);
     return MinID;
@@ -357,7 +357,7 @@ void la_GetTransformCenter2D(MTransformData* td){
 int la_AddTransformObjectsRecursive(MTransformData* td, tnsObject*root){
     int any=0; for(laListItemPointer* lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){
         tnsObject* o=lip->p; if(o->ChildObjects.pFirst) any+=la_AddTransformObjectsRecursive(td,o);
-        if(!o->Flags&TNS_OBJECT_FLAGS_SELECTED) continue;
+        if(!(o->Flags&TNS_OBJECT_FLAGS_SELECTED)) continue;
         arrEnsureLength(&td->Originals, td->next, &td->max, sizeof(MTOrigObject));
         MTOrigObject* to=arrElement(td->Originals, td->next, sizeof(MTOrigObject)); td->next++; to->o=o;
         memcpy(to->Global, o->GlobalTransform, sizeof(tnsMatrix44d));
@@ -379,13 +379,15 @@ int la_PopulateTransformVerticies(MTransformData* td, tnsMeshObject* mo){
     arrEnsureLength(&td->Originals, 0, &td->max, sizeof(MTOrigMVert));
     tnsInverse44d(td->obmatinv, mo->Base.GlobalTransform);
     for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){
-        if(!mv->flags&TNS_MESH_FLAG_SELECTED) continue;
+        if(!(mv->flags&TNS_MESH_FLAG_SELECTED)) continue;
+        printf("v %d ",mv->i);
         arrEnsureLength(&td->Originals, td->next, &td->max, sizeof(MTOrigMVert));
         MTOrigMVert* to=arrElement(td->Originals, td->next, sizeof(MTOrigMVert)); td->next++; to->mv=mv;
         tnsApplyTransform43d(to->p, mo->Base.GlobalTransform, mv->p);
         memcpy(to->origp, mv->p, sizeof(tnsVector3d)); any++;
         tnsVectorAccum3d(td->TCenter,to->p);
     }
+    printf(" [totmv %d]\n",mo->totmv);
     tnsVectorMultiSelf3d(td->TCenter, 1.0f/any);
     la_GetTransformCenter2D(td);
     return any;
@@ -597,14 +599,14 @@ int OPMOD_Transformation(laOperator *a, laEvent *e){
 
 int la_ParentableRecursive(tnsObject* root, tnsObject* parent){
     for(laListItemPointer* lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){
-        tnsObject* o=lip->p; if(!o || !o->Flags&TNS_OBJECT_FLAGS_SELECTED || o==parent) continue;
+        tnsObject* o=lip->p; if((!o) || (!(o->Flags&TNS_OBJECT_FLAGS_SELECTED)) || (o==parent)) continue;
         if(!tnsCheckParentable(o,parent)) return 0;
         if(!la_ParentableRecursive(o,parent)) return 0;
     }return 1;
 }
 void la_MakeParentExecuteRecursive(tnsObject* root, tnsObject* parent, int Unparent, int KeepTransform){
     for(laListItemPointer* lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){
-        tnsObject* o=lip->p; if(!o || !o->Flags&TNS_OBJECT_FLAGS_SELECTED) continue;
+        tnsObject* o=lip->p; if((!o) || (!(o->Flags&TNS_OBJECT_FLAGS_SELECTED))) continue;
         if(Unparent) tnsUnparentObject(o, KeepTransform);
         else tnsParentObject(o, parent, KeepTransform);
         la_MakeParentExecuteRecursive(o,parent,Unparent,KeepTransform);
@@ -623,7 +625,7 @@ int OPINV_MakeParent(laOperator *a, laEvent *e){
     if(strSame(action,"unparent")){  Unparent=1; }
     if(strSame(keep,"false")){ KeepTransform=0; }
 
-    if(!Unparent){ if(!mo || !mo->Flags&TNS_OBJECT_FLAGS_SELECTED) return LA_FINISHED; 
+    if(!Unparent){ if((!mo) || (!(mo->Flags&TNS_OBJECT_FLAGS_SELECTED))) return LA_FINISHED; 
         if(!la_ParentableRecursive(root,mo)) laEnableMessagePanel(0,0,"It didn't work","There are loops in parenting",e->x,e->y,0,e); return LA_FINISHED;
     }
 
@@ -648,7 +650,7 @@ void laui_Unparent(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn
 
 int la_ClearTransformationRecursive(tnsObject* root, int global,int location,int rotation,int scale){
     int any=0; for(laListItemPointer* lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){
-        tnsObject* o=lip->p; if(!o->Flags&TNS_OBJECT_FLAGS_SELECTED) continue;
+        tnsObject* o=lip->p; if(!(o->Flags&TNS_OBJECT_FLAGS_SELECTED)) continue;
         if(location){
             if(global){ o->GLocation[0]=o->GLocation[1]=o->GLocation[2]=0;}
             else{ o->Location[0]=o->Location[1]=o->Location[2]=0; }
@@ -922,6 +924,133 @@ void laui_Delete(laUiList *uil, laPropPack *pp, laPropPack *actinst, laColumn *e
     laShowItemFull(uil,c,pp,"_this_M_delete",0,"mode=only_faces;text=Only Faces",0,0);
 }
 
+STRUCTURE(MIslandInfo){
+    laListItem Item;
+    laListHandle v,e,f;int numv,nume,numf;
+    int Paired,HasBranches;
+};
+STRUCTURE(MMakeData){
+    laListHandle Islands; int NumIslands;
+};
+
+#define M_SHOULD_INCL_PRIM(m)\
+    ((!(m->flags&TNS_MESH_FLAG_PICKED)) && (m->flags&TNS_MESH_FLAG_SELECTED))
+#define M_SHOULD_USE_OE(oe,sf)\
+    (((!oe->fl)&&(oe->fr!=sf))||((!oe->fr)&&(oe->fl!=sf)))
+
+MIslandInfo* la_NewMIsland(MMakeData* md){ MIslandInfo* ii=memAcquireSimple(sizeof(MIslandInfo)); lstAppendItem(&md->Islands, ii); md->NumIslands++; return ii; }
+void la_FillIslandFromVert(MIslandInfo* ii, tnsMVert* mv, int SelectMode){
+    mv->flags|=TNS_MESH_FLAG_PICKED; lstAppendPointer(&ii->v,mv); ii->numv++; int connections=0;
+    for(laListItemPointer* lip=mv->elink.pFirst;lip;lip=lip->pNext){ tnsMEdge* oe=lip->p; tnsMVert* ov=tnsMMeshEdgeAnotherVert(oe,mv);
+        if(ov->flags&TNS_MESH_FLAG_SELECTED) connections++; else continue;
+        if(SelectMode==LA_CANVAS_SELECT_MODE_EDGES){ if(!(oe->flags&TNS_MESH_FLAG_SELECTED)) continue; }
+        if(M_SHOULD_INCL_PRIM(ov)){ la_FillIslandFromVert(ii,ov,SelectMode); } else { continue; }
+        if(M_SHOULD_INCL_PRIM(oe)){ lstAppendPointer(&ii->e,oe); ii->nume++; oe->flags|=TNS_MESH_FLAG_PICKED; }
+        if(oe->fl&&M_SHOULD_INCL_PRIM(oe->fl)){ lstAppendPointer(&ii->f,oe->fl); ii->numf++; oe->fl->flags|=TNS_MESH_FLAG_PICKED; }
+        if(oe->fr&&M_SHOULD_INCL_PRIM(oe->fr)){ lstAppendPointer(&ii->f,oe->fr); ii->numf++; oe->fr->flags|=TNS_MESH_FLAG_PICKED; }
+    }
+    if(connections>2) ii->HasBranches=1;
+}
+void la_GetSelectionIslands(tnsMeshObject* mo, MMakeData* md, int SelectMode){
+    tnsMMeshClearPickedFlags(mo);
+    for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){
+        if(M_SHOULD_INCL_PRIM(mv)){ MIslandInfo* ii=la_NewMIsland(md); la_FillIslandFromVert(ii,mv,SelectMode); }
+    }
+}
+void la_ClearIslands(MMakeData* md){ MIslandInfo* ii; while(ii=lstPopItem(&md->Islands)){ while(lstPopPointer(&ii->v)); memFree(ii); } }
+tnsMFace* la_MakeFacesFrom1Vert(tnsMeshObject* mo, tnsMVert* mv){
+    tnsMEdge* oe1=0,*oe2=0; tnsMVert* ov1,*ov2;
+    for(laListItemPointer* lip=mv->elink.pFirst;lip;lip=lip->pNext){
+        tnsMEdge* oe=lip->p; if(oe->flags&TNS_MESH_FLAG_SELECTED) continue; if((!oe->fl)||(!oe->fr)){ if(!oe1)oe1=oe;elif(!oe2)oe2=oe;else return 0; /* more than 2 empty edges connected */ }
+    } if(!oe1||!oe2) return 0;
+    ov1=tnsMMeshEdgeAnotherVert(oe1,mv); ov2=tnsMMeshEdgeAnotherVert(oe2,mv);
+    laListHandle vl={0}; lstAppendPointer(&vl,ov1); lstAppendPointer(&vl,mv); lstAppendPointer(&vl,ov2);
+    tnsMFace* f=tnsMMeshMakeFaceN(mo, 3, &vl); while(lstPopPointer(&vl)); return f;
+}
+tnsMFace* la_MakeFacesFrom2Verts(tnsMeshObject* mo, tnsMVert* mv1, tnsMVert* mv2){
+    tnsMEdge* oe1=0,*oe2=0; tnsMVert* ov1,*ov2; tnsMFace* sf=0;
+    tnsMEdge* se=tnsMMeshVertShareEdge(mv1,mv2);  if(se->fl && se->fr) return 0; sf=se->fl?se->fl:se->fr;
+    for(laListItemPointer* lip=mv1->elink.pFirst;lip;lip=lip->pNext){
+        tnsMEdge* oe=lip->p; if(oe->flags&TNS_MESH_FLAG_SELECTED) continue; if(M_SHOULD_USE_OE(oe,sf)){ if(!oe1)oe1=oe;else return 0; /* more than 1 empty edge connected */ }
+    }
+    for(laListItemPointer* lip=mv2->elink.pFirst;lip;lip=lip->pNext){
+        tnsMEdge* oe=lip->p; if(oe->flags&TNS_MESH_FLAG_SELECTED) continue; if(M_SHOULD_USE_OE(oe,sf)){ if(!oe2)oe2=oe;else return 0; /* more than 1 empty edge connected */ }
+    }
+    if(!oe1||!oe2) return 0;
+    ov1=tnsMMeshEdgeAnotherVert(oe1,mv1); ov2=tnsMMeshEdgeAnotherVert(oe2,mv2);
+    ov1->flags|=TNS_MESH_FLAG_SELECTED;ov2->flags|=TNS_MESH_FLAG_SELECTED; mv1->flags&=(~TNS_MESH_FLAG_SELECTED);mv2->flags&=(~TNS_MESH_FLAG_SELECTED);
+    laListHandle vl={0}; lstAppendPointer(&vl,ov1); lstAppendPointer(&vl,mv1); lstAppendPointer(&vl,mv2); lstAppendPointer(&vl,ov2);
+    tnsMFace* f=tnsMMeshMakeFaceN(mo, 4, &vl); while(lstPopPointer(&vl)); return f;
+}
+int la_IsEndingVert(tnsMVert* mv){
+    int sel=0; for(laListItemPointer*lip=mv->elink.pFirst;lip;lip=lip->pNext){ tnsMEdge* me=lip->p;
+        if(tnsMMeshEdgeAnotherVert(me,mv)->flags&TNS_MESH_FLAG_SELECTED){ sel++; if(sel>1) return 0; }
+    }
+    if(sel==1) return 1; return 0;
+}
+void la_EnsureIslandVertsSequence(MIslandInfo* ii){
+    laListHandle l={0}; tnsMVert* startv=((laListItemPointer*)ii->v.pFirst)->p; laListItemPointer* nextlip;
+    for(laListItemPointer*lip=ii->v.pFirst;lip;lip=lip->pNext){ if(la_IsEndingVert(lip->p)){ startv=lip->p; break; } } // otherwise a loop, doesn't matter.
+    lstRemovePointer(&ii->v, startv); lstAppendPointer(&l, startv);
+    while(ii->v.pFirst){
+        for(laListItemPointer*lip=ii->v.pFirst;lip;lip=lip->pNext){ nextlip=lip->pNext;
+            if(tnsMMeshVertShareEdge(startv, lip->p)){ startv=lip->p; lstRemoveItem(&ii->v,lip); lstAppendItem(&l, lip); break; }
+        }
+    }
+    memcpy(&ii->v, &l, sizeof(laListHandle));
+}
+MIslandInfo* la_GetNeighborIsland(tnsMVert* from, MMakeData* md){
+    int found=0; real dist=1e10; MIslandInfo* rii=0; for(MIslandInfo* ii=md->Islands.pFirst;ii;ii=ii->Item.pNext){ if(ii->Paired) continue;
+        found=1; tnsMVert* mv1=((laListItemPointer*)ii->v.pFirst)->p,*mv2=((laListItemPointer*)ii->v.pLast)->p;
+        real d1=tnsDist3dv(mv1->p, from->p),d2=tnsDist3dv(mv2->p, from->p);
+        if(d2<d1 && d2<dist){ lstReverse(&ii->v); } if(d1<dist || d2<dist){ rii=ii; }
+    }
+    if(rii) rii->Paired=1;
+    return rii;
+}
+int la_MakeFacesFromIslands(tnsMeshObject* mo, MMakeData* md){
+    int success=0;
+    if(!md->Islands.pFirst) return 0;
+    if(md->Islands.pFirst==md->Islands.pLast){
+        MIslandInfo* ii=md->Islands.pFirst; if(ii->HasBranches) return 0;
+        if(ii->numv==1){ laListItemPointer*lip=ii->v.pFirst; if(la_MakeFacesFrom1Vert(mo,lip->p)) success++; }
+        elif(ii->numv==2){ laListItemPointer*lip=ii->v.pFirst,*lip2=ii->v.pLast; if(la_MakeFacesFrom2Verts(mo,lip->p, lip2->p)) success++; }
+        else{ la_EnsureIslandVertsSequence(ii); if(tnsMMeshMakeFaceN(mo, ii->numv, &ii->v)) success++; }
+    }else{
+        for(MIslandInfo* ii=md->Islands.pFirst;ii;ii=ii->Item.pNext){ if(ii->HasBranches) return 0; la_EnsureIslandVertsSequence(ii); }
+        laListHandle final={0}; int vcount=0;
+        MIslandInfo* ii=md->Islands.pFirst; ii->Paired=1; for(laListItemPointer* lip=ii->v.pFirst;lip;lip=lip->pNext){ lstAppendPointer(&final, lip->p); vcount++; }
+        while((ii=la_GetNeighborIsland(((laListItemPointer*)ii->v.pLast)->p, md))){
+            for(laListItemPointer* lip=ii->v.pFirst;lip;lip=lip->pNext){ lstAppendPointer(&final, lip->p); vcount++; }
+        }
+        if(tnsMMeshMakeFaceN(mo,vcount,&final)) success++;
+        while(lstPopPointer(&final));
+    }
+    return success;
+}
+int OPINV_Make(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;
+    tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return 0;
+    tnsMeshObject* mo=root->Active;
+    
+    if(mo->Base.Type!=TNS_OBJECT_MESH || mo->Mode!=TNS_MESH_EDIT_MODE){ return LA_CANCELED; }
+
+    MMakeData md={0};
+    la_GetSelectionIslands(mo,&md,ex->SelectMode);
+    int success=la_MakeFacesFromIslands(mo,&md);
+    la_ClearIslands(&md);
+
+    //tnsMMeshDeselectAll(mo);
+    tnsMMeshRefreshIndex(mo);
+    tnsMMeshEnsureSelection(mo,ex->SelectMode);
+    tnsInvaliateMeshBatch(mo);
+    if(laRecordInstanceDifferences(mo, "tns_mesh_object")) laPushDifferences("Make primitives", TNS_HINT_GEOMETRY);
+    laNotifyUsers("tns.world");
+    
+    return LA_FINISHED;
+}
+
 void la_RegisterModellingOperators(){
     laPropContainer *pc; laProp *p;
     laOperatorType *at;
@@ -940,5 +1069,6 @@ void la_RegisterModellingOperators(){
     laCreateOperatorType("M_extrude", "Extrude", "Extrude parts of the mesh", 0, 0, 0, OPINV_Extrude, OPMOD_Transformation, 0, 0);
     at=laCreateOperatorType("M_delete", "Delete", "Delete parts of the mesh", 0, 0, 0, OPINV_Delete, OPMOD_FinishOnData, 0, 0);
     at->UiDefine=laui_Delete;
-
+    laCreateOperatorType("M_make", "Make", "Make mesh primitive from selected ones", 0, 0, 0, OPINV_Make, 0, 0, 0);
+    
 }

+ 1 - 0
source/lagui/resources/la_widgets_viewers.c

@@ -1001,4 +1001,5 @@ void la_RegisterUiTypesViewerWidgets(){
     laAssignNewKey(km, 0, "M_extrude", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 'e', 0);
     laAssignNewKey(km, 0, "M_extrude", LA_KM_SEL_UI_EXTRA, LA_KEY_SHIFT, LA_KEY_DOWN, 'd', "duplicate_only=true;text=Duplicate;");
     laAssignNewKey(km, 0, "M_delete", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 'x', 0);
+    laAssignNewKey(km, 0, "M_make", LA_KM_SEL_UI_EXTRA, 0, LA_KEY_DOWN, 'f', 0);
 }