Esempio n. 1
0
void BKE_defvert_extract_vgroup_to_polyweights(
    MDeformVert *dvert, const int defgroup, const int num_verts, MLoop *loops, const int UNUSED(num_loops),
    MPoly *polys, const int num_polys, float *r_weights, const bool invert_vgroup)
{
    if (dvert && defgroup != -1) {
        int i = num_polys;
        float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);

        BKE_defvert_extract_vgroup_to_vertweights(dvert, defgroup, num_verts, tmp_weights, invert_vgroup);

        while (i--) {
            MPoly *mp = &polys[i];
            MLoop *ml = &loops[mp->loopstart];
            int j = mp->totloop;
            float w = 0.0f;

            for (; j--; ml++) {
                w += tmp_weights[ml->v];
            }
            r_weights[i] = w / (float)mp->totloop;
        }

        MEM_freeN(tmp_weights);
    }
    else {
        fill_vn_fl(r_weights, num_polys, 0.0f);
    }
}
Esempio n. 2
0
float normalize_vn_vn(float *array_tar, const float *array_src, const int size)
{
	double d = len_squared_vn(array_src, size);
	float d_sqrt;
	if (d > 1.0e-35) {
		d_sqrt = (float)sqrt(d);
		mul_vn_vn_fl(array_tar, array_src, size, 1.0f / d_sqrt);
	}
	else {
		fill_vn_fl(array_tar, size, 0.0f);
		d_sqrt = 0.0f;
	}
	return d_sqrt;
}
Esempio n. 3
0
void BKE_defvert_extract_vgroup_to_vertweights(
    MDeformVert *dvert, const int defgroup, const int num_verts, float *r_weights, const bool invert_vgroup)
{
    if (dvert && defgroup != -1) {
        int i = num_verts;

        while (i--) {
            const float w = defvert_find_weight(&dvert[i], defgroup);
            r_weights[i] = invert_vgroup ? (1.0f - w) : w;
        }
    }
    else {
        fill_vn_fl(r_weights, num_verts, invert_vgroup ? 1.0f : 0.0f);
    }
}
Esempio n. 4
0
void BKE_defvert_extract_vgroup_to_edgeweights(
    MDeformVert *dvert, const int defgroup, const int num_verts, MEdge *edges, const int num_edges,
    float *r_weights, const bool invert_vgroup)
{
    if (dvert && defgroup != -1) {
        int i = num_edges;
        float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);

        BKE_defvert_extract_vgroup_to_vertweights(dvert, defgroup, num_verts, tmp_weights, invert_vgroup);

        while (i--) {
            MEdge *me = &edges[i];

            r_weights[i] = (tmp_weights[me->v1] + tmp_weights[me->v2]) * 0.5f;
        }

        MEM_freeN(tmp_weights);
    }
    else {
        fill_vn_fl(r_weights, num_edges, 0.0f);
    }
}
Esempio n. 5
0
void BKE_defvert_extract_vgroup_to_loopweights(
    MDeformVert *dvert, const int defgroup, const int num_verts, MLoop *loops, const int num_loops,
    float *r_weights, const bool invert_vgroup)
{
    if (dvert && defgroup != -1) {
        int i = num_loops;
        float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);

        BKE_defvert_extract_vgroup_to_vertweights(dvert, defgroup, num_verts, tmp_weights, invert_vgroup);

        while (i--) {
            MLoop *ml = &loops[i];

            r_weights[i] = tmp_weights[ml->v];
        }

        MEM_freeN(tmp_weights);
    }
    else {
        fill_vn_fl(r_weights, num_loops, 0.0f);
    }
}
Esempio n. 6
0
/* 
 * Use the libTIFF scanline API to read a TIFF image.
 * This method is most flexible and can handle multiple different bit depths 
 * and RGB channel orderings.
 */
static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image)
{
	ImBuf *tmpibuf;
	int success = 0;
	short bitspersample, spp, config;
	size_t scanline;
	int ib_flag = 0, row, chan;
	float *fbuf = NULL;
	unsigned short *sbuf = NULL;

	TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample);
	TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp);     /* number of 'channels' */
	TIFFGetField(image, TIFFTAG_PLANARCONFIG, &config);

	if (spp == 4) {
		/* HACK: this is really tricky hack, which is only needed to force libtiff
		 *       do not touch RGB channels when there's alpha channel present
		 *       The thing is: libtiff will premul RGB if alpha mode is set to
		 *       unassociated, which really conflicts with blender's assumptions
		 *
		 *       Alternative would be to unpremul after load, but it'll be really
		 *       lossy and unwanted behavior
		 *
		 *       So let's keep this thing here for until proper solution is found (sergey)
		 */

		unsigned short extraSampleTypes[1];
		extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA;
		TIFFSetField(image, TIFFTAG_EXTRASAMPLES, 1, extraSampleTypes);
	}

	imb_read_tiff_resolution(ibuf, image);

	scanline = TIFFScanlineSize(image);
	
	if (bitspersample == 32) {
		ib_flag = IB_rectfloat;
		fbuf = (float *)_TIFFmalloc(scanline);
	}
	else if (bitspersample == 16) {
		ib_flag = IB_rectfloat;
		sbuf = (unsigned short *)_TIFFmalloc(scanline);
	}
	else {
		ib_flag = IB_rect;
	}
	
	tmpibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ib_flag);
	
	/* simple RGBA image */
	if (!(bitspersample == 32 || bitspersample == 16)) {
		success |= TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0);
	}
	/* contiguous channels: RGBRGBRGB */
	else if (config == PLANARCONFIG_CONTIG) {
		for (row = 0; row < ibuf->y; row++) {
			int ib_offset = ibuf->x * ibuf->y * 4 - ibuf->x * 4 * (row + 1);
		
			if (bitspersample == 32) {
				success |= TIFFReadScanline(image, fbuf, row, 0);
				scanline_contig_32bit(tmpibuf->rect_float + ib_offset, fbuf, ibuf->x, spp);
				
			}
			else if (bitspersample == 16) {
				success |= TIFFReadScanline(image, sbuf, row, 0);
				scanline_contig_16bit(tmpibuf->rect_float + ib_offset, sbuf, ibuf->x, spp);
			}
		}
		/* separate channels: RRRGGGBBB */
	}
	else if (config == PLANARCONFIG_SEPARATE) {
		
		/* imbufs always have 4 channels of data, so we iterate over all of them
		 * but only fill in from the TIFF scanline where necessary. */
		for (chan = 0; chan < 4; chan++) {
			for (row = 0; row < ibuf->y; row++) {
				int ib_offset = ibuf->x * ibuf->y * 4 - ibuf->x * 4 * (row + 1);
				
				if (bitspersample == 32) {
					if (chan == 3 && spp == 3) /* fill alpha if only RGB TIFF */
						fill_vn_fl(fbuf, ibuf->x, 1.0f);
					else if (chan >= spp) /* for grayscale, duplicate first channel into G and B */
						success |= TIFFReadScanline(image, fbuf, row, 0);
					else
						success |= TIFFReadScanline(image, fbuf, row, chan);
					scanline_separate_32bit(tmpibuf->rect_float + ib_offset, fbuf, ibuf->x, chan);
					
				}
				else if (bitspersample == 16) {
					if (chan == 3 && spp == 3) /* fill alpha if only RGB TIFF */
						fill_vn_ushort(sbuf, ibuf->x, 65535);
					else if (chan >= spp) /* for grayscale, duplicate first channel into G and B */
						success |= TIFFReadScanline(image, fbuf, row, 0);
					else
						success |= TIFFReadScanline(image, sbuf, row, chan);
					scanline_separate_16bit(tmpibuf->rect_float + ib_offset, sbuf, ibuf->x, chan);
					
				}
			}
		}
	}
	
	if (bitspersample == 32)
		_TIFFfree(fbuf);
	else if (bitspersample == 16)
		_TIFFfree(sbuf);

	if (success) {
		/* Code seems to be not needed for 16 bits tif, on PPC G5 OSX (ton) */
		if (bitspersample < 16)
			if (ENDIAN_ORDER == B_ENDIAN)
				IMB_convert_rgba_to_abgr(tmpibuf);
		
		/* assign rect last */
		if (tmpibuf->rect_float)
			ibuf->rect_float = tmpibuf->rect_float;
		else
			ibuf->rect = tmpibuf->rect;
		ibuf->mall |= ib_flag;
		ibuf->flags |= ib_flag;
		
		tmpibuf->mall &= ~ib_flag;
	}

	IMB_freeImBuf(tmpibuf);
	
	return success;
}
Esempio n. 7
0
/* 
 * Use the libTIFF scanline API to read a TIFF image.
 * This method is most flexible and can handle multiple different bit depths 
 * and RGB channel orderings.
 */
static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image, int premul)
{
	ImBuf *tmpibuf;
	int success= 0;
	short bitspersample, spp, config;
	size_t scanline;
	int ib_flag=0, row, chan;
	float *fbuf=NULL;
	unsigned short *sbuf=NULL;

	TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample);
	TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp);		/* number of 'channels' */
	TIFFGetField(image, TIFFTAG_PLANARCONFIG, &config);

	imb_read_tiff_resolution(ibuf, image);

	scanline = TIFFScanlineSize(image);
	
	if (bitspersample == 32) {
		ib_flag = IB_rectfloat;
		fbuf = (float *)_TIFFmalloc(scanline);
	}
	else if (bitspersample == 16) {
		ib_flag = IB_rectfloat;
		sbuf = (unsigned short *)_TIFFmalloc(scanline);
	}
	else {
		ib_flag = IB_rect;
	}
	
	tmpibuf= IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ib_flag);
	
	/* simple RGBA image */
	if (!(bitspersample == 32 || bitspersample == 16)) {
		success |= TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0);
	}
	/* contiguous channels: RGBRGBRGB */
	else if (config == PLANARCONFIG_CONTIG) {
		for (row = 0; row < ibuf->y; row++) {
			int ib_offset = ibuf->x*ibuf->y*4 - ibuf->x*4 * (row+1);
		
			if (bitspersample == 32) {
				success |= TIFFReadScanline(image, fbuf, row, 0);
				scanline_contig_32bit(tmpibuf->rect_float+ib_offset, fbuf, ibuf->x, spp);
				
			}
			else if (bitspersample == 16) {
				success |= TIFFReadScanline(image, sbuf, row, 0);
				scanline_contig_16bit(tmpibuf->rect_float+ib_offset, sbuf, ibuf->x, spp);
			}
		}
	/* separate channels: RRRGGGBBB */
	}
	else if (config == PLANARCONFIG_SEPARATE) {
		
		/* imbufs always have 4 channels of data, so we iterate over all of them
		 * but only fill in from the TIFF scanline where necessary. */
		for (chan = 0; chan < 4; chan++) {
			for (row = 0; row < ibuf->y; row++) {
				int ib_offset = ibuf->x*ibuf->y*4 - ibuf->x*4 * (row+1);
				
				if (bitspersample == 32) {
					if (chan == 3 && spp == 3) /* fill alpha if only RGB TIFF */
						fill_vn_fl(fbuf, ibuf->x, 1.0f);
					else if (chan >= spp) /* for grayscale, duplicate first channel into G and B */
						success |= TIFFReadScanline(image, fbuf, row, 0);
					else
						success |= TIFFReadScanline(image, fbuf, row, chan);
					scanline_separate_32bit(tmpibuf->rect_float+ib_offset, fbuf, ibuf->x, chan);
					
				}
				else if (bitspersample == 16) {
					if (chan == 3 && spp == 3) /* fill alpha if only RGB TIFF */
						fill_vn_ushort(sbuf, ibuf->x, 65535);
					else if (chan >= spp) /* for grayscale, duplicate first channel into G and B */
						success |= TIFFReadScanline(image, fbuf, row, 0);
					else
						success |= TIFFReadScanline(image, sbuf, row, chan);
					scanline_separate_16bit(tmpibuf->rect_float+ib_offset, sbuf, ibuf->x, chan);
					
				}
			}
		}
	}
	
	if (bitspersample == 32)
		_TIFFfree(fbuf);
	else if (bitspersample == 16)
		_TIFFfree(sbuf);

	if (success) {
		ibuf->profile = (bitspersample==32)?IB_PROFILE_LINEAR_RGB:IB_PROFILE_SRGB;

//		Code seems to be not needed for 16 bits tif, on PPC G5 OSX (ton)
		if (bitspersample < 16)
			if (ENDIAN_ORDER == B_ENDIAN)
				IMB_convert_rgba_to_abgr(tmpibuf);
		if (premul) {
			IMB_premultiply_alpha(tmpibuf);
			ibuf->flags |= IB_premul;
		}
		
		/* assign rect last */
		if (tmpibuf->rect_float)
			ibuf->rect_float= tmpibuf->rect_float;
		else	
			ibuf->rect= tmpibuf->rect;
		ibuf->mall |= ib_flag;
		ibuf->flags |= ib_flag;
		
		tmpibuf->mall &= ~ib_flag;
	}

	IMB_freeImBuf(tmpibuf);
	
	return success;
}
Esempio n. 8
0
static DerivedMesh *applyModifier(
        ModifierData *md, Object *ob,
        DerivedMesh *dm,
        ModifierApplyFlag UNUSED(flag))
{
	unsigned int i;
	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;

	/* 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);

	/* weights */
	MDeformVert *dvert, *dv = NULL;
	const int defgrp_invert = ((smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0);
	int defgrp_index;

	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, (int)numVerts,
		            orig_mloop, orig_mpoly,
		            (int)numLoops, (int)numFaces,
		            face_nors, true);
	}

	STACK_INIT(new_vert_arr);
	STACK_INIT(new_edge_arr);

	if (smd->flag & MOD_SOLIDIFY_RIM) {
		BLI_bitmap *orig_mvert_tag = BLI_BITMAP_NEW(numVerts, __func__);
		unsigned int eidx;

#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
		fill_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_SET(orig_mvert_tag, ed->v1);
				BLI_BITMAP_SET(orig_mvert_tag, ed->v2);
				STACK_PUSH(new_edge_arr, eidx);
				newFaces++;
				newLoops += 4;
			}
		}

#undef INVALID_UNUSED
#undef INVALID_PAIR

		for (i = 0; i < numVerts; i++) {
			if (BLI_BITMAP_GET(orig_mvert_tag, i)) {
				old_vert_arr[i] = STACK_SIZE(new_vert_arr);
				STACK_PUSH(new_vert_arr, i);
				newEdges++;
			}
		}

		MEM_freeN(orig_mvert_tag);
	}

	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 * 2),
	                            (int)((numEdges * 2) + newEdges), 0,
	                            (int)((numLoops * 2) + newLoops),
	                            (int)((numFaces * 2) + newFaces));

	mpoly = CDDM_get_polys(result);
	mloop = CDDM_get_loops(result);
	medge = CDDM_get_edges(result);
	mvert = CDDM_get_verts(result);

	DM_copy_edge_data(dm, result, 0, 0, (int)numEdges);
	DM_copy_edge_data(dm, result, 0, (int)numEdges, (int)numEdges);

	DM_copy_vert_data(dm, result, 0, 0, (int)numVerts);
	DM_copy_vert_data(dm, result, 0, (int)numVerts, (int)numVerts);

	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);

	/* flip normals */
	mp = mpoly + numFaces;
	for (i = 0; i < dm->numPolyData; i++, mp++) {
		MLoop *ml2;
		unsigned int e;
		int j;

		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) {
			vert_lens = MEM_mallocN(sizeof(float) * numVerts, "vert_lens");
			fill_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) {
			scalar_short = scalar_short_vgroup = ofs_new / 32767.0f;
			mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? numVerts : 0);
			dv = dvert;
			for (i = 0; i < numVerts; i++, mv++) {
				if (dv) {
					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;
					dv++;
				}
				if (do_clamp) {
					/* always reset becaise we may have set before */
					if (dv == 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) {
			scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f;
			mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? 0 : numVerts); /* as above but swapped */
			dv = dvert;
			for (i = 0; i < numVerts; i++, mv++) {
				if (dv) {
					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;
					dv++;
				}
				if (do_clamp) {
					/* always reset becaise we may have set before */
					if (dv == 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;

		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_angle_to_dist(angle_normalized_v3v3(vert_nors[vidx], face_nors[i])) * angle;
				}
				else {
					vert_angles[vidx] += angle;
				}
#else
				vert_angles[vidx] += shell_angle_to_dist(angle_normalized_v3v3(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) {
			float scalar;

			dv = dvert;
			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_callocN(sizeof(float) * numVerts, "vert_lens");
			const float offset    = fabsf(smd->offset) * smd->offset_clamp;
			const float offset_sq = offset * offset;
			fill_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) {
			mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? numVerts : 0);

			for (i = 0; i < numVerts; i++, mv++) {
				if (vert_accum[i]) { /* zero if unselected */
					madd_v3_v3fl(mv->co, vert_nors[i], ofs_new * (vert_angles[i] / vert_accum[i]));
				}
			}
		}

		if (ofs_orig) {
			/* same as above but swapped, intentional use of 'ofs_new' */
			mv = mvert + (((ofs_new >= ofs_orig) == do_flip) ? 0 : numVerts);

			for (i = 0; i < numVerts; i++, mv++) {
				if (vert_accum[i]) { /* zero if unselected */
					madd_v3_v3fl(mv->co, vert_nors[i], ofs_orig * (vert_angles[i] / vert_accum[i]));
				}
			}
		}

		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 {
		/* 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) {

		/* 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 * 2];
		orig_ed = &origindex_edge[numEdges * 2];
		for (i = 0; i < newEdges; i++, ed++, orig_ed++) {
			ed->v1 = new_vert_arr[i];
			ed->v2 = new_vert_arr[i] + numVerts;
			ed->flag |= ME_EDGEDRAW;

			*orig_ed = ORIGINDEX_NONE;

			if (crease_rim) {
				ed->crease = crease_rim;
			}
		}

		/* faces */
		mp = mpoly + (numFaces * 2);
		ml = mloop + (numLoops * 2);
		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 * 2) + i), 1);
			mp->loopstart = (int)(j + numLoops * 2);
			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 * 2 + j + 0), 1);
			CustomData_copy_data(&dm->loopData, &result->loopData, k1, (int)(numLoops * 2 + j + 1), 1);
			CustomData_copy_data(&dm->loopData, &result->loopData, k1, (int)(numLoops * 2 + j + 2), 1);
			CustomData_copy_data(&dm->loopData, &result->loopData, k2, (int)(numLoops * 2 + j + 3), 1);

			if (flip == FALSE) {
				ml[j].v = ed->v1;
				ml[j++].e = eidx;

				ml[j].v = ed->v2;
				ml[j++].e = numEdges * 2 + old_vert_arr[ed->v2];

				ml[j].v = ed->v2 + numVerts;
				ml[j++].e = eidx + numEdges;

				ml[j].v = ed->v1 + numVerts;
				ml[j++].e = numEdges * 2 + old_vert_arr[ed->v1];
			}
			else {
				ml[j].v = ed->v2;
				ml[j++].e = eidx;

				ml[j].v = ed->v1;
				ml[j++].e = numEdges * 2 + old_vert_arr[ed->v1];

				ml[j].v = ed->v1 + numVerts;
				ml[j++].e = eidx + numEdges;

				ml[j].v = ed->v2 + numVerts;
				ml[j++].e = numEdges * 2 + old_vert_arr[ed->v2];
			}

			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 */
				unsigned char *cr = (unsigned char *)&(ed->crease);
				int tcr = *cr + crease_outer;
				*cr = tcr > 255 ? 255 : tcr;
			}

			if (crease_inner) {
				/* crease += crease_inner; without wrapping */
				unsigned char *cr = (unsigned char *)&(medge[numEdges + eidx].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 * 2);
			for (i = 0; i < newEdges; 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);
	}

	STACK_FREE(new_vert_arr);
	STACK_FREE(new_edge_arr);

	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;
}