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;
}
Example #2
0
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 = &lt->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;
}
Example #3
0
static int lattice_select_more_less(bContext *C, const bool select)
{
	Object *obedit = CTX_data_edit_object(C);
	Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
	BPoint *bp;
	const int tot = lt->pntsu * lt->pntsv * lt->pntsw;
	int u, v, w;
	BLI_bitmap *selpoints;

	lt->actbp = LT_ACTBP_NONE;

	selpoints = BLI_BITMAP_NEW(tot, __func__);
	BKE_lattice_bitmap_from_flag(lt, selpoints, SELECT, false, false);

	bp = lt->def;
	for (w = 0; w < lt->pntsw; w++) {
		for (v = 0; v < lt->pntsv; v++) {
			for (u = 0; u < lt->pntsu; u++) {
				if ((bp->hide == 0) && (((bp->f1 & SELECT) == 0) == select)) {
					if (lattice_test_bitmap_uvw(lt, selpoints, u + 1, v, w, select) ||
					    lattice_test_bitmap_uvw(lt, selpoints, u - 1, v, w, select) ||
					    lattice_test_bitmap_uvw(lt, selpoints, u, v + 1, w, select) ||
					    lattice_test_bitmap_uvw(lt, selpoints, u, v - 1, w, select) ||
					    lattice_test_bitmap_uvw(lt, selpoints, u, v, w + 1, select) ||
					    lattice_test_bitmap_uvw(lt, selpoints, u, v, w - 1, select))
					{
						BKE_BIT_TEST_SET(bp->f1, select, SELECT);
					}
				}
				bp++;
			}
		}
	}

	MEM_freeN(selpoints);

	WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
	return OPERATOR_FINISHED;
}
Example #4
0
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 = &lt->def[i];
		if (!bp->hide) {
			if (BLI_BITMAP_TEST(selpoints, i_flip)) {
				bp->f1 |= SELECT;
			}
			else {
				if (!extend) {
					bp->f1 &= ~SELECT;
				}
			}
		}
	}


	MEM_freeN(selpoints);
}
Example #5
0
static BLI_bitmap get_tface_mesh_marked_edge_info(Mesh *me)
{
	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++) {
				BLI_BITMAP_SET(bitmap_edge_flags, edge_vis_index(ml->e));
				if (select_set) BLI_BITMAP_SET(bitmap_edge_flags, edge_sel_index(ml->e));
			}
		}
	}

	return bitmap_edge_flags;
}
Example #6
0
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;
	}
}
Example #7
0
/* 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, NULL);
	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_MODIFY(gh, y * key.grid_size + x,
					                  action == PARTIALVIS_HIDE);

					any_changed = true;
				}

				/* keep track of whether any elements are still hidden */
				if (BLI_BITMAP_GET(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);
	}
}
/* 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;
}
Example #10
0
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);
}
Example #11
0
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);
}
Example #12
0
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);
}
Example #13
0
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;
}