#include "la_5.h"
#include <math.h>

extern tnsMain *T;


tnsEdgeHash* tnsCreateEdgeHash(int OptionalInitialVertCount){
    tnsEdgeHash*eh=memAcquireSimple(sizeof(tnsEdgeHash));
    eh->max=OptionalInitialVertCount;
    if(eh->max<16) eh->max=16;
    arrInitLength(&eh->vl,eh->max,&eh->max, sizeof(tnsEdgeHashVert));
    return eh;
}
void tnsDestroyEdgeHash(tnsEdgeHash* eh){
    for(int i=0;i<eh->max;i++){
        tnsEdgeHashVert* ehv=&eh->vl[i];
        if(ehv->e) free(ehv->e);
    }
    if(eh->vl) free(eh->vl);
    memFree(eh);
}
void tnsEdgeHashAddVertPair(tnsEdgeHash* eh, int v1, int v2){
    if(v1==v2) return; if(v1>v2) LA_SWAP(int, v1,v2); if(v1>=eh->max) return;
    tnsEdgeHashVert* ehv=&eh->vl[v1];
    if(!ehv->e) arrInitLength(&ehv->e,1,&ehv->max, sizeof(tnsEdgeHashEdge));
    else arrEnsureLength(&ehv->e, ehv->next, &ehv->max, sizeof(tnsEdgeHashEdge));
    for(int i=0;i<ehv->next;i++){ if(ehv->e[i].tv==v2) return; }
    ehv->e[ehv->next].tv=v2;
    ehv->next++;
}
tnsEdgeHashEdge* tnsEdgeHashGetEdge(tnsEdgeHash* eh, int v1, int v2){
    if(v1==v2) return 0; if(v1>v2) LA_SWAP(int, v1,v2); if(v1>=eh->max) return 0;
    tnsEdgeHashVert* ehv=&eh->vl[v1];
    for(int i=0;i<ehv->next;i++){ if(ehv->e[i].tv==v2) return &ehv->e[i]; }
    return 0;
}

void tnsInitMesh(tnsMeshObject* mo, int Initialv, int Initiale, int Initialf){
    arrInitLength(&mo->v, Initialv, &mo->maxv, sizeof(tnsVert));
    arrInitLength(&mo->e, Initiale, &mo->maxe, sizeof(tnsEdge));
    arrInitLength(&mo->f, Initialf, &mo->maxf, sizeof(tnsFace));
}
tnsVert* tnsFillVertI(tnsMeshObject* mo, int index, real x, real y, real z){
    arrEnsureLength(&mo->v, index, &mo->maxv, sizeof(tnsVert));
    mo->v[index].p[0]=x;
    mo->v[index].p[1]=y;
    mo->v[index].p[2]=z;
    return &mo->v[index];
}
tnsVert* tnsFillVert(tnsMeshObject* mo, real x, real y, real z){
    int index=mo->totv; mo->totv++;
    return tnsFillVertI(mo, index, x,y,z);
}
tnsFace* tnsFillFace(tnsMeshObject* mo, int vertcount, ...){
    arrEnsureLength(&mo->f, mo->totf, &mo->maxf, sizeof(tnsFace));
    arrInitLength(&mo->f[mo->totf].loop, vertcount, &mo->f[mo->totf].looplen, sizeof(int));
    va_list list; va_start(list, vertcount); int id=va_arg(list, int); if(id==-1){ va_end(list); mo->totf++; return &mo->f[mo->totf-1]; }
    for(int i=0;i<vertcount;i++){ mo->f[mo->totf].loop[i]=id; id=va_arg(list, int); }
    va_end(list);
    mo->totf++; return &mo->f[mo->totf-1];
}
void tnsFillFaceLoop(tnsFace* f, int i, int v){ f->loop[i]=v; }

void tnsInitMeshPlane(tnsMeshObject* mo, real size){
    tnsInitMesh(mo, 4,0,1);
    tnsFillVert(mo, size, size,0); tnsFillVert(mo,-size, size,0);
    tnsFillVert(mo,-size,-size,0); tnsFillVert(mo, size,-size,0);
    tnsFillFace(mo, 4, 0,1,2,3);
    mo->v[0].flags|=TNS_MESH_FLAG_SELECTED;
    mo->v[1].flags|=TNS_MESH_FLAG_SELECTED;
    mo->v[2].flags|=TNS_MESH_FLAG_SELECTED;
    mo->v[3].flags|=TNS_MESH_FLAG_SELECTED;
    tnsMMeshEnsureSelectionFromVert(mo);
}

void tnsTrangulateFaceSimple(tnsMeshObject* mo, tnsFace* f, int* ebuf){
    for(int i=0;i<f->looplen-2;i++){
        ebuf[i*3]=f->loop[0];
        ebuf[i*3+1]=f->loop[i+1];
        ebuf[i*3+2]=f->loop[i+2];
    }
}
void tnsTrangulateFaceSimpleM(tnsMeshObject* mo, tnsMFace* mf, int* ebuf){
    tnsMVert* mv=0,*mvs; int i=0;
    for(laListItemPointer* lip=mf->l.pFirst;lip;lip=lip->pNext){
        laListItemPointer* next=lip->pNext; if(!next) next=mf->l.pFirst; tnsMEdge* me0=lip->p, *me1=next->p;
        if(next==mf->l.pLast){ break; }
        mvs=tnsMMeshEdgeStartingVert(me0,me1);
        if(!mv) mv=mvs;
        ebuf[i*3]=mv->i; tnsMVert*mm=tnsMMeshEdgeAnotherVert(me0,mvs);
        ebuf[i*3+1]=mm->i;
        ebuf[i*3+2]=tnsMMeshEdgeAnotherVert(me1,mm)->i; i++;
    }
}
int* tnsGetTriangulatedBatch(tnsMeshObject* mo, int* totelem){
    int tottri=0;
    if(mo->Mode==TNS_MESH_OBJECT_MODE){ for(int i=0;i<mo->totf;i++){ tottri+=(mo->f[i].looplen-2); } }
    else{ for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ tottri+=mf->looplen-2; } }
    if(!tottri) return 0;
    int* ebuf=calloc(1,sizeof(int)*tottri*3); int* pebuf=ebuf;
    if(mo->Mode==TNS_MESH_OBJECT_MODE){ for(int i=0;i<mo->totf;i++){ tnsTrangulateFaceSimple(mo, &mo->f[i], pebuf); pebuf+=(mo->f[i].looplen-2)*3; } }
    else{ for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ tnsTrangulateFaceSimpleM(mo, mf, pebuf); pebuf+=(mf->looplen-2)*3; } }
    *totelem = tottri;
    return ebuf;
}
float* tnsGetDrawingVertArray(tnsMeshObject* mo, int* r_totv, int DoIdColors, float** idcolors, int DoEditModeColors, float** editcolors){
    if(!mo->totv&&!mo->totmv) return 0;
    int totv=mo->Mode==TNS_MESH_EDIT_MODE?mo->totmv:mo->totv; *r_totv=totv;
    float* p=calloc(1,totv*3*sizeof(float));
    if(DoIdColors){ (*idcolors)=calloc(1,totv*3*sizeof(float)); }
    if(DoEditModeColors){ (*editcolors)=calloc(1,totv*4*sizeof(float)); }
    if(mo->Mode==TNS_MESH_OBJECT_MODE){ for(int i=0;i<totv;i++){ p[i*3]=mo->v[i].p[0]; p[i*3+1]=mo->v[i].p[1]; p[i*3+2]=mo->v[i].p[2]; } }
    else{ for(tnsMVert*mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ int i=mv->i;
        p[i*3]=mv->p[0]; p[i*3+1]=mv->p[1]; p[i*3+2]=mv->p[2];
        if(DoIdColors){
            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;
        }
        if(DoEditModeColors){
            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];
        }
    } }
    return p;
}
int* tnsGetEdgeBatch(tnsMeshObject* mo){
    if(!mo->totme) return 0;
    int* ebuf=calloc(1,sizeof(int)*mo->totme*2); int* pebuf=ebuf; int i=0;
    for(tnsMEdge* me=mo->me.pFirst;me;me=me->Item.pNext){ ebuf[i*2]=me->vl->i; ebuf[i*2+1]=me->vr->i; i++; }
    return ebuf;
}

void tnsInvaliateMeshBatch(tnsMeshObject* mo){
    if(mo->Base.Type!=TNS_OBJECT_MESH) return;
    if(mo->Batch) tnsDeleteBatch(mo->Batch); mo->Batch=0;
}
void tnsRegenerateMeshBatch(tnsMeshObject* mo){
    if(!mo) return;
    if(mo->Batch) tnsDeleteBatch(mo->Batch); mo->Batch=0;
    real meshcolor[4]={0.8,0.8,0.8,0.6};

    int tottri; int* elem = tnsGetTriangulatedBatch(mo, &tottri);
    float* idcolors=0,*editcolors=0; int docolors=mo->Mode==TNS_MESH_EDIT_MODE;
    int totv;
    float* v = tnsGetDrawingVertArray(mo,&totv,docolors,&idcolors,docolors,&editcolors);
    if(!v){ if(elem){free(elem);} return; }
    mo->Batch = tnsCreateBatch(totv, 3, v, 0, 0, 4, editcolors);
    tnsBatchCommand*c=tnsCreateCommand(mo->Batch, "body", tottri, 3, GL_TRIANGLES, elem, 0);
    tnsCommandUseUniformColor(c,meshcolor);
    free(elem); free(v);

    if(mo->Mode==TNS_MESH_EDIT_MODE){
        elem=tnsGetEdgeBatch(mo); if(elem) {
            c= tnsCreateCommand(mo->Batch, "lines", mo->totme, 2, GL_LINES, elem, 0); free(elem);
            //tnsCommandUseUniformColor(c, laAccentColor(LA_BT_EDGE));
        }
        c= tnsCreateCommand(mo->Batch, "verts", mo->totmv, 3, GL_POINTS, 0, 0);
        c= tnsCreateCommand(mo->Batch, "verts_select", mo->totmv, 3, GL_POINTS, 0, 1);
        tnsCommandOverrideColorArray(c, mo->Batch->NumVert, 3, idcolors);
    }
    if(idcolors) free(idcolors); if(editcolors) free(editcolors);
}
void tnsEnsureMeshBatch(tnsMeshObject* mo){
    if(mo->Base.Type!=TNS_OBJECT_MESH) return;
    if(mo->Batch) return;
    tnsRegenerateMeshBatch(mo);
}
void tnsDrawMeshObject(tnsMeshObject* mo, int DrawAsSelection, tnsMeshObject* Active){
    tnsEnsureMeshBatch(mo);
    if(DrawAsSelection){
        if(mo->Base.Flags&TNS_OBJECT_FLAGS_SELECTED && mo->Mode!=TNS_MESH_EDIT_MODE){
            real* color=(Active==mo)?laAccentColor(LA_BT_TEXT):laAccentColor(LA_BT_NORMAL);
            tnsDrawBatch(mo->Batch, "body", color, 0);
        }
    }else{
        if(T->BindedShader==T->SelectionShader){
            int i=mo->Base.SelectID; real color[4]={0,0,0,1};
            color[0]=(real)((i & 0x000000FF)>>0)/255.0;
            color[1]=(real)((i & 0x0000FF00)>>8)/255.0;
            color[2]=(real)((i & 0x00FF0000)>>16)/255.0;
            tnsDrawBatch(mo->Batch,"body",color,0);
        }else tnsDrawBatch(mo->Batch,0,0,0);
    }
}

tnsMFace* tnsMMeshNewFace(tnsMeshObject* mo){ tnsMFace* mf=memAcquireSimple(sizeof(tnsMFace)); mf->i=mo->totmf; mo->totmf++; lstAppendItem(&mo->mf,mf); return mf; }
tnsMEdge* tnsMMeshNewEdge(tnsMeshObject* mo){ tnsMEdge* me=memAcquireSimple(sizeof(tnsMEdge)); me->i=mo->totme; mo->totme++; lstAppendItem(&mo->me,me); return me; }
tnsMVert* tnsMMeshNewVert(tnsMeshObject* mo){ tnsMVert* mv=memAcquireSimple(sizeof(tnsMVert)); mv->i=mo->totmv; mo->totmv++; lstAppendItem(&mo->mv,mv); return mv; }
void tnsMMeshEdgeAssignVerts(tnsMEdge* me,tnsMVert* mv1,tnsMVert* mv2){
    if(me->vl||me->vr){ return; }  //if((me->vl==mv1&&me->vr=mv2) || (me->vl==mv2&&me->vr=mv1))
    me->vl=mv1; me->vr=mv2;
    lstAppendPointer(&me->vl->elink,me); lstAppendPointer(&me->vr->elink,me);
}
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;
    return 0;
}
tnsMVert* tnsMMeshEdgeAnotherVert(tnsMEdge* me, tnsVert* v){
    if(me->vl==v) return me->vr; if(me->vr==v) return me->vl;
    return 0;
}
tnsMVert* tnsMMeshEdgeStartingVert(tnsMEdge* me0, tnsMEdge* me1){
    tnsMVert* sv=tnsMMeshEdgeShareVert(me0,me1); if(!sv) return 0;
    return tnsMMeshEdgeAnotherVert(me0, sv);
}
void tnsMMeshFaceAddEdge(tnsMFace* mf, tnsMEdge* me){
    lstAppendPointer(&mf->l, me); mf->looplen++;
    if(!me->fl) me->fl=mf; elif(!me->fr) me->fr=mf;
}
tnsMFace* tnsMMeshFaceHasVert(tnsMFace* mf, tnsMVert* mv){
    if(!mf||!mv) return 0; for(laListItemPointer*lip=mf->l.pFirst;lip;lip=lip->pNext){ if(tnsMMeshEdgeAnotherVert(lip->p, mv)) return mf; }
    return 0;
}
tnsMFace* tnsMMeshVertsShareFace(tnsMVert* v1, tnsMVert* v2){
    tnsMFace* mf=0; for(laListItemPointer*lip=v1->elink.pFirst;lip;lip=lip->pNext){
        tnsMEdge* me=lip->p; if((mf=tnsMMeshFaceHasVert(me->fl, v2)) || (mf=tnsMMeshFaceHasVert(me->fr, v2))) return mf;
    } return 0;
}
int tnsMMeshSplitFace(tnsMeshObject* mo, tnsMFace* mf, tnsMEdge* me, tnsMFace** r_f1, tnsMFace** r_f2){
    tnsMEdge* NextE; laListItemPointer* NextLip, *StartLip=0, *EndLip=0; int guard=0;
    for(laListItemPointer*lip=mf->l.pFirst;lip;lip=NextLip){
        NextLip=lip->pNext?lip->pNext:mf->l.pFirst; NextE=NextLip->p;
        if(tnsMMeshEdgeShareVert(me,tnsMMeshEdgeShareVert(NextE,lip->p))){ if(!StartLip) StartLip=lip; else{EndLip=lip; break;} }
        guard++; if(guard>mf->looplen) return 0; // ve is not across mf.
    }
    tnsMFace* f1=tnsMMeshNewFace(mo);
    for(laListItemPointer*lip=StartLip;lip;lip=NextLip){ NextLip=lip->pNext?lip->pNext:mf->l.pFirst; 
         if(lip==EndLip){ tnsMMeshFaceAddEdge(f1, me); break; } tnsMMeshFaceAddEdge(f1, lip->p);
    }
    tnsMFace* f2=tnsMMeshNewFace(mo);
    for(laListItemPointer*lip=EndLip;lip;lip=NextLip){ NextLip=lip->pNext?lip->pNext:mf->l.pFirst; 
         if(lip==StartLip){ tnsMMeshFaceAddEdge(f2, me); break; } tnsMMeshFaceAddEdge(f2, lip->p);
    }
    tnsMMeshRemoveFaceOnly(mo, mf);
    if(r_f1){ *r_f1=f1; }  if(r_f2){ *r_f2=f2; }
    return 1;
}
tnsMEdge* tnsMMeshMakeEdge(tnsMeshObject* mo, tnsMVert* v1, tnsMVert* v2){
    for(laListItemPointer*lip=v1->elink.pFirst;lip;lip=lip->pNext){ if(tnsMMeshEdgeAnotherVert(lip->p, v1)==v2) return lip->p; }
    // for(laListItemPointer*lip=v2->elink.pFirst;lip;lip=lip->pNext){ if(tnsMMeshEdgeAnotherVert(lip->p, v2)==v1) return lip->p; } shouldn't need.
    tnsMFace* mf=tnsMMeshVertsShareFace(v1,v2);
    tnsMEdge* me=tnsMMeshNewEdge(mo); tnsMMeshEdgeAssignVerts(me, v1, v2);
    if(mf){ tnsMMeshSplitFace(mo, mf, me, 0,0); }
    return me;
}
int tnsMMeshFaceMatches(tnsMFace* mf, int ecount, ...){
    if(!mf||mf->looplen!=ecount) return 0;
    va_list list; va_start(list, ecount);
    for(int i=0;i<ecount;i++){
        tnsMEdge* me=va_arg(list, tnsMEdge*); int found=0;
        for(laListItemPointer* lip=mf->l.pFirst;lip;lip=lip->pNext){
            if(lip->p==me){ found=1; break; }
        }
        if(!found){ va_end(list); return 0; }
    }
    va_end(list); return 1;
}
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);
    if(tnsMMeshFaceMatches(e1->fl,4,e1,e2,e3,e4)) return e1->fl; if(tnsMMeshFaceMatches(e1->fr,4,e1,e2,e3,e4)) return e1->fr; //should not need more
    if((e1->fl&&e1->fr) || (e2->fl&&e2->fr) || (e3->fl&&e3->fr) || (e4->fl&&e4->fr)) return 0;
    tnsMFace* mf=tnsMMeshNewFace(mo);
    tnsMMeshFaceAddEdge(mf,e1); tnsMMeshFaceAddEdge(mf,e2);
    tnsMMeshFaceAddEdge(mf,e3); tnsMMeshFaceAddEdge(mf,e4);
    return mf;
}
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; }
    lstRemoveItem(&mo->mf,mf); memLeave(mf); mo->totmf--;
}
void tnsMMeshRemoveEdgeFace(tnsMeshObject* mo, tnsMEdge* me){
    if(!me) return;
    tnsMMeshRemoveFaceOnly(mo, me->fl); tnsMMeshRemoveFaceOnly(mo, me->fr);
    lstRemovePointerLeave(&me->vl->elink, me); lstRemovePointerLeave(&me->vr->elink, me);
    lstRemoveItem(&mo->me,me); memLeave(me); mo->totme--;
}
void tnsMMeshRemoveVertEdgeFace(tnsMeshObject* mo, tnsMVert* mv){
    if(!mv) return; tnsMEdge* me;
    while(me=lstPopPointerLeave(&mv->elink)){ tnsMMeshRemoveEdgeFace(mo,me); }
    lstRemoveItem(&mo->mv,mv); memLeave(mv); mo->totmv--;
}

void tnsMMeshRefreshIndex(tnsMeshObject* mo){
    int i;
    i=0; for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ mv->i=i; i++; } mo->totmv=i;
    i=0; for(tnsMEdge* me=mo->me.pFirst;me;me=me->Item.pNext){ me->i=i; i++; } mo->totme=i;
    i=0; for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ mf->i=i; i++; } mo->totmf=i;
}

void tnsClearMesh(tnsMeshObject* mo){
    arrFree(&mo->v, &mo->maxv); mo->totv=0;
    arrFree(&mo->e, &mo->maxe); mo->tote=0;
    for(int i=0;i<mo->totf;i++){ if(mo->f[i].loop) free(mo->f[i].loop); }
    arrFree(&mo->f, &mo->maxf); mo->totf=0;
}
void tnsClearMMesh(tnsMeshObject* mo){
    tnsMFace* mf; tnsMEdge* me; tnsMVert* mv;
    while(mf=lstPopItem(&mo->mf)){ while(lstPopPointerLeave(&mf->l)); memLeave(mf); }
    while(me=lstPopItem(&mo->me)){ memLeave(me); }
    while(mv=lstPopItem(&mo->mv)){ while(lstPopPointerLeave(&mv->elink)); memLeave(mv); }
    mo->totmv=mo->totme=mo->totmf=0;
}

void tnsMMeshFromMesh(tnsMeshObject* mo){
    tnsEdgeHash* eh=tnsCreateEdgeHash(mo->totv); //mo->totmv=mo->totv; mo->totme=mo->tote; mo->totmf=mo->totf;
    for(int i=0;i<mo->totf;i++){
        tnsFace* f=&mo->f[i];
        for(int j=0;j<f->looplen-1;j++){ tnsEdgeHashAddVertPair(eh, f->loop[j], f->loop[j+1]); }
        tnsEdgeHashAddVertPair(eh, f->loop[f->looplen-1], f->loop[0]);
    }
    for(int i=0;i<eh->max;i++){
        tnsEdgeHashVert* ehv=&eh->vl[i];
        for(int j=0;j<ehv->next;j++){ tnsMEdge*me=tnsMMeshNewEdge(mo); ehv->e[j].me=me; }
    }
    for(int i=0;i<mo->totv;i++){ tnsVert*v=&mo->v[i]; tnsMVert*mv=tnsMMeshNewVert(mo); eh->vl[i].mv=mv;
        mv->p[0]=mo->v[i].p[0]; mv->p[1]=mo->v[i].p[1]; mv->p[2]=mo->v[i].p[2]; mv->flags=mo->v[i].flags; }
    for(int i=0;i<mo->totf;i++){
        tnsFace* f=&mo->f[i]; tnsMFace* mf=tnsMMeshNewFace(mo); mf->flags=f->flags;
        for(int j=0;j<f->looplen;j++){ int v2=j+1; if(j==f->looplen-1) v2=0;
            tnsEdgeHashEdge* ehe=tnsEdgeHashGetEdge(eh,f->loop[j],f->loop[v2]);
            tnsMEdge* me=ehe->me; tnsMMeshEdgeAssignVerts(me,eh->vl[f->loop[j]].mv,eh->vl[f->loop[v2]].mv);
            tnsMMeshFaceAddEdge(mf,me);
        }
    }
    tnsMMeshEnsureSelectionFromVert(mo);
    tnsDestroyEdgeHash(eh);
}
void tnsMeshFromMMesh(tnsMeshObject* mo){
    tnsClearMesh(mo); 
    tnsInitMesh(mo, mo->totmv, 0, mo->totmf); int i=0;
    /* Vertex index should already correct. */
    //for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ mv->i=i; i++; }
    for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ tnsVert* v=tnsFillVert(mo, mv->p[0], mv->p[1], mv->p[2]); v->flags=mv->flags; }
    for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ tnsFace* f=tnsFillFace(mo, mf->looplen, -1);  f->flags=mf->flags;
        int j=0; for(laListItemPointer* lip=mf->l.pFirst;lip;lip=lip->pNext){
            laListItemPointer* next=lip->pNext; if(!next) next=mf->l.pFirst; tnsMEdge* me0=lip->p, *me1=next->p;
            tnsFillFaceLoop(f, j, tnsMMeshEdgeStartingVert(me0,me1)->i); j++;
        }
    }
    mo->totv=mo->totmv; mo->totf=mo->totmf;
    tnsClearMMesh(mo);
}

void tnsMeshEnterEditMode(tnsMeshObject* mo){
    if(mo->Mode==TNS_MESH_EDIT_MODE || mo->mv.pFirst) return;
    tnsMMeshFromMesh(mo);
    mo->Mode = TNS_MESH_EDIT_MODE;
    tnsInvaliateMeshBatch(mo);
}
void tnsMeshLeaveEditMode(tnsMeshObject* mo){
    if(mo->Mode==TNS_MESH_OBJECT_MODE) return;
    tnsMeshFromMMesh(mo);
    mo->Mode = TNS_MESH_OBJECT_MODE;
    tnsInvaliateMeshBatch(mo);
}

int tnsMMeshAnySelected(tnsMeshObject* mo){
    for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ if(mv->flags&TNS_MESH_FLAG_SELECTED) return 1; }
    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 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); }
    for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ mf->flags&=(~TNS_MESH_FLAG_SELECTED); }
}
void tnsMMeshSelectAll(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; }
    for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ mf->flags|=TNS_MESH_FLAG_SELECTED; }
}
void tnsMMeshSelectVert(tnsMeshObject* mo, tnsMVert* mv, int select, int toggle){
    if(!mo) return;
    if(toggle) tnsMMeshSelectVert(mo,mv,(mv->flags&TNS_MESH_FLAG_SELECTED?0:1),0);
    elif(select) mv->flags|=TNS_MESH_FLAG_SELECTED; else mv->flags&=(~TNS_MESH_FLAG_SELECTED);
}
void tnsMMeshEnsureSelectionFromVert(tnsMeshObject* mo){
    for(tnsMEdge* me=mo->me.pFirst;me;me=me->Item.pNext){ me->flags&=(~TNS_MESH_FLAG_SELECTED);
        if(me->vl->flags&me->vr->flags&TNS_MESH_FLAG_SELECTED) me->flags|=TNS_MESH_FLAG_SELECTED; 
    }
    for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ int sel=1; mf->flags&=(~TNS_MESH_FLAG_SELECTED);
        for(laListItemPointer* lip=mf->l.pFirst;lip;lip=lip->pNext){ tnsMEdge*me=lip->p; if(!(me->flags&TNS_MESH_FLAG_SELECTED)){ sel=0; break; } }
        if(sel){ mf->flags|=TNS_MESH_FLAG_SELECTED; }
    }
}

tnsMeshObject *tnsCreateMeshPlane(tnsObject *under, char *Name, real AtX, real AtY, real AtZ, real size){
    tnsMeshObject *mo; tnsWorld *w = &T->World;

    mo = memAcquireHyper(sizeof(tnsMeshObject));
    tnsInitObjectBase(&mo->Base, under, Name, TNS_OBJECT_MESH, AtX, AtY, AtZ, 0, 0, 0, 1.0f, TNS_ROTATION_XYZ_EULER, 1.0f);
    tnsInitMeshPlane(mo, size);
    tnsInvaliateMeshBatch(mo);

    return mo;
}