/** * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** * * 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 2 * 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, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Contributor(s): Jiri Hnidek. * * ***** END GPL LICENSE BLOCK ***** */ #ifdef WITH_VERSE #include #include "MEM_guardedalloc.h" #include "mydevice.h" #include "DNA_object_types.h" #include "DNA_mesh_types.h" #include "BLI_dynamiclist.h" #include "BLI_blenlib.h" #include "BLI_edgehash.h" #include "BLI_editVert.h" #include "BKE_customdata.h" #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_mesh.h" #include "BKE_utildefines.h" #include "BKE_verse.h" #include "BIF_verse.h" #include "BIF_editmesh.h" #include "BIF_space.h" #include "BIF_screen.h" #include "BSE_edit.h" #include "editmesh.h" #include "verse.h" /* prototypes of static functions */ static void mark_changed_face_obsolete(struct VerseFace *vface); static void sync_verseface_with_editface(struct VLayer *vlayer, struct VerseFace *vface); void createVerseVertNL(struct EditVert *ev, struct VNode *vnode, struct VLayer *vlayer); static void createAllVerseVerts(struct VNode *vnode, struct VLayer *vlayer); void createVerseFaceNL(struct EditFace *efa, struct VNode *vnode, struct VLayer *vlayer); static void createAllVerseFaces(struct VNode *vnode, struct VLayer *vlayer); /*============================================================================= * * functions handling verse/blender FACES * *===========================================================================*/ /* * create new VerseFace (polygon), due to information about EditFace * put VerseFace to queue ... send to verse host (server) */ void createVerseFace(EditFace *efa) { if(G.editMesh->vnode) { struct VLayer *vlayer = find_verse_layer_type((VGeomData*)((VNode*)G.editMesh->vnode)->data, POLYGON_LAYER); createVerseFaceNL(efa, (VNode*)G.editMesh->vnode, vlayer); } else efa->vface = NULL; } /* * create new VerseFace (polygon), due to information about EditFace * put VerseFace to queue ... send to verse host (server) * NL version of function (infomration about verse node and * layer is known ... optimalisation) */ void createVerseFaceNL(EditFace *efa, VNode *vnode, VLayer *vlayer) { struct VerseFace *vface; vface = (VerseFace*)create_verse_face(vlayer, vlayer->counter, -1, -1, -1, -1); vface->face = (void*)efa; efa->vface = (void*)vface; vface->flag |= FACE_SEND_READY; /* EditVert #1 */ if(efa->v1) { if(efa->v1->vvert) { vface->vvert0 = (VerseVert*)efa->v1->vvert; if(((VerseVert*)efa->v1->vvert)->flag & VERT_RECEIVED) { vface->v0 = ((VerseVert*)efa->v1->vvert)->id; vface->counter--; } else vface->flag &= ~FACE_SEND_READY; } } else vface->counter--; /* EditVert #2 */ if(efa->v2) { if(efa->v2->vvert) { vface->vvert1 = (VerseVert*)efa->v2->vvert; if(((VerseVert*)efa->v2->vvert)->flag & VERT_RECEIVED) { vface->v1 = ((VerseVert*)efa->v2->vvert)->id; vface->counter--; } else vface->flag &= ~FACE_SEND_READY; } } else vface->counter--; /* EditVert #3 */ if(efa->v3) { if(efa->v3->vvert) { vface->vvert2 = (VerseVert*)efa->v3->vvert; if(((VerseVert*)efa->v3->vvert)->flag & VERT_RECEIVED) { vface->v2 = ((VerseVert*)efa->v3->vvert)->id; vface->counter--; } else vface->flag &= ~FACE_SEND_READY; } } else vface->counter--; /* EditVert #4 */ if(efa->v4) { if(efa->v4->vvert) { vface->vvert3 = (VerseVert*)efa->v4->vvert; if(((VerseVert*)efa->v4->vvert)->flag & VERT_RECEIVED) { vface->v3 = ((VerseVert*)efa->v4->vvert)->id; vface->counter--; } else vface->flag &= ~FACE_SEND_READY; } } else vface->counter--; add_item_to_send_queue(&(vlayer->queue), (void*)vface, VERSE_FACE); } /* * creates new verse faces, add all of then to queue ... send to verse server */ static void createAllVerseFaces(VNode *vnode, VLayer *vlayer) { if(G.obedit) { struct EditMesh *em; struct EditFace *efa; em = G.editMesh; efa = em->faces.first; /* push all EditFaces to the verse server */ while(efa){ createVerseFaceNL(efa, vnode, vlayer); efa = efa->next; } } } /* * When verse face is changed during sending/receiving from verse server, then * this verse face is marked as obsolete and it will be send again, when it * will be received from verse server */ static void mark_changed_face_obsolete(VerseFace *vface) { struct EditFace *efa = (EditFace*)vface->face; if(efa) { if(vface->vvert0->vertex != efa->v1) vface->flag |= FACE_OBSOLETE; if(vface->vvert1->vertex != efa->v2) vface->flag |= FACE_OBSOLETE; if(vface->vvert2->vertex != efa->v3) vface->flag |= FACE_OBSOLETE; if(vface->vvert3 && (vface->vvert3->vertex != efa->v4)) vface->flag |= FACE_OBSOLETE; } } /* * this function will sync EditFace and VerseFace and it will send changes to * verse server too */ static void sync_verseface_with_editface(VLayer *vlayer, VerseFace *vface) { struct EditFace *efa = (EditFace*)vface->face; int dosend = 0; /* edit face and probably verse face was deleted */ if(!efa || (vface->flag & FACE_DELETED)) return; /* blender nor verse don't support such crazy things */ if(!(vface->vvert0) || !(vface->vvert1) || !(vface->vvert2)) { printf("\tERROR: vface->vvert0: %p, vface->vvert1: %p, vface->vvert2: %p\n", vface->vvert0, vface->vvert1, vface->vvert2); return; } /* initialize verse face flag */ vface->flag |= FACE_SEND_READY; /* debug print */ #if 0 printf("\n"); if(efa->v4) { printf("\tefa->v1,v2,v3,v4->vvert->id: %d, %d, %d, %d\n", ((VerseVert*)efa->v1->vvert)->id, ((VerseVert*)efa->v2->vvert)->id, ((VerseVert*)efa->v3->vvert)->id, ((VerseVert*)efa->v4->vvert)->id); printf("\tefa->v1,v2,v3,v4->vvert: %p, %p, %p, %p\n", efa->v1->vvert, efa->v2->vvert, efa->v3->vvert, efa->v4->vvert); } else { printf("\tefa->v1,v2,v3->vvert->id: %d, %d, %d, NULL\n", ((VerseVert*)efa->v1->vvert)->id, ((VerseVert*)efa->v2->vvert)->id, ((VerseVert*)efa->v3->vvert)->id); printf("\tefa->v1,v2,v3,v4->vvert: %p, %p, %p, NULL\n", efa->v1->vvert, efa->v2->vvert, efa->v3->vvert); } printf("\tvface->vvert0, vvvert1, vvvert2, vvvert3: %p, %p, %p, %p\n", vface->vvert0, vface->vvert1, vface->vvert2, vface->vvert3); printf("\tefa->v1, v2, v3, v4: %p, %p, %p, %p\n", efa->v1, efa->v2, efa->v3, efa->v4); if(vface->vvert3) { printf("\tvface->v0,v1,v2,v3: %d, %d, %d, %d\n", vface->v0, vface->v1, vface->v2, vface->v3); printf("\tvface->vvert0,vvvert1,vvvert2,vvvert3->vertex: %p, %p, %p, %p\n", vface->vvert0->vertex, vface->vvert1->vertex, vface->vvert2->vertex, vface->vvert3->vertex); } else { printf("\tvface->v0,v1,v2,v3: %d, %d, %d, NULL\n", vface->v0, vface->v1, vface->v2); printf("\tvface->vvert0,vvvert1,vvvert2->vertex: %p, %p, %p, NULL\n", vface->vvert0->vertex, vface->vvert1->vertex, vface->vvert2->vertex); } #endif /* initialize counter of unreceived vertexes */ vface->counter = 4; /* 1st vertex */ if(vface->vvert0->vertex != efa->v1) { dosend = 1; vface->vvert0->counter--; vface->vvert0 = (VerseVert*)efa->v1->vvert; vface->v0 = vface->vvert0->id; if(vface->vvert0->flag & VERT_RECEIVED) vface->counter--; else vface->flag &= ~FACE_SEND_READY; } else vface->counter--; /* 2nd vertex */ if(vface->vvert1->vertex != efa->v2) { dosend = 1; vface->vvert1->counter--; vface->vvert1 = (VerseVert*)efa->v2->vvert; vface->v1 = vface->vvert1->id; if(vface->vvert1->flag & VERT_RECEIVED) vface->counter--; else vface->flag &= ~FACE_SEND_READY; } else vface->counter--; /* 3th vertex */ if(vface->vvert2->vertex != efa->v3) { dosend = 1; vface->vvert2->counter--; vface->vvert2 = (VerseVert*)efa->v3->vvert; vface->v2 = vface->vvert2->id; if(vface->vvert2->flag & VERT_RECEIVED) vface->counter--; else vface->flag &= ~FACE_SEND_READY; } else vface->counter--; /* 4th vertex */ if(vface->vvert3 && ((vface->vvert3->vertex != efa->v4) || (vface->vvert3 && !efa->v4) || (vface->v3 != vface->vvert3->id))) { dosend = 1; if(efa->v4) { vface->vvert3->counter--; vface->vvert3 = (VerseVert*)efa->v4->vvert; vface->v3 = vface->vvert3->id; if(vface->vvert3->flag & VERT_RECEIVED) vface->counter--; else vface->flag &= ~FACE_SEND_READY; } else { vface->vvert3->counter--; vface->vvert3 = NULL; vface->v3 = -1; vface->counter--; } } /* verse face has 4 vertexes now, not 3 vertexes as in past */ else if(!(vface->vvert3) && efa->v4) { dosend = 1; vface->vvert3 = (VerseVert*)efa->v4->vvert; vface->v3 = vface->vvert3->id; if(vface->vvert3->flag & VERT_RECEIVED) vface->counter--; else vface->flag &= ~FACE_SEND_READY; } else vface->counter--; if(dosend) { /* printf("\tsending CHANGED FACE\n"); printf("\t\tnew: %d %d %d %d\n", vface->v0, vface->v1, vface->v2, vface->v3);*/ vface->flag |= FACE_CHANGED; /* remove verse face from list of received faces */ BLI_dlist_rem_item(&(vlayer->dl), vface->id); /* and add verse face again to sending queue */ add_item_to_send_queue(&(vlayer->queue), (void*)vface, VERSE_FACE); } } /* * this function will sync all VerseFaces with coresponding EditFaces, * this is useful, when some editmesh tool has changed editface pointers at * vertexes (edges), parameter of this function is geometry node */ void sync_all_versefaces_with_editfaces(VNode *vnode) { struct VLayer *vlayer; struct VerseFace *vface, *nvface; if(vnode->type != V_NT_GEOMETRY) return; vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); /* mark changed verse faces in sending queue as obsolete at the first time */ vface = vlayer->queue.first; while(vface) { mark_changed_face_obsolete(vface); vface = vface->next; } /* send all received and changed verse face again to verse server */ vface = vlayer->dl.lb.first; while(vface) { nvface = vface->next; sync_verseface_with_editface(vlayer, vface); vface = nvface; } } /* * send delete polygon command to verse server */ void b_verse_send_face_delete(EditFace *efa) { ((VerseFace*)efa->vface)->face = NULL; send_verse_face_delete((VerseFace*)efa->vface); efa->vface = NULL; } /*============================================================================= * * functions handling verse/blender VERTEXES * *===========================================================================*/ /* * this function will sync position of all VerseVerts with EditVerts * this function is called after actions: Smooth, Noise and To Sphere, * because position of vertexes isn't managed by transform system */ void sync_all_verseverts_with_editverts(VNode *vnode) { struct VLayer *vlayer; struct VerseVert *vvert; if(vnode->type != V_NT_GEOMETRY) return; vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); vvert = vlayer->dl.lb.first; /* sync all received vertexes */ while(vvert) { send_versevert_pos(vvert); vvert = vvert->next; } vvert = vlayer->queue.first; /* sync all unreceived vertexes (mar pos as obsolete, when * actual position was changed) */ while(vvert) { send_versevert_pos(vvert); vvert = vvert->next; } verse_callback_update(0); } /* * send delete vertex command to verse server */ void b_verse_send_vertex_delete(EditVert *eve) { ((VerseVert*)eve->vvert)->vertex = NULL; send_verse_vertex_delete((VerseVert*)eve->vvert); eve->vvert = NULL; } /* * send position of verse vertex to verse server */ void send_versevert_pos(VerseVert *vvert) { /* delete command was sent to verse server ... sending one * more position command would create new vertex */ if ((vvert->flag & VERT_DELETED) | (vvert->flag & VERT_OBSOLETE)) return; /* don't send position of verse vertex to verse server, because it could create * new vertex */ if(vvert->flag & VERT_RECEIVED) { if(vvert->flag & VERT_LOCKED) { /* when position of verse vert was sent to verse server * and it wasn't received yet, then mark sent position * as obsolete ... blender will automaticaly send actual * position, when old will be received */ vvert->flag |= VERT_POS_OBSOLETE; } else { struct EditVert *eve = (EditVert*)vvert->vertex; /* send position to verse server, when it is different from actual position */ if(eve && (eve->co[0]!=vvert->co[0] || eve->co[1]!=vvert->co[1] || eve->co[2]!=vvert->co[2])) { /* lock vertex and send its position to verse server, * locking of vertex prevents from sending too many * informations about vertex position during draging * of vertex */ vvert->flag |= VERT_LOCKED; VECCOPY(vvert->co, eve->co); send_verse_vertex(vvert); } } } else { /* we created this vertex and we sent new position to verse server, but "confirmation" command about * position of vertex didn't arrived yet, then we can't send new position of vertex ... we only mark * position of vertex as obsolete and new position will be sent to verse server, when confirmation * command will arive */ struct EditVert *eve = (EditVert*)vvert->vertex; if(eve && (eve->co[0]!=vvert->co[0] || eve->co[1]!=vvert->co[1] || eve->co[2]!=vvert->co[2])) { vvert->flag |= VERT_POS_OBSOLETE; } } verse_callback_update(0); } /* * create new VerseVert due to information about EditVert, * put VerseVert to queue ... send to verse host (server) */ void createVerseVert(EditVert *eve) { if(G.editMesh->vnode) { struct VLayer *vlayer = find_verse_layer_type((VGeomData*)((VNode*)G.editMesh->vnode)->data, VERTEX_LAYER); createVerseVertNL(eve, (VNode*)G.editMesh->vnode, vlayer); } else eve->vvert = NULL; } /* * create new VerseVert due to information about EditVert, * put VerseVert to queue ... send to verse host (server) * NL version of function (infomration about verse node and * layer is known ... optimalisation) */ void createVerseVertNL(EditVert *eve, VNode *vnode, VLayer *vlayer) { struct VerseVert *vvert; vvert = create_verse_vertex(vlayer, vlayer->counter, eve->co[0], eve->co[1], eve->co[2]); vvert->vertex = (void*)eve; eve->vvert = (void*)vvert; vvert->flag |= VERT_LOCKED; /* printf("\tsend_versevert_pos: %d create and LOCK \n", vvert->id);*/ /* add vvert to sending queue */ add_item_to_send_queue(&(vlayer->queue), (void*)vvert, VERSE_VERT); } /* * create new verse vertexes due to all vertexes and send all of them to verse server */ static void createAllVerseVerts(VNode *vnode, VLayer *vlayer) { if(G.obedit) { struct EditMesh *em; struct EditVert *eve; em = G.editMesh; eve = em->verts.first; /* push all EditVertexes to the verse server */ while(eve){ createVerseVertNL(eve, vnode, vlayer); eve = eve->next; } } } /* * unsubscribe from verse geometry layers of verse geometry node * and clear bindings between verse node and mesh */ void unsubscribe_from_geom_node(VNode *vnode) { struct VerseSession *session = vnode->session; struct VLayer *vlayer = ((VGeomData*)vnode->data)->layers.lb.first; if(vnode->type != V_NT_GEOMETRY) return; /* free bindings between verse node and blender mesh*/ if(((VGeomData*)vnode->data)->mesh) { ((Mesh*)((VGeomData*)vnode->data)->mesh)->vnode = NULL; ((VGeomData*)vnode->data)->mesh = NULL; } /* free bindings between verse node and blender editmesh */ if(((VGeomData*)vnode->data)->editmesh) { ((EditMesh*)((VGeomData*)vnode->data)->editmesh)->vnode = NULL; ((VGeomData*)vnode->data)->editmesh = NULL; } /* free all verse layer data and unsubscribe from all layers */ while(vlayer) { BLI_dlist_reinit(&(vlayer->dl)); BLI_freelistN(&(vlayer->queue)); BLI_freelistN(&(vlayer->orphans)); if(session->flag & VERSE_CONNECTED) verse_send_g_layer_unsubscribe(vnode->id, vlayer->id); vlayer = vlayer->next; } } /* =================================================================================== * * Function executed after execution of callback functions * * ===================================================================================*/ /* * Actions executed after new VerseLayer is created */ void post_layer_create(struct VLayer *vlayer) { /* if we are owners of VerseNode, then push geometry to verse server */ if(vlayer->vnode->owner_id == VN_OWNER_MINE) { switch(vlayer->type){ case VN_G_LAYER_VERTEX_XYZ: /* if(vlayer->id==0) createAllVerseVerts(vlayer->vnode, vlayer); break;*/ case VN_G_LAYER_POLYGON_CORNER_UINT32: /* if(vlayer->id==1) createAllVerseFaces(vlayer->vnode, vlayer); break;*/ case VN_G_LAYER_VERTEX_UINT32: case VN_G_LAYER_VERTEX_REAL: case VN_G_LAYER_POLYGON_CORNER_REAL: case VN_G_LAYER_POLYGON_FACE_UINT8: case VN_G_LAYER_POLYGON_FACE_UINT32: case VN_G_LAYER_POLYGON_FACE_REAL: break; } } else { switch(vlayer->type) { case VN_G_LAYER_VERTEX_XYZ: case VN_G_LAYER_POLYGON_CORNER_UINT32: case VN_G_LAYER_VERTEX_UINT32: case VN_G_LAYER_VERTEX_REAL: case VN_G_LAYER_POLYGON_CORNER_REAL: case VN_G_LAYER_POLYGON_FACE_UINT8: case VN_G_LAYER_POLYGON_FACE_UINT32: case VN_G_LAYER_POLYGON_FACE_REAL: break; } } } /* * Actions after destroying of VerseLayer */ void post_layer_destroy(struct VLayer *vlayer) { } /* * Actions executed after creating of new VerseVert, when object is in edit * mode, and this client didn't create this VerseVert (vvert->vertex is NULL), * then new editvert is created */ void post_vertex_create(VerseVert *vvert) { struct VNode *obj_vnode; struct VNode *geom_vnode = vvert->vlayer->vnode; struct EditMesh *em=NULL; if(G.obedit && (((Mesh*)G.obedit->data)->vnode==geom_vnode)) { em = (EditMesh*)((VGeomData*)geom_vnode->data)->editmesh; } /* when vert was changed during sending to verse server, then * we have to send it to verse server again */ if(vvert->flag & VERT_POS_OBSOLETE) { vvert->flag &= ~VERT_POS_OBSOLETE; if(em && (vvert->vertex)) { struct EditVert *eve = (EditVert*)vvert->vertex; VECCOPY(vvert->co, eve->co); send_verse_vertex(vvert); verse_callback_update(0); return; } } if(em && !(vvert->vertex)) { struct EditVert *eve; /* to prevent never ending loop of sending and receiving * vertexes, because addvertlist() sends new vertex to verse * server if em->vnode isn't NULL */ em->vnode = NULL; eve = addvertlist(vvert->co, NULL); em->vnode = (void*)geom_vnode; eve->vvert = (void*)vvert; vvert->vertex = (void*)eve; countall(); recalc_editnormals(); } if(((VGeomData*)geom_vnode->data)->vlink) { obj_vnode = ((VGeomData*)geom_vnode->data)->vlink->source; DAG_object_flush_update(G.scene, (Object*)((VObjectData*)obj_vnode->data)->object, OB_RECALC_DATA); allqueue(REDRAWVIEW3D, 1); } } /* * Actions executed, when position of VerseVert was changed * position of EditVert is changed in edit mode */ void post_vertex_set_xyz(VerseVert *vvert) { struct VNode *obj_vnode; struct VNode *geom_vnode = vvert->vlayer->vnode; struct EditVert *eve = NULL; /* when vert was changed during sending to verse server, then * we have to send it to verse server again */ if(vvert->flag & VERT_POS_OBSOLETE) { if(vvert->vertex) { vvert->flag &= ~VERT_POS_OBSOLETE; vvert->flag |= VERT_LOCKED; eve = (EditVert*)vvert->vertex; VECCOPY(vvert->co, eve->co); send_verse_vertex(vvert); verse_callback_update(0); } else { printf("\terror: vvert->vertex shouldn't be NULL\n"); } return; } /* when shared object is in edit mode, then update editmesh */ if(G.obedit && (((Mesh*)G.obedit->data)->vnode==geom_vnode)) { if(vvert->vertex) { eve = (EditVert*)vvert->vertex; VECCOPY(eve->co, vvert->co); recalc_editnormals(); } else { printf("\terror: vvert->vertex shouldn't be NULL\n"); } } if(((VGeomData*)geom_vnode->data)->vlink) { obj_vnode = ((VGeomData*)geom_vnode->data)->vlink->source; DAG_object_flush_update(G.scene, (Object*)((VObjectData*)obj_vnode->data)->object, OB_RECALC_DATA); allqueue(REDRAWVIEW3D, 1); } } /* * Actions executed after deleting of VerseVert */ void post_vertex_delete(VerseVert *vvert) { struct VNode *obj_vnode; struct VNode *geom_vnode = vvert->vlayer->vnode; struct EditMesh *em = NULL; struct EditEdge *ed, *edn; struct EditVert *eve = NULL; if(G.obedit && (((Mesh*)G.obedit->data)->vnode==geom_vnode)) { em = (EditMesh*)((VGeomData*)geom_vnode->data)->editmesh; eve = (EditVert*)vvert->vertex; } if(em && eve) { /*printf("\tPOST_VERTEX_DELETE()\n");*/ /* delete all edges needing eve vertex */ ed = em->edges.first; while(ed) { edn = ed->next; if(ed->v1==eve || ed->v2==eve) { remedge(ed); free_editedge(ed); } ed = edn; } eve->vvert = NULL; BLI_remlink(&em->verts, eve); free_editvert(eve); vvert->vertex = NULL; countall(); recalc_editnormals(); } if(((VGeomData*)geom_vnode->data)->vlink) { obj_vnode = ((VGeomData*)geom_vnode->data)->vlink->source; DAG_object_flush_update(G.scene, (Object*)((VObjectData*)obj_vnode->data)->object, OB_RECALC_DATA); allqueue(REDRAWVIEW3D, 1); } } /* * free constraint between VerseVert and EditVert */ void post_vertex_free_constraint(VerseVert *vvert) { if(vvert->vertex) { ((EditVert*)vvert->vertex)->vvert=NULL; vvert->vertex=NULL; } } /* * Action executed after setting up uint8 value of polygon */ void post_polygon_set_uint8(VerseFace *vface) { } /* * Action executed after creating of new VerseFace */ void post_polygon_create(VerseFace *vface) { struct VNode *obj_vnode; struct VNode *geom_vnode = vface->vlayer->vnode; struct EditMesh *em = NULL; /* if verse face was set as deleted during sending to verse server, then send * delete command to verse server now ... we know verse face id */ /* if(vface->flag & FACE_DELETED) { send_verse_face_delete(vface); return; }*/ if(G.obedit && (((Mesh*)G.obedit->data)->vnode==geom_vnode)) { em = (EditMesh*)((VGeomData*)geom_vnode->data)->editmesh; } /* when face was changed during sending to verse server, then * we have to send it to verse server again */ if(vface->flag & FACE_OBSOLETE) { vface->flag &= ~FACE_OBSOLETE; sync_verseface_with_editface(vface->vlayer, vface); return; } if(em && !(vface->face) && (vface->counter==0)) { struct VLayer *vlayer; struct VerseVert *vvert; struct EditFace *efa; struct EditVert *eves[4]={NULL, NULL, NULL, NULL}; uint32 vert_ids[4]={vface->v0, vface->v1, vface->v2, vface->v3}; int i; /*printf("\tPOST_POLYGON_CREATE()\n");*/ vlayer = find_verse_layer_type((VGeomData*)geom_vnode->data, VERTEX_LAYER); for(i=0; i<4; i++) { if(vert_ids[i] != -1) { vvert = BLI_dlist_find_link(&(vlayer->dl), vert_ids[i]); if(vvert) eves[i] = (EditVert*)vvert->vertex; } } /* to prevent never ending loop of sending and receiving * faces, because addfacelist() sends new face to verse * server if em->vnode isn't NULL */ em->vnode = NULL; efa = addfacelist(eves[0], eves[1], eves[2], eves[3], NULL, NULL); em->vnode = geom_vnode; if(efa) { efa->vface = vface; vface->face = efa; } countall(); recalc_editnormals(); } if(((VGeomData*)geom_vnode->data)->vlink) { obj_vnode = ((VGeomData*)geom_vnode->data)->vlink->source; DAG_object_flush_update(G.scene, (Object*)((VObjectData*)obj_vnode->data)->object, OB_RECALC_DATA); allqueue(REDRAWVIEW3D, 1); } } /* * Action executed after changes of VerseFace * ... order of vertexes was fliped, etc. */ void post_polygon_set_corner(VerseFace *vface) { struct VNode *obj_vnode; struct VNode *geom_vnode = vface->vlayer->vnode; struct EditMesh *em = NULL; struct EditFace *efa = NULL; struct EditEdge *eed, *eedn; if(G.obedit && (((Mesh*)G.obedit->data)->vnode==geom_vnode)) { em = (EditMesh*)((VGeomData*)geom_vnode->data)->editmesh; efa = (EditFace*)vface->face; } if(em && efa) { /* when face was changed during sending to verse server, then * we have to send it to verse server again */ if(vface->flag & FACE_OBSOLETE) { vface->flag &= ~FACE_OBSOLETE; sync_verseface_with_editface(vface->vlayer, vface); return; } /* mark all edges, which are part of face efa */ efa->e1->f2 = 1; efa->e2->f2 = 1; efa->e3->f2 = 1; if(efa->e4) efa->e4->f2 = 1; /* change pointers at EdtitVerts and decrease counters of "old" * VerseVertexes reference ... less VerseFaces will need them */ if(vface->vvert0 != efa->v1->vvert) efa->v1 = (EditVert*)vface->vvert0->vertex; if(vface->vvert1 != efa->v2->vvert) efa->v2 = (EditVert*)vface->vvert1->vertex; if(vface->vvert2 != efa->v3->vvert) efa->v3 = (EditVert*)vface->vvert2->vertex; if(efa->v4) { if(!vface->vvert3) efa->v4 = NULL; else if(vface->vvert3 != efa->v4->vvert) efa->v4 = (EditVert*)vface->vvert3->vertex; } /* change pointers at EditEdges */ /* 1st edge */ eed = findedgelist(efa->v1, efa->v2); if(eed) efa->e1 = eed; else efa->e1 = addedgelist(efa->v1, efa->v2, NULL); /* 2nd edge */ eed = findedgelist(efa->v2, efa->v3); if(eed) efa->e2 = eed; else efa->e2 = addedgelist(efa->v2, efa->v3, NULL); if(efa->v4) { /* 3th edge */ eed = findedgelist(efa->v2, efa->v3); if(eed) efa->e3 = eed; else efa->e3 = addedgelist(efa->v2, efa->v3, NULL); /* 4th edge */ eed = findedgelist(efa->v4, efa->v1); if(eed) efa->e4 = eed; else efa->e4 = addedgelist(efa->v4, efa->v1, NULL); } else { /* 3th edge */ eed = findedgelist(efa->v3, efa->v1); if(eed) efa->e3 = eed; else efa->e3 = addedgelist(efa->v3, efa->v1, NULL); /* 4th edge */ efa->e4 = NULL; } /* unmark needed edges */ efa = em->faces.first; while(efa) { efa->e1->f2 = 0; efa->e2->f2 = 0; efa->e3->f2 = 0; if(efa->e4) efa->e4->f2 = 0; efa = efa->next; } /* delete all unneeded edges */ eed = em->edges.first; while(eed) { eedn = eed->next; if(eed->f2) { remedge(eed); free_editedge(eed); } eed = eedn; } countall(); recalc_editnormals(); } if(((VGeomData*)geom_vnode->data)->vlink) { obj_vnode = ((VGeomData*)geom_vnode->data)->vlink->source; DAG_object_flush_update(G.scene, (Object*)((VObjectData*)obj_vnode->data)->object, OB_RECALC_DATA); allqueue(REDRAWVIEW3D, 1); } } /* * Action executed after deleting of VerseFace */ void post_polygon_delete(VerseFace *vface) { struct VNode *obj_vnode; struct VNode *geom_vnode = vface->vlayer->vnode; struct EditMesh *em = NULL; struct EditFace *efa = NULL; struct EditEdge *eed, *eedn; if(G.obedit && (((Mesh*)G.obedit->data)->vnode==geom_vnode)) { em = (EditMesh*)((VGeomData*)geom_vnode->data)->editmesh; efa = (EditFace*)vface->face; } if(em && efa) { /*printf("\tPOST_POLYGON_DELETE()\n");*/ /* mark all edges, which are part of face efa */ efa->e1->f2 = 1; efa->e2->f2 = 1; efa->e3->f2 = 1; if(efa->e4) efa->e4->f2 = 1; efa->vface = NULL; BLI_remlink(&em->faces, efa); free_editface(efa); vface->face = NULL; /* following two crazy loops wouldn't be neccessary if verse spec * would support edges */ /* unmark needed edges */ efa = em->faces.first; while(efa) { efa->e1->f2 = 0; efa->e2->f2 = 0; efa->e3->f2 = 0; if(efa->e4) efa->e4->f2 = 0; efa = efa->next; } /* delete all unneeded edges */ eed = em->edges.first; while(eed) { eedn = eed->next; if(eed->f2) { remedge(eed); free_editedge(eed); } eed = eedn; } countall(); } if(((VGeomData*)geom_vnode->data)->vlink) { obj_vnode = ((VGeomData*)geom_vnode->data)->vlink->source; DAG_object_flush_update(G.scene, (Object*)((VObjectData*)obj_vnode->data)->object, OB_RECALC_DATA); allqueue(REDRAWVIEW3D, 1); } } /* * free constraint between VerseFace and EditFace */ void post_polygon_free_constraint(VerseFace *vface) { if(vface->face) { ((EditFace*)vface->face)->vface = NULL; vface->face = NULL; } } /* * free constraint between VGeomData, EditMesh and Mesh */ void post_geometry_free_constraint(VNode *vnode) { if(((VGeomData*)vnode->data)->editmesh) { G.editMesh->vnode = NULL; ((VGeomData*)vnode->data)->editmesh = NULL; } if(((VGeomData*)vnode->data)->mesh) { ((Mesh*)((VGeomData*)vnode->data)->mesh)->vnode = NULL; ((VGeomData*)vnode->data)->mesh = NULL; } } /* ========================================================================= * * Functions influencing whole EditMesh or VerseMesh * * ========================================================================= */ /* * free all bindings between EditMesh and "verse mesh" ... it is called between * restorng editmesh from undo stack */ void destroy_versemesh(VNode *vnode) { struct VLayer *vert_vlayer, *face_vlayer; struct VerseVert *vvert; struct VerseFace *vface; if(vnode->type != V_NT_GEOMETRY) return; vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); face_vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); /* send delete command to all received verse faces */ vface = face_vlayer->dl.lb.first; while(vface) { if(vface->face) ((EditFace*)vface->face)->vface = NULL; vface->face = NULL; send_verse_face_delete(vface); vface = vface->next; } /* send delete command to all verse faces waiting in orphan list */ vface = face_vlayer->orphans.first; while(vface) { if(vface->face) ((EditFace*)vface->face)->vface = NULL; vface->face = NULL; send_verse_face_delete(vface); vface = vface->next; } /* mark all verse faces waiting in sending queue as deleted, * send delete command, when this verse face was changed */ vface = face_vlayer->queue.first; while(vface) { if(vface->face) ((EditFace*)vface->face)->vface = NULL; vface->face = NULL; if(vface->flag & FACE_CHANGED) send_verse_face_delete(vface); else { vface->flag |= FACE_DELETED; } vface = vface->next; } /* send delete command to all received verse vertexes */ vvert = vert_vlayer->dl.lb.first; while(vvert) { if(vvert->vertex) ((EditVert*)vvert->vertex)->vvert = NULL; vvert->vertex = NULL; send_verse_vertex_delete(vvert); vvert = vvert->next; } /* mark all verse vertexes waiting in sending queue as deleted * ... verse vertexes will be deleted, when received */ vvert = vert_vlayer->queue.first; while(vvert) { if(vvert->vertex) ((EditVert*)vvert->vertex)->vvert = NULL; vvert->vertex = NULL; vvert = vvert->next; } } /* * duplicate geometry verse node, this can be handy, when you duplicate some * object or make object single user */ VNode *create_geom_vnode_from_geom_vnode(VNode *vnode) { struct VNode *n_vnode; /* new verse geometry node */ struct VGeomData *geom_data; /* new geometry data */ struct VLayer *n_vert_vlayer, *n_face_vlayer; /* new vertex and polygon layer */ struct VLayer *vert_vlayer, *face_vlayer; struct VerseVert *n_vvert, *vvert; struct VerseFace *n_vface, *vface; int i; if(!vnode) return NULL; if(vnode->type != V_NT_GEOMETRY) return NULL; /* create new verse node, when no one exist */ n_vnode= create_verse_node(vnode->session, -1 , V_NT_GEOMETRY, VN_OWNER_MINE); /* create new geometry data */ geom_data = create_geometry_data(); n_vnode->data = (void*)geom_data; /* set up name of VerseNode */ n_vnode->name = (char*)MEM_mallocN(sizeof(char*)*(strlen(vnode->name)-1), "new geom node name"); n_vnode->name[0] = '\0'; strcat(n_vnode->name, vnode->name); /* add node to sending queue */ add_item_to_send_queue(&(vnode->session->queue), n_vnode, VERSE_NODE); /* create vertex verse layer */ n_vert_vlayer = create_verse_layer(n_vnode, 0, "vertex", VN_G_LAYER_VERTEX_XYZ, 0, 0); add_item_to_send_queue(&(geom_data->queue), n_vert_vlayer, VERSE_LAYER); /* create polygon verse layer */ n_face_vlayer = create_verse_layer(n_vnode, 1, "polygon", VN_G_LAYER_POLYGON_CORNER_UINT32, 0, 0); add_item_to_send_queue(&(geom_data->queue), n_face_vlayer, VERSE_LAYER); /* find vertex layer of old verse node */ vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); /* find polygon layer of old verse node */ face_vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); /* duplicate verse vertexes */ for(i=0, vvert = (VerseVert*)vert_vlayer->dl.lb.first; vvert; vvert = vvert->next, i++) { n_vvert = create_verse_vertex(n_vert_vlayer, i, vvert->co[0], vvert->co[1], vvert->co[2]); vvert->tmp.vvert = n_vvert; add_item_to_send_queue(&(n_vert_vlayer->queue), n_vvert, VERSE_VERT); } /* duplicate verse faces (polyons) */ for(i=0, vface = (VerseFace*)face_vlayer->dl.lb.first; vface; vface = vface->next, i++) { n_vface = create_verse_face(n_face_vlayer, i, -1, -1, -1, -1); n_vface->vvert0 = vface->vvert0->tmp.vvert; n_vface->vvert1 = vface->vvert1->tmp.vvert; n_vface->vvert2 = vface->vvert2->tmp.vvert; if(vface->vvert3) n_vface->vvert3 = vface->vvert3->tmp.vvert; else n_vface->vvert3 = NULL; add_item_to_send_queue(&(n_face_vlayer->queue), n_vface, VERSE_FACE); } return n_vnode; } /* * create new geometry node, make bindings between geometry node and editmesh, * make bindings between editverts and verseverts, make bindings between editfaces * and versefaces */ VNode *create_geom_vnode_data_from_editmesh(VerseSession *session, EditMesh *em) { struct VNode *vnode; struct Mesh *me; struct VGeomData *geom_data; struct VLayer *vert_vlayer, *face_vlayer; if(!session) return NULL; if(!em) return NULL; /* some verse geometry node already exists */ if(em->vnode) return NULL; /* we will need mesh too (mesh name, creating bindings between verse node, etc.) */ me = get_mesh(G.obedit); /* create new verse node, when no one exist */ vnode = create_verse_node(session, -1 , V_NT_GEOMETRY, VN_OWNER_MINE); /* create new geometry data */ geom_data = create_geometry_data(); vnode->data = (void*)geom_data; /* set up name of VerseNode */ vnode->name = (char*)MEM_mallocN(sizeof(char*)*(strlen(me->id.name)-1), "geom node name"); vnode->name[0] = '\0'; strcat(vnode->name, me->id.name+2); /* set up bindings */ me->vnode = (void*)vnode; em->vnode = (void*)vnode; geom_data->mesh = (void*)me; geom_data->editmesh = (void*)em; /* add node to sending queue */ add_item_to_send_queue(&(session->queue), vnode, VERSE_NODE); /* create vertex verse layer */ vert_vlayer = create_verse_layer(vnode, 0, "vertex", VN_G_LAYER_VERTEX_XYZ, 0, 0); add_item_to_send_queue(&(geom_data->queue), vert_vlayer, VERSE_LAYER); /* create polygon verse layer */ face_vlayer = create_verse_layer(vnode, 1, "polygon", VN_G_LAYER_POLYGON_CORNER_UINT32, 0, 0); add_item_to_send_queue(&(geom_data->queue), face_vlayer, VERSE_LAYER); /* create all verse verts and add them to sending queue */ createAllVerseVerts(vnode, vert_vlayer); /* create all verse faces and add tehm to sending queue */ createAllVerseFaces(vnode, face_vlayer); return vnode; } /* * create new geometry node, make bindings between geometry node and mesh and * fill geometry node with new data based at mesh data */ VNode *create_geom_vnode_data_from_mesh(VerseSession *session, Mesh *me) { struct VNode *vnode; struct VGeomData *geom_data; struct VLayer *vert_vlayer, *face_vlayer; struct VerseVert *vvert, **vverts; struct VerseFace *vface; struct MVert *mvert; struct MFace *mface; int i; if(!session) return NULL; if(!me) return NULL; /* some verse geometry node already exists */ if(me->vnode) return NULL; /* create new verse node, when no one exist */ vnode = create_verse_node(session, -1 , V_NT_GEOMETRY, VN_OWNER_MINE); /* create new geometry data */ geom_data = create_geometry_data(); vnode->data = (void*)geom_data; /* set up name of VerseNode */ vnode->name = (char*)MEM_mallocN(sizeof(char*)*(strlen(me->id.name)-1), "geom node name"); vnode->name[0] = '\0'; strcat(vnode->name, me->id.name+2); /* set up bindings */ me->vnode = (void*)vnode; geom_data->mesh = (void*)me; /* add node to sending queue */ add_item_to_send_queue(&(session->queue), vnode, VERSE_NODE); /* create vertex verse layer */ vert_vlayer = create_verse_layer(vnode, 0, "vertex", VN_G_LAYER_VERTEX_XYZ, 0, 0); add_item_to_send_queue(&(geom_data->queue), vert_vlayer, VERSE_LAYER); /* create polygon verse layer */ face_vlayer = create_verse_layer(vnode, 1, "polygon", VN_G_LAYER_POLYGON_CORNER_UINT32, 0, 0); add_item_to_send_queue(&(geom_data->queue), face_vlayer, VERSE_LAYER); /* temporary array of VerseVerts */ vverts = (VerseVert**)MEM_mallocN(sizeof(VerseVert*)*me->totvert,"temp array of vverts"); /* "fill vertex layer with vertexes" and add them to sending queue (send them to verse server) */ for(i=0, mvert=me->mvert; itotvert; i++, mvert++) { vverts[i] = vvert = create_verse_vertex(vert_vlayer, i, mvert->co[0], mvert->co[1], mvert->co[2]); add_item_to_send_queue(&(vert_vlayer->queue), vvert, VERSE_VERT); } /* "fill face/polygon layer with faces" and them to sending queue */ for(i=0, mface = me->mface; itotface; i++, mface++) { if(mface->v4) { vface = create_verse_face(face_vlayer, i, mface->v1, mface->v2, mface->v3, mface->v4); vface->vvert0 = vverts[mface->v1]; vface->vvert1 = vverts[mface->v2]; vface->vvert2 = vverts[mface->v3]; vface->vvert3 = vverts[mface->v4]; vface->counter = 4; } else { vface = create_verse_face(face_vlayer, i, mface->v1, mface->v2, mface->v3, -1); vface->vvert0 = vverts[mface->v1]; vface->vvert1 = vverts[mface->v2]; vface->vvert2 = vverts[mface->v3]; vface->counter = 3; } add_item_to_send_queue(&(face_vlayer->queue), vface, VERSE_FACE); } MEM_freeN(vverts); return vnode; } /* * creates Mesh from verse geometry node and create bindings * between them */ Mesh *create_mesh_from_geom_node(VNode *vnode) { struct Mesh *me; if(vnode->type != V_NT_GEOMETRY) return NULL; /* add new empty mesh*/ me = add_mesh("Mesh"); /* set up bindings between mesh and verse node */ me->vnode = (void*)vnode; ((VGeomData*)vnode->data)->mesh = (void*)me; return me; } /* * create mesh from verse mesh */ void create_meshdata_from_geom_node(Mesh *me, VNode *vnode) { struct VLayer *vert_vlayer, *face_vlayer; struct VerseVert *vvert; struct VerseFace *vface; struct MVert *mvert; struct MFace *mface; struct EdgeHash *edges; int index; if(!me || !vnode) return; if(vnode->type != V_NT_GEOMETRY) return; vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); face_vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); CustomData_free(&me->fdata, me->totface); mesh_update_customdata_pointers(me); if(me->mselect) { MEM_freeN(me->mselect); me->mselect = NULL; } me->totvert = vert_vlayer ? vert_vlayer->dl.da.count : 0; me->totface = face_vlayer ? face_vlayer->dl.da.count : 0; me->totselect = 0; CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert); CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, me->totface); mesh_update_customdata_pointers(me); mvert = me->mvert; mface = me->mface; index = 0; vvert = vert_vlayer ? vert_vlayer->dl.lb.first : NULL; while(vvert) { VECCOPY(mvert->co, vvert->co); VECCOPY(mvert->no, vvert->no); mvert->flag = 0; mvert->mat_nr = 0; vvert->tmp.index = index++; vvert = vvert->next; mvert++; } edges = BLI_edgehash_new(); vface = face_vlayer ? face_vlayer->dl.lb.first : NULL; while(vface) { mface->v1 = vface->vvert0->tmp.index; mface->v2 = vface->vvert1->tmp.index; mface->v3 = vface->vvert2->tmp.index; if(!BLI_edgehash_haskey(edges, mface->v1, mface->v2)) BLI_edgehash_insert(edges, mface->v1, mface->v2, NULL); if(!BLI_edgehash_haskey(edges, mface->v2, mface->v3)) BLI_edgehash_insert(edges, mface->v2, mface->v3, NULL); if(vface->vvert3) { mface->v4 = vface->vvert3->tmp.index; if(!BLI_edgehash_haskey(edges, mface->v3, mface->v4)) BLI_edgehash_insert(edges, mface->v3, mface->v4, NULL); if(!BLI_edgehash_haskey(edges, mface->v4, mface->v1)) BLI_edgehash_insert(edges, mface->v4, mface->v1, NULL); } else { mface->v4 = 0; if(!BLI_edgehash_haskey(edges, mface->v3, mface->v1)) BLI_edgehash_insert(edges, mface->v3, mface->v1, NULL); } mface->flag = 0; mface->pad = 0; mface->mat_nr = 0; mface->edcode = 0; /* index 0 isn't allowed at location 3 or 4 */ test_index_face(mface, NULL, 0, vface->vvert3?4:3); /* printf("\t mface: %d, %d, %d, %d\n", mface->v1, mface->v2, mface->v3, mface->v4);*/ vface = vface->next; mface++; } me->totedge = BLI_edgehash_size(edges); if(me->totedge) { EdgeHashIterator *i; MEdge *medge = me->medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, NULL, me->totedge); for(i = BLI_edgehashIterator_new(edges); !BLI_edgehashIterator_isDone(i); BLI_edgehashIterator_step(i), ++medge) { memset(medge, 0, sizeof(struct MEdge)); BLI_edgehashIterator_getKey(i, (int*)&medge->v1, (int*)&medge->v2); } BLI_edgehashIterator_free(i); } BLI_edgehash_free(edges, NULL); mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL); } /* * Create EditMesh from VerseMesh and keep system in consitant state, this * function is called, when edit mode is entered ... edit mesh is generated * from verse mesh (not from Mesh: (Mesh*)ob->data) */ void create_edit_mesh_from_geom_node(VNode *vnode) { struct VLayer *vert_layer, *face_layer; struct VerseVert *vvert; struct VerseFace *vface; struct Mesh *me; struct EditVert *eve, *eve0, *eve1, *eve2, *eve3; struct EditFace *efa; unsigned int keyindex; if(!(G.obedit && G.obedit->type==OB_MESH)) return; me = (Mesh*)G.obedit->data; if(vnode!=(VNode*)me->vnode || vnode->type!=V_NT_GEOMETRY) return; vert_layer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); face_layer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); if(!(vert_layer && face_layer)) return; waitcursor(1); /* free old editMesh */ free_editMesh(G.editMesh); G.editMesh->vnode = NULL; vvert = vert_layer->dl.lb.first; keyindex = 0; /* create all EditVerts */ while(vvert) { eve = addvertlist(vvert->co, NULL); eve->f = 0; eve->h = 0; eve->data = NULL; eve->keyindex = keyindex; eve->vvert = (void*)vvert; vvert->vertex = (void*)eve; keyindex++; vvert = vvert->next; } vface = face_layer->dl.lb.first; /* create all EditFaces and EditEdges */ while(vface) { if(vface->vvert0) eve0= vface->vvert0->vertex; else eve0 = NULL; if(vface->vvert1) eve1= vface->vvert1->vertex; else eve1 = NULL; if(vface->vvert2) eve2= vface->vvert2->vertex; else eve2 = NULL; if(vface->vvert3) eve3= vface->vvert3->vertex; else eve3 = NULL; efa= addfacelist(eve0, eve1, eve2, eve3, NULL, NULL); if(efa) { efa->f = 0; efa->h = 0; efa->vface = (void*)vface; vface->face = (void*)efa; } vface = vface->next; } countall(); recalc_editnormals(); G.editMesh->vnode = (void*)vnode; ((VGeomData*)vnode->data)->editmesh = (void*)G.editMesh; waitcursor(0); } /* * destroy bindings between EditMesh and VerseMesh and send delete commands * for all VerseVerts and VerseFaces to verse server, Verse Node has to be * geometry node */ void destroy_verse_mesh(VNode *vnode) { struct VLayer *vert_vlayer, *face_vlayer; struct VerseFace *vface; struct VerseVert *vvert; if(vnode->type != V_NT_GEOMETRY) return; face_vlayer = find_verse_layer_type((VGeomData*)vnode->data, POLYGON_LAYER); vface = face_vlayer->dl.lb.first; while(vface) { ((EditFace*)vface->face)->vface = NULL; vface->face = NULL; vface = vface->next; } vert_vlayer = find_verse_layer_type((VGeomData*)vnode->data, VERTEX_LAYER); vvert = vert_vlayer->dl.lb.first; while(vvert) { ((EditVert*)vvert->vertex)->vvert = NULL; vvert->vertex = NULL; vvert = vvert->next; } destroy_geometry(vnode); } #endif