void heat_bone_weighting(Object *ob, Mesh *me, float (*verts)[3], int numsource,
                         bDeformGroup **dgrouplist, bDeformGroup **dgroupflip,
                         float (*root)[3], float (*tip)[3], int *selected, const char **err_str)
{
	LaplacianSystem *sys;
	MLoopTri *mlooptri;
	MPoly *mp;
	MLoop *ml;
	float solution, weight;
	int *vertsflipped = NULL, *mask = NULL;
	int a, tottri, j, bbone, firstsegment, lastsegment;
	bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;

	MVert *mvert = me->mvert;
	bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
	bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;

	*err_str = NULL;

	/* bone heat needs triangulated faces */
	tottri = poly_to_tri_count(me->totpoly, me->totloop);

	/* count triangles and create mask */
	if (ob->mode & OB_MODE_WEIGHT_PAINT &&
	    (use_face_sel || use_vert_sel))
	{
		mask = MEM_callocN(sizeof(int) * me->totvert, "heat_bone_weighting mask");

		/*  (added selectedVerts content for vertex mask, they used to just equal 1) */
		if (use_vert_sel) {
			for (a = 0, mp = me->mpoly; a < me->totpoly; mp++, a++) {
				for (j = 0, ml = me->mloop + mp->loopstart; j < mp->totloop; j++, ml++) {
					mask[ml->v] = (mvert[ml->v].flag & SELECT) != 0;
				}
			}
		}
		else if (use_face_sel) {
			for (a = 0, mp = me->mpoly; a < me->totpoly; mp++, a++) {
				if (mp->flag & ME_FACE_SEL) {
					for (j = 0, ml = me->mloop + mp->loopstart; j < mp->totloop; j++, ml++) {
						mask[ml->v] = 1;
					}
				}
			}
		}
	}

	/* create laplacian */
	sys = laplacian_system_construct_begin(me->totvert, tottri, 1);

	sys->heat.tottri = poly_to_tri_count(me->totpoly, me->totloop);
	mlooptri = MEM_mallocN(sizeof(*sys->heat.mlooptri) * sys->heat.tottri, __func__);

	BKE_mesh_recalc_looptri(
	        me->mloop, me->mpoly,
	        me->mvert,
	        me->totloop, me->totpoly,
	        mlooptri);

	sys->heat.mlooptri = mlooptri;
	sys->heat.mloop = me->mloop;
	sys->heat.totvert = me->totvert;
	sys->heat.verts = verts;
	sys->heat.root = root;
	sys->heat.tip = tip;
	sys->heat.numsource = numsource;

	heat_ray_tree_create(sys);
	heat_laplacian_create(sys);

	laplacian_system_construct_end(sys);

	if (dgroupflip) {
		vertsflipped = MEM_callocN(sizeof(int) * me->totvert, "vertsflipped");
		for (a = 0; a < me->totvert; a++)
			vertsflipped[a] = mesh_get_x_mirror_vert(ob, NULL, a, use_topology);
	}
	
	/* compute weights per bone */
	for (j = 0; j < numsource; j++) {
		if (!selected[j])
			continue;

		firstsegment = (j == 0 || dgrouplist[j - 1] != dgrouplist[j]);
		lastsegment = (j == numsource - 1 || dgrouplist[j] != dgrouplist[j + 1]);
		bbone = !(firstsegment && lastsegment);

		/* clear weights */
		if (bbone && firstsegment) {
			for (a = 0; a < me->totvert; a++) {
				if (mask && !mask[a])
					continue;

				ED_vgroup_vert_remove(ob, dgrouplist[j], a);
				if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0)
					ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
			}
		}

		/* fill right hand side */
		laplacian_begin_solve(sys, -1);

		for (a = 0; a < me->totvert; a++)
			if (heat_source_closest(sys, a, j))
				laplacian_add_right_hand_side(sys, a,
				                              sys->heat.H[a] * sys->heat.p[a]);

		/* solve */
		if (laplacian_system_solve(sys)) {
			/* load solution into vertex groups */
			for (a = 0; a < me->totvert; a++) {
				if (mask && !mask[a])
					continue;

				solution = laplacian_system_get_solution(sys, a);
				
				if (bbone) {
					if (solution > 0.0f)
						ED_vgroup_vert_add(ob, dgrouplist[j], a, solution,
						                   WEIGHT_ADD);
				}
				else {
					weight = heat_limit_weight(solution);
					if (weight > 0.0f)
						ED_vgroup_vert_add(ob, dgrouplist[j], a, weight,
						                   WEIGHT_REPLACE);
					else
						ED_vgroup_vert_remove(ob, dgrouplist[j], a);
				}

				/* do same for mirror */
				if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
					if (bbone) {
						if (solution > 0.0f)
							ED_vgroup_vert_add(ob, dgroupflip[j], vertsflipped[a],
							                   solution, WEIGHT_ADD);
					}
					else {
						weight = heat_limit_weight(solution);
						if (weight > 0.0f)
							ED_vgroup_vert_add(ob, dgroupflip[j], vertsflipped[a],
							                   weight, WEIGHT_REPLACE);
						else
							ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
					}
				}
			}
		}
		else if (*err_str == NULL) {
			*err_str = N_("Bone Heat Weighting: failed to find solution for one or more bones");
			break;
		}

		/* remove too small vertex weights */
		if (bbone && lastsegment) {
			for (a = 0; a < me->totvert; a++) {
				if (mask && !mask[a])
					continue;

				weight = ED_vgroup_vert_weight(ob, dgrouplist[j], a);
				weight = heat_limit_weight(weight);
				if (weight <= 0.0f)
					ED_vgroup_vert_remove(ob, dgrouplist[j], a);

				if (vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
					weight = ED_vgroup_vert_weight(ob, dgroupflip[j], vertsflipped[a]);
					weight = heat_limit_weight(weight);
					if (weight <= 0.0f)
						ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
				}
			}
		}
	}

	/* free */
	if (vertsflipped) MEM_freeN(vertsflipped);
	if (mask) MEM_freeN(mask);

	heat_system_free(sys);

	laplacian_system_delete(sys);
}
Esempio n. 2
0
void heat_bone_weighting(Object *ob, Mesh *me, float (*verts)[3], int numsource, bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], int *selected, const char **err_str)
{
	LaplacianSystem *sys;
	MFace *mface;
	float solution, weight;
	int *vertsflipped = NULL, *mask= NULL;
	int a, totface, j, bbone, firstsegment, lastsegment;

	*err_str= NULL;

	/* count triangles and create mask */
	if(me->editflag & ME_EDIT_PAINT_MASK)
		mask= MEM_callocN(sizeof(int)*me->totvert, "heat_bone_weighting mask");

	for(totface=0, a=0, mface=me->mface; a<me->totface; a++, mface++) {
		totface++;
		if(mface->v4) totface++;

		if(mask && (mface->flag & ME_FACE_SEL)) {
			mask[mface->v1]= 1;
			mask[mface->v2]= 1;
			mask[mface->v3]= 1;
			if(mface->v4)
				mask[mface->v4]= 1;
		}
	}

	/* create laplacian */
	sys = laplacian_system_construct_begin(me->totvert, totface, 1);

	sys->heat.mface= me->mface;
	sys->heat.totface= me->totface;
	sys->heat.totvert= me->totvert;
	sys->heat.verts= verts;
	sys->heat.root= root;
	sys->heat.tip= tip;
	sys->heat.numsource= numsource;

	heat_ray_tree_create(sys);
	heat_laplacian_create(sys);

	laplacian_system_construct_end(sys);

	if(dgroupflip) {
		vertsflipped = MEM_callocN(sizeof(int)*me->totvert, "vertsflipped");
		for(a=0; a<me->totvert; a++)
			vertsflipped[a] = mesh_get_x_mirror_vert(ob, a);
	}

	/* compute weights per bone */
	for(j=0; j<numsource; j++) {
		if(!selected[j])
			continue;

		firstsegment= (j == 0 || dgrouplist[j-1] != dgrouplist[j]);
		lastsegment= (j == numsource-1 || dgrouplist[j] != dgrouplist[j+1]);
		bbone= !(firstsegment && lastsegment);

		/* clear weights */
		if(bbone && firstsegment) {
			for(a=0; a<me->totvert; a++) {
				if(mask && !mask[a])
					continue;

				ED_vgroup_vert_remove(ob, dgrouplist[j], a);
				if(vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0)
					ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
			}
		}

		/* fill right hand side */
		laplacian_begin_solve(sys, -1);

		for(a=0; a<me->totvert; a++)
			if(heat_source_closest(sys, a, j))
				laplacian_add_right_hand_side(sys, a,
					sys->heat.H[a]*sys->heat.p[a]);

		/* solve */
		if(laplacian_system_solve(sys)) {
			/* load solution into vertex groups */
			for(a=0; a<me->totvert; a++) {
				if(mask && !mask[a])
					continue;

				solution= laplacian_system_get_solution(a);
				
				if(bbone) {
					if(solution > 0.0f)
						ED_vgroup_vert_add(ob, dgrouplist[j], a, solution,
							WEIGHT_ADD);
				}
				else {
					weight= heat_limit_weight(solution);
					if(weight > 0.0f)
						ED_vgroup_vert_add(ob, dgrouplist[j], a, weight,
							WEIGHT_REPLACE);
					else
						ED_vgroup_vert_remove(ob, dgrouplist[j], a);
				}

				/* do same for mirror */
				if(vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
					if(bbone) {
						if(solution > 0.0f)
							ED_vgroup_vert_add(ob, dgroupflip[j], vertsflipped[a],
								solution, WEIGHT_ADD);
					}
					else {
						weight= heat_limit_weight(solution);
						if(weight > 0.0f)
							ED_vgroup_vert_add(ob, dgroupflip[j], vertsflipped[a],
								weight, WEIGHT_REPLACE);
						else
							ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
					}
				}
			}
		}
		else if(*err_str == NULL) {
			*err_str= "Bone Heat Weighting: failed to find solution for one or more bones";
			break;
		}

		/* remove too small vertex weights */
		if(bbone && lastsegment) {
			for(a=0; a<me->totvert; a++) {
				if(mask && !mask[a])
					continue;

				weight= ED_vgroup_vert_weight(ob, dgrouplist[j], a);
				weight= heat_limit_weight(weight);
				if(weight <= 0.0f)
					ED_vgroup_vert_remove(ob, dgrouplist[j], a);

				if(vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
					weight= ED_vgroup_vert_weight(ob, dgroupflip[j], vertsflipped[a]);
					weight= heat_limit_weight(weight);
					if(weight <= 0.0f)
						ED_vgroup_vert_remove(ob, dgroupflip[j], vertsflipped[a]);
				}
			}
		}
	}

	/* free */
	if(vertsflipped) MEM_freeN(vertsflipped);
	if(mask) MEM_freeN(mask);

	heat_system_free(sys);

	laplacian_system_delete(sys);
}