*/}}
Browse Source

Basic knife tool.

YimingWu 1 year ago
parent
commit
a5d643d241
8 changed files with 197 additions and 43 deletions
  1. 2 2
      la_data.c
  2. 1 0
      la_interface.h
  3. 10 2
      la_tns.h
  4. 2 0
      la_tns_kernel.c
  5. 2 2
      la_tns_mesh.c
  6. 175 34
      resources/la_modelling.c
  7. 1 1
      resources/la_properties.c
  8. 4 2
      resources/la_widgets_viewers.c

+ 2 - 2
la_data.c

@@ -4448,8 +4448,8 @@ laDiff* laSwapDBState(int Redo){
     else{ MAIN.HeadDifference=diff; }
     return diff;
 }
-void laUndo(){ laDiff* d; if(d=laSwapDBState(0)){ la_ExecUndoPtrSync(d); } }
-void laRedo(){ laDiff* d; if(d=laSwapDBState(1)){ la_ExecUndoPtrSync(d); } }
+void laUndo(){ laDiff* d; if(d=laSwapDBState(0)){ la_ExecUndoPtrSync(d); laNotifyUsers("la.differences"); } }
+void laRedo(){ laDiff* d; if(d=laSwapDBState(1)){ la_ExecUndoPtrSync(d); laNotifyUsers("la.differences"); } }
 
 void laPrintDBInst(laDBInst* dbi, int Level){
     if(dbi!=&MAIN.RootDBInst){

+ 1 - 0
la_interface.h

@@ -889,6 +889,7 @@ STRUCTURE(laCanvasTemplate){
 #define LA_CANVAS_SELECT_MODE_VERTS 0
 #define LA_CANVAS_SELECT_MODE_EDGES 1
 //#define LA_CANVAS_SELECT_MODE_FACES 2
+#define LA_CANVAS_SELECT_MODE_KNIFE 3
 
 #define LA_CANVAS_SELECT_THROUGH_OFF 0
 #define LA_CANVAS_SELECT_THROUGH_ON  1

+ 10 - 2
la_tns.h

@@ -560,6 +560,8 @@ NEED_STRUCTURE(tnsRenderVert);
 
 #define TNS_OBJECT_FLAGS_SELECTED 1
 
+NEED_STRUCTURE(tnsBatch)
+
 typedef struct _tnsObject tnsObject;
 struct _tnsObject
 {
@@ -596,6 +598,8 @@ struct _tnsObject
     laListHandle ChildObjects;
 
     laListHandle Drivers; // rack
+
+    tnsBatch *ExtraBatch; // for root.
 };
 
 NEED_STRUCTURE(laNodeInSocket);
@@ -617,8 +621,6 @@ STRUCTURE(tnsMakeTransformNode){
 #define TNS_ORTHOGRAPHICAL_CAMERA 1
 #define TNS_FISHEYE_CAMERA 2
 
-NEED_STRUCTURE(tnsBatch)
-
 #define TNS_ID_TO_COLOR(color, i) \
     {color[0]=(real)((i & 0x000000FF)>>0)/255.0; color[1]=(real)((i & 0x0000FF00)>>8)/255.0; color[2]=(real)((i & 0x00FF0000)>>16)/255.0;}
 
@@ -1169,6 +1171,10 @@ void tnsDeselectAllObjects(tnsObject* parent);
 void tnsSelectAllObjects(tnsObject* parent);
 void tnsSelectObject(tnsObject* o, int Select, int Toggle);
 
+#define TNS_MMESH_EDGE_BIT 0x800000
+#define TNS_MMESH_FACE_BIT 0xC00000
+#define TNS_MMESH_TYPE_BIT (TNS_MMESH_FACE_BIT|TNS_MMESH_EDGE_BIT)
+
 tnsMFace* tnsMMeshNewFace(tnsMeshObject* mo);
 tnsMEdge* tnsMMeshNewEdge(tnsMeshObject* mo);
 tnsMVert* tnsMMeshNewVert(tnsMeshObject* mo);
@@ -1181,6 +1187,8 @@ 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);
+tnsMFace* tnsMMeshVertsShareFace(tnsMVert* v1, tnsMVert* v2);
+tnsMEdge* tnsMMeshMakeEdge(tnsMeshObject* mo, tnsMVert* v1, tnsMVert* v2);
 void tnsMMeshFaceAddEdge(tnsMFace* mf, tnsMEdge* me);
 int tnsMMeshFaceMatches(tnsMFace* mf, int ecount, ...);
 tnsMFace* tnsMMeshMakeFaceN(tnsMeshObject* mo, int count, laListHandle* vip, tnsMEdge** r_fallback_me);

+ 2 - 0
la_tns_kernel.c

@@ -3763,6 +3763,8 @@ void tnsDrawObjectTree(tnsObject *from, tnsObject *active, int DrawAsObjectSelec
         if (o->ChildObjects.pFirst){ tnsDrawObjectTree(o, active, DrawAsObjectSelection, MeshSelectionMode); }
         tnsPopMatrix();
     }
+    glDisable(GL_DEPTH_TEST);
+    tnsDrawBatch(from->ExtraBatch,0,0,0);
 }
 void tnsDrawObjectOrigins(tnsObject *from, tnsObject *active, int AllOrigins){
     tnsObject *o; if(!from) return;

+ 2 - 2
la_tns_mesh.c

@@ -203,7 +203,7 @@ float* tnsGetDrawingVertArray(tnsMeshObject* mo, int* r_tot_render_v, float** r_
         (*editcolors)=calloc(1,totv*4*sizeof(float)*extraverts);
         for(tnsMVert*mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ int i=mv->i;
             tnsVectorSet3v(&p[i*3],mv->p); tnsVectorSet3v(&n[i*3],mv->n);
-            int id=i+1; real r=(real)((id & 0x000000FF)>>0)/255.0; real g=(real)((id & 0x0000FF00)>>8)/255.0; real b=(real)((id & 0x00FF0000)>>16)/255.0;
+            int id=(i+1); real r=(real)((id & 0x000000FF)>>0)/255.0; real g=(real)((id & 0x0000FF00)>>8)/255.0; real b=(real)((id & 0x00FF0000)>>16)/255.0;
             (*idcolors)[i*3]=r; (*idcolors)[i*3+1]=g; (*idcolors)[i*3+2]=b;
             real* c=(mv->flags&TNS_MESH_FLAG_SELECTED)?laAccentColor(LA_BT_SVERTEX):laAccentColor(LA_BT_VERTEX);
             (*editcolors)[i*4]=c[0]; (*editcolors)[i*4+1]=c[1]; (*editcolors)[i*4+2]=c[2]; (*editcolors)[i*4+3]=c[3];
@@ -211,7 +211,7 @@ float* tnsGetDrawingVertArray(tnsMeshObject* mo, int* r_tot_render_v, float** r_
         for(tnsMEdge*me=mo->me.pFirst;me;me=me->Item.pNext){ int ei=me->i;
             (*edgeelems)[ei*2]=mo->totmv+mo->totmf+ei*2; (*edgeelems)[ei*2+1]=mo->totmv+mo->totmf+ei*2+1;
             float* eidcolor1=&(*idcolors)[(*edgeelems)[ei*2]*3], *eidcolor2=&(*idcolors)[(*edgeelems)[ei*2+1]*3];
-            int id=ei+1; real r=(real)((id & 0x000000FF)>>0)/255.0; real g=(real)((id & 0x0000FF00)>>8)/255.0; real b=(real)((id & 0x00FF0000)>>16)/255.0;
+            int id=((ei+1)|TNS_MMESH_EDGE_BIT); real r=(real)((id & 0x000000FF)>>0)/255.0; real g=(real)((id & 0x0000FF00)>>8)/255.0; real b=(real)((id & 0x00FF0000)>>16)/255.0;
             tnsVectorSet3(eidcolor1,r,g,b); tnsVectorSet3(eidcolor2,r,g,b);
             int se1=(*edgeelems)[ei*2];   tnsVectorSet3v(&p[se1*3],me->vl->p);
             int se2=(*edgeelems)[ei*2+1]; tnsVectorSet3v(&p[se2*3],me->vr->p);

+ 175 - 34
resources/la_modelling.c

@@ -86,7 +86,8 @@ int OPINV_ToggleEdit(laOperator *a, laEvent *e){
 STRUCTURE(MSelectData){
     tnsOffscreen* FBO;
     tnsTexture* Color;
-    void** Refs; int next,max;
+    void** RefsV; int nextV,maxV;
+    void** RefsE; int nextE,maxE;
 };
 MSelectData* la_InitSelectData(int w, int h, tnsCamera* camera){
     MSelectData* sd=memAcquireSimple(sizeof(MSelectData));
@@ -100,16 +101,16 @@ MSelectData* la_InitSelectData(int w, int h, tnsCamera* camera){
 void la_AssignObjectSelectIDRecursive(tnsObject* root, MSelectData* sd){
     for(laListItemPointer*lip=root->ChildObjects.pFirst;lip;lip=lip->pNext){
         tnsObject* o=lip->p; if(!o) continue;
-        arrEnsureLength(&sd->Refs, sd->next, &sd->max, sizeof(tnsObject*));
-        sd->Refs[sd->next]=o; o->SelectID=sd->next; sd->next++;
+        arrEnsureLength(&sd->RefsV, sd->nextV, &sd->maxV, sizeof(tnsObject*));
+        sd->RefsV[sd->nextV]=o; o->SelectID=sd->nextV; sd->nextV++;
         if(o->ChildObjects.pFirst){ la_AssignObjectSelectIDRecursive(o,sd); }
     }
 }
 void la_PopulateSelectDataObjects(MSelectData* sd, tnsObject* root, tnsCamera* camera){
-    arrEnsureLength(&sd->Refs,0,&sd->max,sizeof(tnsObject*));
-    sd->next++; // starting from 1;
+    arrEnsureLength(&sd->RefsV,0,&sd->maxV,sizeof(tnsObject*));
+    sd->nextV++; // starting from 1;
     la_AssignObjectSelectIDRecursive(root, sd);
-    if(sd->next==1) return; int w=sd->Color->Width, h=sd->Color->Height;
+    if(sd->nextV==1) return; int w=sd->Color->Width, h=sd->Color->Height;
     tnsUnbindTexture(); tnsUniformUseTexture(T->immShader,0,0); tnsUseMultiplyColor(0);
     tnsEnableShaderv(T->SelectionShader);
     glDisableVertexAttribArray(T->SelectionShader->iColor); glVertexAttrib4f(T->SelectionShader->iColor,0,0,0,0);
@@ -121,26 +122,28 @@ void la_PopulateSelectDataObjects(MSelectData* sd, tnsObject* root, tnsCamera* c
     glDisable(GL_DEPTH_TEST);
 }
 void la_PopulateSelectVerts(MSelectData* sd, tnsMeshObject* mo){
-    arrEnsureLength(&sd->Refs,0,&sd->max,sizeof(tnsMVert*));
+    arrEnsureLength(&sd->RefsV,0,&sd->maxV,sizeof(tnsMVert*));
     if(mo->Base.Type!=TNS_OBJECT_MESH||!mo->mv.pFirst){ return; }
     for(tnsMVert* v=mo->mv.pFirst;v;v=v->Item.pNext){
-        arrEnsureLength(&sd->Refs, v->i, &sd->max, sizeof(tnsObject*));
-        sd->Refs[v->i]=v; sd->next=TNS_MAX2(v->i, sd->next);
+        arrEnsureLength(&sd->RefsV, v->i, &sd->maxV, sizeof(tnsObject*));
+        sd->RefsV[v->i]=v; sd->nextV=TNS_MAX2(v->i, sd->nextV);
     }
-    sd->next++;
+    sd->nextV++;
 }
 void la_PopulateSelectEdges(MSelectData* sd, tnsMeshObject* mo){
-    arrEnsureLength(&sd->Refs,0,&sd->max,sizeof(tnsMEdge*));
+    arrEnsureLength(&sd->RefsE,0,&sd->maxE,sizeof(tnsMEdge*));
     if(mo->Base.Type!=TNS_OBJECT_MESH||!mo->mv.pFirst){ return; }
     for(tnsMEdge* e=mo->me.pFirst;e;e=e->Item.pNext){
-        arrEnsureLength(&sd->Refs, e->i, &sd->max, sizeof(tnsObject*));
-        sd->Refs[e->i]=e; sd->next=TNS_MAX2(e->i, sd->next);
+        arrEnsureLength(&sd->RefsE, e->i, &sd->maxE, sizeof(tnsObject*));
+        sd->RefsE[e->i]=e; sd->nextE=TNS_MAX2(e->i, sd->nextE);
     }
-    sd->next++;
+    sd->nextE++;
 }
 void la_PopulateSelectDataPrimitives(MSelectData* sd, tnsMeshObject* mo, tnsCamera* camera, int WhatPrim){
-    if(WhatPrim==LA_CANVAS_SELECT_MODE_VERTS){ la_PopulateSelectVerts(sd,mo); }
-    elif(WhatPrim==LA_CANVAS_SELECT_MODE_EDGES){ la_PopulateSelectEdges(sd,mo); }
+    int DoVerts=(WhatPrim==LA_CANVAS_SELECT_MODE_VERTS),DoEdges=(WhatPrim==LA_CANVAS_SELECT_MODE_EDGES);
+    int Knife=(WhatPrim==LA_CANVAS_SELECT_MODE_KNIFE);
+    if(DoVerts || Knife){ la_PopulateSelectVerts(sd,mo); }
+    if(DoEdges || Knife){ la_PopulateSelectEdges(sd,mo); }
     int w=sd->Color->Width, h=sd->Color->Height;
     tnsUnbindTexture(); tnsUniformUseTexture(T->immShader,0,0); tnsUseMultiplyColor(0);
     tnsEnableShaderv(T->SelectionShader);
@@ -149,7 +152,10 @@ void la_PopulateSelectDataPrimitives(MSelectData* sd, tnsMeshObject* mo, tnsCame
     glClearColor(0,0,0,0); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
     glEnable(GL_DEPTH_TEST);
     tnsPushMatrix(); tnsApplyObjectMatrix(mo);
-    tnsDrawBatch(mo->Batch, (WhatPrim==LA_CANVAS_SELECT_MODE_VERTS)?"verts_select":"edges_select",0,0);
+    if(Knife){ glPointSize(10); }
+    if(DoEdges || Knife){ tnsDrawBatch(mo->Batch, "edges_select",0,0); }
+    if(DoVerts || Knife){ tnsDrawBatch(mo->Batch, "verts_select",0,0); }
+    if(Knife){ glPointSize(1); }
     tnsPopMatrix();
     glDisable(GL_DEPTH_TEST);
 }
@@ -196,18 +202,19 @@ uint8_t* la_ReadSelectionBox(MSelectData* sd, int uix, int uiy, int uix2, int ui
     la_PadSelectionBuffer(buf, w, h, _startx-startx, _starty-starty, endx-_endx, endy-_endy,_endx);
     return buf;
 }
-int la_SelectGetClosest(MSelectData* sd, int uix, int uiy, int radius){
-    uint8_t* buf=la_ReadSelectionRadius(sd, uix, uiy, radius); if(!buf) return 0;
-    int w=radius*2; int MinD=INT_MAX; int MinID=0, d;
+int la_SelectGetClosest(MSelectData* sd, int uix, int uiy, int radius, int *ElemType){
+    *ElemType=0; uint8_t* buf=la_ReadSelectionRadius(sd, uix, uiy, radius); if(!buf) return 0;
+    int w=radius*2; int MinD=INT_MAX; int MinID=0, d, elemtype=0;
     for(int i=0;i<w;i++){
         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; }
+            uint8_t* p=&buf[(i*w+j)*4]; int id=(p[0])|(p[1]<<8)|((p[2]<<16)&(~TNS_MMESH_TYPE_BIT));
+            if(id && (d=tnsDistIdv2(i,j, radius, radius))<MinD ){ MinD=d; MinID=id; elemtype=((p[2]<<16)&TNS_MMESH_TYPE_BIT); }
             //printf("%d ",buf[(i*w+j)*4]);
         }
         //printf("\n");
     }
     free(buf);
+    *ElemType=elemtype;
     return MinID;
 }
 int* la_SelectGetBox(MSelectData* sd, int uix, int uiy, int uix2, int uiy2, int* r_length){
@@ -220,7 +227,7 @@ int* la_SelectGetBox(MSelectData* sd, int uix, int uiy, int uix2, int uiy2, int*
     arrEnsureLength(&ids, next, &max, sizeof(int));
     for(int i=0;i<h;i++){
         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);
+            uint8_t* p=&buf[(i*w+j)*4]; int id=(p[0])|(p[1]<<8)|((p[2]<<16)&(~TNS_MMESH_TYPE_BIT));
             if(id){ int found=0;
                 for(int a=0;a<next;a++){ if(ids[a]==id){ found=1; break; } }
                 if(!found){
@@ -234,9 +241,15 @@ int* la_SelectGetBox(MSelectData* sd, int uix, int uiy, int uix2, int uiy2, int*
 }
 void la_FreeSelectData(MSelectData* sd){
     tnsDelete2DOffscreen(sd->FBO);
-    free(sd->Refs);
+    free(sd->RefsV);
+    free(sd->RefsE);
     memFree(sd);
 }
+void* la_SelectGetRef(MSelectData* sd, int id, int elemtype){
+    if(!elemtype){ if(id>=0 && id<sd->nextV){ return sd->RefsV[id]; } }
+    elif(elemtype==TNS_MMESH_EDGE_BIT){ if(id>=0 && id<sd->nextE){ return sd->RefsE[id]; } }
+    return 0;
+}
 
 int OPCHK_ViewportAndSceneExists(laPropPack *This, laStringSplitor *ss){
     if(!This || !This->EndInstance){ return 0; } laCanvasExtra* ex=This->EndInstance;
@@ -262,6 +275,9 @@ STRUCTURE(MSelectExtra){
     tnsCamera* cam;
     int Mode;
     int InSelect;
+    
+    laListHandle KnifeElements;
+    void* PendingElem; int PendingElemType, BatchDirty;
 };
 
 int OPINV_Select(laOperator *a, laEvent *e){
@@ -294,8 +310,8 @@ int OPINV_Select(laOperator *a, laEvent *e){
             ex->OnX=e->x; ex->OnX=e->y;
             a->CustomData=se; se->sd=sd; se->Mode=LA_SELECT_MODE_BOX; se->mo=mo; se->cam=c; ex->DrawCursor=1; se->root=root; return LA_RUNNING;
         }
-        int id=la_SelectGetClosest(sd, e->x-ui->L, e->y-ui->U, LA_RH)-1;
-        void* p; if(id>=0 && id<sd->next){ p=sd->Refs[id]; }
+        int elemtype,id=la_SelectGetClosest(sd, e->x-ui->L, e->y-ui->U, LA_RH,&elemtype)-1;
+        void* p=la_SelectGetRef(sd,id,elemtype); printf("%d %d\n",id,elemtype);
         la_DoMeshSelect(mo, p, ex->SelectMode, DeselectAll, 1, 1); tnsMMeshEnsureSelection(mo,ex->SelectMode);
         tnsInvalidateMeshBatch(mo);
         laNotifyUsers("tns.world"); laRecordAndPush(0,"tns.world","Mesh selection",TNS_HINT_GEOMETRY);
@@ -306,8 +322,8 @@ int OPINV_Select(laOperator *a, laEvent *e){
             ex->OnX=e->x; ex->OnX=e->y;
             a->CustomData=se; se->sd=sd; se->Mode=LA_SELECT_MODE_BOX; se->cam=c; ex->DrawCursor=1; se->root=root; return LA_RUNNING;
         }
-        int id=la_SelectGetClosest(sd, e->x-ui->L, e->y-ui->U, LA_RH*2);
-        if(id && id<sd->next){ la_DoObjectSelect(root, sd->Refs[id], ex, DeselectAll, 1, 1);  }
+        int elemtype,id=la_SelectGetClosest(sd, e->x-ui->L, e->y-ui->U, LA_RH*2,&elemtype); printf("%d\n",elemtype);
+        void* p=la_SelectGetRef(sd,id,elemtype); if(p){ la_DoObjectSelect(root, p, ex, DeselectAll, 1, 1);  }
         else{ la_DoObjectSelect(root, 0, ex, DeselectAll, 1, 1); }
         laNotifyUsers("tns.world"); laRecordAndPush(0,"tns.world","Object selection",TNS_HINT_TRANSFORM);
     }
@@ -326,7 +342,7 @@ int OPMOD_Select(laOperator *a, laEvent *e){
     if(e->Type==LA_L_MOUSE_DOWN){ se->InSelect=1; ex->DrawCursor=2; ex->ClickedX=e->x; ex->ClickedY=e->y; laRedrawCurrentPanel(); }
     if(e->Type&LA_MOUSE_EVENT){ ex->OnX=e->x; ex->OnY=e->y; laRedrawCurrentPanel(); }
     if(e->Type==LA_R_MOUSE_DOWN || (e->Type == LA_KEY_DOWN && e->key==LA_KEY_ESCAPE)){
-        ex->DrawCursor=0; la_FreeSelectData(se->sd); laNotifyUsers("tns.world"); return LA_FINISHED;
+        ex->DrawCursor=0; la_FreeSelectData(se->sd); memFree(se); laNotifyUsers("tns.world"); return LA_FINISHED;
     }
 
     int DeselectAll=1;
@@ -339,7 +355,7 @@ int OPMOD_Select(laOperator *a, laEvent *e){
             la_DoMeshSelect(mo, 0, ex->SelectMode, DeselectAll, 0, 0);
             int len; int* ids=la_SelectGetBox(se->sd, ex->ClickedX-ui->L, ex->ClickedY-ui->U, e->x-ui->L, e->y-ui->U, &len);
             for(int i=0;i<len;i++){
-                int id=ids[i]-1; void* p; if(id>=0 && id<se->sd->next){ p=se->sd->Refs[id]; }
+                int id=ids[i]-1; void* p=la_SelectGetRef(se->sd,id,ex->SelectMode==LA_CANVAS_SELECT_MODE_EDGES?TNS_MMESH_EDGE_BIT:0);
                 la_DoMeshSelect(mo, p, ex->SelectMode, 0, !Remove, 0);
             }
             tnsMMeshEnsureSelection(mo,ex->SelectMode);
@@ -348,12 +364,13 @@ int OPMOD_Select(laOperator *a, laEvent *e){
             la_DoObjectSelect(se->root, 0, ex, DeselectAll, 0, 0);
             int len; int* ids=la_SelectGetBox(se->sd, ex->ClickedX, ex->ClickedY, e->x-ui->L, e->y-ui->U, &len);
             for(int i=0;i<len;i++){
-                int id=ids[i]; if(id && id<se->sd->next){ la_DoObjectSelect(se->root, se->sd->Refs[id], ex, 0, !Remove, 0);  }
+                int id=ids[i]; void* p=la_SelectGetRef(se->sd,id,0);
+                if(p){ la_DoObjectSelect(se->root, p, ex, 0, !Remove, 0);  }
             }
         }
         laNotifyUsers("tns.world"); laRecordAndPush(0,"tns.world","Box selection",is_geo?TNS_HINT_GEOMETRY:TNS_HINT_TRANSFORM);
         ex->DrawCursor=0;
-        la_FreeSelectData(se->sd);
+        la_FreeSelectData(se->sd); memFree(se);
         laRedrawCurrentPanel();
         return LA_FINISHED;
     }
@@ -1039,7 +1056,10 @@ tnsMFace* la_MakeFacesFrom1Vert(tnsMeshObject* mo, tnsMVert* mv){
     } 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, 0); while(lstPopPointer(&vl)); return f;
+    tnsMFace* f=tnsMMeshMakeFaceN(mo, 3, &vl, 0);
+    ov1->flags|=TNS_MESH_FLAG_SELECTED;ov2->flags|=TNS_MESH_FLAG_SELECTED; mv->flags&=(~TNS_MESH_FLAG_SELECTED); 
+    tnsMMeshEnsureSelectionFromVerts(mo);
+    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;
@@ -1054,7 +1074,8 @@ tnsMFace* la_MakeFacesFrom2Verts(tnsMeshObject* mo, tnsMVert* mv1, tnsMVert* mv2
     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, 0); while(lstPopPointer(&vl)); return f;
+    tnsMFace* f=tnsMMeshMakeFaceN(mo, 4, &vl, 0); tnsMMeshEnsureSelectionFromVerts(mo);
+    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;
@@ -1272,6 +1293,124 @@ int OPINV_RecalculateNormals(laOperator *a, laEvent *e){
     return LA_FINISHED;
 }
 
+STRUCTURE(MKnifeElement){
+    laListItem Item;
+    void* p;
+    int Type;
+};
+void la_KnifeUpdateToolBatch(MSelectExtra* se,tnsObject* o){
+    if(se->root->ExtraBatch) tnsDeleteBatch(se->root->ExtraBatch); se->root->ExtraBatch=0;
+    int count=lstCountElements(&se->KnifeElements); if((!count) && (!se->PendingElem)) return;
+    float* points=calloc((count+1)*3,sizeof(real));
+    float* p=points; real tmp[3],trans[4];
+    for(MKnifeElement* ke=se->KnifeElements.pFirst;ke;ke=ke->Item.pNext){
+        if(ke->Type==TNS_MMESH_EDGE_BIT){ tnsMEdge* me=ke->p; tnsVectorSet3v(tmp,me->vl->p); tnsVectorAccum3d(tmp,me->vr->p);
+            tnsVectorMultiSelf3d(tmp,0.5); tnsApplyTransform43d(trans,o->GlobalTransform,tmp); tnsVectorSet3v(p,trans); }
+        else{ tnsMVert* mv=ke->p; tnsApplyTransform43d(trans,o->GlobalTransform,mv->p); tnsVectorSet3v(p,trans); }
+        p+=3;
+    }
+    if(se->PendingElem){
+        if(se->PendingElemType==TNS_MMESH_EDGE_BIT){ tnsMEdge* me=se->PendingElem; tnsVectorSet3v(tmp,me->vl->p); tnsVectorAccum3d(tmp,me->vr->p);
+        tnsVectorMultiSelf3d(tmp,0.5); tnsApplyTransform43d(trans,o->GlobalTransform,tmp); tnsVectorSet3v(p,trans); }
+        else{ tnsMVert* mv=se->PendingElem; tnsApplyTransform43d(trans,o->GlobalTransform,mv->p); tnsVectorSet3v(p,trans); }
+    }elif(count){
+        tnsVectorSet3v(p,p-3);
+    }
+    uint32_t elem=count;
+    tnsBatch* batch=tnsCreateBatch(count+1,3,points,0,0,0,0); tnsBatchCommand*c;
+    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);
+        tnsCommandUseUniformColor(c,laAccentColor(LA_BT_NORMAL));
+        c=tnsCreateCommand(batch, "points", count+1, 3, GL_POINTS, 0, 0);
+        tnsCommandUseUniformColor(c,laAccentColor(LA_BT_NORMAL));
+    }
+    se->root->ExtraBatch=batch;
+    free(points);
+}
+int la_KnifeIsDuplicated(MSelectExtra* se, void* ref){
+    for(MKnifeElement* ke=se->KnifeElements.pFirst;ke;ke=ke->Item.pNext){
+        if(ke->Type==TNS_MMESH_EDGE_BIT && ke->p==ref){ return 1; }
+    }
+    return 0;
+}
+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){
+    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;
+    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; }
+    }
+    if(changed){ tnsMMeshRefreshIndex(mo); tnsMMeshCalculateNormal(mo); }
+    return changed;
+}
+void la_KnifeFinish(MSelectExtra* se, tnsObject*o){
+    if(o->ExtraBatch) tnsDeleteBatch(o->ExtraBatch); o->ExtraBatch=0;
+    while(lstPopPointer(&se->KnifeElements));
+    la_FreeSelectData(se->sd); memFree(se);
+}
+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;
+    tnsObject*root=ui?ui->PP.EndInstance:0; if(!root) return LA_CANCELED;
+    tnsMeshObject* mo=root->Active; int ran=0;
+    
+    if(!mo || mo->Base.Type!=TNS_OBJECT_MESH || mo->Mode!=TNS_MESH_EDIT_MODE){ return LA_CANCELED; }
+
+    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");
+
+    return LA_RUNNING;
+}
+int OPMOD_Knife(laOperator *a, laEvent *e){
+    if(!a->This || !a->This->EndInstance || !a->CustomData){ 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;
+    MSelectExtra* se=a->CustomData;
+    tnsMeshObject* mo=root->Active;
+    int changed=0;
+
+    if(e->Type==LA_R_MOUSE_DOWN || (e->Type == LA_KEY_DOWN && e->key==LA_KEY_ESCAPE)){
+        la_KnifeFinish(se,root); laNotifyUsers("tns.world"); return LA_FINISHED;
+    }
+
+    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; }
+    }
+
+    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");
+        }
+        la_KnifeFinish(se,root); return LA_FINISHED;
+    }
+    
+    if(changed){
+        la_KnifeUpdateToolBatch(se,mo); laRedrawCurrentPanel();
+    }
+    
+    return LA_RUNNING;
+}
+
+
 void la_RegisterModellingOperators(){
     laPropContainer *pc; laProp *p;
     laOperatorType *at;
@@ -1299,4 +1438,6 @@ void la_RegisterModellingOperators(){
     laCreateOperatorType("M_combine", "Combine", "Combine mesh objects", 0, 0, 0, OPINV_Combine, 0, 0, 0);
     laCreateOperatorType("M_duplicate", "Duplicate", "Duplicate objects", 0, 0, 0, OPINV_Duplicate, OPMOD_Transformation, 0, 0);
     laCreateOperatorType("M_recalculate_normals", "Recalculate Normals", "Recalculate normals", 0, 0, 0, OPINV_RecalculateNormals, 0, 0, 0);
+    laCreateOperatorType("M_knife", "Knife", "Cut through edges", OPCHK_ViewportAndSceneExists, 0, 0, OPINV_Knife, OPMOD_Knife, 0, LA_EXTRA_TO_PANEL);
+    
 }

+ 1 - 1
resources/la_properties.c

@@ -1480,7 +1480,7 @@ void la_RegisterInternalProps(){
         laAddSubGroup(p, "children", "Children", "The Children Of This Object", "tns_child_object",0,0,0,-1, 0,0,0,0,0,0,offsetof(tnsObject, ChildObjects), 0);
     }
     p = laAddPropertyContainer("tns_mesh_object", "Mesh Object", "Mesh object", 0,0,sizeof(tnsMeshObject), tnspost_Object, 0,2);{
-        //laPropContainerExtraFunctions(p,0,0,tnstouched_Object,tnspropagate_Object,0);
+        laPropContainerExtraFunctions(p,0,0,tnstouched_Object,0/*tnspropagate_Object*/,0);
         TNS_PC_OBJECT_MESH=p;
         laAddStringProperty(p, "name", "Object Name", "The Name Of The Object", 0,0,0,0,1, offsetof(tnsObject, Name), 0,0,0,0,LA_AS_IDENTIFIER);
         laAddSubGroup(p, "base", "Base", "Object base", "tns_object",0,0,0,0,0,0,0,0,0,0,0,LA_UDF_LOCAL);

+ 4 - 2
resources/la_widgets_viewers.c

@@ -160,7 +160,7 @@ void la_RootObjectDraw(laBoxedTheme *bt, tnsObject *root, laUiItem* ui){
         tnsDrawObjectTree(root, 0, 0, e->SelectMode);
         glPointSize(1); glLineWidth(3); 
 
-        glEnable(GL_POLYGON_OFFSET_LINE); glPolygonOffset(-10,-10);
+        glEnable(GL_POLYGON_OFFSET_LINE); glPolygonOffset(-1,-1);
         glDepthMask(GL_FALSE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
         tnsDrawObjectTree(root, root->Active, 1, 0);
         glDepthMask(GL_TRUE); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -496,8 +496,8 @@ void laDefault3DViewOverlay(laUiItem *ui){
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_clear_transformations");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_extrude");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_make");
+        laShowItem(gu,gc,&ui->ExtraPP,"_this_M_knife");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_subdiv");
-        laShowItem(gu,gc,&ui->ExtraPP,"_this_M_add");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_separate");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_combine");
         laShowItem(gu,gc,&ui->ExtraPP,"_this_M_duplicate");
@@ -1064,6 +1064,7 @@ void la_RegisterUiTypesViewerWidgets(){
         laAddOperatorProperty(pc, "_this_M_combine", "Combine", "Combine mesh objects", "M_combine", 0, 0);
         laAddOperatorProperty(pc, "_this_M_duplicate", "Duplicate", "Duplicate objects", "M_duplicate", 0, 0);
         laAddOperatorProperty(pc, "_this_M_recalculate_normals", "Recalculate Normals", "Recalculate normals", "M_recalculate_normals", 0, 0);
+        laAddOperatorProperty(pc, "_this_M_knife", "Knife", "Cut though edges", "M_knife", 0, 0);
     }
 
     km = &ct->KeyMapper;
@@ -1101,4 +1102,5 @@ void la_RegisterUiTypesViewerWidgets(){
     laAssignNewKey(km, 0, "M_combine", LA_KM_SEL_UI_EXTRA, LA_KEY_CTRL, LA_KEY_DOWN, 'j', 0);
     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);
 }