|
@@ -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;
|
|
|
}
|