/* * LaGUI: A graphical application framework. * Copyright (C) 2022-2023 Wu Yiming * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "la_5.h" #include extern tnsMain *T; void tnsPrintMeshTopology(tnsMeshObject*mo){ for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ printf("%d ",mv->i); } printf("\n"); for(tnsMEdge* me=mo->me.pFirst;me;me=me->Item.pNext){ printf("%d-%d ",me->vl->i,me->vr->i); } printf("\n"); for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ printf("%d-[",mf->looplen); for(laListItemPointer* lip=mf->l.pFirst;lip;lip=lip->pNext){ tnsMEdge*ie=lip->p; printf("%d%s%d ",ie->vl->i,(ie->fl&&ie->fr)?":":(ie->fl?"l":(ie->fr?"r":"-")),ie->vr->i); } printf("] "); }printf("\n"); } 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;imax;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;inext;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;inext;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;if[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 tnsFillFaceNormal(tnsFace* f, real* normal){ tnsVectorSet3v(f->n, normal); } void tnsFillVertNormal(tnsVert* v, real* normal){ tnsVectorSet3v(v->n, normal); } int tnsMergeMeshObjects(tnsMeshObject* into, tnsMeshObject* mo){ if(into->Base.Type!=TNS_OBJECT_MESH||mo->Base.Type!=TNS_OBJECT_MESH) return 0; if(!mo->totv){ tnsDestroyObject(mo); return 1; } tnsVector3d v,vf; tnsMatrix44d inv; tnsInverse44d(inv, into->Base.GlobalTransform); for(int i=0;itotv;i++){ tnsVectorSet3v(v,mo->v[i].p); tnsApplyTransform43d(vf, mo->Base.GlobalTransform, v); tnsApplyTransform43d(v, inv, vf); tnsVectorSet3v(mo->v[i].p,v); } for(int i=0;itote;i++){ mo->e[i].l+=into->totv; mo->e[i].r+=into->totv; } for(int i=0;itotf;i++){ for(int l=0;lf[i].looplen;l++){ mo->f[i].loop[l]+=into->totv; } } int origv=into->totv, orige=into->tote, origf=into->totf; into->maxv=into->totv; into->maxe=into->tote; into->maxf=into->totf; into->totv+=mo->totv; into->tote+=mo->tote; into->totf+=mo->totf; arrEnsureLength(&into->v, into->totv, &into->maxv, sizeof(tnsVert)); if(mo->totv) memcpy(&into->v[origv],mo->v,sizeof(tnsVert)*mo->totv); arrEnsureLength(&into->e, into->tote, &into->maxe, sizeof(tnsEdge)); if(mo->tote) memcpy(&into->e[orige],mo->e,sizeof(tnsEdge)*mo->tote); arrEnsureLength(&into->f, into->totf, &into->maxf, sizeof(tnsFace)); if(mo->totf){ memcpy(&into->f[origf],mo->f,sizeof(tnsFace)*mo->totf); for(int i=origf;itotf;i++){ into->f[i].loop=0; arrInitLength(&into->f[i].loop, into->f[i].looplen, &into->f[i].looplen, sizeof(int)); for(int l=0;lf[i].looplen;l++){ into->f[i].loop[l]=mo->f[i-origf].loop[l]; } } } tnsDestroyObject(mo); return 1; } tnsMeshObject* tnsDuplicateMeshObjects(tnsMeshObject* from){ if(from->Base.Type!=TNS_OBJECT_MESH) return 0; tnsMeshObject* to = memAcquireHyper(sizeof(tnsMeshObject)); tnsInitObjectBase(to, from->Base.ParentObject?from->Base.ParentObject:from->Base.InRoot, from->Base.Name->Ptr, TNS_OBJECT_MESH,0,0,0,0,0,0,0,0,1); tnsCopyObjectTransformationsLocal(to,from); if(!from->totv){ return to; } to->totv=from->totv; to->tote=from->tote; to->totf=from->totf; arrInitLength(&to->v, to->totv, &to->maxv, sizeof(tnsVert)); if(from->totv) memcpy(to->v,from->v,sizeof(tnsVert)*from->totv); arrInitLength(&to->e, to->tote, &to->maxe, sizeof(tnsEdge)); if(from->tote) memcpy(to->e,from->e,sizeof(tnsEdge)*from->tote); arrInitLength(&to->f, to->totf, &to->maxf, sizeof(tnsFace)); if(from->totf) memcpy(to->f,from->f,sizeof(tnsFace)*from->totf); for(int i=0;itotf;i++){ to->f[i].loop=0; arrInitLength(&to->f[i].loop, to->f[i].looplen, &to->f[i].looplen, sizeof(int)); for(int l=0;lf[i].looplen;l++){ to->f[i].loop[l]=from->f[i].loop[l]; } } return to; } 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); tnsFace* f=tnsFillFace(mo, 4, 0,1,2,3); tnsVector3d n={0,0,1}; tnsFillFaceNormal(f,n); 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; tnsMMeshEnsureSelectionFromVerts(mo); } void tnsAddMMeshPlane(tnsMeshObject* mo, real size){ tnsMVert *mv1,*mv2,*mv3,*mv4; mv1=tnsMMeshNewVert(mo); mv1->p[0]=size; mv1->p[1]=size; mv1->flags|=TNS_MESH_FLAG_SELECTED; mv2=tnsMMeshNewVert(mo); mv2->p[0]=-size; mv2->p[1]=size; mv2->flags|=TNS_MESH_FLAG_SELECTED; mv3=tnsMMeshNewVert(mo); mv3->p[0]=-size; mv3->p[1]=-size; mv3->flags|=TNS_MESH_FLAG_SELECTED; mv4=tnsMMeshNewVert(mo); mv4->p[0]=size; mv4->p[1]=-size; mv4->flags|=TNS_MESH_FLAG_SELECTED; tnsMMeshMakeFace4v(mo,mv1,mv2,mv3,mv4); } void tnsTrangulateFaceSimple(tnsMeshObject* mo, int fi, tnsFace* f, int* ebuf){ for(int i=0;ilooplen-2;i++){ ebuf[i*3]=f->loop[i]; ebuf[i*3+1]=f->loop[i+1]; ebuf[i*3+2]=fi+mo->totv; //f->loop[i+2]; } } void tnsTrangulateFaceSimpleM(tnsMeshObject* mo, int fi, 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]=mvs->i; tnsMVert*mm=tnsMMeshEdgeAnotherVert(me0,mvs); ebuf[i*3+1]=mm->i; ebuf[i*3+2]=fi+mo->totmv; /*tnsMMeshEdgeAnotherVert(me1,mm)->i;*/ i++; } } tnsMVert* tnsGetMFaceLastVert(tnsMFace* mf){ laListItemPointer* lip=mf->l.pLast; laListItemPointer* next=mf->l.pFirst; tnsMEdge* me0=lip->p, *me1=next->p; return tnsMMeshEdgeStartingVert(me0,me1); } int* tnsGetTriangulatedBatch(tnsMeshObject* mo, int* totelem){ int tottri=0; if(mo->Mode==TNS_MESH_OBJECT_MODE){ for(int i=0;itotf;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;itotf;i++){ tnsTrangulateFaceSimple(mo, i, &mo->f[i], pebuf); pebuf+=(mo->f[i].looplen-2)*3; } } else{ int i=0; for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ tnsTrangulateFaceSimpleM(mo, i, mf, pebuf); pebuf+=(mf->looplen-2)*3; i++; } } *totelem = tottri; return ebuf; } float* tnsGetDrawingVertArray(tnsMeshObject* mo, int* r_tot_render_v, float** r_normals, float** idcolors, float** editcolors, int** edgeelems, int* r_tote){ if(!mo->totv&&!mo->totmv) return 0; int objmode=mo->Mode==TNS_MESH_OBJECT_MODE; int totv=objmode?mo->totv:mo->totmv; int tote=objmode?mo->tote:mo->totme; *r_tote=tote; int totf=objmode?mo->totf:mo->totmf; int extraverts=objmode?mo->totf:(mo->totme*2+mo->totmf); float* p=calloc(1,(totv+extraverts)*3*sizeof(float)); *r_tot_render_v=(totv+extraverts); float* n=calloc(1,(totv+extraverts)*3*sizeof(float)); *r_normals = n; if(objmode){ for(int i=0;iv[i].p); tnsVectorSet3v(&n[i*3],mo->v[i].n); } int start=mo->totv*3; for(int i=0;iv[mo->f[i].loop[mo->f[i].looplen-1]].p); tnsVectorSet3v(&n[start+i*3],mo->f[i].n); } }else{ (*idcolors)=calloc(1,(totv+extraverts)*3*sizeof(float)); if(extraverts){ (*edgeelems)=calloc(1,(extraverts)*2*sizeof(int)); (*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; (*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); if(extraverts){ (*editcolors)[i*4]=c[0]; (*editcolors)[i*4+1]=c[1]; (*editcolors)[i*4+2]=c[2]; (*editcolors)[i*4+3]=c[3];} } if(extraverts){ 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)|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); float* eedcolor1=&(*editcolors)[(*edgeelems)[ei*2]*4], *eedcolor2=&(*editcolors)[(*edgeelems)[ei*2+1]*4]; real* c=(me->flags&TNS_MESH_FLAG_SELECTED)?laAccentColor(LA_BT_SEDGE):laAccentColor(LA_BT_EDGE); tnsVectorSet4v(eedcolor1,c); tnsVectorSet4v(eedcolor2,c); } } int start=mo->totmv*3; int fi=0; for(tnsMFace*mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ tnsMVert* sv=tnsGetMFaceLastVert(mf); tnsVectorSet3v(&p[start+fi*3],sv->p); tnsVectorSet3v(&n[start+fi*3],mf->n); fi++; } } 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 tnsInvalidateMeshBatch(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,1}; int tottri; int* elem = tnsGetTriangulatedBatch(mo, &tottri); float* idcolors=0,*editcolors=0; int docolors=mo->Mode==TNS_MESH_EDIT_MODE; int totv,tote; int* eelems=0; float* n=0; float* v = tnsGetDrawingVertArray(mo,&totv,&n,&idcolors,&editcolors,&eelems,&tote); if(!v){ if(elem){free(elem);} if(eelems){free(eelems);} return; } mo->Batch = tnsCreateBatch(totv, 3, v, 3, n, 4, editcolors); tnsBatchCommand*c; if(elem){ c=tnsCreateCommand(mo->Batch, "body", tottri, 3, GL_TRIANGLES, elem, 0); tnsCommandUseUniformColor(c,meshcolor); } free(elem); free(v); free(n); if(mo->Mode==TNS_MESH_EDIT_MODE){ elem=tnsGetEdgeBatch(mo); if(elem) { c= tnsCreateCommand(mo->Batch, "lines", mo->totme, 2, GL_LINES, elem, 1); free(elem); //tnsCommandUseUniformColor(c, laAccentColor(LA_BT_EDGE)); } c= tnsCreateCommand(mo->Batch, "verts", mo->totmv, 3, GL_POINTS, 0, 1); c= tnsCreateCommand(mo->Batch, "verts_select", mo->totmv, 3, GL_POINTS, 0, 1); tnsCommandOverrideColorArray(c, mo->Batch->NumVert, 3, idcolors); if(eelems){ c= tnsCreateCommand(mo->Batch, "edges_select", mo->totme, 2, GL_LINES, eelems, 1); 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;itotme*2;i++){ printf("%d ",eelems[i]); } printf("\n"); } if(idcolors) free(idcolors); if(editcolors) free(editcolors); if(eelems) free(eelems); } void tnsEnsureMeshBatch(tnsMeshObject* mo){ if(mo->Base.Type!=TNS_OBJECT_MESH) return; if(mo->Batch) return; tnsRegenerateMeshBatch(mo); } void tnsDrawMeshObjectEdit(tnsMeshObject* mo, int MeshSelectionType){ tnsUniformUseOffset(T->immShader,1); glPointSize(6); glLineWidth(3); if(MeshSelectionType==LA_CANVAS_SELECT_MODE_VERTS){ tnsDrawBatch(mo->Batch,"verts",0,0); tnsDrawBatch(mo->Batch,"lines",0,0); }else{ tnsDrawBatch(mo->Batch,"edges",0,0); } glPointSize(1); glLineWidth(1); tnsUniformUseOffset(T->immShader,0); } void tnsDrawMeshObjectOverlay(tnsEvaluatedInstance* ei, la3DObjectDrawExtra* de){ tnsEnsureMeshBatch(ei->Object); tnsDrawMeshObjectEdit(ei->Object, de?de->MeshEditType:LA_CANVAS_SELECT_MODE_VERTS); if(((tnsMeshObject*)ei->Object)->ExtraBatch){ tnsDrawBatch(((tnsMeshObject*)ei->Object)->ExtraBatch,0,0,0); } } void tnsDrawMeshObjectSelectionID(tnsEvaluatedInstance* ei, void* unused){ tnsEnsureMeshBatch(ei->Object); int i=ei->InstanceSelectionID; real color[4]={0,0,0,1}; TNS_ID_TO_COLOR(color,i); tnsDrawBatch(((tnsMeshObject*)ei->Object)->Batch,"body",color,0); } void tnsDrawMeshObjectOutline(tnsEvaluatedInstance* ei, void* unused){ real* color=(ei->IsActive)?laAccentColor(LA_BT_TEXT):laAccentColor(LA_BT_NORMAL); tnsDrawBatch(((tnsMeshObject*)ei->Object)->Batch, "body", color, 0); } void tnsDrawMeshObject(tnsEvaluatedInstance* ei, void* unused){ tnsEnsureMeshBatch(ei->Object); tnsUseNormal(1); tnsDrawBatch(((tnsMeshObject*)ei->Object)->Batch,"body",0,0); tnsUseNormal(0); } void tnsEvaluateMeshObject(tnsMeshObject* mo, tnsEvaluateData* ed){ tnsEnsureMeshBatch(mo); tnsAddEvaluatedInstance(ed,mo,tnsDrawMeshObject,TNS_EVAL_LAYER_SOLID,0,0,0); if(ed->FillOutline){ if((mo->Base.Flags&TNS_OBJECT_FLAGS_SELECTED) && (mo->Mode!=TNS_MESH_EDIT_MODE)){ tnsAddEvaluatedInstance(ed,mo,tnsDrawMeshObjectOutline,TNS_EVAL_LAYER_OUTLINE,ed->Active==mo,0,0); } } if(ed->FillSelectionID){ int i=ed->OverrideID?ed->OverrideID:mo->Base.SelectID; tnsAddEvaluatedInstance(ed,mo,tnsDrawMeshObjectSelectionID,TNS_EVAL_LAYER_SELECTION,0,1,i); } if(mo->Mode==TNS_MESH_EDIT_MODE){ tnsAddEvaluatedInstance(ed,mo,tnsDrawMeshObjectOverlay,TNS_EVAL_LAYER_OVERLAY,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 tnsMMeshCalculateNormalFrom(tnsMFace* mf){ tnsVector3d accum={0}; tnsVector3d d0,d1; tnsVector3d naccum={0},nn={0};if(mf->flags&TNS_MESH_FLAG_PICKED) return; 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; tnsMVert* v0=tnsMMeshEdgeStartingVert(me0,me1); tnsMVert* v1=tnsMMeshEdgeShareVert(me0,me1); tnsMVert* v2=tnsMMeshEdgeAnotherVert(me1, v1); tnsVectorAccum3d(accum, v0->p); tnsVectorAccum3d(accum, v1->p); tnsVectorAccum3d(accum, v2->p); tnsVectorMinus3d(d0,v0->p,v1->p); tnsVectorMinus3d(d1,v2->p,v1->p); real len=tnsVectorCross3d(nn,d0,d1); tnsVectorMultiSelf3d(nn, len); tnsVectorAccum3d(naccum, nn); //if(v0==me0->vr){ me0->flags|=TNS_MESH_FLAG_LOOP_REVERSE; } // TODO: consistency not implemented. } tnsNormalize3d(mf->n, naccum); tnsVectorMulti3d(mf->c, accum, 1.0/(mf->looplen+2)); mf->flags|=TNS_MESH_FLAG_PICKED; if(mf->flags&TNS_MESH_FLAG_SELECTED){ for(laListItemPointer* lip=mf->l.pFirst;lip;lip=lip->pNext){ tnsMEdge* me=lip->p; tnsMFace* of=((mf==me->fl)?me->fr:me->fl); if(of) tnsMMeshCalculateNormalFrom(of); } } } void tnsMMeshCalculateFaceVertNormal(tnsMFace* mf){ tnsVector3d accum={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; tnsMVert* mv=tnsMMeshEdgeStartingVert(me0,me1); int fcount=0; for(laListItemPointer* el=mv->elink.pFirst;el;el=el->pNext){ tnsMEdge* ve=lip->p; if(ve->fl){ tnsVectorAccum3d(accum, ve->fl->n); fcount++; } if(ve->fr){ tnsVectorAccum3d(accum, ve->fr->n); fcount++; } } if(!fcount) accum[2]=1; else tnsNormalizeSelf3d(accum); tnsVectorSet3v(mv->n, accum); } } int tnsMMeshCalculateNormal(tnsMeshObject* mo){ tnsMMeshClearExtraFlags(mo); int ran=0; for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ if(!(mv->flags&TNS_MESH_FLAG_SELECTED)) continue; for(laListItemPointer* lip=mv->elink.pFirst; lip;lip=lip->pNext){ tnsMEdge*me=lip->p; if((!me->fl) && (!me->fr)) continue; if(me->fl) tnsMMeshCalculateNormalFrom(me->fl); if(me->fr) tnsMMeshCalculateNormalFrom(me->fr); ran=1; } } //for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ if((!(mf->flags&TNS_MESH_FLAG_SELECTED))||(mf->flags&TNS_MESH_FLAG_PICKED)) continue; // tnsMMeshCalculateNormalFrom(mf); ran=1; //} for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ if(!(mf->flags&TNS_MESH_FLAG_PICKED)) continue; tnsMMeshCalculateFaceVertNormal(mf); ran=1; } return ran; } 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); } tnsMEdge* tnsMMeshVertsShareEdge(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; } 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; return 0; } tnsMVert* tnsMMeshEdgeAnotherVert(tnsMEdge* me, tnsMVert* 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; } void tnsMMeshFaceAddEdgeReplaceFace(tnsMFace* mf, tnsMEdge* me, tnsMFace* to_be_replaced){ lstAppendPointer(&mf->l, me); mf->looplen++; if(me->fl==to_be_replaced) me->fl=mf; elif(me->fr==to_be_replaced) me->fr=mf; elif(!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(tnsMMeshEdgeHasVert(me,tnsMMeshEdgeShareVert(NextE,lip->p))){ if(!StartLip) StartLip=NextLip; else{EndLip=NextLip; break;} } guard++; if(guard>mf->looplen) return 0; // me 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){ tnsMMeshFaceAddEdgeReplaceFace(f1, me, mf); break; } tnsMMeshFaceAddEdgeReplaceFace(f1, lip->p, mf); } tnsMFace* f2=tnsMMeshNewFace(mo); for(laListItemPointer*lip=EndLip;lip;lip=NextLip){ NextLip=lip->pNext?lip->pNext:mf->l.pFirst; if(lip==StartLip){ tnsMMeshFaceAddEdgeReplaceFace(f2, me, mf); break; } tnsMMeshFaceAddEdgeReplaceFace(f2, lip->p, mf); } tnsMMeshRemoveFaceOnly(mo, mf); if(r_f1){ *r_f1=f1; } if(r_f2){ *r_f2=f2; } //tnsPrintMeshTopology(mo); 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 tnsMMeshFaceMatchesN(tnsMFace* mf, int ecount, laListHandle* eip){ if(!mf||mf->looplen!=ecount) return 0; laListItemPointer* lipe=eip->pFirst; for(int i=0;ip; 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); for(int i=0;il.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* tnsMMeshMakeFaceN(tnsMeshObject* mo, int count, laListHandle* vip, tnsMEdge** r_fallback_me){ if(count<2){ return 0; } if(count==2){ tnsMEdge* me=tnsMMeshMakeEdge(mo,((laListItemPointer*)vip->pFirst)->p,((laListItemPointer*)vip->pLast)->p); if(r_fallback_me)*r_fallback_me=me; return 0; } 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, tnsMVert* v1,tnsMVert* v2,tnsMVert* v3,tnsMVert* 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; } 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; tnsMMeshEdgeAssignVerts(me1,me->vl,mv); tnsMMeshEdgeAssignVerts(me2,mv,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; if(me->fl) 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++; } if(me->fr) 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++; } me->fl=me->fr=0; tnsMMeshRemoveEdgeFace(mo,me); if(ref_e1v_optional&&tnsMMeshEdgeShareVert(me1, ref_e1v_optional)){ if(r_e1)*r_e1=me1; if(r_e2)*r_e2=me2; }else{ if(r_e1)*r_e1=me2; if(r_e2)*r_e2=me1; } //tnsPrintMeshTopology(mo); return 1; } tnsMVert* tnsMMeshEdgeInsertVertAt(tnsMeshObject* mo, tnsMEdge* me, real at, tnsMVert* ref_e1v_optional, tnsMEdge** r_e1, tnsMEdge** r_e2){ if(!me||at<=0||at>=1) return 0; tnsMVert* mv=tnsMMeshNewVert(mo); tnsInterpolate3dv(me->vl->p, me->vr->p, at, mv->p); if(tnsMMeshEdgeInsertVert(mo,me,mv,ref_e1v_optional,r_e1,r_e2)) return mv; return 0; } 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--; tnsMMeshClearFirstLastSelection(mo); } 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--; tnsMMeshClearFirstLastSelection(mo); } 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--; tnsMMeshClearFirstLastSelection(mo); } 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; if(mo->f) for(int i=0;itotf;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){ tnsClearMMesh(mo); tnsEdgeHash* eh=tnsCreateEdgeHash(mo->totv); //mo->totmv=mo->totv; mo->totme=mo->tote; mo->totmf=mo->totf; for(int i=0;itotf;i++){ tnsFace* f=&mo->f[i]; for(int j=0;jlooplen-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;imax;i++){ tnsEdgeHashVert* ehv=&eh->vl[i]; for(int j=0;jnext;j++){ tnsMEdge*me=tnsMMeshNewEdge(mo); ehv->e[j].me=me; } } for(int i=0;itotv;i++){ tnsVert*v=&mo->v[i]; tnsMVert*mv=tnsMMeshNewVert(mo); eh->vl[i].mv=mv; tnsVectorSet3v(mv->p,mo->v[i].p); mv->flags=mo->v[i].flags; tnsVectorSet3v(mv->n,v->n); } for(int i=0;itotf;i++){ tnsFace* f=&mo->f[i]; tnsMFace* mf=tnsMMeshNewFace(mo); mf->flags=f->flags; for(int j=0;jlooplen;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); tnsVectorSet3v(mf->n,f->n); } } tnsMMeshEnsureSelectionFromVerts(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, LA_COLOR3(mv->p)); tnsFillVertNormal(v,mv->n); 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++; } tnsFillFaceNormal(f,mf->n); } mo->totv=mo->totmv; mo->totf=mo->totmf; mo->maxv=mo->totv; mo->maxe=mo->tote; mo->maxf=mo->totf; if((!mo->maxv) && mo->v) arrFree(&mo->v, &mo->maxv); if((!mo->maxe) && mo->e) arrFree(&mo->e, &mo->maxe); if((!mo->maxf) && mo->f) arrFree(&mo->f, &mo->maxf); tnsClearMMesh(mo); } void tnsMeshEnterEditMode(tnsMeshObject* mo){ if(mo->Mode==TNS_MESH_EDIT_MODE) return; tnsMMeshFromMesh(mo); mo->Mode = TNS_MESH_EDIT_MODE; tnsInvalidateMeshBatch(mo); tnsInvalidateEvaluation(mo); } void tnsMeshLeaveEditMode(tnsMeshObject* mo){ if(mo->Mode==TNS_MESH_OBJECT_MODE) return; tnsMeshFromMMesh(mo); mo->Mode = TNS_MESH_OBJECT_MODE; tnsInvalidateMeshBatch(mo); tnsInvalidateEvaluation(mo); } void tnsMMeshClearFirstLastSelection(tnsMeshObject* mo){ mo->FirstSelectE=mo->FirstSelectV=mo->LastSelectE=mo->LastSelectV=0; } 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 tnsMMeshClearExtraFlags(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); /*me->flags&=(~TNS_MESH_FLAG_LOOP_REVERSE);*/ } 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); } for(tnsMFace* mf=mo->mf.pFirst;mf;mf=mf->Item.pNext){ mf->flags&=(~TNS_MESH_FLAG_SELECTED); } tnsMMeshClearFirstLastSelection(mo); } 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); if(!mo->FirstSelectV) mo->FirstSelectV=mv; mo->LastSelectV=mv; } void tnsMMeshSelectEdge(tnsMeshObject* mo, tnsMEdge* me, int select, int toggle){ if(!mo) return; if(toggle) tnsMMeshSelectEdge(mo,me,(me->flags&TNS_MESH_FLAG_SELECTED?0:1),0); elif(select) me->flags|=TNS_MESH_FLAG_SELECTED; else me->flags&=(~TNS_MESH_FLAG_SELECTED); if(!mo->FirstSelectE) mo->FirstSelectE=me; mo->LastSelectE=me; } void tnsMMeshEnsureSelectionFromVerts(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; } } } void tnsMMeshEnsureSelectionFromEdges(tnsMeshObject* mo){ for(tnsMVert* mv=mo->mv.pFirst;mv;mv=mv->Item.pNext){ mv->flags&=(~TNS_MESH_FLAG_SELECTED); for(laListItemPointer* lip=mv->elink.pFirst;lip;lip=lip->pNext){ tnsMEdge*me=lip->p; if(me->flags&TNS_MESH_FLAG_SELECTED){ mv->flags|=TNS_MESH_FLAG_SELECTED; break; } } } 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; } } } void tnsMMeshEnsureSelection(tnsMeshObject* mo, int SelectMode){ if(SelectMode==LA_CANVAS_SELECT_MODE_VERTS){ tnsMMeshEnsureSelectionFromVerts(mo); } elif(SelectMode==LA_CANVAS_SELECT_MODE_EDGES){ tnsMMeshEnsureSelectionFromEdges(mo); } } tnsMEdge* tnsMMeshGetRingEdge(tnsMEdge* me, int from_right){ tnsMEdge* candidate=0; tnsMVert* VL=from_right?me->vr:me->vl, *VR=from_right?me->vl:me->vr; for(laListItemPointer* lip=VL->elink.pFirst;lip;lip=lip->pNext){ tnsMEdge* te=lip->p; if(te==me || (te->flags&TNS_MESH_FLAG_PICKED)){ continue; } if((!te->fl) || (!te->fr)){ candidate=te; break; } } if(candidate){ candidate->flags|=TNS_MESH_FLAG_PICKED; return candidate; } if(lstCountElements(&VL->elink)!=4) return 0; tnsMFace* f1=0,*f2=0; for(laListItemPointer* lip=VL->elink.pFirst;lip;lip=lip->pNext){ tnsMEdge* te=lip->p; if(te==me || (te->flags&TNS_MESH_FLAG_PICKED)){ continue; } if(tnsMMeshFaceHasVert(te->fl,VR) || tnsMMeshFaceHasVert(te->fr,VR)){ continue; } else{ te->flags|=TNS_MESH_FLAG_PICKED; return te; } } return 0; } void tnsMMeshExpandRingList(tnsMeshObject* mo, tnsMEdge* me, laListHandle* lst){ tnsMEdge* el,*er; int flc=0,frc=0; if(me->fl) flc=me->fl->looplen; if(me->fr) frc=me->fr->looplen; if(TNS_MAX2(flc,frc)>4){ if(flc>frc){ for(laListItemPointer* lip=me->fl->l.pFirst;lip;lip=lip->pNext){ lstAppendPointer(lst,lip->p); } return; } elif(flcfr->l.pFirst;lip;lip=lip->pNext){ lstAppendPointer(lst,lip->p); } return; } } if(el=tnsMMeshGetRingEdge(me,0)){ lstAppendPointer(lst,el); tnsMMeshExpandRingList(mo,el,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; 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))){ te->flags|=TNS_MESH_FLAG_PICKED; return te; } } return 0; } void tnsMMeshExpandBandList(tnsMeshObject* mo, tnsMEdge* me, laListHandle* lst){ tnsMEdge* el,*er; if(el=tnsMMeshGetBandEdge(me,0)){ lstAppendPointer(lst,el); tnsMMeshExpandBandList(mo,el,lst); } if(er=tnsMMeshGetBandEdge(me,1)){ lstAppendPointer(lst,er); tnsMMeshExpandBandList(mo,er,lst); } } int tnsMMeshSelectRingBandFrom(tnsMeshObject* mo, tnsMEdge* me, int ring_band, int select, int toggle){ if(!ring_band) return 0; tnsMMeshClearExtraFlags(mo); laListHandle lst={0}; lstAppendPointer(&lst,me); me->flags|=TNS_MESH_FLAG_PICKED; if(ring_band==1) tnsMMeshExpandRingList(mo,me,&lst); elif(ring_band==2) tnsMMeshExpandBandList(mo,me,&lst); if(lst.pFirst==lst.pLast){ while(lstPopPointer(&lst)); return 0; } int has_idle=0; if(toggle){ for(laListItemPointer* lip=lst.pFirst;lip;lip=lip->pNext){ tnsMEdge* te=lip->p; if(te!=me && (!(te->flags&TNS_MESH_FLAG_SELECTED))) has_idle=1; if(has_idle){ break; } } if(has_idle){ select=1; }else{ select=0; } } for(laListItemPointer* lip=lst.pFirst;lip;lip=lip->pNext){ tnsMEdge* te=lip->p; if(select){ te->flags|=TNS_MESH_FLAG_SELECTED; te->vl->flags|=TNS_MESH_FLAG_SELECTED; te->vr->flags|=TNS_MESH_FLAG_SELECTED; } else{ te->flags&=(~TNS_MESH_FLAG_SELECTED); te->vl->flags&=(~TNS_MESH_FLAG_SELECTED); te->vr->flags&=(~TNS_MESH_FLAG_SELECTED); } } while(lstPopPointer(&lst)); return 1; } void tnsMMeshReduceFaceEdge(tnsMeshObject* mo, tnsMFace* mf, tnsMEdge* me){ lstRemovePointerLeave(&mf->l,me); mf->looplen--; } void tnsMMeshFaceReplaceEdgeWith(tnsMFace* mf, tnsMEdge* to_be_replaced, tnsMEdge* as){ if(!mf) return; for(laListItemPointer* lip=mf->l.pFirst;lip;lip=lip->pNext){ if(lip->p==to_be_replaced){ lip->p=as; return; } } } void tnsMMeshReduceZippedFace(tnsMeshObject* mo, tnsMFace* mf){ if(mf->looplen>2) return; if(mf->looplen<2){ printf("mf->looplen < 2 ?\n");return; } tnsMEdge* me1=((laListItemPointer*)mf->l.pFirst)->p,*me2=((laListItemPointer*)mf->l.pLast)->p; tnsMVert* vl=me1->vl; tnsMVert*vr=me1->vr; lstRemovePointerLeave(&vl->elink,me2); lstRemovePointerLeave(&vr->elink,me2); tnsMMeshFaceReplaceEdgeWith(me1->fl,me2,me1); tnsMMeshFaceReplaceEdgeWith(me1->fr,me2,me1); tnsMMeshFaceReplaceEdgeWith(me2->fl,me2,me1); tnsMMeshFaceReplaceEdgeWith(me2->fr,me2,me1); if(me1->fl==mf){ me1->fl=((me2->fl==mf)?me2->fr:me2->fl); }elif(me1->fr==mf){ me1->fr=((me2->fl==mf)?me2->fr:me2->fl); } lstRemoveItem(&mo->me,me2); mo->totme--; memLeave(me2); lstRemoveItem(&mo->mf,mf); mo->totmf--; memLeave(mf); tnsMMeshClearFirstLastSelection(mo); } int tnsMMeshVertsCanMerge(tnsMeshObject* mo, tnsMVert* into, tnsMVert* mv){ for(laListItemPointer* lip=into->elink.pFirst;lip;lip=lip->pNext){ tnsMEdge* me=lip->p; for(laListItemPointer* lip2=mv->elink.pFirst;lip2;lip2=lip2->pNext){ tnsMEdge* me2=lip2->p; if(me==me2) continue; tnsMVert* mvs=tnsMMeshEdgeShareVert(me,me2); if((!mvs)||mvs==mv||mvs==into) continue; int count=0; if(me->fl) count++; if(me->fr) count++; if(me2->fl) count++; if(me2->fr) count++; tnsMFace* sf=tnsMMeshEdgeShareFace(me,me2); if(sf){count--;} if ((sf==me->fl && me->fr && (me->fr==me2->fl||me->fr==me2->fr))|| (sf==me->fr && me->fl && (me->fl==me2->fl||me->fl==me2->fr))){ return 0; } if((sf && count>3) || (!sf && count>2)){ return 0; } } } return 1; } int tnsMMeshMergeVerts(tnsMeshObject* mo, tnsMVert* into, tnsMVert* mv){ if(!tnsMMeshVertsCanMerge(mo,into,mv)) return 0; tnsMEdge* me=tnsMMeshMakeEdge(mo,into,mv); if(me->fl){ tnsMMeshReduceFaceEdge(mo,me->fl,me); } if(me->fr){ tnsMMeshReduceFaceEdge(mo,me->fr,me); } tnsMEdge* me2; while(me2=lstPopPointerLeave(&mv->elink)){ if(me2==me) continue; lstAppendPointer(&into->elink,me2); if(me2->vl==mv){ me2->vl=into; }elif(me2->vr==mv){ me2->vr=into; } } if(me->fl){ tnsMMeshReduceZippedFace(mo,me->fl); } if(me->fr){ tnsMMeshReduceZippedFace(mo,me->fr); } lstRemovePointerLeave(&into->elink,me); lstRemoveItem(&mo->mv,mv); memLeave(mv); mo->totmv--; lstRemoveItem(&mo->me,me); memLeave(me); mo->totme--; tnsMMeshClearFirstLastSelection(mo); //tnsPrintMeshTopology(mo); return 1; } tnsMeshObject *tnsCreateMeshEmpty(tnsObject *under, char *Name, real AtX, real AtY, real AtZ){ tnsMeshObject *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); return mo; } tnsMeshObject *tnsCreateMeshPlane(tnsObject *under, char *Name, real AtX, real AtY, real AtZ, real size){ tnsMeshObject *mo=tnsCreateMeshEmpty(under, Name, AtX, AtY, AtZ); tnsInitMeshPlane(mo, size); tnsInvalidateMeshBatch(mo); return mo; }