예제 #1
0
/* Main shrinkwrap function */
void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd, Object *ob, DerivedMesh *dm, float (*vertexCos)[3], int numVerts)
{

	DerivedMesh *ss_mesh	= NULL;
	ShrinkwrapCalcData calc = NULL_ShrinkwrapCalcData;

	//remove loop dependencies on derived meshs (TODO should this be done elsewhere?)
	if (smd->target == ob) smd->target = NULL;
	if (smd->auxTarget == ob) smd->auxTarget = NULL;


	//Configure Shrinkwrap calc data
	calc.smd = smd;
	calc.ob = ob;
	calc.numVerts = numVerts;
	calc.vertexCos = vertexCos;

	//DeformVertex
	calc.vgroup = defgroup_name_index(calc.ob, calc.smd->vgroup_name);
	if (dm) {
		calc.dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
	}
	else if (calc.ob->type == OB_LATTICE) {
		calc.dvert = BKE_lattice_deform_verts_get(calc.ob);
	}


	if (smd->target) {
		calc.target = object_get_derived_final(smd->target);

		//TODO there might be several "bugs" on non-uniform scales matrixs
		//because it will no longer be nearest surface, not sphere projection
		//because space has been deformed
		space_transform_setup(&calc.local2target, ob, smd->target);

		//TODO: smd->keepDist is in global units.. must change to local
		calc.keepDist = smd->keepDist;
	}



	calc.vgroup = defgroup_name_index(calc.ob, smd->vgroup_name);

	if (dm != NULL && smd->shrinkType == MOD_SHRINKWRAP_PROJECT) {
		//Setup arrays to get vertexs positions, normals and deform weights
		calc.vert   = dm->getVertDataArray(dm, CD_MVERT);
		calc.dvert  = dm->getVertDataArray(dm, CD_MDEFORMVERT);

		//Using vertexs positions/normals as if a subsurface was applied 
		if (smd->subsurfLevels) {
			SubsurfModifierData ssmd= {{NULL}};
			ssmd.subdivType	= ME_CC_SUBSURF;		//catmull clark
			ssmd.levels		= smd->subsurfLevels;	//levels

			ss_mesh = subsurf_make_derived_from_derived(dm, &ssmd, FALSE, NULL, 0, 0, (ob->mode & OB_MODE_EDIT));

			if (ss_mesh) {
				calc.vert = ss_mesh->getVertDataArray(ss_mesh, CD_MVERT);
				if (calc.vert) {
					/* TRICKY: this code assumes subsurface will have the transformed original vertices
					 * in their original order at the end of the vert array. */
					calc.vert = calc.vert + ss_mesh->getNumVerts(ss_mesh) - dm->getNumVerts(dm);
				}
			}

			//Just to make sure we are not leaving any memory behind
			assert(ssmd.emCache == NULL);
			assert(ssmd.mCache == NULL);
		}
	}

	//Projecting target defined - lets work!
	if (calc.target) {
		switch (smd->shrinkType) {
			case MOD_SHRINKWRAP_NEAREST_SURFACE:
				BENCH(shrinkwrap_calc_nearest_surface_point(&calc));
			break;

			case MOD_SHRINKWRAP_PROJECT:
				BENCH(shrinkwrap_calc_normal_projection(&calc));
			break;

			case MOD_SHRINKWRAP_NEAREST_VERTEX:
				BENCH(shrinkwrap_calc_nearest_vertex(&calc));
			break;
		}
	}

	//free memory
	if (ss_mesh)
		ss_mesh->release(ss_mesh);
}
static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData,
                                  ModifierApplyFlag UNUSED(flag))
{
	WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *) md;
	DerivedMesh *dm = derivedData;
	MDeformVert *dvert = NULL;
	MDeformWeight **dw, **tdw;
	int numVerts;
	float (*v_cos)[3] = NULL; /* The vertices coordinates. */
	Object *obr = NULL; /* Our target object. */
	int defgrp_idx;
	float *tw = NULL;
	float *org_w = NULL;
	float *new_w = NULL;
	int *tidx, *indices = NULL;
	int numIdx = 0;
	int i;
	/* Flags. */
#if 0
	int do_prev = (wmd->modifier.mode & eModifierMode_DoWeightPreview);
#endif

#if DO_PROFILE
	TIMEIT_START(perf)
#endif

	/* Get number of verts. */
	numVerts = dm->getNumVerts(dm);

	/* Check if we can just return the original mesh.
	 * Must have verts and therefore verts assigned to vgroups to do anything useful!
	 */
	if ((numVerts == 0) || (ob->defbase.first == NULL))
		return dm;

	/* Get our target object. */
	obr = wmd->proximity_ob_target;
	if (obr == NULL)
		return dm;

	/* Get vgroup idx from its name. */
	defgrp_idx = defgroup_name_index(ob, wmd->defgrp_name);
	if (defgrp_idx < 0)
		return dm;

	dvert = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MDEFORMVERT, numVerts);
	/* If no vertices were ever added to an object's vgroup, dvert might be NULL.
	 * As this modifier never add vertices to vgroup, just return. */
	if (!dvert)
		return dm;

	/* Find out which vertices to work on (all vertices in vgroup), and get their relevant weight.
	 */
	tidx = MEM_mallocN(sizeof(int) * numVerts, "WeightVGProximity Modifier, tidx");
	tw = MEM_mallocN(sizeof(float) * numVerts, "WeightVGProximity Modifier, tw");
	tdw = MEM_mallocN(sizeof(MDeformWeight *) * numVerts, "WeightVGProximity Modifier, tdw");
	for (i = 0; i < numVerts; i++) {
		MDeformWeight *_dw = defvert_find_index(&dvert[i], defgrp_idx);
		if (_dw) {
			tidx[numIdx] = i;
			tw[numIdx] = _dw->weight;
			tdw[numIdx++] = _dw;
		}
	}
	/* If no vertices found, return org data! */
	if (numIdx == 0) {
		MEM_freeN(tidx);
		MEM_freeN(tw);
		MEM_freeN(tdw);
		return dm;
	}
	if (numIdx != numVerts) {
		indices = MEM_mallocN(sizeof(int) * numIdx, "WeightVGProximity Modifier, indices");
		memcpy(indices, tidx, sizeof(int) * numIdx);
		org_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, org_w");
		memcpy(org_w, tw, sizeof(float) * numIdx);
		dw = MEM_mallocN(sizeof(MDeformWeight *) * numIdx, "WeightVGProximity Modifier, dw");
		memcpy(dw, tdw, sizeof(MDeformWeight *) * numIdx);
		MEM_freeN(tw);
		MEM_freeN(tdw);
	}
	else {
		org_w = tw;
		dw = tdw;
	}
	new_w = MEM_mallocN(sizeof(float) * numIdx, "WeightVGProximity Modifier, new_w");
	MEM_freeN(tidx);

	/* Get our vertex coordinates. */
	v_cos = MEM_mallocN(sizeof(float[3]) * numIdx, "WeightVGProximity Modifier, v_cos");
	if (numIdx != numVerts) {
		/* XXX In some situations, this code can be up to about 50 times more performant
		 *     than simply using getVertCo for each affected vertex...
		 */
		float (*tv_cos)[3] = MEM_mallocN(sizeof(float[3]) * numVerts, "WeightVGProximity Modifier, tv_cos");
		dm->getVertCos(dm, tv_cos);
		for (i = 0; i < numIdx; i++)
			copy_v3_v3(v_cos[i], tv_cos[indices[i]]);
		MEM_freeN(tv_cos);
	}
	else
		dm->getVertCos(dm, v_cos);

	/* Compute wanted distances. */
	if (wmd->proximity_mode == MOD_WVG_PROXIMITY_OBJECT) {
		const float dist = get_ob2ob_distance(ob, obr);
		for (i = 0; i < numIdx; i++)
			new_w[i] = dist;
	}
	else if (wmd->proximity_mode == MOD_WVG_PROXIMITY_GEOMETRY) {
		const short use_trgt_verts = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_VERTS);
		const short use_trgt_edges = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_EDGES);
		const short use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES);

		if (use_trgt_verts || use_trgt_edges || use_trgt_faces) {
			DerivedMesh *target_dm = obr->derivedFinal;
			short free_target_dm = FALSE;
			if (!target_dm) {
				if (ELEM3(obr->type, OB_CURVE, OB_SURF, OB_FONT))
					target_dm = CDDM_from_curve(obr);
				else if (obr->type == OB_MESH) {
					Mesh *me = (Mesh *)obr->data;
					if (me->edit_btmesh)
						target_dm = CDDM_from_BMEditMesh(me->edit_btmesh, me, FALSE, FALSE);
					else
						target_dm = CDDM_from_mesh(me, obr);
				}
				free_target_dm = TRUE;
			}

			/* We must check that we do have a valid target_dm! */
			if (target_dm) {
				SpaceTransform loc2trgt;
				float *dists_v = use_trgt_verts ? MEM_mallocN(sizeof(float) * numIdx, "dists_v") : NULL;
				float *dists_e = use_trgt_edges ? MEM_mallocN(sizeof(float) * numIdx, "dists_e") : NULL;
				float *dists_f = use_trgt_faces ? MEM_mallocN(sizeof(float) * numIdx, "dists_f") : NULL;

				space_transform_setup(&loc2trgt, ob, obr);
				get_vert2geom_distance(numIdx, v_cos, dists_v, dists_e, dists_f,
				                       target_dm, &loc2trgt);
				for (i = 0; i < numIdx; i++) {
					new_w[i] = dists_v ? dists_v[i] : FLT_MAX;
					if (dists_e)
						new_w[i] = minf(dists_e[i], new_w[i]);
					if (dists_f)
						new_w[i] = minf(dists_f[i], new_w[i]);
				}
				if (free_target_dm) target_dm->release(target_dm);
				if (dists_v) MEM_freeN(dists_v);
				if (dists_e) MEM_freeN(dists_e);
				if (dists_f) MEM_freeN(dists_f);
			}
			/* Else, fall back to default obj2vert behavior. */
			else {
				get_vert2ob_distance(numIdx, v_cos, new_w, ob, obr);
			}
		}
		else {
			get_vert2ob_distance(numIdx, v_cos, new_w, ob, obr);
		}
	}

	/* Map distances to weights. */
	do_map(new_w, numIdx, wmd->min_dist, wmd->max_dist, wmd->falloff_type);

	/* Do masking. */
	weightvg_do_mask(numIdx, indices, org_w, new_w, ob, dm, wmd->mask_constant,
	                 wmd->mask_defgrp_name, wmd->modifier.scene, wmd->mask_texture,
	                 wmd->mask_tex_use_channel, wmd->mask_tex_mapping,
	                 wmd->mask_tex_map_obj, wmd->mask_tex_uvlayer_name);

	/* Update vgroup. Note we never add nor remove vertices from vgroup here. */
	weightvg_update_vg(dvert, defgrp_idx, dw, numIdx, indices, org_w, FALSE, 0.0f, FALSE, 0.0f);

	/* If weight preview enabled... */
#if 0 /* XXX Currently done in mod stack :/ */
	if (do_prev)
		DM_update_weight_mcol(ob, dm, 0, org_w, numIdx, indices);
#endif

	/* Freeing stuff. */
	MEM_freeN(org_w);
	MEM_freeN(new_w);
	MEM_freeN(dw);
	if (indices)
		MEM_freeN(indices);
	MEM_freeN(v_cos);

#if DO_PROFILE
	TIMEIT_END(perf)
#endif

	/* Return the vgroup-modified mesh. */
	return dm;
}
예제 #3
0
static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc)
{
	int i;

	//Options about projection direction
	const char use_normal	= calc->smd->shrinkOpts;
	float proj_axis[3]		= {0.0f, 0.0f, 0.0f};

	//Raycast and tree stuff
	BVHTreeRayHit hit;
	BVHTreeFromMesh treeData= NULL_BVHTreeFromMesh;

	//auxiliary target
	DerivedMesh *auxMesh	= NULL;
	BVHTreeFromMesh auxData	= NULL_BVHTreeFromMesh;
	SpaceTransform local2aux;

	//If the user doesn't allows to project in any direction of projection axis
	//then theres nothing todo.
	if ((use_normal & (MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR | MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR)) == 0)
		return;


	//Prepare data to retrieve the direction in which we should project each vertex
	if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
		if (calc->vert == NULL) return;
	}
	else {
		//The code supports any axis that is a combination of X,Y,Z
		//although currently UI only allows to set the 3 different axis
		if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_X_AXIS) proj_axis[0] = 1.0f;
		if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Y_AXIS) proj_axis[1] = 1.0f;
		if (calc->smd->projAxis & MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS) proj_axis[2] = 1.0f;

		normalize_v3(proj_axis);

		//Invalid projection direction
		if (dot_v3v3(proj_axis, proj_axis) < FLT_EPSILON)
			return; 
	}

	if (calc->smd->auxTarget) {
		auxMesh = object_get_derived_final(calc->smd->auxTarget);
		if (!auxMesh)
			return;
		space_transform_setup(&local2aux, calc->ob, calc->smd->auxTarget);
	}

	//After sucessufuly build the trees, start projection vertexs
	if (bvhtree_from_mesh_faces(&treeData, calc->target, 0.0, 4, 6) &&
	    (auxMesh == NULL || bvhtree_from_mesh_faces(&auxData, auxMesh, 0.0, 4, 6)))
	{

#ifndef __APPLE__
#pragma omp parallel for private(i,hit) schedule(static)
#endif
		for (i = 0; i<calc->numVerts; ++i) {
			float *co = calc->vertexCos[i];
			float tmp_co[3], tmp_no[3];
			float weight = defvert_array_find_weight_safe(calc->dvert, i, calc->vgroup);

			if (weight == 0.0f) continue;

			if (calc->vert) {
				/* calc->vert contains verts from derivedMesh  */
				/* this coordinated are deformed by vertexCos only for normal projection (to get correct normals) */
				/* for other cases calc->varts contains undeformed coordinates and vertexCos should be used */
				if (calc->smd->projAxis == MOD_SHRINKWRAP_PROJECT_OVER_NORMAL) {
					copy_v3_v3(tmp_co, calc->vert[i].co);
					normal_short_to_float_v3(tmp_no, calc->vert[i].no);
				}
				else {
					copy_v3_v3(tmp_co, co);
					copy_v3_v3(tmp_no, proj_axis);
				}
			}
			else {
				copy_v3_v3(tmp_co, co);
				copy_v3_v3(tmp_no, proj_axis);
			}


			hit.index = -1;
			hit.dist = 10000.0f; //TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that

			//Project over positive direction of axis
			if (use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_POS_DIR) {

				if (auxData.tree)
					normal_projection_project_vertex(0, tmp_co, tmp_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData);

				normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, tmp_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData);
			}

			//Project over negative direction of axis
			if (use_normal & MOD_SHRINKWRAP_PROJECT_ALLOW_NEG_DIR && hit.index == -1) {
				float inv_no[3];
				negate_v3_v3(inv_no, tmp_no);

				if (auxData.tree)
					normal_projection_project_vertex(0, tmp_co, inv_no, &local2aux, auxData.tree, &hit, auxData.raycast_callback, &auxData);

				normal_projection_project_vertex(calc->smd->shrinkOpts, tmp_co, inv_no, &calc->local2target, treeData.tree, &hit, treeData.raycast_callback, &treeData);
			}


			if (hit.index != -1) {
				madd_v3_v3v3fl(hit.co, hit.co, tmp_no, calc->keepDist);
				interp_v3_v3v3(co, co, hit.co, weight);
			}
		}
	}

	//free data structures
	free_bvhtree_from_mesh(&treeData);
	free_bvhtree_from_mesh(&auxData);
}