[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