/* get a vector, vec, that points from v1->co to wherever makes sense to * the bevel operation as a whole based on the relationship between v1 and v2 * (won't necessarily be a vec from v1->co to v2->co, though it probably will be); * the return value is -1 for failure, 0 if we used vert co's, and 1 if we used transform origins */ static int BME_bevel_get_vec(float *vec, BME_Vert *v1, BME_Vert *v2, BME_TransData_Head *td) { BME_TransData *vtd1, *vtd2; vtd1 = BME_get_transdata(td,v1); vtd2 = BME_get_transdata(td,v2); if (!vtd1 || !vtd2) { //printf("BME_bevel_get_vec() got called without proper BME_TransData\n"); return -1; } /* compare the transform origins to see if we can use the vert co's; * if they belong to different origins, then we will use the origins to determine * the vector */ if (compare_v3v3(vtd1->org,vtd2->org,0.000001f)) { VECSUB(vec,v2->co,v1->co); if (len_v3(vec) < 0.000001f) { mul_v3_fl(vec,0); } return 0; } else { VECSUB(vec,vtd2->org,vtd1->org); if (len_v3(vec) < 0.000001f) { mul_v3_fl(vec,0); } return 1; } }
static float BME_bevel_set_max(BME_Vert *v1, BME_Vert *v2, float value, BME_TransData_Head *td) { BME_TransData *vtd1, *vtd2; float max, fac1, fac2, vec1[3], vec2[3], vec3[3]; BME_bevel_get_vec(vec1,v1,v2,td); vtd1 = BME_get_transdata(td,v1); vtd2 = BME_get_transdata(td,v2); if (vtd1->loc == NULL) { fac1 = 0; } else { VECCOPY(vec2,vtd1->vec); mul_v3_fl(vec2,vtd1->factor); if (dot_v3v3(vec1, vec1)) { project_v3_v3v3(vec2,vec2,vec1); fac1 = len_v3(vec2)/value; } else { fac1 = 0; } } if (vtd2->loc == NULL) { fac2 = 0; } else { VECCOPY(vec3,vtd2->vec); mul_v3_fl(vec3,vtd2->factor); if (dot_v3v3(vec1, vec1)) { project_v3_v3v3(vec2,vec3,vec1); fac2 = len_v3(vec2)/value; } else { fac2 = 0; } } if (fac1 || fac2) { max = len_v3(vec1)/(fac1 + fac2); if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) { *vtd1->max = max; } if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) { *vtd2->max = max; } } else { max = -1; } return max; }
static void BME_bevel_add_vweight(BME_TransData_Head *td, BME_Mesh *bm, BME_Vert *v, float weight, float factor, int options) { BME_TransData *vtd; if (v->tflag1 & BME_BEVEL_NONMAN) return; v->tflag1 |= BME_BEVEL_BEVEL; if ( (vtd = BME_get_transdata(td, v)) ) { if (options & BME_BEVEL_EMIN) { vtd->factor = 1.0; if (vtd->weight < 0 || weight < vtd->weight) { vtd->weight = weight; } } else if (options & BME_BEVEL_EMAX) { vtd->factor = 1.0; if (weight > vtd->weight) { vtd->weight = weight; } } else if (vtd->weight < 0) { vtd->factor = factor; vtd->weight = weight; } else { vtd->factor += factor; /* increment number of edges with weights (will be averaged) */ vtd->weight += weight; /* accumulate all the weights */ } } else { /* we'll use vtd->loc == NULL to mark that this vert is not moving */ vtd = BME_assign_transdata(td, bm, v, v->co, NULL, NULL, NULL, factor, weight, -1, NULL); } }
/* options that can be passed: * BME_BEVEL_VWEIGHT <---- v, Look at vertex weights; use defgrp_index if option is present * BME_BEVEL_SELECT <---- v,e, check selection for verts and edges * BME_BEVEL_ANGLE <---- v,e, don't bevel-tag verts - tag verts per edge * BME_BEVEL_VERT <---- e, don't tag edges * BME_BEVEL_EWEIGHT <---- e, use crease flag for now * BME_BEVEL_PERCENT <---- Will need to think about this one; will probably need to incorporate into actual bevel routine * BME_BEVEL_RADIUS <---- Will need to think about this one; will probably need to incorporate into actual bevel routine * All weights/limits are stored per-vertex */ BME_Mesh *BME_bevel(BME_Mesh *bm, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd) { BME_Vert *v; BME_TransData_Head *td; BME_TransData *vtd; int i; float fac=1, d; td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE); BME_bevel_initialize(bm, options, defgrp_index, angle, td); /* recursion math courtesy of Martin Poirier (theeth) */ for (i=0; i<res-1; i++) { if (i==0) fac += 1.0f/3.0f; else fac += 1.0f/(3 * i * 2.0f); } d = 1.0f/fac; /* crazy idea. if res == 0, don't remove original geometry */ for (i=0; i<res || (res==0 && i==0); i++) { if (i != 0) BME_bevel_reinitialize(bm); BME_model_begin(bm); BME_bevel_mesh(bm,d,res,options,defgrp_index,td); BME_model_end(bm); if (i==0) d /= 3; else d /= 2; } BME_tesselate(bm); if (rtd) { *rtd = td; return bm; } /* transform pass */ for (v = bm->verts.first; v; v=v->next) { if ( (vtd = BME_get_transdata(td, v)) ) { if (vtd->max && (*vtd->max > 0 && value > *vtd->max)) { d = *vtd->max; } else { d = value; } VECADDFAC(v->co,vtd->org,vtd->vec,vtd->factor*d); } v->tflag1 = 0; } BME_free_transdata(td); return bm; }
/* adds the geometry in the bmesh to editMesh (does not free editMesh) * if td != NULL, the transdata will be mapped to the EditVert's co */ void BME_bmesh_to_editmesh(BME_Mesh *bm, BME_TransData_Head *td, EditMesh *em) { BME_Vert *v1; BME_Edge *e; BME_Poly *f; BME_TransData *vtd; EditVert *eve1, *eve2, *eve3, *eve4, **evlist; EditEdge *eed; EditFace *efa; int totvert, len, i, numTex, numCol; if (em == NULL) return; CustomData_copy(&bm->vdata, &em->vdata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_copy(&bm->edata, &em->edata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_copy(&bm->pdata, &em->fdata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_from_bmeshpoly(&em->fdata, &bm->pdata, &bm->ldata,0); numTex = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL); /* convert to EditMesh */ /* make editverts */ totvert = BLI_countlist(&(bm->verts)); evlist= (EditVert **)MEM_mallocN(totvert*sizeof(void *),"evlist"); for (i=0,v1=bm->verts.first;v1;v1=v1->next,i++) { v1->tflag1 = i; eve1 = NULL; //XXX addvertlist(v1->co,NULL); if (td && (vtd = BME_get_transdata(td,v1))) { vtd->loc = eve1->co; } eve1->keyindex = i; evlist[i]= eve1; eve1->f = (unsigned char)v1->flag; eve1->h = (unsigned char)v1->h; eve1->bweight = v1->bweight; CustomData_em_copy_data(&bm->vdata, &em->vdata, v1->data, &eve1->data); } /* make edges */ for (e=bm->edges.first;e;e=e->next) { if(0) { //XXX if(!(findedgelist(evlist[e->v1->tflag1], evlist[e->v2->tflag1]))){ eed= NULL; //XXX addedgelist(evlist[e->v1->tflag1], evlist[e->v2->tflag1], NULL); eed->crease = e->crease; eed->bweight = e->bweight; if(e->flag & ME_SEAM) eed->seam = 1; if(e->flag & ME_SHARP) eed->sharp = 1; if(e->flag & SELECT) eed->f |= SELECT; //XXX if(e->flag & ME_FGON) eed->h= EM_FGON; // 2 different defines! if(e->flag & ME_HIDE) eed->h |= 1; if(em->selectmode==SCE_SELECT_EDGE) { ; //XXX EM_select_edge(eed, eed->f & SELECT); } CustomData_em_copy_data(&bm->edata, &em->edata, e->data, &eed->data); } } /* make faces */ for (f=bm->polys.first;f;f=f->next) { len = BME_cycle_length(f->loopbase); if (len==3 || len==4) { eve1= evlist[f->loopbase->v->tflag1]; eve2= evlist[f->loopbase->next->v->tflag1]; eve3= evlist[f->loopbase->next->next->v->tflag1]; if (len == 4) { eve4= evlist[f->loopbase->prev->v->tflag1]; } else { eve4= NULL; } efa = NULL; //XXX addfacelist(eve1, eve2, eve3, eve4, NULL, NULL); efa->mat_nr = (unsigned char)f->mat_nr; efa->flag= f->flag & ~ME_HIDE; if(f->flag & ME_FACE_SEL) { efa->f |= SELECT; } if(f->flag & ME_HIDE) efa->h= 1; // XXX flag depricated // if((G.f & G_FACESELECT) && (efa->f & SELECT)) //XXX EM_select_face(efa, 1); /* flush down */ CustomData_em_copy_data(&bm->pdata, &em->fdata, f->data, &efa->data); BME_loops_to_corners(bm, &em->fdata, efa->data, f,numCol,numTex); } } MEM_freeN(evlist); }
/** * BME_bevel_poly * * Polygon inset tool: * * Insets a polygon/face based on the tflag1's of its vertices * and edges. Used by the bevel tool only, for now. * The parameter "value" is the distance to inset (should be negative). * The parameter "options" is not currently used. * * Returns - * A BME_Poly pointer to the resulting inner face. */ static BME_Poly *BME_bevel_poly(BME_Mesh *bm, BME_Poly *f, float value, int options, BME_TransData_Head *td) { BME_Loop *l, *ol; BME_TransData *vtd1, *vtd2; float up_vec[3], vec1[3], vec2[3], vec3[3], fac1, fac2, max = -1; int len, i; up_vec[0] = 0.0f; up_vec[1] = 0.0f; up_vec[2] = 0.0f; /* find a good normal for this face (there's better ways, I'm sure) */ ol = f->loopbase; l = ol->next; for (i=0,ol=f->loopbase,l=ol->next; l->next!=ol; l=l->next) { BME_bevel_get_vec(vec1,l->next->v,ol->v,td); BME_bevel_get_vec(vec2,l->v,ol->v,td); cross_v3_v3v3(vec3,vec2,vec1); VECADD(up_vec,up_vec,vec3); i++; } mul_v3_fl(up_vec,1.0f/i); normalize_v3(up_vec); for (i=0,len=f->len; i<len; i++,l=l->next) { if ((l->e->tflag1 & BME_BEVEL_BEVEL) && (l->e->tflag1 & BME_BEVEL_ORIG)) { max = 1.0f; l = BME_bevel_edge(bm, l, value, options, up_vec, td); } else if ((l->v->tflag1 & BME_BEVEL_BEVEL) && (l->v->tflag1 & BME_BEVEL_ORIG) && (l->prev->e->tflag1 & BME_BEVEL_BEVEL) == 0) { max = 1.0f; l = BME_bevel_vert(bm, l, value, options, up_vec, td); } } /* max pass */ if (value > 0.5 && max > 0) { max = -1; for (i=0,len=f->len; i<len; i++,l=l->next) { if ((l->e->tflag1 & BME_BEVEL_BEVEL) || (l->e->tflag1 & BME_BEVEL_ORIG)) { BME_bevel_get_vec(vec1,l->v,l->next->v,td); vtd1 = BME_get_transdata(td,l->v); vtd2 = BME_get_transdata(td,l->next->v); if (vtd1->loc == NULL) { fac1 = 0; } else { VECCOPY(vec2,vtd1->vec); mul_v3_fl(vec2,vtd1->factor); if (dot_v3v3(vec1, vec1)) { project_v3_v3v3(vec2,vec2,vec1); fac1 = len_v3(vec2)/value; } else { fac1 = 0; } } if (vtd2->loc == NULL) { fac2 = 0; } else { VECCOPY(vec3,vtd2->vec); mul_v3_fl(vec3,vtd2->factor); if (dot_v3v3(vec1, vec1)) { project_v3_v3v3(vec2,vec3,vec1); fac2 = len_v3(vec2)/value; } else { fac2 = 0; } } if (fac1 || fac2) { max = len_v3(vec1)/(fac1 + fac2); if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) { *vtd1->max = max; } if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) { *vtd2->max = max; } } } } } return l->f; }
/* BME_bevel_split_edge() is the main math work-house; its responsibilities are: * using the vert and the loop passed, get or make the split vert, set its coordinates * and transform properties, and set the max limits. * Finally, return the split vert. */ static BME_Vert *BME_bevel_split_edge(BME_Mesh *bm, BME_Vert *v, BME_Vert *v1, BME_Loop *l, float *up_vec, float value, BME_TransData_Head *td) { BME_TransData *vtd, *vtd1, *vtd2; BME_Vert *sv, *v2, *v3, *ov; BME_Loop *lv1, *lv2; BME_Edge *ne, *e1, *e2; float maxfactor, scale, len, dis, vec1[3], vec2[3], t_up_vec[3]; int is_edge, forward, is_split_vert; if (l == NULL) { /* what you call operator overloading in C :) * I wanted to use the same function for both wire edges and poly loops * so... here we walk around edges to find the needed verts */ forward = 1; is_split_vert = 0; if (v->edge == NULL) { //printf("We can't split a loose vert's edge!\n"); return NULL; } e1 = v->edge; /* we just use the first two edges */ e2 = BME_disk_nextedge(v->edge, v); if (e1 == e2) { //printf("You need at least two edges to use BME_bevel_split_edge()\n"); return NULL; } v2 = BME_edge_getothervert(e1, v); v3 = BME_edge_getothervert(e2, v); if (v1 != v2 && v1 != v3) { //printf("Error: more than 2 edges in v's disk cycle, or v1 does not share an edge with v\n"); return NULL; } if (v1 == v2) { v2 = v3; } else { e1 = e2; } ov = BME_edge_getothervert(e1,v); sv = BME_split_edge(bm,v,e1,&ne,0); //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /*this is technically wrong...*/ //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25); //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25); BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */ sv->tflag1 |= BME_BEVEL_BEVEL; ne->tflag1 = BME_BEVEL_ORIG; /* mark edge as original, even though it isn't */ BME_bevel_get_vec(vec1,v1,v,td); BME_bevel_get_vec(vec2,v2,v,td); cross_v3_v3v3(t_up_vec,vec1,vec2); normalize_v3(t_up_vec); up_vec = t_up_vec; } else { /* establish loop direction */ if (l->v == v) { forward = 1; lv1 = l->next; lv2 = l->prev; v1 = l->next->v; v2 = l->prev->v; } else if (l->next->v == v) { forward = 0; lv1 = l; lv2 = l->next->next; v1 = l->v; v2 = l->next->next->v; } else { //printf("ERROR: BME_bevel_split_edge() - v must be adjacent to l\n"); return NULL; } if (BME_bevel_is_split_vert(lv1)) { is_split_vert = 1; sv = v1; if (forward) v1 = l->next->next->v; else v1 = l->prev->v; } else { is_split_vert = 0; ov = BME_edge_getothervert(l->e,v); sv = BME_split_edge(bm,v,l->e,&ne,0); //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /*this is technically wrong...*/ //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25); //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25); BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */ sv->tflag1 |= BME_BEVEL_BEVEL; ne->tflag1 = BME_BEVEL_ORIG; /* mark edge as original, even though it isn't */ } if (BME_bevel_is_split_vert(lv2)) { if (forward) v2 = lv2->prev->v; else v2 = lv2->next->v; } } is_edge = BME_bevel_get_vec(vec1,v,v1,td); /* get the vector we will be projecting onto */ BME_bevel_get_vec(vec2,v,v2,td); /* get the vector we will be projecting parallel to */ len = len_v3(vec1); normalize_v3(vec1); vtd = BME_get_transdata(td, sv); vtd1 = BME_get_transdata(td, v); vtd2 = BME_get_transdata(td,v1); if (vtd1->loc == NULL) { /* this is a vert with data only for calculating initial weights */ if (vtd1->weight < 0) { vtd1->weight = 0; } scale = vtd1->weight/vtd1->factor; if (!vtd1->max) { vtd1->max = BME_new_transdata_float(td); *vtd1->max = -1; } } else { scale = vtd1->weight; } vtd->max = vtd1->max; if (is_edge && vtd1->loc != NULL) { maxfactor = vtd1->maxfactor; } else { maxfactor = scale*BME_bevel_project_vec(vec1,vec2,up_vec,forward,td); if (vtd->maxfactor > 0 && vtd->maxfactor < maxfactor) { maxfactor = vtd->maxfactor; } } dis = (v1->tflag1 & BME_BEVEL_ORIG)? len/3 : len/2; if (is_edge || dis > maxfactor*value) { dis = maxfactor*value; } VECADDFAC(sv->co,v->co,vec1,dis); VECSUB(vec1,sv->co,vtd1->org); dis = len_v3(vec1); normalize_v3(vec1); BME_assign_transdata(td, bm, sv, vtd1->org, vtd1->org, vec1, sv->co, dis, scale, maxfactor, vtd->max); return sv; }