[Bf-blender-cvs] [3ee9d04db37] hair_guides: Shape binding for groom curves to the scalp mesh surface using face groups.

Lukas Tönne noreply at git.blender.org
Sun Jan 7 13:31:50 CET 2018


Commit: 3ee9d04db3757fd284a55a3146e3d4d3c3c7fa0a
Author: Lukas Tönne
Date:   Sun Jan 7 12:28:06 2018 +0000
Branches: hair_guides
https://developer.blender.org/rB3ee9d04db3757fd284a55a3146e3d4d3c3c7fa0a

Shape binding for groom curves to the scalp mesh surface using face groups.

Face groups are used for now to define the origin of a groom bundle. This
is probably going to be replaced at some point with a dedicated surface
graph/mesh, but is much easier to implement and gives sufficient functionality.

When a face map is selected as the basis of a hair bundle it will update
the curve shape automatically. In the future this should perhaps be an
optional step (user operator) because it destructively changes existing
shapes.

===================================================================

M	release/scripts/startup/bl_ui/properties_data_groom.py
M	source/blender/blenkernel/BKE_groom.h
M	source/blender/blenkernel/BKE_mesh_sample.h
M	source/blender/blenkernel/intern/groom.c
M	source/blender/blenkernel/intern/mesh_sample.c
M	source/blender/blenloader/intern/readfile.c
M	source/blender/blenloader/intern/writefile.c
M	source/blender/bmesh/CMakeLists.txt
M	source/blender/bmesh/intern/bmesh_opdefines.c
M	source/blender/bmesh/intern/bmesh_operators_private.h
M	source/blender/bmesh/intern/bmesh_walkers_impl.c
A	source/blender/bmesh/operators/bmo_face_island.c
M	source/blender/editors/groom/editgroom.c
M	source/blender/makesdna/DNA_groom_types.h
M	source/blender/makesrna/intern/rna_groom.c

===================================================================

diff --git a/release/scripts/startup/bl_ui/properties_data_groom.py b/release/scripts/startup/bl_ui/properties_data_groom.py
index 0cb6a8e138b..7c637794f07 100644
--- a/release/scripts/startup/bl_ui/properties_data_groom.py
+++ b/release/scripts/startup/bl_ui/properties_data_groom.py
@@ -32,9 +32,9 @@ class GROOM_UL_bundles(bpy.types.UIList):
             if not bundle.is_bound:
                 row.label(icon='ERROR')
             if groom.scalp_object:
-                row.prop_search(bundle, "scalp_vertex_group", groom.scalp_object, "vertex_groups", text="")
+                row.prop_search(bundle, "scalp_facemap", groom.scalp_object, "face_maps", text="")
             else:
-                row.prop(bundle, "scalp_vertex_group", text="")
+                row.prop(bundle, "scalp_facemap", text="")
 
         elif self.layout_type == 'GRID':
             layout.alignment = 'CENTER'
diff --git a/source/blender/blenkernel/BKE_groom.h b/source/blender/blenkernel/BKE_groom.h
index c0a7ff1c402..5ccc42de36a 100644
--- a/source/blender/blenkernel/BKE_groom.h
+++ b/source/blender/blenkernel/BKE_groom.h
@@ -43,13 +43,13 @@ void BKE_groom_init(struct Groom *groom);
 void *BKE_groom_add(struct Main *bmain, const char *name);
 
 void BKE_groom_free(struct Groom *groom);
+void BKE_groom_bundle_curve_cache_clear(struct GroomBundle *bundle);
 
 void BKE_groom_copy_data(struct Main *bmain, struct Groom *groom_dst, const struct Groom *groom_src, const int flag);
 struct Groom *BKE_groom_copy(struct Main *bmain, const struct Groom *groom);
 
 void BKE_groom_make_local(struct Main *bmain, struct Groom *groom, const bool lib_local);
 
-
 bool BKE_groom_minmax(struct Groom *groom, float min[3], float max[3]);
 void BKE_groom_boundbox_calc(struct Groom *groom, float r_loc[3], float r_size[3]);
 
@@ -66,7 +66,8 @@ void BKE_groom_bundle_unbind(struct GroomBundle *bundle);
 /* === Depsgraph evaluation === */
 
 void BKE_groom_eval_curve_cache(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
-void BKE_groom_clear_curve_cache(struct Object *ob);
+void BKE_groom_eval_curve_cache_clear(struct Object *ob);
+
 void BKE_groom_eval_geometry(const struct EvaluationContext *eval_ctx, struct Groom *groom);
 
 
diff --git a/source/blender/blenkernel/BKE_mesh_sample.h b/source/blender/blenkernel/BKE_mesh_sample.h
index 7add4dfadba..94560d8c017 100644
--- a/source/blender/blenkernel/BKE_mesh_sample.h
+++ b/source/blender/blenkernel/BKE_mesh_sample.h
@@ -44,9 +44,12 @@ typedef bool (*MeshSampleRayFp)(void *userdata, void *thread_ctx, float ray_star
 
 float* BKE_mesh_sample_calc_triangle_weights(struct DerivedMesh *dm, MeshSampleVertexWeightFp vertex_weight_cb, void *userdata, float *r_area);
 
+void BKE_mesh_sample_weights_from_loc(struct MeshSample *sample, struct DerivedMesh *dm, int face_index, const float loc[3]);
+
 
 /* ==== Evaluate ==== */
 
+bool BKE_mesh_sample_is_valid(const struct MeshSample *sample);
 bool BKE_mesh_sample_is_volume_sample(const struct MeshSample *sample);
 
 bool BKE_mesh_sample_eval(struct DerivedMesh *dm, const struct MeshSample *sample, float loc[3], float nor[3], float tang[3]);
diff --git a/source/blender/blenkernel/intern/groom.c b/source/blender/blenkernel/intern/groom.c
index aea4a992365..34efda1952e 100644
--- a/source/blender/blenkernel/intern/groom.c
+++ b/source/blender/blenkernel/intern/groom.c
@@ -44,20 +44,28 @@
 #include "BLT_translation.h"
 
 #include "DNA_groom_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 
 #include "BKE_animsys.h"
-#include "BKE_deform.h"
+#include "BKE_customdata.h"
+#include "BKE_cdderivedmesh.h"
 #include "BKE_global.h"
 #include "BKE_groom.h"
 #include "BKE_hair.h"
+#include "BKE_bvhutils.h"
 #include "BKE_library.h"
 #include "BKE_main.h"
+#include "BKE_mesh_sample.h"
 #include "BKE_object.h"
+#include "BKE_object_facemap.h"
 
 #include "DEG_depsgraph.h"
 
+#include "bmesh.h"
+
 
 void BKE_groom_init(Groom *groom)
 {
@@ -80,18 +88,28 @@ void *BKE_groom_add(Main *bmain, const char *name)
 	return groom;
 }
 
+void BKE_groom_bundle_curve_cache_clear(GroomBundle *bundle)
+{
+	if (bundle->curvecache)
+	{
+		MEM_freeN(bundle->curvecache);
+		bundle->curvecache = NULL;
+		bundle->totcurvecache = 0;
+	}
+	if (bundle->shapecache)
+	{
+		MEM_freeN(bundle->shapecache);
+		bundle->shapecache = NULL;
+		bundle->totshapecache = 0;
+	}
+}
+
 static void groom_bundles_free(ListBase *bundles)
 {
 	for (GroomBundle *bundle = bundles->first; bundle; bundle = bundle->next)
 	{
-		if (bundle->curvecache)
-		{
-			MEM_freeN(bundle->curvecache);
-		}
-		if (bundle->shapecache)
-		{
-			MEM_freeN(bundle->shapecache);
-		}
+		BKE_groom_bundle_curve_cache_clear(bundle);
+		
 		if (bundle->sections)
 		{
 			MEM_freeN(bundle->sections);
@@ -255,30 +273,216 @@ void BKE_groom_bind_scalp_regions(Groom *groom)
 	}
 }
 
-static bool groom_region_is_valid(Groom *groom, GroomBundle *bundle)
+static bool groom_shape_rebuild(GroomBundle *bundle, int numshapeverts, Object *scalp_ob)
 {
-	if (!groom->scalp_object)
+	BLI_assert(bundle->scalp_region != NULL);
+	BLI_assert(scalp_ob->type == OB_MESH);
+	
+	bool result = true;
+	float (*shape)[2] = MEM_mallocN(sizeof(*shape) * numshapeverts, "groom section shape");
+	
+	Mesh *me = scalp_ob->data;
+	// XXX MeshSample will use Mesh instead of DerivedMesh in the future
+	DerivedMesh *dm = CDDM_from_mesh(me);
+	
+	/* last sample is the center position */
+	MeshSample *center_sample = &bundle->scalp_region[numshapeverts];
+	float center_co[3], center_nor[3], center_tang[3], center_binor[3];
+	if (!BKE_mesh_sample_eval(dm, center_sample, center_co, center_nor, center_tang))
 	{
-		return false;
+		result = false;
+		goto cleanup;
+	}
+	cross_v3_v3v3(center_binor, center_nor, center_tang);
+	
+	MeshSample *sample = bundle->scalp_region;
+	GroomSectionVertex *vert0 = bundle->verts;
+	for (int i = 0; i < numshapeverts; ++i, ++sample, ++vert0)
+	{
+		/* 3D position of the shape vertex origin on the mesh */
+		float co[3], nor[3], tang[3];
+		if (!BKE_mesh_sample_eval(dm, sample, co, nor, tang))
+		{
+			result = false;
+			goto cleanup;
+		}
+		/* Get relative offset from the center */
+		sub_v3_v3(co, center_co);
+		/* Convert mesh surface positions to 2D shape
+		 * by projecting onto the normal plane
+		 */
+		shape[i][0] = dot_v3v3(co, center_binor);
+		shape[i][1] = dot_v3v3(co, center_tang);
 	}
 	
-	if (!defgroup_find_name(groom->scalp_object, bundle->scalp_vgroup_name))
+	bundle->numloopverts = numshapeverts;
+	bundle->totverts = numshapeverts * bundle->totsections;
+	bundle->verts = MEM_reallocN_id(bundle->verts, sizeof(*bundle->verts) * bundle->totverts, "groom bundle vertices");
+	/* Set the shape for all sections */
+	GroomSectionVertex *vert = bundle->verts;
+	for (int i = 0; i < bundle->totsections; ++i)
+	{
+		for (int j = 0; j < numshapeverts; ++j, ++vert)
+		{
+			copy_v2_v2(vert->co, shape[j]);
+			vert->flag = 0;
+		}
+	}
+	
+cleanup:
+	MEM_freeN(shape);
+	dm->release(dm);
+	
+	return result;
+}
+
+static BMesh *groom_create_scalp_bmesh(Mesh *me)
+{
+	const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me);
+	
+	BMesh *bm = BM_mesh_create(&allocsize, &((struct BMeshCreateParams){
+	        .use_toolflags = true,
+	         }));
+	
+	BM_mesh_bm_from_me(bm, me, (&(struct BMeshFromMeshParams){
+	        .calc_face_normal = true, .use_shapekey = false,
+	        }));
+	
+	return bm;
+}
+
+static bool groom_bundle_region_from_mesh_fmap(GroomBundle *bundle, Object *scalp_ob)
+{
+	BLI_assert(scalp_ob->type == OB_MESH);
+	
+	BKE_groom_bundle_curve_cache_clear(bundle);
+	
+	Mesh *me = scalp_ob->data;
+	const int scalp_fmap_nr = BKE_object_facemap_name_index(scalp_ob, bundle->scalp_facemap_name);
+	const int cd_fmap_offset = CustomData_get_offset(&me->pdata, CD_FACEMAP);
+	if (scalp_fmap_nr < 0 || cd_fmap_offset < 0)
 	{
 		return false;
 	}
 	
-	return true;
+	BMesh *bm = groom_create_scalp_bmesh(me);
+	bool result = true;
+	
+	/* Tag faces in the face map for the BMO walker */
+	{
+		BMFace *f;
+		BMIter iter;
+		BM_ITER_MESH(f, &iter, bm, BM_FACES_OF_MESH)
+		{
+			int fmap = BM_ELEM_CD_GET_INT(f, cd_fmap_offset);
+			BM_elem_flag_set(f, BM_ELEM_TAG, fmap == scalp_fmap_nr);
+		}
+	}
+	
+	BMOperator op;
+	BMO_op_initf(bm, &op, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE),
+	             "face_island_boundary faces=%hf", BM_ELEM_TAG);
+	
+	BMO_op_exec(bm, &op);
+	if (BMO_error_occurred(bm))
+	{
+		result = false;
+		goto finalize;
+	}
+	
+	const int numshapeverts = BMO_slot_buffer_count(op.slots_out, "boundary");
+	bundle->scalp_region = MEM_callocN(sizeof(*bundle->scalp_region) * (numshapeverts + 1), "groom bundle scalp region");
+	
+	float center_co[3]; /* average vertex location for placing the center */
+	{
+		BMLoop *l;
+		BMOIter oiter;
+		MeshSample *sample = bundle->scalp_region;
+		zero_v3(center_co);
+		BMO_ITER (l, &oiter, op.slots_out, "boundary", BM_LOOP)
+		{
+			sample->orig_poly = BM_elem_index_get(l->f);
+			sample->orig_loops[0] = BM_elem_index_get(l);
+			sample->orig_verts[0] = BM_elem_index_get(l->v);
+			sample->orig_weights[0] = 1.0f;
+			BLI_assert(BKE_mesh_sample_is_valid(sample));
+			
+			add_v3_v3(center_co, l->v->co);
+			
+			++sample;
+		}
+		if (numshapeverts > 0)
+		{
+			mul_v3_fl(center_co, 1.0f / (float)(numshapeverts));
+		}
+	}
+	
+	{
+		/* BVH tree for binding the region center location */
+		DerivedMesh *dm = CDDM_from_mesh(me);
+		DM_ensure_tessface(dm);
+		BVHTreeFromMesh bvhtree;
+		//bvhtree_from_mesh_looptri(&bvhtree, dm, 0.0f, 4, 6);
+		bvhtree_from_mesh_faces(&bvhtree, dm, 0.0f, 4, 6);
+		if (bvhtree.tree != NULL) {
+			BVHTreeNearest nearest;
+			nearest.index = -1;
+			nearest.dist_sq = FLT_MAX;
+			
+			BLI_bvhtree_find_nearest(bvhtree.tree, center_co, &nearest, bvhtree.nearest_callback, &bvhtree);
+			if (nearest.index >= 0)
+			{
+				/* last sample is the center position */
+				MeshSample *center_sample = &bundle->scalp_region[numshapeverts];
+				BKE_mesh_sample_weights_from_loc(center_sample, dm, nearest.index, nearest.co);
+				BLI_assert(BKE_mesh_sample_is_valid(center_sample));
+			}
+		}
+		else
+		{
+			result = fal

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list