static BLI_bitmap *get_tface_mesh_marked_edge_info(Mesh *me, bool draw_select_edges) { BLI_bitmap *bitmap_edge_flags = BLI_BITMAP_NEW(me->totedge * 2, __func__); MPoly *mp; MLoop *ml; int i, j; bool select_set; for (i = 0; i < me->totpoly; i++) { mp = &me->mpoly[i]; if (!(mp->flag & ME_HIDE)) { select_set = (mp->flag & ME_FACE_SEL) != 0; ml = me->mloop + mp->loopstart; for (j = 0; j < mp->totloop; j++, ml++) { if ((draw_select_edges == false) && (select_set && BLI_BITMAP_TEST(bitmap_edge_flags, edge_sel_index(ml->e)))) { BLI_BITMAP_DISABLE(bitmap_edge_flags, edge_vis_index(ml->e)); } else { BLI_BITMAP_ENABLE(bitmap_edge_flags, edge_vis_index(ml->e)); if (select_set) { BLI_BITMAP_ENABLE(bitmap_edge_flags, edge_sel_index(ml->e)); } } } } } return bitmap_edge_flags; }
static bool sculpt_undo_restore_hidden( bContext *C, DerivedMesh *dm, SculptUndoNode *unode) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; int i; if (unode->maxvert) { MVert *mvert = ss->mvert; for (i = 0; i < unode->totvert; i++) { MVert *v = &mvert[unode->index[i]]; if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) { BLI_BITMAP_FLIP(unode->vert_hidden, i); v->flag ^= ME_HIDE; v->flag |= ME_VERT_PBVH_UPDATE; } } } else if (unode->maxgrid && dm->getGridData) { BLI_bitmap **grid_hidden = dm->getGridHidden(dm); for (i = 0; i < unode->totgrid; i++) { SWAP(BLI_bitmap *, unode->grid_hidden[i], grid_hidden[unode->grids[i]]); } }
static DMDrawOption draw_mesh_face_select__setHiddenOpts(void *userData, int index) { drawMeshFaceSelect_userData *data = userData; Mesh *me = data->me; if (me->drawflag & ME_DRAWEDGES) { if ((me->drawflag & ME_HIDDENEDGES) || (BLI_BITMAP_TEST(data->edge_flags, edge_vis_index(index)))) return DM_DRAW_OPTION_NORMAL; else return DM_DRAW_OPTION_SKIP; } else if (BLI_BITMAP_TEST(data->edge_flags, edge_sel_index(index))) return DM_DRAW_OPTION_NORMAL; else return DM_DRAW_OPTION_SKIP; }
static bool lattice_test_bitmap_uvw(Lattice *lt, BLI_bitmap *selpoints, int u, int v, int w, const bool selected) { if ((u < 0 || u >= lt->pntsu) || (v < 0 || v >= lt->pntsv) || (w < 0 || w >= lt->pntsw)) { return false; } else { int i = BKE_lattice_index_from_uvw(lt, u, v, w); if (lt->def[i].hide == 0) { return (BLI_BITMAP_TEST(selpoints, i) != 0) == selected; } return false; } }
static int lattice_select_mirror_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt; const bool extend = RNA_boolean_get(op->ptr, "extend"); const int axis = RNA_enum_get(op->ptr, "axis"); bool flip_uvw[3] = {false}; int tot, i; BPoint *bp; BLI_bitmap *selpoints; tot = lt->pntsu * lt->pntsv * lt->pntsw; flip_uvw[axis] = true; if (!extend) { lt->actbp = LT_ACTBP_NONE; } /* store "original" selection */ selpoints = BLI_BITMAP_NEW(tot, __func__); BKE_lattice_bitmap_from_flag(lt, selpoints, SELECT, false, false); /* actual (de)selection */ for (i = 0; i < tot; i++) { const int i_flip = BKE_lattice_index_flip(lt, i, flip_uvw[0], flip_uvw[1], flip_uvw[2]); bp = <->def[i]; if (!bp->hide) { if (BLI_BITMAP_TEST(selpoints, i_flip)) { bp->f1 |= SELECT; } else { if (!extend) { bp->f1 &= ~SELECT; } } } } MEM_freeN(selpoints); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); return OPERATOR_FINISHED; }
static void ed_lattice_select_mirrored(Lattice *lt, const int axis, const bool extend) { const int tot = lt->pntsu * lt->pntsv * lt->pntsw; int i; BPoint *bp; BLI_bitmap *selpoints; bool flip_uvw[3] = {false}; flip_uvw[axis] = true; /* we could flip this too */ if (!extend) { lt->actbp = LT_ACTBP_NONE; } /* store "original" selection */ selpoints = BLI_BITMAP_NEW(tot, __func__); BKE_lattice_bitmap_from_flag(lt, selpoints, SELECT, false, false); /* actual (de)selection */ for (i = 0; i < tot; i++) { const int i_flip = BKE_lattice_index_flip(lt, i, flip_uvw[0], flip_uvw[1], flip_uvw[2]); bp = <->def[i]; if (!bp->hide) { if (BLI_BITMAP_TEST(selpoints, i_flip)) { bp->f1 |= SELECT; } else { if (!extend) { bp->f1 &= ~SELECT; } } } } MEM_freeN(selpoints); }
/** * Calculate UV islands. * * \note Currently we only consider edges tagges as seams as UV boundaries. This has the advantages of simplicity, * and being valid/common to all UV maps. However, it means actual UV islands whithout matching UV seams * will not be handled correctly... * * \note All this could be optimized... * Not sure it would be worth the more complex code, though, those loops are supposed to be really quick to do... */ bool BKE_mesh_calc_islands_loop_poly_uv( MVert *UNUSED(verts), const int UNUSED(totvert), MEdge *edges, const int totedge, MPoly *polys, const int totpoly, MLoop *loops, const int totloop, MeshIslandStore *r_island_store) { int *poly_groups = NULL; int num_poly_groups; /* map vars */ MeshElemMap *edge_poly_map; int *edge_poly_mem; int *poly_indices; int *loop_indices; int num_pidx, num_lidx; /* Those are used to detect 'inner cuts', i.e. edges that are borders, and yet have two or more polys of * a same group using them (typical case: seam used to unwrap properly a cylinder). */ BLI_bitmap *edge_borders = NULL; int num_edge_borders = 0; char *edge_border_count = NULL; int *edge_innercut_indices = NULL; int num_einnercuts = 0; int grp_idx, p_idx, pl_idx, l_idx; BKE_mesh_loop_islands_clear(r_island_store); BKE_mesh_loop_islands_init(r_island_store, MISLAND_TYPE_LOOP, totloop, MISLAND_TYPE_POLY, MISLAND_TYPE_EDGE); BKE_mesh_edge_poly_map_create(&edge_poly_map, &edge_poly_mem, edges, totedge, polys, totpoly, loops, totloop); poly_edge_loop_islands_calc( edges, totedge, polys, totpoly, loops, totloop, edge_poly_map, false, mesh_check_island_boundary_uv, &poly_groups, &num_poly_groups, &edge_borders, &num_edge_borders); if (!num_poly_groups) { /* Should never happen... */ MEM_freeN(edge_poly_map); MEM_freeN(edge_poly_mem); if (edge_borders) { MEM_freeN(edge_borders); } return false; } if (num_edge_borders) { edge_border_count = MEM_mallocN(sizeof(*edge_border_count) * (size_t)totedge, __func__); edge_innercut_indices = MEM_mallocN(sizeof(*edge_innercut_indices) * (size_t)num_edge_borders, __func__); } poly_indices = MEM_mallocN(sizeof(*poly_indices) * (size_t)totpoly, __func__); loop_indices = MEM_mallocN(sizeof(*loop_indices) * (size_t)totloop, __func__); /* Note: here we ignore '0' invalid group - this should *never* happen in this case anyway? */ for (grp_idx = 1; grp_idx <= num_poly_groups; grp_idx++) { num_pidx = num_lidx = 0; if (num_edge_borders) { num_einnercuts = 0; memset(edge_border_count, 0, sizeof(*edge_border_count) * (size_t)totedge); } for (p_idx = 0; p_idx < totpoly; p_idx++) { MPoly *mp; if (poly_groups[p_idx] != grp_idx) { continue; } mp = &polys[p_idx]; poly_indices[num_pidx++] = p_idx; for (l_idx = mp->loopstart, pl_idx = 0; pl_idx < mp->totloop; l_idx++, pl_idx++) { MLoop *ml = &loops[l_idx]; loop_indices[num_lidx++] = l_idx; if (num_edge_borders && BLI_BITMAP_TEST(edge_borders, ml->e) && (edge_border_count[ml->e] < 2)) { edge_border_count[ml->e]++; if (edge_border_count[ml->e] == 2) { edge_innercut_indices[num_einnercuts++] = (int)ml->e; } } } } BKE_mesh_loop_islands_add(r_island_store, num_lidx, loop_indices, num_pidx, poly_indices, num_einnercuts, edge_innercut_indices); } MEM_freeN(edge_poly_map); MEM_freeN(edge_poly_mem); MEM_freeN(poly_indices); MEM_freeN(loop_indices); MEM_freeN(poly_groups); if (edge_borders) { MEM_freeN(edge_borders); } if (num_edge_borders) { MEM_freeN(edge_border_count); MEM_freeN(edge_innercut_indices); } return true; }
static void poly_edge_loop_islands_calc( const MEdge *medge, const int totedge, const MPoly *mpoly, const int totpoly, const MLoop *mloop, const int totloop, MeshElemMap *edge_poly_map, const bool use_bitflags, MeshRemap_CheckIslandBoundary edge_boundary_check, int **r_poly_groups, int *r_totgroup, BLI_bitmap **r_edge_borders, int *r_totedgeborder) { int *poly_groups; int *poly_stack; BLI_bitmap *edge_borders = NULL; int num_edgeborders = 0; int poly_prev = 0; const int temp_poly_group_id = 3; /* Placeholder value. */ const int poly_group_id_overflowed = 5; /* Group we could not find any available bit, will be reset to 0 at end */ int tot_group = 0; bool group_id_overflow = false; /* map vars */ int *edge_poly_mem = NULL; if (totpoly == 0) { *r_totgroup = 0; *r_poly_groups = NULL; if (r_edge_borders) { *r_edge_borders = NULL; *r_totedgeborder = 0; } return; } if (r_edge_borders) { edge_borders = BLI_BITMAP_NEW(totedge, __func__); *r_totedgeborder = 0; } if (!edge_poly_map) { BKE_mesh_edge_poly_map_create(&edge_poly_map, &edge_poly_mem, medge, totedge, mpoly, totpoly, mloop, totloop); } poly_groups = MEM_callocN(sizeof(int) * (size_t)totpoly, __func__); poly_stack = MEM_mallocN(sizeof(int) * (size_t)totpoly, __func__); while (true) { int poly; int bit_poly_group_mask = 0; int poly_group_id; int ps_curr_idx = 0, ps_end_idx = 0; /* stack indices */ for (poly = poly_prev; poly < totpoly; poly++) { if (poly_groups[poly] == 0) { break; } } if (poly == totpoly) { /* all done */ break; } poly_group_id = use_bitflags ? temp_poly_group_id : ++tot_group; /* start searching from here next time */ poly_prev = poly + 1; poly_groups[poly] = poly_group_id; poly_stack[ps_end_idx++] = poly; while (ps_curr_idx != ps_end_idx) { const MPoly *mp; const MLoop *ml; int j; poly = poly_stack[ps_curr_idx++]; BLI_assert(poly_groups[poly] == poly_group_id); mp = &mpoly[poly]; for (ml = &mloop[mp->loopstart], j = mp->totloop; j--; ml++) { /* loop over poly users */ const int me_idx = (int)ml->e; const MEdge *me = &medge[me_idx]; const MeshElemMap *map_ele = &edge_poly_map[me_idx]; const int *p = map_ele->indices; int i = map_ele->count; if (!edge_boundary_check(mp, ml, me, i)) { for (; i--; p++) { /* if we meet other non initialized its a bug */ BLI_assert(ELEM(poly_groups[*p], 0, poly_group_id)); if (poly_groups[*p] == 0) { poly_groups[*p] = poly_group_id; poly_stack[ps_end_idx++] = *p; } } } else { if (edge_borders && !BLI_BITMAP_TEST(edge_borders, me_idx)) { BLI_BITMAP_ENABLE(edge_borders, me_idx); num_edgeborders++; } if (use_bitflags) { /* Find contiguous smooth groups already assigned, these are the values we can't reuse! */ for (; i--; p++) { int bit = poly_groups[*p]; if (!ELEM(bit, 0, poly_group_id, poly_group_id_overflowed) && !(bit_poly_group_mask & bit)) { bit_poly_group_mask |= bit; } } } } } } /* And now, we have all our poly from current group in poly_stack (from 0 to (ps_end_idx - 1)), as well as * all smoothgroups bits we can't use in bit_poly_group_mask. */ if (use_bitflags) { int i, *p, gid_bit = 0; poly_group_id = 1; /* Find first bit available! */ for (; (poly_group_id & bit_poly_group_mask) && (gid_bit < 32); gid_bit++) { poly_group_id <<= 1; /* will 'overflow' on last possible iteration. */ } if (UNLIKELY(gid_bit > 31)) { /* All bits used in contiguous smooth groups, we can't do much! * Note: this is *very* unlikely - theoretically, four groups are enough, I don't think we can reach * this goal with such a simple algo, but I don't think either we'll never need all 32 groups! */ printf("Warning, could not find an available id for current smooth group, faces will me marked " "as out of any smooth group...\n"); poly_group_id = poly_group_id_overflowed; /* Can't use 0, will have to set them to this value later. */ group_id_overflow = true; } if (gid_bit > tot_group) { tot_group = gid_bit; } /* And assign the final smooth group id to that poly group! */ for (i = ps_end_idx, p = poly_stack; i--; p++) { poly_groups[*p] = poly_group_id; } } } if (use_bitflags) { /* used bits are zero-based. */ tot_group++; } if (UNLIKELY(group_id_overflow)) { int i = totpoly, *gid = poly_groups; for (; i--; gid++) { if (*gid == poly_group_id_overflowed) { *gid = 0; } } /* Using 0 as group id adds one more group! */ tot_group++; } if (edge_poly_mem) { MEM_freeN(edge_poly_map); MEM_freeN(edge_poly_mem); } MEM_freeN(poly_stack); *r_totgroup = tot_group; *r_poly_groups = poly_groups; if (r_edge_borders) { *r_edge_borders = edge_borders; *r_totedgeborder = num_edgeborders; } }
/* basic method: deselect if control point doesn't have all neighbors selected */ static int select_less_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit = CTX_data_edit_object(C); ListBase *editnurb = object_editcurve_get(obedit); Nurb *nu; BPoint *bp; BezTriple *bezt; int a; int sel = 0; short lastsel = false; if (obedit->type == OB_SURF) { for (nu = editnurb->first; nu; nu = nu->next) { BLI_bitmap *selbpoints; a = nu->pntsu * nu->pntsv; bp = nu->bp; selbpoints = BLI_BITMAP_NEW(a, "selectlist"); while (a--) { if ((bp->hide == 0) && (bp->f1 & SELECT)) { sel = 0; /* check if neighbors have been selected */ /* edges of surface are an exception */ if ((a + 1) % nu->pntsu == 0) { sel++; } else { bp--; if (BLI_BITMAP_TEST(selbpoints, a + 1) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++; bp++; } if ((a + 1) % nu->pntsu == 1) { sel++; } else { bp++; if ((bp->hide == 0) && (bp->f1 & SELECT)) sel++; bp--; } if (a + 1 > nu->pntsu * nu->pntsv - nu->pntsu) { sel++; } else { bp -= nu->pntsu; if (BLI_BITMAP_TEST(selbpoints, a + nu->pntsu) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++; bp += nu->pntsu; } if (a < nu->pntsu) { sel++; } else { bp += nu->pntsu; if ((bp->hide == 0) && (bp->f1 & SELECT)) sel++; bp -= nu->pntsu; } if (sel != 4) { select_bpoint(bp, DESELECT, SELECT, VISIBLE); BLI_BITMAP_ENABLE(selbpoints, a); } } else { lastsel = false; } bp++; } MEM_freeN(selbpoints); } } else { for (nu = editnurb->first; nu; nu = nu->next) { lastsel = false; /* check what type of curve/nurb it is */ if (nu->type == CU_BEZIER) { a = nu->pntsu; bezt = nu->bezt; while (a--) { if ((bezt->hide == 0) && (bezt->f2 & SELECT)) { sel = (lastsel == 1); /* check if neighbors have been selected */ /* first and last are exceptions */ if (a == nu->pntsu - 1) { sel++; } else { bezt--; if ((bezt->hide == 0) && (bezt->f2 & SELECT)) sel++; bezt++; } if (a == 0) { sel++; } else { bezt++; if ((bezt->hide == 0) && (bezt->f2 & SELECT)) sel++; bezt--; } if (sel != 2) { select_beztriple(bezt, DESELECT, SELECT, VISIBLE); lastsel = true; } else { lastsel = false; } } else { lastsel = false; } bezt++; } } else { a = nu->pntsu * nu->pntsv; bp = nu->bp; while (a--) { if ((lastsel == 0) && (bp->hide == 0) && (bp->f1 & SELECT)) { if (lastsel != 0) sel = 1; else sel = 0; /* first and last are exceptions */ if (a == nu->pntsu * nu->pntsv - 1) { sel++; } else { bp--; if ((bp->hide == 0) && (bp->f1 & SELECT)) sel++; bp++; } if (a == 0) { sel++; } else { bp++; if ((bp->hide == 0) && (bp->f1 & SELECT)) sel++; bp--; } if (sel != 2) { select_bpoint(bp, DESELECT, SELECT, VISIBLE); lastsel = true; } else { lastsel = false; } } else { lastsel = false; } bp++; } } } } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); BKE_curve_nurb_vert_active_validate(obedit->data); return OPERATOR_FINISHED; }
static int select_more_exec(bContext *C, wmOperator *UNUSED(op)) { Object *obedit = CTX_data_edit_object(C); ListBase *editnurb = object_editcurve_get(obedit); Nurb *nu; BPoint *bp, *tempbp; int a; short sel = 0; /* note that NURBS surface is a special case because we mimic */ /* the behavior of "select more" of mesh tools. */ /* The algorithm is designed to work in planar cases so it */ /* may not be optimal always (example: end of NURBS sphere) */ if (obedit->type == OB_SURF) { for (nu = editnurb->first; nu; nu = nu->next) { BLI_bitmap *selbpoints; a = nu->pntsu * nu->pntsv; bp = nu->bp; selbpoints = BLI_BITMAP_NEW(a, "selectlist"); while (a > 0) { if ((!BLI_BITMAP_TEST(selbpoints, a)) && (bp->hide == 0) && (bp->f1 & SELECT)) { /* upper control point */ if (a % nu->pntsu != 0) { tempbp = bp - 1; if (!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, SELECT, VISIBLE); } /* left control point. select only if it is not selected already */ if (a - nu->pntsu > 0) { sel = 0; tempbp = bp + nu->pntsu; if (!(tempbp->f1 & SELECT)) sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE); /* make sure selected bpoint is discarded */ if (sel == 1) BLI_BITMAP_ENABLE(selbpoints, a - nu->pntsu); } /* right control point */ if (a + nu->pntsu < nu->pntsu * nu->pntsv) { tempbp = bp - nu->pntsu; if (!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, SELECT, VISIBLE); } /* lower control point. skip next bp in case selection was made */ if (a % nu->pntsu != 1) { sel = 0; tempbp = bp + 1; if (!(tempbp->f1 & SELECT)) sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE); if (sel) { bp++; a--; } } } bp++; a--; } MEM_freeN(selbpoints); } } else { select_adjacent_cp(editnurb, 1, 0, SELECT); select_adjacent_cp(editnurb, -1, 0, SELECT); } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); return OPERATOR_FINISHED; }
static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index, const bool select) { MPoly *mp; MLoop *ml; int a, b; bool do_it = true; bool mark = false; BLI_bitmap *edge_tag = BLI_BITMAP_NEW(me->totedge, __func__); BLI_bitmap *poly_tag = BLI_BITMAP_NEW(me->totpoly, __func__); if (index != (unsigned int)-1) { /* only put face under cursor in array */ mp = &me->mpoly[index]; BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); BLI_BITMAP_ENABLE(poly_tag, index); } else { /* fill array by selection */ mp = me->mpoly; for (a = 0; a < me->totpoly; a++, mp++) { if (mp->flag & ME_HIDE) { /* pass */ } else if (mp->flag & ME_FACE_SEL) { BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); BLI_BITMAP_ENABLE(poly_tag, a); } } } while (do_it) { do_it = false; /* expand selection */ mp = me->mpoly; for (a = 0; a < me->totpoly; a++, mp++) { if (mp->flag & ME_HIDE) { continue; } if (!BLI_BITMAP_TEST(poly_tag, a)) { mark = false; ml = me->mloop + mp->loopstart; for (b = 0; b < mp->totloop; b++, ml++) { if ((me->medge[ml->e].flag & ME_SEAM) == 0) { if (BLI_BITMAP_TEST(edge_tag, ml->e)) { mark = true; break; } } } if (mark) { BLI_BITMAP_ENABLE(poly_tag, a); BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); do_it = true; } } } } MEM_freeN(edge_tag); for (a = 0, mp = me->mpoly; a < me->totpoly; a++, mp++) { if (BLI_BITMAP_TEST(poly_tag, a)) { SET_FLAG_FROM_TEST(mp->flag, select, ME_FACE_SEL); } } MEM_freeN(poly_tag); }
static void normalEditModifier_do_directional( NormalEditModifierData *enmd, Object *ob, DerivedMesh *dm, short (*clnors)[2], float (*loopnors)[3], float (*polynors)[3], const short mix_mode, const float mix_factor, const float mix_limit, MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, MVert *mvert, const int num_verts, MEdge *medge, const int num_edges, MLoop *mloop, const int num_loops, MPoly *mpoly, const int num_polys) { const bool use_parallel_normals = (enmd->flag & MOD_NORMALEDIT_USE_DIRECTION_PARALLEL) != 0; float (*cos)[3] = MEM_malloc_arrayN((size_t)num_verts, sizeof(*cos), __func__); float (*nos)[3] = MEM_malloc_arrayN((size_t)num_loops, sizeof(*nos), __func__); float target_co[3]; int i; dm->getVertCos(dm, cos); /* Get target's center coordinates in ob local coordinates. */ { float mat[4][4]; invert_m4_m4(mat, ob->obmat); mul_m4_m4m4(mat, mat, enmd->target->obmat); copy_v3_v3(target_co, mat[3]); } if (use_parallel_normals) { float no[3]; sub_v3_v3v3(no, target_co, enmd->offset); normalize_v3(no); for (i = num_loops; i--; ) { copy_v3_v3(nos[i], no); } } else { BLI_bitmap *done_verts = BLI_BITMAP_NEW((size_t)num_verts, __func__); MLoop *ml; float (*no)[3]; /* We reuse cos to now store the 'to target' normal of the verts! */ for (i = num_loops, no = nos, ml = mloop; i--; no++, ml++) { const int vidx = ml->v; float *co = cos[vidx]; if (!BLI_BITMAP_TEST(done_verts, vidx)) { sub_v3_v3v3(co, target_co, co); normalize_v3(co); BLI_BITMAP_ENABLE(done_verts, vidx); } copy_v3_v3(*no, co); } MEM_freeN(done_verts); } if (loopnors) { mix_normals(mix_factor, dvert, defgrp_index, use_invert_vgroup, mix_limit, mix_mode, num_verts, mloop, loopnors, nos, num_loops); } if (polygons_check_flip(mloop, nos, dm->getLoopDataLayout(dm), mpoly, polynors, num_polys)) { dm->dirty |= DM_DIRTY_TESS_CDLAYERS; } BKE_mesh_normals_loop_custom_set(mvert, num_verts, medge, num_edges, mloop, nos, num_loops, mpoly, (const float(*)[3])polynors, num_polys, clnors); MEM_freeN(cos); MEM_freeN(nos); }
static void normalEditModifier_do_radial( NormalEditModifierData *enmd, Object *ob, DerivedMesh *dm, short (*clnors)[2], float (*loopnors)[3], float (*polynors)[3], const short mix_mode, const float mix_factor, const float mix_limit, MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup, MVert *mvert, const int num_verts, MEdge *medge, const int num_edges, MLoop *mloop, const int num_loops, MPoly *mpoly, const int num_polys) { int i; float (*cos)[3] = MEM_malloc_arrayN((size_t)num_verts, sizeof(*cos), __func__); float (*nos)[3] = MEM_malloc_arrayN((size_t)num_loops, sizeof(*nos), __func__); float size[3]; BLI_bitmap *done_verts = BLI_BITMAP_NEW((size_t)num_verts, __func__); generate_vert_coordinates(dm, ob, enmd->target, enmd->offset, num_verts, cos, size); /** * size gives us our spheroid coefficients ``(A, B, C)``. * Then, we want to find out for each vert its (a, b, c) triple (proportional to (A, B, C) one). * * Ellipsoid basic equation: ``(x^2/a^2) + (y^2/b^2) + (z^2/c^2) = 1.`` * Since we want to find (a, b, c) matching this equation and proportional to (A, B, C), we can do: * <pre> * m = B / A * n = C / A * </pre> * * hence: * <pre> * (x^2/a^2) + (y^2/b^2) + (z^2/c^2) = 1 * -> b^2*c^2*x^2 + a^2*c^2*y^2 + a^2*b^2*z^2 = a^2*b^2*c^2 * b = ma * c = na * -> m^2*a^2*n^2*a^2*x^2 + a^2*n^2*a^2*y^2 + a^2*m^2*a^2*z^2 = a^2*m^2*a^2*n^2*a^2 * -> m^2*n^2*a^4*x^2 + n^2*a^4*y^2 + m^2*a^4*z^2 = m^2*n^2*a^6 * -> a^2 = (m^2*n^2*x^2 + n^2y^2 + m^2z^2) / (m^2*n^2) = x^2 + (y^2 / m^2) + (z^2 / n^2) * -> b^2 = (m^2*n^2*x^2 + n^2y^2 + m^2z^2) / (n^2) = (m^2 * x^2) + y^2 + (m^2 * z^2 / n^2) * -> c^2 = (m^2*n^2*x^2 + n^2y^2 + m^2z^2) / (m^2) = (n^2 * x^2) + (n^2 * y^2 / m^2) + z^2 * </pre> * * All we have to do now is compute normal of the spheroid at that point: * <pre> * n = (x / a^2, y / b^2, z / c^2) * </pre> * And we are done! */ { const float a = size[0], b = size[1], c = size[2]; const float m2 = (b * b) / (a * a); const float n2 = (c * c) / (a * a); MLoop *ml; float (*no)[3]; /* We reuse cos to now store the ellipsoid-normal of the verts! */ for (i = num_loops, ml = mloop, no = nos; i-- ; ml++, no++) { const int vidx = ml->v; float *co = cos[vidx]; if (!BLI_BITMAP_TEST(done_verts, vidx)) { const float x2 = co[0] * co[0]; const float y2 = co[1] * co[1]; const float z2 = co[2] * co[2]; const float a2 = x2 + (y2 / m2) + (z2 / n2); const float b2 = (m2 * x2) + y2 + (m2 * z2 / n2); const float c2 = (n2 * x2) + (n2 * y2 / m2) + z2; co[0] /= a2; co[1] /= b2; co[2] /= c2; normalize_v3(co); BLI_BITMAP_ENABLE(done_verts, vidx); } copy_v3_v3(*no, co); } } if (loopnors) { mix_normals(mix_factor, dvert, defgrp_index, use_invert_vgroup, mix_limit, mix_mode, num_verts, mloop, loopnors, nos, num_loops); } if (polygons_check_flip(mloop, nos, dm->getLoopDataLayout(dm), mpoly, polynors, num_polys)) { dm->dirty |= DM_DIRTY_TESS_CDLAYERS; /* We need to recompute vertex normals! */ dm->calcNormals(dm); } BKE_mesh_normals_loop_custom_set(mvert, num_verts, medge, num_edges, mloop, nos, num_loops, mpoly, (const float(*)[3])polynors, num_polys, clnors); MEM_freeN(cos); MEM_freeN(nos); MEM_freeN(done_verts); }
/* Hide or show elements in multires grids with a special GridFlags * customdata layer. */ static void partialvis_update_grids(Object *ob, PBVH *pbvh, PBVHNode *node, PartialVisAction action, PartialVisArea area, float planes[4][4]) { CCGElem **grids; CCGKey key; BLI_bitmap **grid_hidden; int *grid_indices, totgrid, i; bool any_changed = false, any_visible = false; /* get PBVH data */ BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, &grids); grid_hidden = BKE_pbvh_grid_hidden(pbvh); BKE_pbvh_get_grid_key(pbvh, &key); sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); for (i = 0; i < totgrid; i++) { int any_hidden = 0; int g = grid_indices[i], x, y; BLI_bitmap *gh = grid_hidden[g]; if (!gh) { switch (action) { case PARTIALVIS_HIDE: /* create grid flags data */ gh = grid_hidden[g] = BLI_BITMAP_NEW(key.grid_area, "partialvis_update_grids"); break; case PARTIALVIS_SHOW: /* entire grid is visible, nothing to show */ continue; } } else if (action == PARTIALVIS_SHOW && area == PARTIALVIS_ALL) { /* special case if we're showing all, just free the * grid */ MEM_freeN(gh); grid_hidden[g] = NULL; any_changed = true; any_visible = true; continue; } for (y = 0; y < key.grid_size; y++) { for (x = 0; x < key.grid_size; x++) { CCGElem *elem = CCG_grid_elem(&key, grids[g], x, y); const float *co = CCG_elem_co(&key, elem); float mask = key.has_mask ? *CCG_elem_mask(&key, elem) : 0.0f; /* skip grid element if not in the effected area */ if (is_effected(area, planes, co, mask)) { /* set or clear the hide flag */ BLI_BITMAP_SET(gh, y * key.grid_size + x, action == PARTIALVIS_HIDE); any_changed = true; } /* keep track of whether any elements are still hidden */ if (BLI_BITMAP_TEST(gh, y * key.grid_size + x)) any_hidden = true; else any_visible = true; } } /* if everything in the grid is now visible, free the grid * flags */ if (!any_hidden) { MEM_freeN(gh); grid_hidden[g] = NULL; } } /* mark updates if anything was hidden/shown */ if (any_changed) { BKE_pbvh_node_mark_rebuild_draw(node); BKE_pbvh_node_fully_hidden_set(node, !any_visible); multires_mark_as_modified(ob, MULTIRES_HIDDEN_MODIFIED); } }
static DMDrawOption draw_mesh_face_select__setSelectOpts(void *userData, int index) { drawMeshFaceSelect_userData *data = userData; return (BLI_BITMAP_TEST(data->edge_flags, edge_sel_index(index)) && BLI_BITMAP_TEST(data->edge_flags, edge_vis_index(index))) ? DM_DRAW_OPTION_NORMAL : DM_DRAW_OPTION_SKIP; }
static DerivedMesh *applyModifier( ModifierData *md, Object *ob, DerivedMesh *dm, ModifierApplyFlag UNUSED(flag)) { DerivedMesh *result; const SolidifyModifierData *smd = (SolidifyModifierData *) md; MVert *mv, *mvert, *orig_mvert; MEdge *ed, *medge, *orig_medge; MLoop *ml, *mloop, *orig_mloop; MPoly *mp, *mpoly, *orig_mpoly; const unsigned int numVerts = (unsigned int)dm->getNumVerts(dm); const unsigned int numEdges = (unsigned int)dm->getNumEdges(dm); const unsigned int numFaces = (unsigned int)dm->getNumPolys(dm); const unsigned int numLoops = (unsigned int)dm->getNumLoops(dm); unsigned int newLoops = 0, newFaces = 0, newEdges = 0, newVerts = 0, rimVerts = 0; /* only use material offsets if we have 2 or more materials */ const short mat_nr_max = ob->totcol > 1 ? ob->totcol - 1 : 0; const short mat_ofs = mat_nr_max ? smd->mat_ofs : 0; const short mat_ofs_rim = mat_nr_max ? smd->mat_ofs_rim : 0; /* use for edges */ /* over-alloc new_vert_arr, old_vert_arr */ unsigned int *new_vert_arr = NULL; STACK_DECLARE(new_vert_arr); unsigned int *new_edge_arr = NULL; STACK_DECLARE(new_edge_arr); unsigned int *old_vert_arr = MEM_callocN(sizeof(*old_vert_arr) * (size_t)numVerts, "old_vert_arr in solidify"); unsigned int *edge_users = NULL; char *edge_order = NULL; float (*vert_nors)[3] = NULL; float (*face_nors)[3] = NULL; const bool need_face_normals = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) || (smd->flag & MOD_SOLIDIFY_EVEN); const float ofs_orig = -(((-smd->offset_fac + 1.0f) * 0.5f) * smd->offset); const float ofs_new = smd->offset + ofs_orig; const float offset_fac_vg = smd->offset_fac_vg; const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0; const bool do_clamp = (smd->offset_clamp != 0.0f); const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == 0; /* weights */ MDeformVert *dvert; const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; int defgrp_index; /* array size is doubled in case of using a shell */ const unsigned int stride = do_shell ? 2 : 1; modifier_get_vgroup(ob, dm, smd->defgrp_name, &dvert, &defgrp_index); orig_mvert = dm->getVertArray(dm); orig_medge = dm->getEdgeArray(dm); orig_mloop = dm->getLoopArray(dm); orig_mpoly = dm->getPolyArray(dm); if (need_face_normals) { /* calculate only face normals */ face_nors = MEM_mallocN(sizeof(*face_nors) * (size_t)numFaces, __func__); BKE_mesh_calc_normals_poly( orig_mvert, NULL, (int)numVerts, orig_mloop, orig_mpoly, (int)numLoops, (int)numFaces, face_nors, true); } STACK_INIT(new_vert_arr, numVerts * 2); STACK_INIT(new_edge_arr, numEdges * 2); if (smd->flag & MOD_SOLIDIFY_RIM) { BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(numVerts, __func__); unsigned int eidx; unsigned int i; #define INVALID_UNUSED ((unsigned int)-1) #define INVALID_PAIR ((unsigned int)-2) new_vert_arr = MEM_mallocN(sizeof(*new_vert_arr) * (size_t)(numVerts * 2), __func__); new_edge_arr = MEM_mallocN(sizeof(*new_edge_arr) * (size_t)((numEdges * 2) + numVerts), __func__); edge_users = MEM_mallocN(sizeof(*edge_users) * (size_t)numEdges, "solid_mod edges"); edge_order = MEM_mallocN(sizeof(*edge_order) * (size_t)numEdges, "solid_mod eorder"); /* save doing 2 loops here... */ #if 0 copy_vn_i(edge_users, numEdges, INVALID_UNUSED); #endif for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { edge_users[eidx] = INVALID_UNUSED; } for (i = 0, mp = orig_mpoly; i < numFaces; i++, mp++) { MLoop *ml_prev; int j; ml = orig_mloop + mp->loopstart; ml_prev = ml + (mp->totloop - 1); for (j = 0; j < mp->totloop; j++, ml++) { /* add edge user */ eidx = ml_prev->e; if (edge_users[eidx] == INVALID_UNUSED) { ed = orig_medge + eidx; BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2)); edge_users[eidx] = (ml_prev->v > ml->v) == (ed->v1 < ed->v2) ? i : (i + numFaces); edge_order[eidx] = j; } else { edge_users[eidx] = INVALID_PAIR; } ml_prev = ml; } } for (eidx = 0, ed = orig_medge; eidx < numEdges; eidx++, ed++) { if (!ELEM(edge_users[eidx], INVALID_UNUSED, INVALID_PAIR)) { BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v1); BLI_BITMAP_ENABLE(orig_mvert_tag, ed->v2); STACK_PUSH(new_edge_arr, eidx); newFaces++; newLoops += 4; } } for (i = 0; i < numVerts; i++) { if (BLI_BITMAP_TEST(orig_mvert_tag, i)) { old_vert_arr[i] = STACK_SIZE(new_vert_arr); STACK_PUSH(new_vert_arr, i); rimVerts++; } else { old_vert_arr[i] = INVALID_UNUSED; } } MEM_freeN(orig_mvert_tag); } if (do_shell == false) { /* only add rim vertices */ newVerts = rimVerts; /* each extruded face needs an opposite edge */ newEdges = newFaces; } else { /* (stride == 2) in this case, so no need to add newVerts/newEdges */ BLI_assert(newVerts == 0); BLI_assert(newEdges == 0); } if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) { vert_nors = MEM_callocN(sizeof(float) * (size_t)numVerts * 3, "mod_solid_vno_hq"); dm_calc_normal(dm, face_nors, vert_nors); } result = CDDM_from_template(dm, (int)((numVerts * stride) + newVerts), (int)((numEdges * stride) + newEdges + rimVerts), 0, (int)((numLoops * stride) + newLoops), (int)((numFaces * stride) + newFaces)); mpoly = CDDM_get_polys(result); mloop = CDDM_get_loops(result); medge = CDDM_get_edges(result); mvert = CDDM_get_verts(result); if (do_shell) { DM_copy_vert_data(dm, result, 0, 0, (int)numVerts); DM_copy_vert_data(dm, result, 0, (int)numVerts, (int)numVerts); DM_copy_edge_data(dm, result, 0, 0, (int)numEdges); DM_copy_edge_data(dm, result, 0, (int)numEdges, (int)numEdges); DM_copy_loop_data(dm, result, 0, 0, (int)numLoops); DM_copy_loop_data(dm, result, 0, (int)numLoops, (int)numLoops); DM_copy_poly_data(dm, result, 0, 0, (int)numFaces); DM_copy_poly_data(dm, result, 0, (int)numFaces, (int)numFaces); } else { int i, j; DM_copy_vert_data(dm, result, 0, 0, (int)numVerts); for (i = 0, j = (int)numVerts; i < numVerts; i++) { if (old_vert_arr[i] != INVALID_UNUSED) { DM_copy_vert_data(dm, result, i, j, 1); j++; } } DM_copy_edge_data(dm, result, 0, 0, (int)numEdges); for (i = 0, j = (int)numEdges; i < numEdges; i++) { if (!ELEM(edge_users[i], INVALID_UNUSED, INVALID_PAIR)) { MEdge *ed_src, *ed_dst; DM_copy_edge_data(dm, result, i, j, 1); ed_src = &medge[i]; ed_dst = &medge[j]; ed_dst->v1 = old_vert_arr[ed_src->v1] + numVerts; ed_dst->v2 = old_vert_arr[ed_src->v2] + numVerts; j++; } } /* will be created later */ DM_copy_loop_data(dm, result, 0, 0, (int)numLoops); DM_copy_poly_data(dm, result, 0, 0, (int)numFaces); } #undef INVALID_UNUSED #undef INVALID_PAIR /* initializes: (i_end, do_shell_align, mv) */ #define INIT_VERT_ARRAY_OFFSETS(test) \ if (((ofs_new >= ofs_orig) == do_flip) == test) { \ i_end = numVerts; \ do_shell_align = true; \ mv = mvert; \ } \ else { \ if (do_shell) { \ i_end = numVerts; \ do_shell_align = true; \ } \ else { \ i_end = newVerts ; \ do_shell_align = false; \ } \ mv = &mvert[numVerts]; \ } (void)0 /* flip normals */ if (do_shell) { unsigned int i; mp = mpoly + numFaces; for (i = 0; i < dm->numPolyData; i++, mp++) { MLoop *ml2; unsigned int e; int j; /* reverses the loop direction (MLoop.v as well as custom-data) * MLoop.e also needs to be corrected too, done in a separate loop below. */ ml2 = mloop + mp->loopstart + dm->numLoopData; for (j = 0; j < mp->totloop; j++) { CustomData_copy_data(&dm->loopData, &result->loopData, mp->loopstart + j, mp->loopstart + (mp->totloop - j - 1) + dm->numLoopData, 1); } if (mat_ofs) { mp->mat_nr += mat_ofs; CLAMP(mp->mat_nr, 0, mat_nr_max); } e = ml2[0].e; for (j = 0; j < mp->totloop - 1; j++) { ml2[j].e = ml2[j + 1].e; } ml2[mp->totloop - 1].e = e; mp->loopstart += dm->numLoopData; for (j = 0; j < mp->totloop; j++) { ml2[j].e += numEdges; ml2[j].v += numVerts; } } for (i = 0, ed = medge + numEdges; i < numEdges; i++, ed++) { ed->v1 += numVerts; ed->v2 += numVerts; } } /* note, copied vertex layers don't have flipped normals yet. do this after applying offset */ if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) { /* no even thickness, very simple */ float scalar_short; float scalar_short_vgroup; /* for clamping */ float *vert_lens = NULL; const float offset = fabsf(smd->offset) * smd->offset_clamp; const float offset_sq = offset * offset; if (do_clamp) { unsigned int i; vert_lens = MEM_mallocN(sizeof(float) * numVerts, "vert_lens"); copy_vn_fl(vert_lens, (int)numVerts, FLT_MAX); for (i = 0; i < numEdges; i++) { const float ed_len_sq = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); vert_lens[medge[i].v1] = min_ff(vert_lens[medge[i].v1], ed_len_sq); vert_lens[medge[i].v2] = min_ff(vert_lens[medge[i].v2], ed_len_sq); } } if (ofs_new != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; scalar_short = scalar_short_vgroup = ofs_new / 32767.0f; INIT_VERT_ARRAY_OFFSETS(false); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short; } if (do_clamp) { /* always reset becaise we may have set before */ if (dvert == NULL) { scalar_short_vgroup = scalar_short; } if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; scalar_short_vgroup *= scalar; } } madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); } } if (ofs_orig != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f; /* as above but swapped */ INIT_VERT_ARRAY_OFFSETS(true); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (dvert) { MDeformVert *dv = &dvert[i]; if (defgrp_invert) scalar_short_vgroup = 1.0f - defvert_find_weight(dv, defgrp_index); else scalar_short_vgroup = defvert_find_weight(dv, defgrp_index); scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) * scalar_short; } if (do_clamp) { /* always reset becaise we may have set before */ if (dvert == NULL) { scalar_short_vgroup = scalar_short; } if (vert_lens[i] < offset_sq) { float scalar = sqrtf(vert_lens[i]) / offset; scalar_short_vgroup *= scalar; } } madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup); } } if (do_clamp) { MEM_freeN(vert_lens); } } else { #ifdef USE_NONMANIFOLD_WORKAROUND const bool check_non_manifold = (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) != 0; #endif /* same as EM_solidify() in editmesh_lib.c */ float *vert_angles = MEM_callocN(sizeof(float) * numVerts * 2, "mod_solid_pair"); /* 2 in 1 */ float *vert_accum = vert_angles + numVerts; unsigned int vidx; unsigned int i; if (vert_nors == NULL) { vert_nors = MEM_mallocN(sizeof(float) * numVerts * 3, "mod_solid_vno"); for (i = 0, mv = mvert; i < numVerts; i++, mv++) { normal_short_to_float_v3(vert_nors[i], mv->no); } } for (i = 0, mp = mpoly; i < numFaces; i++, mp++) { /* #BKE_mesh_calc_poly_angles logic is inlined here */ float nor_prev[3]; float nor_next[3]; int i_curr = mp->totloop - 1; int i_next = 0; ml = &mloop[mp->loopstart]; sub_v3_v3v3(nor_prev, mvert[ml[i_curr - 1].v].co, mvert[ml[i_curr].v].co); normalize_v3(nor_prev); while (i_next < mp->totloop) { float angle; sub_v3_v3v3(nor_next, mvert[ml[i_curr].v].co, mvert[ml[i_next].v].co); normalize_v3(nor_next); angle = angle_normalized_v3v3(nor_prev, nor_next); /* --- not related to angle calc --- */ if (angle < FLT_EPSILON) { angle = FLT_EPSILON; } vidx = ml[i_curr].v; vert_accum[vidx] += angle; #ifdef USE_NONMANIFOLD_WORKAROUND /* skip 3+ face user edges */ if ((check_non_manifold == false) || LIKELY(((orig_medge[ml[i_curr].e].flag & ME_EDGE_TMP_TAG) == 0) && ((orig_medge[ml[i_next].e].flag & ME_EDGE_TMP_TAG) == 0))) { vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_nors[i]) * angle; } else { vert_angles[vidx] += angle; } #else vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], face_nors[i]) * angle; #endif /* --- end non-angle-calc section --- */ /* step */ copy_v3_v3(nor_prev, nor_next); i_curr = i_next; i_next++; } } /* vertex group support */ if (dvert) { MDeformVert *dv = dvert; float scalar; if (defgrp_invert) { for (i = 0; i < numVerts; i++, dv++) { scalar = 1.0f - defvert_find_weight(dv, defgrp_index); scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); vert_angles[i] *= scalar; } } else { for (i = 0; i < numVerts; i++, dv++) { scalar = defvert_find_weight(dv, defgrp_index); scalar = offset_fac_vg + (scalar * offset_fac_vg_inv); vert_angles[i] *= scalar; } } } if (do_clamp) { float *vert_lens_sq = MEM_mallocN(sizeof(float) * numVerts, "vert_lens"); const float offset = fabsf(smd->offset) * smd->offset_clamp; const float offset_sq = offset * offset; copy_vn_fl(vert_lens_sq, (int)numVerts, FLT_MAX); for (i = 0; i < numEdges; i++) { const float ed_len = len_squared_v3v3(mvert[medge[i].v1].co, mvert[medge[i].v2].co); vert_lens_sq[medge[i].v1] = min_ff(vert_lens_sq[medge[i].v1], ed_len); vert_lens_sq[medge[i].v2] = min_ff(vert_lens_sq[medge[i].v2], ed_len); } for (i = 0; i < numVerts; i++) { if (vert_lens_sq[i] < offset_sq) { float scalar = sqrtf(vert_lens_sq[i]) / offset; vert_angles[i] *= scalar; } } MEM_freeN(vert_lens_sq); } if (ofs_new != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; INIT_VERT_ARRAY_OFFSETS(false); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (vert_accum[i_other]) { /* zero if unselected */ madd_v3_v3fl(mv->co, vert_nors[i_other], ofs_new * (vert_angles[i_other] / vert_accum[i_other])); } } } if (ofs_orig != 0.0f) { unsigned int i_orig, i_end; bool do_shell_align; /* same as above but swapped, intentional use of 'ofs_new' */ INIT_VERT_ARRAY_OFFSETS(true); for (i_orig = 0; i_orig < i_end; i_orig++, mv++) { const unsigned int i_other = do_shell_align ? i_orig : new_vert_arr[i_orig]; if (vert_accum[i_other]) { /* zero if unselected */ madd_v3_v3fl(mv->co, vert_nors[i_other], ofs_orig * (vert_angles[i_other] / vert_accum[i_other])); } } } MEM_freeN(vert_angles); } if (vert_nors) MEM_freeN(vert_nors); /* must recalculate normals with vgroups since they can displace unevenly [#26888] */ if ((dm->dirty & DM_DIRTY_NORMALS) || (smd->flag & MOD_SOLIDIFY_RIM) || dvert) { result->dirty |= DM_DIRTY_NORMALS; } else if (do_shell) { unsigned int i; /* flip vertex normals for copied verts */ mv = mvert + numVerts; for (i = 0; i < numVerts; i++, mv++) { negate_v3_short(mv->no); } } if (smd->flag & MOD_SOLIDIFY_RIM) { unsigned int i; /* bugger, need to re-calculate the normals for the new edge faces. * This could be done in many ways, but probably the quickest way * is to calculate the average normals for side faces only. * Then blend them with the normals of the edge verts. * * at the moment its easiest to allocate an entire array for every vertex, * even though we only need edge verts - campbell */ #define SOLIDIFY_SIDE_NORMALS #ifdef SOLIDIFY_SIDE_NORMALS const bool do_side_normals = !(result->dirty & DM_DIRTY_NORMALS); /* annoying to allocate these since we only need the edge verts, */ float (*edge_vert_nos)[3] = do_side_normals ? MEM_callocN(sizeof(float) * numVerts * 3, __func__) : NULL; float nor[3]; #endif const unsigned char crease_rim = smd->crease_rim * 255.0f; const unsigned char crease_outer = smd->crease_outer * 255.0f; const unsigned char crease_inner = smd->crease_inner * 255.0f; int *origindex_edge; int *orig_ed; unsigned int j; if (crease_rim || crease_outer || crease_inner) { result->cd_flag |= ME_CDFLAG_EDGE_CREASE; } /* add faces & edges */ origindex_edge = result->getEdgeDataArray(result, CD_ORIGINDEX); ed = &medge[(numEdges * stride) + newEdges]; /* start after copied edges */ orig_ed = &origindex_edge[(numEdges * stride) + newEdges]; for (i = 0; i < rimVerts; i++, ed++, orig_ed++) { ed->v1 = new_vert_arr[i]; ed->v2 = (do_shell ? new_vert_arr[i] : i) + numVerts; ed->flag |= ME_EDGEDRAW; *orig_ed = ORIGINDEX_NONE; if (crease_rim) { ed->crease = crease_rim; } } /* faces */ mp = mpoly + (numFaces * stride); ml = mloop + (numLoops * stride); j = 0; for (i = 0; i < newFaces; i++, mp++) { unsigned int eidx = new_edge_arr[i]; unsigned int fidx = edge_users[eidx]; int k1, k2; bool flip; if (fidx >= numFaces) { fidx -= numFaces; flip = true; } else { flip = false; } ed = medge + eidx; /* copy most of the face settings */ DM_copy_poly_data(dm, result, (int)fidx, (int)((numFaces * stride) + i), 1); mp->loopstart = (int)(j + (numLoops * stride)); mp->flag = mpoly[fidx].flag; /* notice we use 'mp->totloop' which is later overwritten, * we could lookup the original face but theres no point since this is a copy * and will have the same value, just take care when changing order of assignment */ k1 = mpoly[fidx].loopstart + (((edge_order[eidx] - 1) + mp->totloop) % mp->totloop); /* prev loop */ k2 = mpoly[fidx].loopstart + (edge_order[eidx]); mp->totloop = 4; CustomData_copy_data(&dm->loopData, &result->loopData, k2, (int)((numLoops * stride) + j + 0), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k1, (int)((numLoops * stride) + j + 1), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k1, (int)((numLoops * stride) + j + 2), 1); CustomData_copy_data(&dm->loopData, &result->loopData, k2, (int)((numLoops * stride) + j + 3), 1); if (flip == false) { ml[j].v = ed->v1; ml[j++].e = eidx; ml[j].v = ed->v2; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; ml[j++].e = (do_shell ? eidx : i) + numEdges; ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; } else { ml[j].v = ed->v2; ml[j++].e = eidx; ml[j].v = ed->v1; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v1] + newEdges; ml[j].v = (do_shell ? ed->v1 : old_vert_arr[ed->v1]) + numVerts; ml[j++].e = (do_shell ? eidx : i) + numEdges; ml[j].v = (do_shell ? ed->v2 : old_vert_arr[ed->v2]) + numVerts; ml[j++].e = (numEdges * stride) + old_vert_arr[ed->v2] + newEdges; } origindex_edge[ml[j - 3].e] = ORIGINDEX_NONE; origindex_edge[ml[j - 1].e] = ORIGINDEX_NONE; /* use the next material index if option enabled */ if (mat_ofs_rim) { mp->mat_nr += mat_ofs_rim; CLAMP(mp->mat_nr, 0, mat_nr_max); } if (crease_outer) { /* crease += crease_outer; without wrapping */ char *cr = &(ed->crease); int tcr = *cr + crease_outer; *cr = tcr > 255 ? 255 : tcr; } if (crease_inner) { /* crease += crease_inner; without wrapping */ char *cr = &(medge[numEdges + (do_shell ? eidx : i)].crease); int tcr = *cr + crease_inner; *cr = tcr > 255 ? 255 : tcr; } #ifdef SOLIDIFY_SIDE_NORMALS if (do_side_normals) { normal_quad_v3(nor, mvert[ml[j - 4].v].co, mvert[ml[j - 3].v].co, mvert[ml[j - 2].v].co, mvert[ml[j - 1].v].co); add_v3_v3(edge_vert_nos[ed->v1], nor); add_v3_v3(edge_vert_nos[ed->v2], nor); } #endif } #ifdef SOLIDIFY_SIDE_NORMALS if (do_side_normals) { ed = medge + (numEdges * stride); for (i = 0; i < rimVerts; i++, ed++) { float nor_cpy[3]; short *nor_short; int k; /* note, only the first vertex (lower half of the index) is calculated */ normalize_v3_v3(nor_cpy, edge_vert_nos[ed->v1]); for (k = 0; k < 2; k++) { /* loop over both verts of the edge */ nor_short = mvert[*(&ed->v1 + k)].no; normal_short_to_float_v3(nor, nor_short); add_v3_v3(nor, nor_cpy); normalize_v3(nor); normal_float_to_short_v3(nor_short, nor); } } MEM_freeN(edge_vert_nos); } #endif MEM_freeN(new_vert_arr); MEM_freeN(new_edge_arr); MEM_freeN(edge_users); MEM_freeN(edge_order); } if (old_vert_arr) MEM_freeN(old_vert_arr); if (face_nors) MEM_freeN(face_nors); if (numFaces == 0 && numEdges != 0) { modifier_setError(md, "Faces needed for useful output"); } return result; }