diff options
Diffstat (limited to 'source/blender/editors')
41 files changed, 7151 insertions, 1385 deletions
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index beb22d43930..49e6ab32f64 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -743,7 +743,6 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES brush.sculpt.cloth brush.sculpt.crease brush.sculpt.displacement_eraser - brush.sculpt.displacement_smear brush.sculpt.draw brush.sculpt.draw_face_sets brush.sculpt.draw_sharp @@ -756,15 +755,18 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES brush.sculpt.mask brush.sculpt.multiplane_scrape brush.sculpt.nudge + brush.sculpt.paint brush.sculpt.pinch brush.sculpt.pose brush.sculpt.rotate brush.sculpt.scrape brush.sculpt.simplify + brush.sculpt.smear brush.sculpt.smooth brush.sculpt.snake_hook brush.sculpt.thumb brush.sculpt.topology + brush.sculpt.vcol_boundary brush.uv_sculpt.grab brush.uv_sculpt.pinch brush.uv_sculpt.relax diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 7ccdc49d291..467dfb66186 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -919,6 +919,9 @@ DEF_ICON_COLOR(BRUSH_TEXFILL) DEF_ICON_COLOR(BRUSH_TEXMASK) DEF_ICON_COLOR(BRUSH_THUMB) DEF_ICON_COLOR(BRUSH_ROTATE) +DEF_ICON_COLOR(BRUSH_VCOL_BOUNDARY) +DEF_ICON_COLOR(BRUSH_PAINT) +DEF_ICON_COLOR(BRUSH_SCULPT_SMEAR) /* grease pencil sculpt */ DEF_ICON_COLOR(GPBRUSH_SMOOTH) diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index cccfc7e934c..38e6bb80c0f 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -61,14 +61,16 @@ #include "mesh_intern.h" /* own include */ +#include "../sculpt_paint/sculpt_intern.h" + static bool geometry_extract_poll(bContext *C) { Object *ob = CTX_data_active_object(C); if (ob != NULL && ob->mode == OB_MODE_SCULPT) { - if (ob->sculpt->bm) { - CTX_wm_operator_poll_msg_set(C, "The geometry can not be extracted with dyntopo activated"); - return false; - } + // if (ob->sculpt->bm) { + // CTX_wm_operator_poll_msg_set(C, "The geometry can not be extracted with dyntopo activated"); + // return false; + //} return ED_operator_object_active_editable_mesh(C); } return false; @@ -120,7 +122,8 @@ static int geometry_extract_apply(bContext *C, .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, new_mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -373,6 +376,7 @@ static int face_set_extract_invoke(bContext *C, wmOperator *op, const wmEvent *U ED_workspace_status_text(C, TIP_("Click on the mesh to select a Face Set")); WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; } @@ -513,7 +517,8 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, new_mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -544,7 +549,8 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, new_ob_mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -580,15 +586,42 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) if (ob->mode == OB_MODE_SCULPT) { SculptSession *ss = ob->sculpt; - ss->face_sets = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS); - if (ss->face_sets) { - /* Assign a new Face Set ID to the new faces created by the slice operation. */ - const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data); - ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id); + + /* Assign a new Face Set ID to the new faces created by the slice operation. */ + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_GRIDS: + case PBVH_FACES: + ss->face_sets = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS); + + if (ss->face_sets) { + const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data); + ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id); + } + break; + case PBVH_BMESH: { + if (ss->bm && CustomData_has_layer(&ss->bm->pdata, CD_SCULPT_FACE_SETS)) { + const int cd_fset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS); + BMFace *f; + BMIter iter; + + const int next_face_set_id = SCULPT_face_set_next_available_get(ss); + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = BM_ELEM_CD_GET_INT(f, cd_fset); + + if (fset == SCULPT_FACE_SET_NONE) { + BM_ELEM_CD_SET_INT(f, cd_fset, next_face_set_id); + } + } + } + break; + } } - ED_sculpt_undo_geometry_end(ob); } + ED_sculpt_undo_geometry_end(ob); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 41a9f426798..4278d3906ff 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4394,7 +4394,8 @@ static Base *mesh_separate_tagged( BM_mesh_normals_update(bm_new); - BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); + BM_mesh_bm_to_me( + bmain, base_new->object, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm_new); ((Mesh *)base_new->object->data)->edit_mesh = NULL; @@ -4460,7 +4461,8 @@ static Base *mesh_separate_arrays(Main *bmain, BM_vert_kill(bm_old, verts[i]); } - BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); + BM_mesh_bm_to_me( + bmain, base_new->object, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm_new); ((Mesh *)base_new->object->data)->edit_mesh = NULL; @@ -4643,6 +4645,7 @@ static bool mesh_separate_loose( if (clear_object_data) { BM_mesh_bm_to_me(NULL, + base_old->object, bm_old, me_old, (&(struct BMeshToMeshParams){ @@ -4734,7 +4737,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) .use_toolflags = true, })); - BM_mesh_bm_from_me(bm_old, me, (&(struct BMeshFromMeshParams){0})); + BM_mesh_bm_from_me(NULL, bm_old, me, (&(struct BMeshFromMeshParams){0})); switch (type) { case MESH_SEPARATE_MATERIAL: @@ -4750,6 +4753,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) if (retval_iter) { BM_mesh_bm_to_me(bmain, + ob, bm_old, me, (&(struct BMeshToMeshParams){ @@ -5936,6 +5940,116 @@ static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot) "Split off face corners instead of merging faces"); } +static int edbm_mres_test_exec(bContext *C, wmOperator *op) +{ + const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); + const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (em->bm->totvertsel == 0) { + continue; + } + + BM_custom_loop_normals_to_vector_layer(em->bm); + + if (!EDBM_op_callf(em, op, "test_mres_smooth")) { + continue; + } + + BM_custom_loop_normals_from_vector_layer(em->bm, false); + + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); + // EDBM_update_generic(obedit->data, true, true); + } + + MEM_freeN(objects); + return OPERATOR_FINISHED; +} + +extern Object *multires_dump_grids_bmesh(Object *bmob, BMesh *bm); + +static int edbm_dump_mres_grids_exec(bContext *C, wmOperator *op) +{ + const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); + const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + multires_dump_grids_bmesh(obedit, em->bm); + } + + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C)); + + MEM_freeN(objects); + return OPERATOR_FINISHED; +} + +static bool mres_test_poll(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_MESH) { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (!em || !CustomData_has_layer(&em->bm->ldata, CD_MDISPS)) { + return false; + } + + return true; + } + + return false; +} + +void MESH_OT_dump_mres_grids(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Dump Multires Grids"; + ot->description = "Dump Multires Grids"; + ot->idname = "MESH_OT_dump_mres_grids"; + + /* api callbacks */ + ot->exec = edbm_dump_mres_grids_exec; + ot->poll = mres_test_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void MESH_OT_mres_test(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Test Multires Boundary Smooth"; + ot->description = "Test multires boundary smooth"; + ot->idname = "MESH_OT_mres_test"; + + /* api callbacks */ + ot->exec = edbm_mres_test_exec; + ot->poll = mres_test_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) { const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index fc9e1aa8b1a..8dc7a1d7675 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -607,7 +607,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ BM_mesh_bm_to_me( - NULL, + NULL, NULL, em->bm, &um->me, (&(struct BMeshToMeshParams){ @@ -679,7 +679,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, bm, &um->me, (&(struct BMeshFromMeshParams){ /* Handled with tessellation. */ diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 85c646d689c..d765f6b50af 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -337,6 +337,7 @@ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) } BM_mesh_bm_to_me(bmain, + ob, bm, me, (&(struct BMeshToMeshParams){ diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index f25317e8e85..721486c149c 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -259,6 +259,7 @@ void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot); void MESH_OT_average_normals(struct wmOperatorType *ot); void MESH_OT_smooth_normals(struct wmOperatorType *ot); void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot); +void MESH_OT_mres_test(struct wmOperatorType *ot); /* *** editmesh_mask_extract.c *** */ void MESH_OT_paint_mask_extract(struct wmOperatorType *ot); @@ -285,3 +286,4 @@ void MESH_OT_customdata_skin_add(struct wmOperatorType *ot); void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); +void MESH_OT_dump_mres_grids(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 34e9a3f45a5..7962e687d7e 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -205,6 +205,9 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_average_normals); WM_operatortype_append(MESH_OT_smooth_normals); WM_operatortype_append(MESH_OT_mod_weighted_strength); + WM_operatortype_append(MESH_OT_mres_test); + WM_operatortype_append(MESH_OT_dump_mres_grids); + } #if 0 /* UNUSED, remove? */ diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 7bbca7ea9e6..aff7ec8ef6c 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -859,7 +859,17 @@ bool ED_object_modifier_apply(Main *bmain, BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in edit mode"); return false; } - if (mode != MODIFIER_APPLY_SHAPE && ID_REAL_USERS(ob->data) > 1) { + + bool allow_multi_user = mode == MODIFIER_APPLY_SHAPE; + if (md) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + + allow_multi_user |= ELEM( + mti->type, eModifierTypeType_NonGeometrical, eModifierTypeType_OnlyDeform); + } + + // bool allow_multi_user = md && md->type == eModifierType_DataTransfer || md->flag & ; + if (!allow_multi_user && ID_REAL_USERS(ob->data) > 1) { BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); return false; } @@ -1396,6 +1406,9 @@ static bool modifier_apply_poll_ex(bContext *C, bool allow_shared) Object *ob = (ptr.owner_id != NULL) ? (Object *)ptr.owner_id : ED_object_active_context(C); ModifierData *md = ptr.data; /* May be NULL. */ + allow_shared = true; + // allow_shared = allow_shared || (md && md->type == eModifierType_DataTransfer); + if (ID_IS_OVERRIDE_LIBRARY(ob) || ((ob->data != NULL) && ID_IS_OVERRIDE_LIBRARY(ob->data))) { CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data"); return false; diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 05f9980e0ab..d3162aa62f0 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -761,7 +761,7 @@ static void quadriflow_update_job(void *customdata, float progress, int *cancel) *(qj->progress) = progress; } -static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes) +static Mesh *remesh_symmetry_bisect(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes) { MirrorModifierData mmd = {{0}}; mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; @@ -782,8 +782,10 @@ static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes) zero_v3(plane_no); plane_no[axis] = -1.0f; mesh_bisect_temp = mesh_bisect; - mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier( + + mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(ob, &mmd, mesh_bisect, axis, plane_co, plane_no); + if (mesh_bisect_temp != mesh_bisect) { BKE_id_free(NULL, mesh_bisect_temp); } @@ -851,7 +853,7 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update bisect_mesh = BKE_mesh_copy_for_eval(mesh, false); /* Bisect the input mesh using the paint symmetry settings */ - bisect_mesh = remesh_symmetry_bisect(bisect_mesh, qj->symmetry_axes); + bisect_mesh = remesh_symmetry_bisect(ob, bisect_mesh, qj->symmetry_axes); new_mesh = BKE_mesh_remesh_quadriflow_to_mesh_nomain( bisect_mesh, diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 3b668a1bd4c..509caed9c2f 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -56,6 +56,7 @@ set(SRC paint_vertex_weight_ops.c paint_vertex_weight_utils.c sculpt.c + sculpt_curvature.c sculpt_automasking.c sculpt_boundary.c sculpt_cloth.c diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index ab2b2f4b16b..2c918033ad5 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1223,7 +1223,7 @@ typedef struct PaintCursorContext { /* Sculpt related data. */ Sculpt *sd; SculptSession *ss; - int prev_active_vertex_index; + SculptVertRef prev_active_vertex_index; bool is_stroke_active; bool is_cursor_over_mesh; bool is_multires; @@ -1589,8 +1589,8 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * paint_cursor_update_object_space_radius(pcontext); - const bool update_previews = pcontext->prev_active_vertex_index != - SCULPT_active_vertex_get(pcontext->ss); + const bool update_previews = pcontext->prev_active_vertex_index.i != + SCULPT_active_vertex_get(pcontext->ss).i; /* Setup drawing. */ wmViewport(&pcontext->region->winrct); diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index da627c6b7db..4e3e074c9bf 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -209,17 +209,15 @@ static void partialvis_update_grids(Depsgraph *depsgraph, } static void partialvis_update_bmesh_verts(BMesh *bm, - GSet *verts, + TableGSet *verts, PartialVisAction action, PartialVisArea area, float planes[4][4], bool *any_changed, bool *any_visible) { - GSetIterator gs_iter; - - GSET_ITER (gs_iter, verts) { - BMVert *v = BLI_gsetIterator_getKey(&gs_iter); + BMVert *v; + TGSET_ITER (v, verts) { float *vmask = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_PAINT_MASK); /* Hide vertex if in the hide volume. */ @@ -237,15 +235,14 @@ static void partialvis_update_bmesh_verts(BMesh *bm, (*any_visible) = true; } } + TGSET_ITER_END } -static void partialvis_update_bmesh_faces(GSet *faces) +static void partialvis_update_bmesh_faces(TableGSet *faces) { - GSetIterator gs_iter; - - GSET_ITER (gs_iter, faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + BMFace *f; + TGSET_ITER (f, faces) { if (paint_is_bmesh_face_hidden(f)) { BM_elem_flag_enable(f, BM_ELEM_HIDDEN); } @@ -253,6 +250,7 @@ static void partialvis_update_bmesh_faces(GSet *faces) BM_elem_flag_disable(f, BM_ELEM_HIDDEN); } } + TGSET_ITER_END } static void partialvis_update_bmesh(Object *ob, @@ -263,7 +261,7 @@ static void partialvis_update_bmesh(Object *ob, float planes[4][4]) { BMesh *bm; - GSet *unique, *other, *faces; + TableGSet *unique, *other, *faces; bool any_changed = false, any_visible = false; bm = BKE_pbvh_get_bmesh(pbvh); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index bd05d309421..2ca19f76672 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1081,10 +1081,15 @@ static bool pixel_bounds_uv(const float uv_quad[4][2], #endif static bool pixel_bounds_array( - float (*uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot) + float (*in_uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot) { /* UV bounds */ float min_uv[2], max_uv[2]; + float(*uv)[2] = in_uv; + + if (tot < 0) { + printf("eek!\n"); + } if (tot == 0) { return false; @@ -1092,9 +1097,8 @@ static bool pixel_bounds_array( INIT_MINMAX2(min_uv, max_uv); - while (tot--) { + for (int i = 0; i < tot; i++, uv++) { minmax_v2v2_v2(min_uv, max_uv, (*uv)); - uv++; } bounds_px->xmin = (int)(ibuf_x * min_uv[0]); @@ -1103,6 +1107,15 @@ static bool pixel_bounds_array( bounds_px->xmax = (int)(ibuf_x * max_uv[0]) + 1; bounds_px->ymax = (int)(ibuf_y * max_uv[1]) + 1; + const int d = -1000; + if (bounds_px->xmin < d || bounds_px->ymin < d || bounds_px->xmax < d || bounds_px->ymax < d) { + printf("error! xmin %d xmax %d ymin %d ymax %d\n", + bounds_px->xmin, + bounds_px->xmax, + bounds_px->ymin, + bounds_px->ymax); + } + // printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]); /* face uses no UV area when quantized to pixels? */ diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 7341d984c91..4f5187a71d0 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -51,6 +51,10 @@ typedef struct CoNo { float no[3]; } CoNo; +#include "DNA_listBase.h" +#include "DNA_scene_types.h" +#include "ED_view3d.h" + /* paint_stroke.c */ typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); @@ -60,6 +64,80 @@ typedef void (*StrokeUpdateStep)(struct bContext *C, typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final); typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke); +typedef struct PaintSample { + float mouse[2]; + float pressure; +} PaintSample; + +typedef struct PaintStroke { + void *mode_data; + void *stroke_cursor; + struct wmTimer *timer; + struct RNG *rng; + + /* Cached values */ + struct ViewContext vc; + struct Brush *brush; + struct UnifiedPaintSettings *ups; + + /* used for lines and curves */ + ListBase line; + + /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs + * to smooth the stroke */ + PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; + int num_samples; + int cur_sample; + int tot_samples; + + float last_mouse_position[2]; + float last_world_space_position[3]; + bool stroke_over_mesh; + /* space distance covered so far */ + float stroke_distance; + float stroke_distance_t; // divided by brush radius + + /* Set whether any stroke step has yet occurred + * e.g. in sculpt mode, stroke doesn't start until cursor + * passes over the mesh */ + bool stroke_started; + /* Set when enough motion was found for rake rotation */ + bool rake_started; + /* event that started stroke, for modal() return */ + int event_type; + /* check if stroke variables have been initialized */ + bool stroke_init; + /* check if various brush mapping variables have been initialized */ + bool brush_init; + float initial_mouse[2]; + /* cached_pressure stores initial pressure for size pressure influence mainly */ + float cached_size_pressure; + /* last pressure will store last pressure value for use in interpolation for space strokes */ + float last_pressure; + int stroke_mode; + + float last_tablet_event_pressure; + + float zoom_2d; + int pen_flip; + + /* Tilt, as read from the event. */ + float x_tilt; + float y_tilt; + + /* line constraint */ + bool constrain_line; + float constrained_pos[2]; + + StrokeGetLocation get_location; + StrokeTestStart test_start; + StrokeUpdateStep update_step; + StrokeRedraw redraw; + StrokeDone done; + + float spacing; +} PaintStroke; + struct PaintStroke *paint_stroke_new(struct bContext *C, struct wmOperator *op, StrokeGetLocation get_location, @@ -91,6 +169,19 @@ bool PAINT_brush_tool_poll(struct bContext *C); void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C)); void paint_cursor_delete_textures(void); +/** +* used by various actions that have their own spacing that +* is coarser then the brush spacing. e.g. sculpt dyntopo. +* +* \param state: pointer to a float used for internal state, should be initialized to zero at start of stroke +* \return false if the action should be skipped. +* +*/ +bool paint_stroke_apply_subspacing(struct PaintStroke *stroke, + const float spacing, + const enum ePaintMode mode, + float *state); + /* paint_vertex.c */ bool weight_paint_poll(struct bContext *C); bool weight_paint_poll_ignore_tool(bContext *C); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index d968b6cc319..b3fcb9155f3 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -679,7 +679,7 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd) { float vertex_normal[3]; - SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal); + SCULPT_vertex_normal_get(sgcontext->ss, vd->vertex, vertex_normal); float dot = dot_v3v3(sgcontext->view_normal, vertex_normal); const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f); @@ -758,7 +758,7 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { - SCULPT_vertex_face_set_set(sgcontext->ss, vd.index, face_set_operation->new_face_set_id); + SCULPT_vertex_face_set_set(sgcontext->ss, vd.vertex, face_set_operation->new_face_set_id); any_updated = true; } } @@ -974,7 +974,8 @@ static void sculpt_gesture_trim_normals_update(SculptGestureContext *sgcontext) .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, trim_mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -1040,7 +1041,7 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext) trim_operation->depth_back = -FLT_MAX; for (int i = 0; i < totvert; i++) { - const float *vco = SCULPT_vertex_co_get(ss, i); + const float *vco = SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)); /* Convert the coordinates to world space to calculate the depth. When generating the trimming * mesh, coordinates are first calculated in world space, then converted to object space to * store them. */ @@ -1210,7 +1211,7 @@ static void sculpt_gesture_trim_geometry_free(SculptGestureContext *sgcontext) static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data)) { - return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0; + return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 0 : 1; } static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) @@ -1220,20 +1221,30 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) Mesh *trim_mesh = trim_operation->mesh; BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); - BM_mesh_bm_from_me(bm, - trim_mesh, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); + if (sgcontext->ss && sgcontext->ss->bm) { + bm = sgcontext->ss->bm; + BM_mesh_normals_update(bm); + } + + else { + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); + bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); + + BM_mesh_bm_from_me(NULL, + bm, + sculpt_mesh, + &((struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); + } - BM_mesh_bm_from_me(bm, - sculpt_mesh, + BM_mesh_bm_from_me(NULL, + bm, + trim_mesh, &((struct BMeshFromMeshParams){ .calc_face_normal = true, })); @@ -1294,15 +1305,29 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) MEM_freeN(looptris); - Mesh *result = BKE_mesh_from_bmesh_nomain(bm, - (&(struct BMeshToMeshParams){ - .calc_object_remap = false, - }), - sculpt_mesh); - BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - BKE_mesh_nomain_to_mesh( - result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true); + if (sgcontext->ss && sgcontext->ss->bm) { //rebuild pbvh + BKE_pbvh_free(sgcontext->ss->pbvh); + sgcontext->ss->pbvh = BKE_pbvh_new(); + + BKE_pbvh_build_bmesh(sgcontext->ss->pbvh, + sgcontext->ss->bm, + false, + sgcontext->ss->bm_log, + sgcontext->ss->cd_vert_node_offset, + sgcontext->ss->cd_face_node_offset, + sgcontext->ss->cd_dyn_vert); + } + else { //save result to mesh + Mesh *result = BKE_mesh_from_bmesh_nomain(bm, + (&(struct BMeshToMeshParams){ + .calc_object_remap = false, + }), + sculpt_mesh); + BM_mesh_free(bm); + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_nomain_to_mesh( + result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true); + } } static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgcontext) @@ -1339,6 +1364,10 @@ static void sculpt_gesture_trim_end(bContext *UNUSED(C), SculptGestureContext *s sculpt_gesture_trim_geometry_free(sgcontext); + if (sgcontext->ss && sgcontext->ss->bm) { + SCULPT_dynamic_topology_triangulate(sgcontext->ss, sgcontext->ss->bm); + } + SCULPT_undo_push_node(sgcontext->vc.obact, NULL, SCULPT_UNDO_GEOMETRY); BKE_mesh_batch_cache_dirty_tag(sgcontext->vc.obact->data, BKE_MESH_BATCH_DIRTY_ALL); DEG_id_tag_update(&sgcontext->vc.obact->id, ID_RECALC_GEOMETRY); @@ -1548,8 +1577,8 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op) { Object *object = CTX_data_active_object(C); SculptSession *ss = object->sculpt; - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { - /* Not supported in Multires and Dyntopo. */ + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + /* Not supported in Multires. */ return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index de01bc3a474..a8cb05c6d2c 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -67,77 +67,6 @@ # include "PIL_time_utildefines.h" #endif -typedef struct PaintSample { - float mouse[2]; - float pressure; -} PaintSample; - -typedef struct PaintStroke { - void *mode_data; - void *stroke_cursor; - wmTimer *timer; - struct RNG *rng; - - /* Cached values */ - ViewContext vc; - Brush *brush; - UnifiedPaintSettings *ups; - - /* used for lines and curves */ - ListBase line; - - /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs - * to smooth the stroke */ - PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; - int num_samples; - int cur_sample; - int tot_samples; - - float last_mouse_position[2]; - float last_world_space_position[3]; - bool stroke_over_mesh; - /* space distance covered so far */ - float stroke_distance; - - /* Set whether any stroke step has yet occurred - * e.g. in sculpt mode, stroke doesn't start until cursor - * passes over the mesh */ - bool stroke_started; - /* Set when enough motion was found for rake rotation */ - bool rake_started; - /* event that started stroke, for modal() return */ - int event_type; - /* check if stroke variables have been initialized */ - bool stroke_init; - /* check if various brush mapping variables have been initialized */ - bool brush_init; - float initial_mouse[2]; - /* cached_pressure stores initial pressure for size pressure influence mainly */ - float cached_size_pressure; - /* last pressure will store last pressure value for use in interpolation for space strokes */ - float last_pressure; - int stroke_mode; - - float last_tablet_event_pressure; - - float zoom_2d; - int pen_flip; - - /* Tilt, as read from the event. */ - float x_tilt; - float y_tilt; - - /* line constraint */ - bool constrain_line; - float constrained_pos[2]; - - StrokeGetLocation get_location; - StrokeTestStart test_start; - StrokeUpdateStep update_step; - StrokeRedraw redraw; - StrokeDone done; -} PaintStroke; - /*** Cursors ***/ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata) { @@ -288,6 +217,26 @@ static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode m return true; } +bool paint_stroke_apply_subspacing(struct PaintStroke *stroke, + const float spacing, + const enum ePaintMode mode, + float *state) +{ + if (!paint_space_stroke_enabled(stroke->brush, mode)) { + return true; + } + + const float t = stroke->stroke_distance_t; + + if (t != 0.0f && t < *state + spacing) { + return false; + } + else { + *state = t; + return true; + } +} + /* Initialize the stroke cache variants from operator properties */ static bool paint_brush_update(bContext *C, Brush *brush, @@ -430,11 +379,13 @@ static bool paint_brush_update(bContext *C, ups->anchored_size /= 2.0f; ups->pixel_radius /= 2.0f; stroke->stroke_distance = ups->pixel_radius; + stroke->stroke_distance_t = 1.0f; } else { copy_v2_v2(ups->anchored_initial_mouse, stroke->initial_mouse); copy_v2_v2(mouse, stroke->initial_mouse); stroke->stroke_distance = ups->pixel_radius; + stroke->stroke_distance_t = 1.0f; } ups->pixel_radius /= stroke->zoom_2d; ups->draw_anchored = true; @@ -767,6 +718,58 @@ static float paint_stroke_integrate_overlap(Brush *br, float factor) return 1.0f / max; } +static float paint_space_get_final_size_intern( + bContext *C, const Scene *scene, PaintStroke *stroke, float pressure, float dpressure) +{ + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + float size = BKE_brush_size_get(scene, stroke->brush) * pressure; + + if (paint_stroke_use_scene_spacing(stroke->brush, mode)) { + if (!BKE_brush_use_locked_size(scene, stroke->brush)) { + float last_object_space_position[3]; + mul_v3_m4v3( + last_object_space_position, stroke->vc.obact->imat, stroke->last_world_space_position); + size = paint_calc_object_space_radius(&stroke->vc, last_object_space_position, size); + } + else { + size = BKE_brush_unprojected_radius_get(scene, stroke->brush) * pressure; + } + } + + return size; +} + +static float paint_space_get_final_size(bContext *C, + const Scene *scene, + PaintStroke *stroke, + float pressure, + float dpressure, + float length) +{ + if (BKE_brush_use_size_pressure(stroke->brush)) { + /* use pressure to modify size. set spacing so that at 100%, the circles + * are aligned nicely with no overlap. for this the spacing needs to be + * the average of the previous and next size. */ + float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure); + float q = s * dpressure / (2.0f * length); + float pressure_fac = (1.0f + q) / (1.0f - q); + + float last_size_pressure = stroke->last_pressure; + float new_size_pressure = stroke->last_pressure * pressure_fac; + + /* average spacing */ + float last_size = paint_space_get_final_size_intern( + C, scene, stroke, last_size_pressure, pressure); + float new_size = paint_space_get_final_size_intern( + C, scene, stroke, new_size_pressure, pressure); + + return 0.5f * (last_size + new_size); + } + else { + return paint_space_get_final_size_intern(C, scene, stroke, 1.0, 0.0); + } +} + static float paint_space_stroke_spacing_variable(bContext *C, const Scene *scene, PaintStroke *stroke, @@ -797,6 +800,8 @@ static float paint_space_stroke_spacing_variable(bContext *C, return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure); } +#include "BLI_compiler_attrs.h" + /* For brushes with stroke spacing enabled, moves mouse in steps * towards the final mouse location. */ static int paint_space_stroke(bContext *C, @@ -866,8 +871,17 @@ static int paint_space_stroke(bContext *C, ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, spacing / no_pressure_spacing); + if (use_scene_spacing) { + float size = paint_space_get_final_size(C, scene, stroke, pressure, dpressure, length); + + stroke->stroke_distance += stroke->ups->pixel_radius * spacing / size; + stroke->stroke_distance_t += spacing / size; + } + else { + stroke->stroke_distance += spacing / stroke->zoom_2d; + stroke->stroke_distance_t += (spacing / stroke->zoom_2d) / stroke->ups->pixel_radius; + } - stroke->stroke_distance += spacing / stroke->zoom_2d; paint_brush_stroke_add_step(C, op, mouse, pressure); length -= spacing; @@ -1233,6 +1247,8 @@ static void paint_line_strokes_spacing(bContext *C, length += *length_residue; *length_residue = 0.0; + stroke->spacing = spacing; + if (length >= spacing) { if (use_scene_spacing) { float final_world_space_position[3]; @@ -1250,6 +1266,8 @@ static void paint_line_strokes_spacing(bContext *C, ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0); stroke->stroke_distance += spacing / stroke->zoom_2d; + stroke->stroke_distance_t += (spacing / stroke->zoom_2d) / stroke->ups->pixel_radius; + paint_brush_stroke_add_step(C, op, mouse, 1.0); length -= spacing; @@ -1472,6 +1490,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) /* one time stroke initialization */ if (!stroke->stroke_started) { stroke->last_pressure = sample_average.pressure; + copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); if (paint_stroke_use_scene_spacing(br, mode)) { stroke->stroke_over_mesh = SCULPT_stroke_get_location( @@ -1558,6 +1577,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) float dmouse[2]; sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position); stroke->stroke_distance += len_v2(dmouse); + stroke->stroke_distance_t += len_v2(dmouse) / stroke->ups->pixel_radius; + paint_brush_stroke_add_step(C, op, mouse, pressure); redraw = true; } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 83388c1aef2..f7785df0d3c 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -46,6 +46,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_ccg.h" #include "BKE_colortools.h" @@ -111,12 +112,34 @@ void SCULPT_vertex_random_access_ensure(SculptSession *ss) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + if (ss->bm) { + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); BM_mesh_elem_table_ensure(ss->bm, BM_VERT); } } +/* Sculpt PBVH abstraction API + * + * This is read-only, for writing use PBVH vertex iterators. There vd.index matches + * the indices used here. + * + * For multi-resolution, the same vertex in multiple grids is counted multiple times, with + * different index for each grid. */ + +void SCULPT_face_random_access_ensure(SculptSession *ss) +{ + if (ss->bm) { + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; + + BM_mesh_elem_index_ensure(ss->bm, BM_FACE); + BM_mesh_elem_table_ensure(ss->bm, BM_FACE); + } +} + int SCULPT_vertex_count_get(SculptSession *ss) { switch (BKE_pbvh_type(ss->pbvh)) { @@ -131,22 +154,39 @@ int SCULPT_vertex_count_get(SculptSession *ss) return 0; } -const float *SCULPT_vertex_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BMVert *v = (BMVert *)vertex.i; + return BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v)->origco; + } + + // XXX implement me + return SCULPT_vertex_co_get(ss, vertex); +} + +const float *SCULPT_vertex_co_get(SculptSession *ss, SculptVertRef index) +{ + if (ss->bm) { + return ((BMVert *)index.i)->co; + } + switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { if (ss->shapekey_active || ss->deform_modifiers_active) { const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); - return mverts[index].co; + return mverts[index.i].co; } - return ss->mvert[index].co; + return ss->mvert[index.i].co; + } + case PBVH_BMESH: { + BMVert *v = (BMVert *)index.i; + return v->co; } - case PBVH_BMESH: - return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = index.i / key->grid_area; + const int vertex_index = index.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index)); } @@ -154,41 +194,53 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index) return NULL; } -const float *SCULPT_vertex_color_get(SculptSession *ss, int index) +const float *SCULPT_vertex_color_get(SculptSession *ss, SculptVertRef index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: if (ss->vcol) { - return ss->vcol[index].color; + return ss->vcol[index.i].color; } break; - case PBVH_BMESH: + case PBVH_BMESH: { + BMVert *v = (BMVert *)index.i; + + if (ss->cd_vcol_offset >= 0) { + MPropCol *col = BM_ELEM_CD_GET_VOID_P(v, ss->cd_vcol_offset); + return col->color; + } + + break; + } case PBVH_GRIDS: break; } return NULL; } -void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_normal_get(SculptSession *ss, SculptVertRef index, float no[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { if (ss->shapekey_active || ss->deform_modifiers_active) { const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); - normal_short_to_float_v3(no, mverts[index].no); + normal_short_to_float_v3(no, mverts[index.i].no); } else { - normal_short_to_float_v3(no, ss->mvert[index].no); + normal_short_to_float_v3(no, ss->mvert[index.i].no); } break; } - case PBVH_BMESH: - copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no); + case PBVH_BMESH: { + BMVert *v = (BMVert *)index.i; + + copy_v3_v3(no, v->no); break; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = index.i / key->grid_area; + const int vertex_index = index.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; copy_v3_v3(no, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); break; @@ -196,32 +248,48 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) } } -const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, + SculptVertRef index, + int cd_pers_co) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + if (cd_pers_co >= 0) { + BMVert *v = (BMVert *)index.i; + float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co); + + return co; + } + + return SCULPT_vertex_co_get(ss, index); + } + if (ss->persistent_base) { - return ss->persistent_base[index].co; + int i = BKE_pbvh_vertex_index_to_table(ss->pbvh, index); + + return ss->persistent_base[i].co; } + return SCULPT_vertex_co_get(ss, index); } -const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index) +const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, SculptVertRef vertex) { /* Always grab active shape key if the sculpt happens on shapekey. */ if (ss->shapekey_active) { const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); - return mverts[index].co; + return mverts[BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)].co; } /* Sculpting on the base mesh. */ if (ss->mvert) { - return ss->mvert[index].co; + return ss->mvert[BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)].co; } /* Everything else, such as sculpting on multires. */ - return SCULPT_vertex_co_get(ss, index); + return SCULPT_vertex_co_get(ss, vertex); } -void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]) +void SCULPT_vertex_limit_surface_get(SculptSession *ss, SculptVertRef index, float r_co[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: @@ -230,8 +298,8 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3] break; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = index.i / key->grid_area; + const int vertex_index = index.i - grid_index * key->grid_area; SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, @@ -242,30 +310,172 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3] } } -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, + SculptVertRef index, + float no[3], + int cd_pers_no) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + if (cd_pers_no >= 0) { + BMVert *v = (BMVert *)index.i; + float(*no2)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no); + + copy_v3_v3(no, (float *)no2); + return; + } + + SCULPT_vertex_normal_get(ss, index, no); + return; + } + if (ss->persistent_base) { - copy_v3_v3(no, ss->persistent_base[index].no); + copy_v3_v3(no, ss->persistent_base[BKE_pbvh_vertex_index_to_table(ss->pbvh, index)].no); return; } SCULPT_vertex_normal_get(ss, index, no); } -float SCULPT_vertex_mask_get(SculptSession *ss, int index) +static bool sculpt_temp_customlayer_get(SculptSession *ss, + AttributeDomain domain, + int proptype, + char *name, + SculptCustomLayer *out, + bool autocreate) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + CustomData *cdata = NULL; + + if (!ss->bm) { + return false; + } + + switch (domain) { + case ATTR_DOMAIN_POINT: + cdata = &ss->bm->vdata; + break; + case ATTR_DOMAIN_FACE: + cdata = &ss->bm->pdata; + break; + default: + return false; + } + + int idx = CustomData_get_named_layer_index(cdata, proptype, name); + + if (idx < 0) { + if (!autocreate) { + return false; + } + + BM_data_layer_add_named(ss->bm, cdata, proptype, name); + idx = CustomData_get_named_layer_index(cdata, proptype, name); + cdata->layers[idx].flag |= CD_FLAG_TEMPORARY; + SCULPT_dyntopo_node_layers_update_offsets(ss); + } + + out->data = NULL; + out->is_cdlayer = true; + out->layer = cdata->layers + idx; + out->cd_offset = out->layer->offset; + out->elemsize = CustomData_get_elem_size(out->layer); + + break; + } + case PBVH_FACES: { + CustomData *cdata = NULL; + int totelem = 0; + + switch (domain) { + case ATTR_DOMAIN_POINT: + totelem = ss->totvert; + cdata = ss->vdata; + break; + case ATTR_DOMAIN_FACE: + totelem = ss->totfaces; + cdata = ss->pdata; + break; + default: + return false; + } + + int idx = CustomData_get_named_layer_index(cdata, proptype, name); + + if (idx < 0) { + if (!autocreate) { + return false; + } + + CustomData_add_layer_named(cdata, proptype, CD_CALLOC, NULL, totelem, name); + idx = CustomData_get_named_layer_index(cdata, proptype, name); + + cdata->layers[idx].flag |= CD_FLAG_TEMPORARY; + } + + out->data = NULL; + out->is_cdlayer = true; + out->layer = cdata->layers + idx; + out->cd_offset = -1; + out->data = out->layer->data; + out->elemsize = CustomData_get_elem_size(out->layer); + break; + } + case PBVH_GRIDS: { + CustomData *cdata = NULL; + int totelem = 0; + + switch (domain) { + case ATTR_DOMAIN_POINT: + totelem = BKE_pbvh_get_grid_num_vertices(ss->pbvh); + cdata = &ss->temp_vdata; + break; + case ATTR_DOMAIN_FACE: + // not supported + return false; + default: + return false; + } + + int idx = CustomData_get_named_layer_index(cdata, proptype, name); + + if (idx < 0) { + if (!autocreate) { + return false; + } + + CustomData_add_layer_named(cdata, proptype, CD_CALLOC, NULL, totelem, name); + idx = CustomData_get_named_layer_index(cdata, proptype, name); + } + + out->data = NULL; + out->is_cdlayer = true; + out->layer = cdata->layers + idx; + out->cd_offset = -1; + out->data = out->layer->data; + out->elemsize = CustomData_get_elem_size(out->layer); + + break; + } + } + + return true; +} + +float SCULPT_vertex_mask_get(SculptSession *ss, SculptVertRef index) { BMVert *v; float *mask; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return ss->vmask[index]; + return ss->vmask[index.i]; case PBVH_BMESH: - v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); + v = (BMVert *)index.i; mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); return *mask; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = index.i / key->grid_area; + const int vertex_index = index.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; return *CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)); } @@ -274,12 +484,27 @@ float SCULPT_vertex_mask_get(SculptSession *ss, int index) return 0.0f; } -int SCULPT_active_vertex_get(SculptSession *ss) +bool SCULPT_temp_customlayer_ensure(SculptSession *ss, + AttributeDomain domain, + int proptype, + char *name) +{ + SculptCustomLayer scl; + return sculpt_temp_customlayer_get(ss, domain, proptype, name, &scl, true); +} + +bool SCULPT_temp_customlayer_get( + SculptSession *ss, AttributeDomain domain, int proptype, char *name, SculptCustomLayer *scl) +{ + return sculpt_temp_customlayer_get(ss, domain, proptype, name, scl, true); +} + +SculptVertRef SCULPT_active_vertex_get(SculptSession *ss) { if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) { return ss->active_vertex_index; } - return 0; + return BKE_pbvh_make_vref(0); } const float *SCULPT_active_vertex_co_get(SculptSession *ss) @@ -332,44 +557,49 @@ int SCULPT_active_face_set_get(SculptSession *ss) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return ss->face_sets[ss->active_face_index]; + return ss->face_sets[ss->active_face_index.i]; case PBVH_GRIDS: { const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, ss->active_grid_index); return ss->face_sets[face_index]; } case PBVH_BMESH: + if (ss->cd_faceset_offset && ss->active_face_index.i) { + BMFace *f = (BMFace *)ss->active_face_index.i; + return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + } + return SCULPT_FACE_SET_NONE; } return SCULPT_FACE_SET_NONE; } -void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible) +void SCULPT_vertex_visible_set(SculptSession *ss, SculptVertRef index, bool visible) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - SET_FLAG_FROM_TEST(ss->mvert[index].flag, !visible, ME_HIDE); - ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE; + SET_FLAG_FROM_TEST(ss->mvert[index.i].flag, !visible, ME_HIDE); + ss->mvert[index.i].flag |= ME_VERT_PBVH_UPDATE; break; case PBVH_BMESH: - BM_elem_flag_set(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN, !visible); + BM_elem_flag_set((BMVert *)index.i, BM_ELEM_HIDDEN, !visible); break; case PBVH_GRIDS: break; } } -bool SCULPT_vertex_visible_get(SculptSession *ss, int index) +bool SCULPT_vertex_visible_get(SculptSession *ss, SculptVertRef index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return !(ss->mvert[index].flag & ME_HIDE); + return !(ss->mvert[index.i].flag & ME_HIDE); case PBVH_BMESH: - return !BM_elem_flag_test(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN); + return !BM_elem_flag_test(((BMVert *)index.i), BM_ELEM_HIDDEN); case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = index.i / key->grid_area; + const int vertex_index = index.i - grid_index * key->grid_area; BLI_bitmap **grid_hidden = BKE_pbvh_get_grid_visibility(ss->pbvh); if (grid_hidden && grid_hidden[grid_index]) { return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index); @@ -396,8 +626,28 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl } } break; - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMFace *f; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + if (abs(fset) != face_set) { + continue; + } + + if (visible) { + fset = abs(fset); + } + else { + fset = -abs(fset); + } + + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); + } break; + } } } @@ -410,8 +660,19 @@ void SCULPT_face_sets_visibility_invert(SculptSession *ss) ss->face_sets[i] *= -1; } break; - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMFace *f; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + fset = -fset; + + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); + } break; + } } } @@ -437,48 +698,108 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible) } } break; - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMFace *f; + + if (!ss->bm) { + return; + } + + // paranoia check of cd_faceset_offset + if (ss->cd_faceset_offset < 0) { + ss->cd_faceset_offset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS); + } + if (ss->cd_faceset_offset < 0) { + return; + } + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + /* This can run on geometry without a face set assigned, so its ID sign can't be changed to + * modify the visibility. Force that geometry to the ID 1 to enable changing the visibility + * here. */ + + if (fset == SCULPT_FACE_SET_NONE) { + fset = 1; + } + + if (visible) { + fset = abs(fset); + } + else { + fset = -abs(fset); + } + + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); + } break; + } } } -bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index) +bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, SculptVertRef index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + MeshElemMap *vert_map = &ss->pmap[index.i]; + for (int j = 0; j < ss->pmap[index.i].count; j++) { if (ss->face_sets[vert_map->indices[j]] > 0) { return true; } } return false; } - case PBVH_BMESH: - return true; + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)index.i; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset); + if (fset >= 0) { + return true; + } + } + + return false; + } case PBVH_GRIDS: return true; } return true; } -bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index) +bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, SculptVertRef index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + MeshElemMap *vert_map = &ss->pmap[index.i]; + for (int j = 0; j < ss->pmap[index.i].count; j++) { if (ss->face_sets[vert_map->indices[j]] < 0) { return false; } } return true; } - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)index.i; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset); + if (fset < 0) { + return false; + } + } + return true; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + const int grid_index = index.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); return ss->face_sets[face_index] > 0; } @@ -486,22 +807,34 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index) return true; } -void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) +void SCULPT_vertex_face_set_set(SculptSession *ss, SculptVertRef index, int face_set) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + MeshElemMap *vert_map = &ss->pmap[index.i]; + for (int j = 0; j < ss->pmap[index.i].count; j++) { if (ss->face_sets[vert_map->indices[j]] > 0) { ss->face_sets[vert_map->indices[j]] = abs(face_set); } } } break; - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)index.i; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset); + if (fset >= 0) { + BM_ELEM_CD_SET_INT(l->f, ss->cd_faceset_offset, abs(face_set)); + } + } + break; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + const int grid_index = index.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); if (ss->face_sets[face_index] > 0) { ss->face_sets[face_index] = abs(face_set); @@ -511,24 +844,39 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) } } -int SCULPT_vertex_face_set_get(SculptSession *ss, int index) +int SCULPT_vertex_face_set_get(SculptSession *ss, SculptVertRef index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; + MeshElemMap *vert_map = &ss->pmap[index.i]; int face_set = 0; - for (int i = 0; i < ss->pmap[index].count; i++) { + for (int i = 0; i < ss->pmap[index.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] > face_set) { face_set = abs(ss->face_sets[vert_map->indices[i]]); } } return face_set; } - case PBVH_BMESH: - return 0; + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)index.i; + int ret = -1; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset); + fset = abs(fset); + + if (fset > ret) { + ret = fset; + } + } + + return ret; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + const int grid_index = index.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); return ss->face_sets[face_index]; } @@ -536,23 +884,40 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index) return 0; } -bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set) +bool SCULPT_vertex_has_face_set(SculptSession *ss, SculptVertRef index, int face_set) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int i = 0; i < ss->pmap[index].count; i++) { + MeshElemMap *vert_map = &ss->pmap[index.i]; + for (int i = 0; i < ss->pmap[index.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] == face_set) { return true; } } return false; } - case PBVH_BMESH: - return true; + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)index.i; + + if (ss->cd_faceset_offset == -1) { + return false; + } + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + BMFace *f = l->f; + + if (abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)) == abs(face_set)) { + return true; + } + } + + return false; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + const int grid_index = index.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); return ss->face_sets[face_index] == face_set; } @@ -560,6 +925,54 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set) return true; } +/* +calcs visibility state based on face sets. +todo: also calc a face set boundary flag. +*/ +void sculpt_vertex_faceset_update_bmesh(SculptSession *ss, SculptVertRef vert) +{ + if (!ss->bm) { + return; + } + + BMVert *v = (BMVert *)vert.i; + BMEdge *e = v->e; + bool ok = false; + const int cd_faceset_offset = ss->cd_faceset_offset; + + if (!e) { + return; + } + + do { + BMLoop *l = e->l; + if (l) { + do { + if (BM_ELEM_CD_GET_INT(l->f, cd_faceset_offset) > 0) { + ok = true; + break; + } + + l = l->radial_next; + } while (l != e->l); + + if (ok) { + break; + } + } + e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + + MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, ss->cd_dyn_vert); + + if (ok) { + mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN; + } + else { + mv->flag |= DYNVERT_VERT_FSET_HIDDEN; + } +} + void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob) { SculptSession *ss = ob->sculpt; @@ -574,16 +987,58 @@ void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob) BKE_sculpt_sync_face_sets_visibility_to_grids(mesh, ss->subdiv_ccg); break; } - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMFace *f; + BMVert *v; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + if (fset < 0) { + BM_elem_flag_enable(f, BM_ELEM_HIDDEN); + } + else { + BM_elem_flag_disable(f, BM_ELEM_HIDDEN); + } + } + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, ss->cd_dyn_vert); + + BMIter iter2; + BMLoop *l; + + int visible = false; + + BM_ITER_ELEM (l, &iter2, v, BM_LOOPS_OF_VERT) { + if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { + visible = true; + break; + } + } + + if (!visible) { + mv->flag |= DYNVERT_VERT_FSET_HIDDEN; + BM_elem_flag_enable(v, BM_ELEM_HIDDEN); + } + else { + mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN; + BM_elem_flag_disable(v, BM_ELEM_HIDDEN); + } + } break; + } } } static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSession *ss, - int index) + SculptVertRef vertex) { + int index = (int)vertex.i; MeshElemMap *vert_map = &ss->pmap[index]; - const bool visible = SCULPT_vertex_visible_get(ss, index); + const bool visible = SCULPT_vertex_visible_get(ss, vertex); + for (int i = 0; i < ss->pmap[index].count; i++) { if (visible) { ss->face_sets[vert_map->indices[i]] = abs(ss->face_sets[vert_map->indices[i]]); @@ -597,28 +1052,68 @@ static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSe void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - for (int i = 0; i < ss->totfaces; i++) { - MPoly *poly = &ss->mpoly[i]; - bool poly_visible = true; - for (int l = 0; l < poly->totloop; l++) { - MLoop *loop = &ss->mloop[poly->loopstart + l]; - if (!SCULPT_vertex_visible_get(ss, (int)loop->v)) { - poly_visible = false; + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + for (int i = 0; i < ss->totfaces; i++) { + MPoly *poly = &ss->mpoly[i]; + bool poly_visible = true; + for (int l = 0; l < poly->totloop; l++) { + MLoop *loop = &ss->mloop[poly->loopstart + l]; + if (!SCULPT_vertex_visible_get(ss, BKE_pbvh_make_vref(loop->v))) { + poly_visible = false; + } + } + if (poly_visible) { + ss->face_sets[i] = abs(ss->face_sets[i]); + } + else { + ss->face_sets[i] = -abs(ss->face_sets[i]); } } - if (poly_visible) { - ss->face_sets[i] = abs(ss->face_sets[i]); + break; + } + case PBVH_GRIDS: + break; + case PBVH_BMESH: { + BMIter iter; + BMFace *f; + + if (!ss->bm) { + return; } - else { - ss->face_sets[i] = -abs(ss->face_sets[i]); + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + bool visible = true; + + do { + if (BM_elem_flag_test(l->v, BM_ELEM_HIDDEN)) { + visible = false; + break; + } + l = l->next; + } while (l != f->l_first); + + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + if (visible) { + fset = abs(fset); + } + else { + fset = -abs(fset); + } + + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); } + + break; } } } -static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index) +static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, SculptVertRef vertex) { + int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex); + MeshElemMap *vert_map = &ss->pmap[index]; int face_set = -1; for (int i = 0; i < ss->pmap[index].count; i++) { @@ -666,18 +1161,41 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss return true; } -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index) +bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, SculptVertRef index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { return sculpt_check_unique_face_set_in_base_mesh(ss, index); } - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)index.i; + int face_set = 0; + bool first = true; + + if (ss->cd_faceset_offset == -1) { + return false; + } + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + BMFace *f = l->f; + int face_set2 = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + if (!first && abs(face_set2) != abs(face_set)) { + return false; + } + + first = false; + face_set = face_set2; + } + return true; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = index.i / key->grid_area; + const int vertex_index = index.i - grid_index * key->grid_area; const SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, .y = vertex_index / key->grid_size}; @@ -686,7 +1204,7 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index) ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); switch (adjacency) { case SUBDIV_CCG_ADJACENT_VERTEX: - return sculpt_check_unique_face_set_in_base_mesh(ss, v1); + return sculpt_check_unique_face_set_in_base_mesh(ss, BKE_pbvh_make_vref(v1)); case SUBDIV_CCG_ADJACENT_EDGE: return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2); case SUBDIV_CCG_ADJACENT_NONE: @@ -711,8 +1229,24 @@ int SCULPT_face_set_next_available_get(SculptSession *ss) next_face_set++; return next_face_set; } - case PBVH_BMESH: - return 0; + case PBVH_BMESH: { + int next_face_set = 0; + BMIter iter; + BMFace *f; + if (!ss->cd_faceset_offset) { + return 0; + } + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)); + if (fset > next_face_set) { + next_face_set = fset; + } + } + + next_face_set++; + return next_face_set; + } } return 0; } @@ -721,10 +1255,12 @@ int SCULPT_face_set_next_available_get(SculptSession *ss) #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 -static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index) +static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, + SculptVertRef neighbor, + int neighbor_index) { for (int i = 0; i < iter->size; i++) { - if (iter->neighbors[i] == neighbor_index) { + if (iter->neighbors[i].i == neighbor.i) { return; } } @@ -733,51 +1269,134 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neigh iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; if (iter->neighbors == iter->neighbors_fixed) { - iter->neighbors = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); - memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(int) * iter->size); + iter->neighbors = MEM_mallocN(iter->capacity * sizeof(SculptVertRef), "neighbor array"); + iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); + + memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(SculptVertRef) * iter->size); + memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size); } else { iter->neighbors = MEM_reallocN_id( - iter->neighbors, iter->capacity * sizeof(int), "neighbor array"); + iter->neighbors, iter->capacity * sizeof(SculptVertRef), "neighbor array"); + iter->neighbor_indices = MEM_reallocN_id( + iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array"); } } - iter->neighbors[iter->size] = neighbor_index; + iter->neighbors[iter->size] = neighbor; + iter->neighbor_indices[iter->size] = neighbor_index; + iter->size++; +} + +static void sculpt_vertex_neighbor_add_nocheck(SculptVertexNeighborIter *iter, + SculptVertRef neighbor, + int neighbor_index) +{ + if (iter->size >= iter->capacity) { + iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + + if (iter->neighbors == iter->neighbors_fixed) { + iter->neighbors = MEM_mallocN(iter->capacity * sizeof(SculptVertRef), "neighbor array"); + iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); + + memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(SculptVertRef) * iter->size); + memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size); + } + else { + iter->neighbors = MEM_reallocN_id( + iter->neighbors, iter->capacity * sizeof(SculptVertRef), "neighbor array"); + iter->neighbor_indices = MEM_reallocN_id( + iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array"); + } + } + + iter->neighbors[iter->size] = neighbor; + iter->neighbor_indices[iter->size] = neighbor_index; iter->size++; } static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss, - int index, + SculptVertRef index, SculptVertexNeighborIter *iter) { - BMVert *v = BM_vert_at_index(ss->bm, index); + BMVert *v = (BMVert *)index.i; BMIter liter; BMLoop *l; + + iter->is_duplicate = false; iter->size = 0; iter->num_duplicates = 0; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; + iter->neighbor_indices = iter->neighbor_indices_fixed; + iter->i = 0; - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { - const BMVert *v_other = adj_v[i]; - if (BM_elem_index_get(v_other) != (int)index) { - sculpt_vertex_neighbor_add(iter, BM_elem_index_get(v_other)); - } - } + // cache profiling revealed a hotspot here, don't use BM_ITER + BMEdge *e = v->e; + + if (!v->e) { + return; } + + const bool have_facesets = ss->cd_faceset_offset >= 0; + const int cd_faceset_offset = ss->cd_faceset_offset; + + do { + BMVert *v2; + BMEdge *e2; + + if (v == e->v1) { + v2 = e->v2; + e2 = e->v1_disk_link.next; + } + else { + v2 = e->v1; + e2 = e->v2_disk_link.next; + } + + // TODO: cache this in MDynTopoVert to avoid excessive DRAM fetches + BMLoop *l = e->l; + bool ok = false; + +#if 0 + if (l && have_facesets) { + do { + if (BM_ELEM_CD_GET_INT(l->f, cd_faceset_offset) > 0) { + ok = true; + break; + } + + l = l->radial_next; + } while (l != e->l); + } + else { + ok = true; + } +#else + ok = true; +#endif + + e = e2; + if (ok) { + sculpt_vertex_neighbor_add_nocheck( + iter, BKE_pbvh_make_vref((intptr_t)v2), BM_elem_index_get(v2)); + } + } while (e != v->e); } static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, - int index, + SculptVertRef vertex, SculptVertexNeighborIter *iter) { + int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex); + MeshElemMap *vert_map = &ss->pmap[index]; iter->size = 0; iter->num_duplicates = 0; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; + iter->neighbor_indices = iter->neighbor_indices_fixed; + iter->is_duplicate = false; for (int i = 0; i < ss->pmap[index].count; i++) { if (ss->face_sets[vert_map->indices[i]] < 0) { @@ -789,7 +1408,7 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) { for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { if (f_adj_v[j] != index) { - sculpt_vertex_neighbor_add(iter, f_adj_v[j]); + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]); } } } @@ -797,17 +1416,21 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, if (ss->fake_neighbors.use_fake_neighbors) { BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); - if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) { - sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]); + if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add(iter, + ss->fake_neighbors.fake_neighbor_index[index], + ss->fake_neighbors.fake_neighbor_index[index].i); } } } static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, - const int index, + const SculptVertRef vertex, const bool include_duplicates, SculptVertexNeighborIter *iter) { + int index = (int)vertex.i; + /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, * maybe provide coordinate and mask pointers directly rather than converting * back and forth between #CCGElem and global index. */ @@ -822,21 +1445,27 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, SubdivCCGNeighbors neighbors; BKE_subdiv_ccg_neighbor_coords_get(ss->subdiv_ccg, &coord, include_duplicates, &neighbors); + iter->is_duplicate = include_duplicates; + iter->size = 0; iter->num_duplicates = neighbors.num_duplicates; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; + iter->neighbor_indices = iter->neighbor_indices_fixed; for (int i = 0; i < neighbors.size; i++) { - sculpt_vertex_neighbor_add(iter, - neighbors.coords[i].grid_index * key->grid_area + - neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x); + int idx = neighbors.coords[i].grid_index * key->grid_area + + neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x; + + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(idx), idx); } if (ss->fake_neighbors.use_fake_neighbors) { BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); - if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) { - sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]); + if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add(iter, + ss->fake_neighbors.fake_neighbor_index[index], + ss->fake_neighbors.fake_neighbor_index[index].i); } } @@ -846,44 +1475,47 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, } void SCULPT_vertex_neighbors_get(SculptSession *ss, - const int index, + const SculptVertRef vertex, const bool include_duplicates, SculptVertexNeighborIter *iter) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - sculpt_vertex_neighbors_get_faces(ss, index, iter); + sculpt_vertex_neighbors_get_faces(ss, vertex, iter); return; case PBVH_BMESH: - sculpt_vertex_neighbors_get_bmesh(ss, index, iter); + sculpt_vertex_neighbors_get_bmesh(ss, vertex, iter); return; case PBVH_GRIDS: - sculpt_vertex_neighbors_get_grids(ss, index, include_duplicates, iter); + sculpt_vertex_neighbors_get_grids(ss, vertex, include_duplicates, iter); return; } } -static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, const int index) +static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, + const SculptVertRef index) { BLI_assert(ss->vertex_info.boundary); - return BLI_BITMAP_TEST(ss->vertex_info.boundary, index); + return BLI_BITMAP_TEST(ss->vertex_info.boundary, + BKE_pbvh_vertex_index_to_table(ss->pbvh, index)); } -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index) +bool SCULPT_vertex_is_boundary(const SculptSession *ss, const SculptVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, ((BMVert *)(vertex.i))); + return mv->flag & DYNVERT_BOUNDARY; + } case PBVH_FACES: { - if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) { + if (!SCULPT_vertex_all_face_sets_visible_get(ss, vertex)) { return true; } - return sculpt_check_boundary_vertex_in_base_mesh(ss, index); - } - case PBVH_BMESH: { - BMVert *v = BM_vert_at_index(ss->bm, index); - return BM_vert_is_boundary(v); + return sculpt_check_boundary_vertex_in_base_mesh(ss, vertex); } case PBVH_GRIDS: { + int index = (int)vertex.i; const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = index / key->grid_area; const int vertex_index = index - grid_index * key->grid_area; @@ -895,10 +1527,10 @@ bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index) ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); switch (adjacency) { case SUBDIV_CCG_ADJACENT_VERTEX: - return sculpt_check_boundary_vertex_in_base_mesh(ss, v1); + return sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v1)); case SUBDIV_CCG_ADJACENT_EDGE: - return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2); + return sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v1)) && + sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v2)); case SUBDIV_CCG_ADJACENT_NONE: return false; } @@ -962,6 +1594,7 @@ bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3], typedef struct NearestVertexTLSData { int nearest_vertex_index; + SculptVertRef nearest_vertex; float nearest_vertex_distance_squared; } NearestVertexTLSData; @@ -979,6 +1612,7 @@ static void do_nearest_vertex_get_task_cb(void *__restrict userdata, if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { nvtd->nearest_vertex_index = vd.index; + nvtd->nearest_vertex = vd.vertex; nvtd->nearest_vertex_distance_squared = distance_squared; } } @@ -993,15 +1627,17 @@ static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata), NearestVertexTLSData *nvtd = chunk; if (join->nearest_vertex_index == -1) { join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } } -int SCULPT_nearest_vertex_get( +SculptVertRef SCULPT_nearest_vertex_get( Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original) { SculptSession *ss = ob->sculpt; @@ -1016,7 +1652,7 @@ int SCULPT_nearest_vertex_get( }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); if (totnode == 0) { - return -1; + return BKE_pbvh_make_vref(-1); } SculptThreadedTaskData task_data = { @@ -1029,6 +1665,7 @@ int SCULPT_nearest_vertex_get( copy_v3_v3(task_data.nearest_vertex_search_co, co); NearestVertexTLSData nvtd; nvtd.nearest_vertex_index = -1; + nvtd.nearest_vertex.i = -1; nvtd.nearest_vertex_distance_squared = FLT_MAX; TaskParallelSettings settings; @@ -1040,7 +1677,7 @@ int SCULPT_nearest_vertex_get( MEM_SAFE_FREE(nodes); - return nvtd.nearest_vertex_index; + return nvtd.nearest_vertex; } bool SCULPT_is_symmetry_iteration_valid(char i, char symm) @@ -1091,23 +1728,29 @@ void SCULPT_floodfill_init(SculptSession *ss, SculptFloodFill *flood) int vertex_count = SCULPT_vertex_count_get(ss); SCULPT_vertex_random_access_ensure(ss); - flood->queue = BLI_gsqueue_new(sizeof(int)); + flood->queue = BLI_gsqueue_new(sizeof(SculptVertRef)); flood->visited_vertices = BLI_BITMAP_NEW(vertex_count, "visited vertices"); } -void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index) +void SCULPT_floodfill_add_initial(SculptFloodFill *flood, SculptVertRef vertex) { - BLI_gsqueue_push(flood->queue, &index); + BLI_gsqueue_push(flood->queue, &vertex); } -void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index) +void SCULPT_floodfill_add_and_skip_initial(SculptSession *ss, + SculptFloodFill *flood, + SculptVertRef vertex) { - BLI_gsqueue_push(flood->queue, &index); - BLI_BITMAP_ENABLE(flood->visited_vertices, index); + BLI_gsqueue_push(flood->queue, &vertex); + BLI_BITMAP_ENABLE(flood->visited_vertices, BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)); } -void SCULPT_floodfill_add_initial_with_symmetry( - Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius) +void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd, + Object *ob, + SculptSession *ss, + SculptFloodFill *flood, + SculptVertRef vertex, + float radius) { /* Add active vertex and symmetric vertices to the queue. */ const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -1115,18 +1758,18 @@ void SCULPT_floodfill_add_initial_with_symmetry( if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { continue; } - int v = -1; + SculptVertRef v = {-1}; if (i == 0) { - v = index; + v = vertex; } else if (radius > 0.0f) { float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius; float location[3]; - flip_v3_v3(location, SCULPT_vertex_co_get(ss, index), i); + flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i); v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false); } - if (v != -1) { + if (v.i != -1) { SCULPT_floodfill_add_initial(flood, v); } } @@ -1141,7 +1784,9 @@ void SCULPT_floodfill_add_active( if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { continue; } - int v = -1; + + SculptVertRef v = {-1}; + if (i == 0) { v = SCULPT_active_vertex_get(ss); } @@ -1151,26 +1796,31 @@ void SCULPT_floodfill_add_active( v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false); } - if (v != -1) { + if (v.i != -1) { SCULPT_floodfill_add_initial(flood, v); } } } -void SCULPT_floodfill_execute( - SculptSession *ss, - SculptFloodFill *flood, - bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata), - void *userdata) +void SCULPT_floodfill_execute(SculptSession *ss, + SculptFloodFill *flood, + bool (*func)(SculptSession *ss, + SculptVertRef from_v, + SculptVertRef to_v, + bool is_duplicate, + void *userdata), + void *userdata) { while (!BLI_gsqueue_is_empty(flood->queue)) { - int from_v; + SculptVertRef from_v; BLI_gsqueue_pop(flood->queue, &from_v); SculptVertexNeighborIter ni; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - const int to_v = ni.index; + const SculptVertRef to_v = ni.vertex; + + const int to_index = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v); - if (BLI_BITMAP_TEST(flood->visited_vertices, to_v)) { + if (BLI_BITMAP_TEST(flood->visited_vertices, to_index)) { continue; } @@ -1178,7 +1828,7 @@ void SCULPT_floodfill_execute( continue; } - BLI_BITMAP_ENABLE(flood->visited_vertices, to_v); + BLI_BITMAP_ENABLE(flood->visited_vertices, to_index); if (func(ss, from_v, to_v, ni.is_duplicate, userdata)) { BLI_gsqueue_push(flood->queue, &to_v); @@ -1213,6 +1863,8 @@ static bool sculpt_tool_needs_original(const char sculpt_tool) SCULPT_TOOL_DRAW_SHARP, SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_SMOOTH, + SCULPT_TOOL_PAINT, + SCULPT_TOOL_VCOL_BOUNDARY, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_POSE); } @@ -1285,10 +1937,14 @@ void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, Scul memset(data, 0, sizeof(*data)); data->unode = unode; + data->pbvh = ss->pbvh; + data->ss = ss; + if (bm) { data->bm_log = ss->bm_log; } else { + data->datatype = data->unode->type; data->coords = data->unode->co; data->normals = data->unode->no; data->vmasks = data->unode->mask; @@ -1300,11 +1956,21 @@ void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, Scul * Initialize a #SculptOrigVertData for accessing original vertex data; * handles #BMesh, #Mesh, and multi-resolution. */ -void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node) +void SCULPT_orig_vert_data_init(SculptOrigVertData *data, + Object *ob, + PBVHNode *node, + SculptUndoType type) { - SculptUndoNode *unode; - unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS); + SculptUndoNode *unode = NULL; + data->ss = ob->sculpt; + + /*do not allocate an undo node for bmesh pbvh*/ + if (!ob->sculpt->bm) { + unode = SCULPT_undo_push_node(ob, node, type); + } + SCULPT_orig_vert_data_unode_init(data, ob, unode); + data->datatype = type; } /** @@ -1312,21 +1978,57 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode * */ void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter) { - if (orig_data->unode->type == SCULPT_UNDO_COORDS) { + // check if we need to update original data for current stroke + if (orig_data->bm_log) { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert); + if (mv->stroke_id != orig_data->ss->stroke_id) { + mv->stroke_id = orig_data->ss->stroke_id; + + copy_v3_v3(mv->origco, iter->bm_vert->co); + copy_v3_v3(mv->origno, iter->bm_vert->no); + + const int cd_vcol = iter->cd_vcol_offset; + const int cd_mask = iter->cd_vert_mask_offset; + + if (cd_vcol >= 0) { + MPropCol *col = BM_ELEM_CD_GET_VOID_P(iter->bm_vert, cd_vcol); + copy_v4_v4(mv->origcolor, col->color); + } + + if (cd_mask >= 0) { + mv->origmask = BM_ELEM_CD_GET_FLOAT(iter->bm_vert, cd_mask); + } + } + } + + if (orig_data->datatype == SCULPT_UNDO_COORDS) { if (orig_data->bm_log) { - BM_log_original_vert_data(orig_data->bm_log, iter->bm_vert, &orig_data->co, &orig_data->no); + orig_data->co = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origco; + + float *no = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origno; + normal_float_to_short_v3(orig_data->_no, no); + orig_data->no = orig_data->_no; + + orig_data->col = iter->cd_vcol_offset >= 0 ? + BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origcolor : + NULL; } else { orig_data->co = orig_data->coords[iter->i]; orig_data->no = orig_data->normals[iter->i]; } } - else if (orig_data->unode->type == SCULPT_UNDO_COLOR) { - orig_data->col = orig_data->colors[iter->i]; + else if (orig_data->datatype == SCULPT_UNDO_COLOR) { + if (orig_data->bm_log) { + orig_data->col = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origcolor; + } + else { + orig_data->col = orig_data->colors[iter->i]; + } } - else if (orig_data->unode->type == SCULPT_UNDO_MASK) { + else if (orig_data->datatype == SCULPT_UNDO_MASK) { if (orig_data->bm_log) { - orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); + orig_data->mask = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origmask; } else { orig_data->mask = orig_data->vmasks[iter->i]; @@ -1450,15 +2152,17 @@ static void sculpt_project_v3(const SculptProjectVector *spvc, const float vec[3 * Same goes for alt-key smoothing. */ bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush) { - return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && + return ( + (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && - (!ss->cache || (!ss->cache->alt_smooth)) && + (!ss->cache || (!ss->cache->alt_smooth)) && - /* Requires mesh restore, which doesn't work with - * dynamic-topology. */ - !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && - - SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); + /* Requires mesh restore, which doesn't work with + * dynamic-topology. */ + !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && + (brush->cached_dyntopo.flag & (DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP)) && + !(brush->cached_dyntopo.flag & DYNTOPO_DISABLED) && + SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); } /*** paint mesh ***/ @@ -1478,7 +2182,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type); } else { - unode = SCULPT_undo_get_node(data->nodes[n]); + unode = SCULPT_undo_get_node(data->nodes[n], type); } if (!unode) { @@ -2001,13 +2705,18 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, int(*orco_tris)[3]; int orco_tris_num; - BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords); + PBVHTriBuf *tribuf = BKE_pbvh_bmesh_get_tris(ss->pbvh, data->nodes[n]); + + for (int i = 0; i < tribuf->tottri; i++) { + PBVHTri *tri = tribuf->tris + i; + SculptVertRef v1 = tribuf->verts[tri->v[0]]; + SculptVertRef v2 = tribuf->verts[tri->v[1]]; + SculptVertRef v3 = tribuf->verts[tri->v[2]]; - for (int i = 0; i < orco_tris_num; i++) { - const float *co_tri[3] = { - orco_coords[orco_tris[i][0]], - orco_coords[orco_tris[i][1]], - orco_coords[orco_tris[i][2]], + const float(*co_tri[3]) = { + SCULPT_vertex_origco_get(ss, v1), + SCULPT_vertex_origco_get(ss, v2), + SCULPT_vertex_origco_get(ss, v3), }; float co[3]; @@ -2059,11 +2768,11 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, if (use_original) { if (unode->bm_entry) { - const float *temp_co; - const short *temp_no_s; - BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &temp_co, &temp_no_s); - copy_v3_v3(co, temp_co); - copy_v3_v3_short(no_s, temp_no_s); + BMVert *v = vd.bm_vert; + MDynTopoVert *mv = BKE_PBVH_DYNVERT(vd.cd_dyn_vert, v); + + normal_float_to_short_v3(no_s, mv->origno); + copy_v3_v3(co, mv->origco); } else { copy_v3_v3(co, unode->co[vd.i]); @@ -2427,7 +3136,10 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_SMOOTH: return flip * alpha * pressure * feather; - + case SCULPT_TOOL_VCOL_BOUNDARY: + return flip * alpha * pressure * feather; + case SCULPT_TOOL_UV_SMOOTH: + return flip * alpha * pressure * feather; case SCULPT_TOOL_PINCH: if (flip > 0.0f) { return alpha * flip * pressure * overlap * feather; @@ -2470,7 +3182,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, const short vno[3], const float fno[3], const float mask, - const int vertex_index, + const SculptVertRef vertex_index, const int thread_id) { StrokeCache *cache = ss->cache; @@ -2874,10 +3586,10 @@ typedef struct { float depth; bool original; - int active_vertex_index; + SculptVertRef active_vertex_index; float *face_normal; - int active_face_grid_index; + SculptFaceRef active_face_grid_index; struct IsectRayPrecalc isect_precalc; } SculptRaycastData; @@ -2899,6 +3611,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; Sculpt *sd = data->sd; const Brush *brush = data->brush; + PBVHNode *node = data->nodes[n]; float direction[3]; copy_v3_v3(direction, ss->cache->grab_delta_symmetry); @@ -2921,20 +3634,37 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + const bool use_curvature = ss->cache->brush->flag2 & BRUSH_CURVATURE_RAKE; + // const bool update_curvature = node->flag & PBVH_UpdateCurvatureDir; + const bool update_curvature = BKE_pbvh_curvature_update_get(node); + + if (use_curvature) { + SCULPT_curvature_begin(ss, node, false); + } + PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + + float direction2[3]; const float fade = bstrength * SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) * + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) * ss->cache->pressure; float avg[3], val[3]; - SCULPT_bmesh_four_neighbor_average(avg, direction, vd.bm_vert); + if (use_curvature) { + SCULPT_curvature_dir_get(ss, vd.vertex, direction2, false); + } + else { + copy_v3_v3(direction2, direction); + } + + SCULPT_bmesh_four_neighbor_average(avg, direction2, vd.bm_vert, data->rake_projection); sub_v3_v3v3(val, avg, vd.co); @@ -2955,8 +3685,20 @@ static void bmesh_topology_rake( Brush *brush = BKE_paint_brush(&sd->paint); const float strength = clamp_f(bstrength, 0.0f, 1.0f); - /* Interactions increase both strength and quality. */ - const int iterations = 3; + Brush local_brush; + + if (brush->flag2 & BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF) { + local_brush = *brush; + brush = &local_brush; + + brush->curve_preset = BRUSH_CURVE_SMOOTH; + + /*note that brush hardness is calculated from ss->cache->paint_brush, + we can't override it by changing the brush here. + this seems desirably though?*/ + } + /* Iterations increase both strength and quality. */ + const int iterations = 3 + ((int)bstrength) * 2; int iteration; const int count = iterations * strength + 1; @@ -2964,13 +3706,12 @@ static void bmesh_topology_rake( for (iteration = 0; iteration <= count; iteration++) { - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .strength = factor, - }; + SculptThreadedTaskData data = {.sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .strength = factor, + .rake_projection = brush->topology_rake_projection}; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -3000,7 +3741,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, } const float fade = SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id); + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id); if (bstrength > 0.0f) { (*vd.mask) += fade * bstrength * (1.0f - *vd.mask); @@ -3044,7 +3785,7 @@ static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) do_mask_brush_draw(sd, ob, nodes, totnode); break; case BRUSH_MASK_SMOOTH: - SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true); + SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true, 0.0f); break; } } @@ -3081,12 +3822,12 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float limit_co[3]; float disp[3]; - SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co); + SCULPT_vertex_limit_surface_get(ss, vd.vertex, limit_co); sub_v3_v3v3(disp, limit_co, vd.co); mul_v3_v3fl(proxy[vd.i], disp, fade); @@ -3146,7 +3887,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; @@ -3173,11 +3914,11 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, float weights_accum = 1.0f; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vertex_disp[3]; float vertex_disp_norm[3]; float neighbor_limit_co[3]; - SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co); + SCULPT_vertex_limit_surface_get(ss, ni.vertex, neighbor_limit_co); sub_v3_v3v3(vertex_disp, ss->cache->limit_surface_co[ni.index], ss->cache->limit_surface_co[vd.index]); @@ -3217,7 +3958,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex( PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { sub_v3_v3v3(ss->cache->prev_displacement[vd.index], - SCULPT_vertex_co_get(ss, vd.index), + SCULPT_vertex_co_get(ss, vd.vertex), ss->cache->limit_surface_co[vd.index]); } BKE_pbvh_vertex_iter_end; @@ -3229,16 +3970,20 @@ static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes SculptSession *ss = ob->sculpt; BKE_curvemapping_init(brush->curve); + SCULPT_vertex_random_access_ensure(ss); const int totvert = SCULPT_vertex_count_get(ss); if (!ss->cache->prev_displacement) { ss->cache->prev_displacement = MEM_malloc_arrayN( totvert, sizeof(float[3]), "prev displacement"); ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co"); + for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]); + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_limit_surface_get(ss, vref, ss->cache->limit_surface_co[i]); sub_v3_v3v3(ss->cache->prev_displacement[i], - SCULPT_vertex_co_get(ss, i), + SCULPT_vertex_co_get(ss, vref), ss->cache->limit_surface_co[i]); } } @@ -3290,7 +4035,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -3347,7 +4092,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; float(*proxy)[3]; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -3358,6 +4103,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3369,7 +4115,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -3429,7 +4175,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; float(*proxy)[3]; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -3440,6 +4186,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3450,7 +4197,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; float current_disp_norm[3]; @@ -3472,10 +4219,10 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vertex_disp[3]; float vertex_disp_norm[3]; - sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co); normalize_v3_v3(vertex_disp_norm, vertex_disp); if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) { madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp)); @@ -3505,31 +4252,31 @@ void SCULPT_relax_vertex(SculptSession *ss, int neighbor_count = 0; zero_v3(smooth_pos); zero_v3(boundary_normal); - const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index); + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { neighbor_count++; if (!filter_boundary_face_sets || - (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) { + (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.vertex))) { /* When the vertex to relax is boundary, use only connected boundary vertices for the average * position. */ if (is_boundary) { - if (!SCULPT_vertex_is_boundary(ss, ni.index)) { + if (!SCULPT_vertex_is_boundary(ss, ni.vertex)) { continue; } - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); avg_count++; /* Calculate a normal for the constraint plane using the edges of the boundary. */ float to_neighbor[3]; - sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co); + sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); normalize_v3(to_neighbor); add_v3_v3(boundary_normal, to_neighbor); } else { - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); avg_count++; } } @@ -3558,7 +4305,7 @@ void SCULPT_relax_vertex(SculptSession *ss, normalize_v3_v3(vno, boundary_normal); } else { - SCULPT_vertex_normal_get(ss, vd->index, vno); + SCULPT_vertex_normal_get(ss, vd->vertex, vno); } if (is_zero_v3(vno)) { @@ -3586,7 +4333,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]); @@ -3597,6 +4344,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3607,7 +4355,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); @@ -3765,6 +4513,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + /* Offset vertex. */ const float fade = SCULPT_brush_strength_factor(ss, brush, @@ -3773,7 +4522,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float val1[3]; float val2[3]; @@ -3819,7 +4568,7 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod /* We divide out the squared alpha and multiply by the squared crease * to give us the pinch strength. */ - crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor; + crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor * 2.0; brush_alpha = BKE_brush_alpha_get(scene, brush); if (brush_alpha > 0.0f) { crease_correction /= brush_alpha * brush_alpha; @@ -3889,7 +4638,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp_center[3]; float x_disp[3]; @@ -3981,7 +4730,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, float(*proxy)[3]; const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -4005,7 +4754,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (grab_silhouette) { @@ -4069,7 +4818,8 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + bool update = false; proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -4125,13 +4875,15 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, mul_v3_fl(final_disp, 1.0f - *vd.mask); } - mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); - - copy_v3_v3(proxy[vd.i], final_disp); + mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); - if (vd.mvert) { - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + if (dot_v3v3(final_disp, final_disp) > 0.0000001) { + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } } + + copy_v3_v3(proxy[vd.i], final_disp); } BKE_pbvh_vertex_iter_end; } @@ -4157,6 +4909,9 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in }; TaskParallelSettings settings; + + SCULPT_vertex_random_access_ensure(ss); + BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); } @@ -4334,7 +5089,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -4419,7 +5174,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); } @@ -4467,7 +5222,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, if (vd.mask) { mul_v3_fl(disp, 1.0f - *vd.mask); } - mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); + mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); copy_v3_v3(proxy[vd.i], disp); } @@ -4530,7 +5285,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, float(*proxy)[3]; const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -4552,7 +5307,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -4603,7 +5358,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, float(*proxy)[3]; const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -4626,7 +5381,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); sub_v3_v3v3(vec, orig_data.co, ss->cache->location); @@ -4672,18 +5427,49 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, Sculpt *sd = data->sd; const Brush *brush = data->brush; - const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT; + bool use_persistent_base = brush->flag & BRUSH_PERSISTENT; + const bool is_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH; + + if (is_bmesh) { + use_persistent_base = use_persistent_base && data->cd_pers_co >= 0; + + // check if we need to zero displacement factor + // in first run of brush stroke + if (!use_persistent_base) { + int nidx = BKE_pbvh_get_node_index(ss->pbvh, data->nodes[n]); + + bool reset_disp = !BLI_BITMAP_TEST(ss->cache->layer_disp_map, nidx); + if (reset_disp) { + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + BMVert *v = (BMVert *)vd.vertex.i; + float *disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_layer_disp); + + *disp_factor = 0.0f; + + BLI_BITMAP_SET(ss->cache->layer_disp_map, nidx, true); + } + BKE_pbvh_vertex_iter_end; + } + } + } + else { + use_persistent_base = use_persistent_base && ss->persistent_base; + } PBVHVertexIter vd; SculptOrigVertData orig_data; const float bstrength = ss->cache->bstrength; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + bool bmeshpbvh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -4697,13 +5483,23 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); const int vi = vd.index; float *disp_factor; if (use_persistent_base) { - disp_factor = &ss->persistent_base[vi].disp; + if (is_bmesh) { + BMVert *v = (BMVert *)vd.vertex.i; + disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_pers_disp); + } + else { + disp_factor = &ss->persistent_base[vi].disp; + } + } + else if (is_bmesh) { + BMVert *v = (BMVert *)vd.vertex.i; + disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_layer_disp); } else { disp_factor = &ss->cache->layer_displacement_factor[vi]; @@ -4733,9 +5529,12 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, float normal[3]; if (use_persistent_base) { - SCULPT_vertex_persistent_normal_get(ss, vi, normal); + SCULPT_vertex_persistent_normal_get(ss, vd.vertex, normal, data->cd_pers_no); mul_v3_fl(normal, brush->height); - madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor); + madd_v3_v3v3fl(final_co, + SCULPT_vertex_persistent_co_get(ss, vd.vertex, data->cd_pers_co), + normal, + *disp_factor); } else { normal_short_to_float_v3(normal, orig_data.no); @@ -4761,17 +5560,58 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + int cd_pers_co = -1, cd_pers_no = -1, cd_pers_disp = -1, cd_layer_disp = -1; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + if (ss->cache->layer_displacement_factor) { + MEM_SAFE_FREE(ss->cache->layer_displacement_factor); + ss->cache->layer_displacement_factor = NULL; + } + + // note that we don't allow dyntopo to split the PBVH during + // the stroke (see DYNTOPO_HAS_DYNAMIC_SPLIT) + // so we don't have to worry about resizing ss->cache->layer_disp_map + if (!ss->cache->layer_disp_map) { + int totnode2 = BKE_pbvh_get_totnodes(ss->pbvh); + + ss->cache->layer_disp_map = BLI_BITMAP_NEW(totnode2, "ss->cache->layer_disp_map"); + ss->cache->layer_disp_map_size = totnode2; + } + + SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO); + SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO); + SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); + + SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP); + + cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO); + cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO); + cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); - if (ss->cache->layer_displacement_factor == NULL) { + cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP); + + // should never happen + if (cd_pers_co < 0 || cd_pers_no < 0 || cd_pers_disp < 0 || cd_layer_disp < 0) { + printf("error!! $d $d $d $d\n", cd_pers_co, cd_pers_no, cd_pers_disp, cd_layer_disp); + return; + } + } + else if (ss->cache->layer_displacement_factor == NULL) { ss->cache->layer_displacement_factor = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss), "layer displacement factor"); } + SCULPT_vertex_random_access_ensure(ss); + SculptThreadedTaskData data = { .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, + .cd_pers_co = cd_pers_co, + .cd_pers_no = cd_pers_no, + .cd_pers_disp = cd_pers_disp, + .cd_layer_disp = cd_layer_disp, }; TaskParallelSettings settings; @@ -4809,7 +5649,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float val[3]; @@ -4922,9 +5762,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { @@ -5077,7 +5916,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -5203,9 +6042,8 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { @@ -5356,7 +6194,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -5454,7 +6292,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -5570,7 +6408,7 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -5709,7 +6547,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -5791,7 +6629,91 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) BKE_keyblock_update_from_vertcos(ob, kb, vertCos); } -/* NOTE: we do the topology update before any brush actions to avoid +static void topology_undopush_cb(PBVHNode *node, void *data) +{ + SculptSearchSphereData *sdata = (SculptSearchSphereData *)data; + + SCULPT_ensure_dyntopo_node_undo( + sdata->ob, + node, + sdata->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS, + 0); + + BKE_pbvh_node_mark_update(node); +} + +int SCULPT_get_symmetry_pass(const SculptSession *ss) +{ + int symidx = ss->cache->mirror_symmetry_pass + (ss->cache->radial_symmetry_pass * 8); + + if (symidx >= SCULPT_MAX_SYMMETRY_PASSES) { + symidx = SCULPT_MAX_SYMMETRY_PASSES - 1; + } + + return symidx; +} + +typedef struct DynTopoAutomaskState { + AutomaskingCache *cache; + SculptSession *ss; +} DynTopoAutomaskState; + +static float sculpt_topology_automasking_cb(SculptVertRef vertex, void *vdata) +{ + DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata; + float mask = SCULPT_automasking_factor_get(state->cache, state->ss, vertex); + float mask2 = 1.0f - SCULPT_vertex_mask_get(state->ss, vertex); + + return mask * mask2; +} + +static float sculpt_topology_automasking_mask_cb(SculptVertRef vertex, void *vdata) +{ + DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata; + return 1.0f - SCULPT_vertex_mask_get(state->ss, vertex); +} + +bool SCULPT_dyntopo_automasking_init(const SculptSession *ss, + const Brush *br, + const Sculpt *sd, + DyntopoMaskCB *r_mask_cb, + void **r_mask_cb_data) +{ + if (!SCULPT_is_automasking_enabled(sd, ss, br)) { + if (CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { + DynTopoAutomaskState *state = MEM_callocN(sizeof(DynTopoAutomaskState), + "DynTopoAutomaskState"); + state->cache = ss->cache->automasking; + state->ss = (SculptSession *)ss; + + *r_mask_cb_data = (void *)state; + *r_mask_cb = sculpt_topology_automasking_mask_cb; + + return true; + } + else { + *r_mask_cb = NULL; + *r_mask_cb_data = NULL; + return false; + } + } + + DynTopoAutomaskState *state = MEM_callocN(sizeof(DynTopoAutomaskState), "DynTopoAutomaskState"); + state->cache = ss->cache->automasking; + state->ss = (SculptSession *)ss; + + *r_mask_cb_data = (void *)state; + *r_mask_cb = sculpt_topology_automasking_cb; + + return true; +} + +void SCULPT_dyntopo_automasking_end(void *mask_data) +{ + MEM_SAFE_FREE(mask_data); +} + +/* Note: we do the topology update before any brush actions to avoid * issues with the proxies. The size of the proxy can't change, so * topology must be updated first. */ static void sculpt_topology_update(Sculpt *sd, @@ -5801,18 +6723,21 @@ static void sculpt_topology_update(Sculpt *sd, { SculptSession *ss = ob->sculpt; + // build brush radius scale + float radius_scale = 1.0f; + + if (brush->autosmooth_factor > 0.0f) { + radius_scale = MAX2(radius_scale, brush->autosmooth_radius_factor); + } + + if (brush->topology_rake_factor > 0.0f) { + radius_scale = MAX2(radius_scale, brush->topology_rake_factor); + } + int n, totnode; /* Build a list of all nodes that are potentially within the brush's area of influence. */ const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; - const float radius_scale = 1.25f; - PBVHNode **nodes = sculpt_pbvh_gather_generic( - ob, sd, brush, use_original, radius_scale, &totnode); - - /* Only act if some verts are inside the brush area. */ - if (totnode == 0) { - return; - } /* Free index based vertex info as it will become invalid after modifying the topology during the * stroke. */ @@ -5822,44 +6747,64 @@ static void sculpt_topology_update(Sculpt *sd, PBVHTopologyUpdateMode mode = 0; float location[3]; - if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) { - if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { + if (brush->cached_dyntopo.mode != DYNTOPO_DETAIL_MANUAL) { + if (brush->cached_dyntopo.flag & DYNTOPO_SUBDIVIDE) { mode |= PBVH_Subdivide; } - if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) { + if (brush->cached_dyntopo.flag & DYNTOPO_COLLAPSE) { mode |= PBVH_Collapse; } } - for (n = 0; n < totnode; n++) { - SCULPT_undo_push_node(ob, - nodes[n], - brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : - SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(nodes[n]); - - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_node_mark_topology_update(nodes[n]); - BKE_pbvh_bmesh_node_save_orig(ss->bm, nodes[n]); - } + if (brush->cached_dyntopo.flag & DYNTOPO_CLEANUP) { + mode |= PBVH_Cleanup; } - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_update_topology(ss->pbvh, - mode, - ss->cache->location, - ss->cache->view_normal, - ss->cache->radius, - (brush->flag & BRUSH_FRONTFACE) != 0, - (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE)); - } - - MEM_SAFE_FREE(nodes); + PBVHNode **nodes = NULL; + SculptSearchSphereData sdata = { + .ss = ss, + .sd = sd, + .ob = ob, + .radius_squared = square_f(ss->cache->radius * radius_scale * 1.25f), + .original = use_original, + .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK, + .center = NULL, + .brush = brush}; + + int symidx = SCULPT_get_symmetry_pass(ss); + + bool modified; + void *mask_cb_data; + DyntopoMaskCB mask_cb; + + SCULPT_dyntopo_automasking_init(ss, brush, sd, &mask_cb, &mask_cb_data); + + /* do nodes under the brush cursor */ + modified = BKE_pbvh_bmesh_update_topology_nodes( + ss->pbvh, + SCULPT_search_sphere_cb, + topology_undopush_cb, + &sdata, + mode, + ss->cache->location, + ss->cache->view_normal, + ss->cache->radius * radius_scale, + (brush->flag & BRUSH_FRONTFACE) != 0, + (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE), + symidx, + DYNTOPO_HAS_DYNAMIC_SPLIT(brush->sculpt_tool), + mask_cb, + mask_cb_data); + + SCULPT_dyntopo_automasking_end(mask_cb_data); /* Update average stroke position. */ copy_v3_v3(location, ss->cache->true_location); mul_m4_v3(ob->obmat, location); + + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; } static void do_brush_action_task_cb(void *__restrict userdata, @@ -5883,11 +6828,20 @@ static void do_brush_action_task_cb(void *__restrict userdata, BKE_pbvh_node_mark_update_mask(data->nodes[n]); } else if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) { - SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR); + if (!ss->bm) { + if (data->brush->vcol_boundary_factor > 0.0f) { + SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + } + + SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR); + } + BKE_pbvh_node_mark_update_color(data->nodes[n]); } else { - SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + if (!ss->bm) { + SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + } BKE_pbvh_node_mark_update(data->nodes[n]); } } @@ -5898,17 +6852,27 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe int totnode; PBVHNode **nodes; - /* Check for unsupported features. */ - PBVHType type = BKE_pbvh_type(ss->pbvh); - if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) { - return; + float radius_scale = 1.0f; + + if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && + brush->autosmooth_factor > 0 && brush->autosmooth_radius_factor != 1.0f) { + radius_scale = MAX2(radius_scale, brush->autosmooth_radius_factor); } - if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) { + if (sculpt_brush_use_topology_rake(ss, brush)) { + radius_scale = MAX2(radius_scale, brush->topology_rake_radius_factor); + } + + /* Check for unsupported features. */ + PBVHType type = BKE_pbvh_type(ss->pbvh); + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) && + !ELEM(type, PBVH_BMESH, PBVH_FACES)) { return; } /* Build a list of all nodes that are potentially within the brush's area of influence */ + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; if (SCULPT_tool_needs_all_pbvh_nodes(brush)) { /* These brushes need to update all nodes as they are not constrained by the brush radius */ @@ -5918,13 +6882,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode); } else { - const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : - ss->cache->original; - float radius_scale = 1.0f; /* With these options enabled not all required nodes are inside the original brush radius, so * the brush can produce artifacts in some situations. */ if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) { - radius_scale = 2.0f; + radius_scale = MAX2(radius_scale, 2.0f); } nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); } @@ -5936,9 +6897,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) { - /* Dynamic-topology does not support Face Sets data, so it can't store/restore it from undo. */ - /* TODO(pablodp606): This check should be done in the undo code and not here, but the rest of - * the sculpt code is not checking for unsupported undo types that may return a null node. */ + // faceset undo node is created below for pbvh_bmesh if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS); } @@ -5970,16 +6929,48 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe } float location[3]; - SculptThreadedTaskData task_data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; + // dyntopo can't push undo nodes inside a thread + if (ss->bm) { + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) { + for (int i = 0; i < totnode; i++) { + int other = brush->vcol_boundary_factor > 0.0f ? SCULPT_UNDO_COORDS : -1; - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COLOR, other); + BKE_pbvh_node_mark_update_color(nodes[i]); + } + } + else if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { + for (int i = 0; i < totnode; i++) { + if (ss->cache->alt_smooth) { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, SCULPT_UNDO_COORDS); + } + else { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, -1); + } + + BKE_pbvh_node_mark_update(nodes[i]); + } + } + else { + for (int i = 0; i < totnode; i++) { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COORDS, -1); + + BKE_pbvh_node_mark_update(nodes[i]); + } + } + } + else { + SculptThreadedTaskData task_data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + } if (sculpt_brush_needs_normal(ss, brush)) { update_sculpt_normal(sd, ob, nodes, totnode); @@ -6013,7 +7004,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe break; case SCULPT_TOOL_SMOOTH: if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_LAPLACIAN) { - SCULPT_do_smooth_brush(sd, ob, nodes, totnode); + SCULPT_do_smooth_brush(sd, ob, nodes, totnode, brush->autosmooth_projection); } else if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE) { SCULPT_do_surface_smooth_brush(sd, ob, nodes, totnode); @@ -6116,21 +7107,89 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe case SCULPT_TOOL_SMEAR: SCULPT_do_smear_brush(sd, ob, nodes, totnode); break; + case SCULPT_TOOL_VCOL_BOUNDARY: + SCULPT_smooth_vcol_boundary(sd, ob, nodes, totnode, ss->cache->bstrength); + break; + case SCULPT_TOOL_UV_SMOOTH: + SCULPT_uv_brush(sd, ob, nodes, totnode); + break; } - if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && - brush->autosmooth_factor > 0) { + bool apply_autosmooth = !ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && + brush->autosmooth_factor > 0; + + if (brush->flag2 & BRUSH_CUSTOM_AUTOSMOOTH_SPACING) { + float spacing = (float)brush->autosmooth_spacing / 100.0f; + + apply_autosmooth = apply_autosmooth && + paint_stroke_apply_subspacing( + ss->cache->stroke, + spacing, + PAINT_MODE_SCULPT, + &ss->cache->last_smooth_t[SCULPT_get_symmetry_pass(ss)]); + } + + if (apply_autosmooth) { + float start_radius = ss->cache->radius; + + if (brush->autosmooth_radius_factor != 1.0f) { + // note that we expanded the pbvh node search radius earlier in this function + // so we just have to adjust the brush radius that's inside ss->cache + + ss->cache->radius *= brush->autosmooth_radius_factor; + ss->cache->radius_squared = ss->cache->radius * ss->cache->radius; + } + if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) { - SCULPT_smooth( - sd, ob, nodes, totnode, brush->autosmooth_factor * (1.0f - ss->cache->pressure), false); + SCULPT_smooth(sd, + ob, + nodes, + totnode, + brush->autosmooth_factor * (1.0f - ss->cache->pressure), + false, + brush->autosmooth_projection); } else { - SCULPT_smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, false); + SCULPT_smooth( + sd, ob, nodes, totnode, brush->autosmooth_factor, false, brush->autosmooth_projection); + } + + if (brush->autosmooth_radius_factor != 1.0f) { + ss->cache->radius = start_radius; + ss->cache->radius_squared = ss->cache->radius * ss->cache->radius; } } - if (sculpt_brush_use_topology_rake(ss, brush)) { + bool use_topology_rake = sculpt_brush_use_topology_rake(ss, brush); + + if (brush->flag2 & BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING) { + float spacing = (float)brush->topology_rake_spacing / 100.0f; + + use_topology_rake = use_topology_rake && + paint_stroke_apply_subspacing( + ss->cache->stroke, + spacing, + PAINT_MODE_SCULPT, + &ss->cache->last_rake_t[SCULPT_get_symmetry_pass(ss)]); + } + + if (use_topology_rake) { + float start_radius = ss->cache->radius; + + if (brush->topology_rake_radius_factor != 1.0f) { + // note that we expanded the pbvh node search radius earlier in this function + // so we just have to adjust the brush radius that's inside ss->cache + + ss->cache->radius *= brush->topology_rake_radius_factor; + ss->cache->radius_squared = ss->cache->radius * ss->cache->radius; + } + bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); + + if (brush->topology_rake_radius_factor != 1.0f) { + ss->cache->radius = start_radius; + ss->cache->radius_squared = ss->cache->radius * ss->cache->radius; + } } /* The cloth brush adds the gravity as a regular force and it is processed in the solver. */ @@ -6214,7 +7273,9 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata, if (use_orco) { if (ss->bm) { - copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert)); + float *co = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, vd.bm_vert)->origco; + copy_v3_v3(val, co); + // copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert)); } else { copy_v3_v3(val, orco[vd.i]); @@ -6304,6 +7365,10 @@ static void SCULPT_flush_stroke_deform_task_cb(void *__restrict userdata, PBVHVertexIter vd; + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + } + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { sculpt_flush_pbvhvert_deform(ob, &vd); @@ -6590,6 +7655,24 @@ bool SCULPT_vertex_colors_poll(bContext *C) if (!U.experimental.use_sculpt_vertex_colors) { return false; } + + Object *ob = CTX_data_active_object(C); + + return SCULPT_mode_poll(C); +} + +bool SCULPT_vertex_colors_poll_no_bmesh(bContext *C) +{ + if (!U.experimental.use_sculpt_vertex_colors) { + return false; + } + + Object *ob = CTX_data_active_object(C); + + if (ob && ob->sculpt && ob->sculpt->bm) { + return false; + } + return SCULPT_mode_poll(C); } @@ -6677,6 +7760,10 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Paint Brush"; case SCULPT_TOOL_SMEAR: return "Smear Brush"; + case SCULPT_TOOL_VCOL_BOUNDARY: + return "Color Boundary"; + case SCULPT_TOOL_UV_SMOOTH: + return "UV Smooth"; } return "Sculpting"; @@ -6696,6 +7783,10 @@ void SCULPT_cache_free(StrokeCache *cache) MEM_SAFE_FREE(cache->prev_displacement); MEM_SAFE_FREE(cache->limit_surface_co); + MEM_SAFE_FREE(cache->layer_disp_map); + cache->layer_disp_map = NULL; + cache->layer_disp_map_size = 0; + if (cache->pose_ik_chain) { SCULPT_pose_ik_chain_free(cache->pose_ik_chain); } @@ -6703,6 +7794,7 @@ void SCULPT_cache_free(StrokeCache *cache) for (int i = 0; i < PAINT_SYMM_AREAS; i++) { if (cache->boundaries[i]) { SCULPT_boundary_data_free(cache->boundaries[i]); + cache->boundaries[i] = NULL; } } @@ -6935,8 +8027,8 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo } } -/* In these brushes the grab delta is calculated always from the initial stroke location, which is - * generally used to create grab deformations. */ +/* In these brushes the grab delta is calculated always from the initial stroke location, which + * is generally used to create grab deformations. */ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) { if (ELEM(brush->sculpt_tool, @@ -7149,9 +8241,9 @@ static void sculpt_update_cache_paint_variants(StrokeCache *cache, const Brush * 1.0f - cache->pressure : cache->pressure; - /* This makes wet mix more sensible in higher values, which allows to create brushes that have - * a wider pressure range were they only blend colors without applying too much of the brush - * color. */ + /* This makes wet mix more sensible in higher values, which allows to create brushes that + * have a wider pressure range were they only blend colors without applying too much of the + * brush color. */ cache->paint_brush.wet_mix = 1.0f - pow2f(1.0f - cache->paint_brush.wet_mix); } @@ -7289,6 +8381,9 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd, (brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || (brush->autosmooth_factor > 0) || ((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) || (brush->sculpt_tool == SCULPT_TOOL_POSE) || + (brush->sculpt_tool == SCULPT_TOOL_VCOL_BOUNDARY) || + (brush->sculpt_tool == SCULPT_TOOL_UV_SMOOTH) || + (brush->sculpt_tool == SCULPT_TOOL_PAINT && brush->vcol_boundary_factor > 0.0f) || (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) || (brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) || (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || @@ -7325,7 +8420,7 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) } else { /* Intersect with coordinates from before we started stroke. */ - SculptUndoNode *unode = SCULPT_undo_get_node(node); + SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS); origco = (unode) ? unode->co : NULL; use_origco = origco ? true : false; } @@ -7341,7 +8436,8 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) &srd->depth, &srd->active_vertex_index, &srd->active_face_grid_index, - srd->face_normal)) { + srd->face_normal, + srd->ss->stroke_id)) { srd->hit = true; *tmin = srd->depth; } @@ -7362,7 +8458,7 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t } else { /* Intersect with coordinates from before we started stroke. */ - SculptUndoNode *unode = SCULPT_undo_get_node(node); + SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS); origco = (unode) ? unode->co : NULL; use_origco = origco ? true : false; } @@ -7375,7 +8471,8 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t srd->ray_start, srd->ray_normal, &srd->depth, - &srd->dist_sq_to_ray)) { + &srd->dist_sq_to_ray, + srd->ss->stroke_id)) { srd->hit = true; *tmin = srd->dist_sq_to_ray; } @@ -7418,8 +8515,9 @@ float SCULPT_raycast_init(ViewContext *vc, return dist; } -/* Gets the normal, location and active vertex location of the geometry under the cursor. This also - * updates the active vertex and cursor related data of the SculptSession using the mouse position +/* Gets the normal, location and active vertex location of the geometry under the cursor. This + * also updates the active vertex and cursor related data of the SculptSession using the mouse + * position */ bool SCULPT_cursor_geometry_info_update(bContext *C, SculptCursorGeometryInfo *out, @@ -7465,7 +8563,8 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, .face_normal = face_normal, }; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); - BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + BKE_pbvh_raycast( + ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original, srd.ss->stroke_id); /* Cursor is not over the mesh, return default values. */ if (!srd.hit) { @@ -7477,6 +8576,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, /* Update the active vertex of the SculptSession. */ ss->active_vertex_index = srd.active_vertex_index; + SCULPT_vertex_random_access_ensure(ss); copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss)); @@ -7486,11 +8586,11 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, ss->active_grid_index = 0; break; case PBVH_GRIDS: - ss->active_face_index = 0; - ss->active_grid_index = srd.active_face_grid_index; + ss->active_face_index.i = 0; + ss->active_grid_index = srd.active_face_grid_index.i; break; case PBVH_BMESH: - ss->active_face_index = 0; + ss->active_face_index = srd.active_face_grid_index; ss->active_grid_index = 0; break; } @@ -7577,11 +8677,6 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2]) depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BM_mesh_elem_table_ensure(ss->bm, BM_VERT); - BM_mesh_elem_index_ensure(ss->bm, BM_VERT); - } - bool hit = false; { SculptRaycastData srd; @@ -7594,7 +8689,8 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2]) srd.face_normal = face_normal; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); - BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + BKE_pbvh_raycast( + ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original, srd.ss->stroke_id); if (srd.hit) { hit = true; copy_v3_v3(out, ray_normal); @@ -7788,6 +8884,13 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) } } +bool all_nodes_callback(PBVHNode *node, void *data) +{ + return true; +} + +void sculpt_undo_print_nodes(void *active); + void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags) { /* After we are done drawing the stroke, check if we need to do a more @@ -7839,12 +8942,31 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); } - if (update_flags & SCULPT_UPDATE_COLOR) { - BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor); - } - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { BKE_pbvh_bmesh_after_stroke(ss->pbvh); +#if 0 + if (update_flags & SCULPT_UPDATE_COLOR) { + PBVHNode **nodes; + int totnode = 0; + + // BKE_pbvh_get_nodes(ss->pbvh, PBVH_UpdateColor, &nodes, &totnode); + BKE_pbvh_search_gather(ss->pbvh, all_nodes_callback, NULL, &nodes, &totnode); + + for (int i = 0; i < totnode; i++) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COLOR); + } + + if (nodes) { + MEM_freeN(nodes); + } + } +#endif + + sculpt_undo_print_nodes(NULL); + } + + if (update_flags & SCULPT_UPDATE_COLOR) { + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor); } /* Optimization: if there is locked key and active modifiers present in */ @@ -7885,6 +9007,8 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); + // increment stroke_id to flag origdata update + ss->stroke_id++; sculpt_update_cache_invariants(C, sd, ss, op, mouse); SCULPT_undo_push_begin(ob, sculpt_tool_name(sd)); @@ -7894,35 +9018,57 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f return false; } -static void sculpt_stroke_update_step(bContext *C, - struct PaintStroke *UNUSED(stroke), - PointerRNA *itemptr) +static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - const Brush *brush = BKE_paint_brush(&sd->paint); + Brush *brush = BKE_paint_brush(&sd->paint); + + ss->cache->stroke_distance = stroke->stroke_distance; + ss->cache->stroke_distance_t = stroke->stroke_distance_t; + ss->cache->stroke = stroke; + + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + ss->cache->last_dyntopo_t = 0.0f; + memset((void *)ss->cache->last_smooth_t, 0, sizeof(ss->cache->last_smooth_t)); + memset((void *)ss->cache->last_rake_t, 0, sizeof(ss->cache->last_rake_t)); + } + + BKE_brush_get_dyntopo(brush, sd, &brush->cached_dyntopo); SCULPT_stroke_modifiers_check(C, ob, brush); sculpt_update_cache_variants(C, sd, ob, itemptr); sculpt_restore_mesh(sd, ob); - if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + if (brush->cached_dyntopo.mode == DYNTOPO_DETAIL_CONSTANT || + brush->cached_dyntopo.mode == DYNTOPO_DETAIL_MANUAL) { + float object_space_constant_detail = 1.0f / (brush->cached_dyntopo.constant_detail * + mat4_to_scale(ob->obmat)); + BKE_pbvh_bmesh_detail_size_set( + ss->pbvh, object_space_constant_detail, brush->cached_dyntopo.detail_range); } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); + else if (brush->cached_dyntopo.mode == DYNTOPO_DETAIL_BRUSH) { + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, + ss->cache->radius * brush->cached_dyntopo.detail_percent / + 100.0f, + brush->cached_dyntopo.detail_range); } else { BKE_pbvh_bmesh_detail_size_set(ss->pbvh, (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * - (sd->detail_size * U.pixelsize) / 0.4f); + (brush->cached_dyntopo.detail_size * U.pixelsize) / 0.4f, + brush->cached_dyntopo.detail_range); } if (SCULPT_stroke_is_dynamic_topology(ss, brush)) { - do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); + float spacing = (float)brush->cached_dyntopo.spacing / 100.0f; + + if (paint_stroke_apply_subspacing( + ss->cache->stroke, spacing, PAINT_MODE_SCULPT, &ss->cache->last_dyntopo_t)) { + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); + } } do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); @@ -8161,13 +9307,43 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) MEM_SAFE_FREE(ss->persistent_base); + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + ss->persistent_base = NULL; + + SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO); + SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO); + SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); + + int cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO); + int cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO); + int cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); + + const int totvert = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + BMVert *v = (BMVert *)vertex.i; + + float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co); + float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no); + float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp); + + copy_v3_v3(co, SCULPT_vertex_co_get(ss, vertex)); + SCULPT_vertex_normal_get(ss, vertex, no); + *disp = 0.0f; + } + + return OPERATOR_FINISHED; + } + const int totvert = SCULPT_vertex_count_get(ss); ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert, "layer persistent base"); for (int i = 0; i < totvert; i++) { - copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i)); - SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, vertex)); + SCULPT_vertex_normal_get(ss, vertex, ss->persistent_base[i].no); ss->persistent_base[i].disp = 0.0f; } @@ -8252,7 +9428,6 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) * parts that symmetrize modifies). */ SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize"); SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); - BM_log_before_all_removed(ss->bm, ss->bm_log); BM_mesh_toolflags_set(ss->bm, true); @@ -8263,15 +9438,21 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) sd->symmetrize_direction, dist, true); - SCULPT_dynamic_topology_triangulate(ss->bm); + SCULPT_dynamic_topology_triangulate(ss, ss->bm); /* Bisect operator flags edges (keep tags clean for edge queue). */ BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); BM_mesh_toolflags_set(ss->bm, false); + BKE_pbvh_recalc_bmesh_boundary(ss->pbvh); + + // symmetrize is messing up ids, regenerate them from scratch + BM_reassign_ids(ss->bm); + + BM_log_full_mesh(ss->bm, ss->bm_log); + /* Finish undo. */ - BM_log_all_added(ss->bm, ss->bm_log); SCULPT_undo_push_end(); break; @@ -8344,10 +9525,10 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, /* Here we can detect geometry that was just added to Sculpt Mode as it has the * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */ /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not - * initialized, which is used is some operators that modify the mesh topology to perform certain - * actions in the new polys. After these operations are finished, all polys should have a valid - * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility - * correctly. */ + * initialized, which is used is some operators that modify the mesh topology to perform + * certain actions in the new polys. After these operations are finished, all polys should have + * a valid face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their + * visibility correctly. */ /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new * objects, like moving the transform pivot position to the new area or masking existing * geometry. */ @@ -8427,10 +9608,12 @@ void ED_object_sculptmode_enter_ex(Main *bmain, /* Needed because we may be entering this mode before the undo system loads. */ wmWindowManager *wm = bmain->wm.first; bool has_undo = wm->undo_stack != NULL; + /* Undo push is needed to prevent memory leak. */ if (has_undo) { SCULPT_undo_push_begin(ob, "Dynamic topology enable"); } + SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); if (has_undo) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); @@ -8610,29 +9793,38 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2; if (ss->preview_vert_index_list == NULL) { - ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines"); + ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(SculptVertRef), + "preview lines"); } - GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int)); - int active_v = SCULPT_active_vertex_get(ss); + GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(SculptVertRef)); + SculptVertRef active_v = SCULPT_active_vertex_get(ss); BLI_gsqueue_push(not_visited_vertices, &active_v); while (!BLI_gsqueue_is_empty(not_visited_vertices)) { - int from_v; + SculptVertRef from_v; + BLI_gsqueue_pop(not_visited_vertices, &from_v); + SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { if (totpoints + (ni.size * 2) < max_preview_vertices) { - int to_v = ni.index; + SculptVertRef to_v = ni.vertex; + int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v); + ss->preview_vert_index_list[totpoints] = from_v; totpoints++; ss->preview_vert_index_list[totpoints] = to_v; totpoints++; - if (BLI_BITMAP_TEST(visited_vertices, to_v)) { + + if (BLI_BITMAP_TEST(visited_vertices, to_v_i)) { continue; } - BLI_BITMAP_ENABLE(visited_vertices, to_v); + + BLI_BITMAP_ENABLE(visited_vertices, to_v_i); + const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v); + if (len_squared_v3v3(brush_co, co) < radius * radius) { BLI_gsqueue_push(not_visited_vertices, &to_v); } @@ -8705,7 +9897,7 @@ static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot) ot->idname = "SCULPT_OT_vertex_to_loop_colors"; /* api callbacks */ - ot->poll = SCULPT_vertex_colors_poll; + ot->poll = SCULPT_vertex_colors_poll_no_bmesh; ot->exec = vertex_to_loop_colors_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -8768,7 +9960,7 @@ static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot) ot->idname = "SCULPT_OT_loop_to_vertex_colors"; /* api callbacks */ - ot->poll = SCULPT_vertex_colors_poll; + ot->poll = SCULPT_vertex_colors_poll_no_bmesh; ot->exec = loop_to_vertex_colors_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -8783,7 +9975,7 @@ static int sculpt_sample_color_invoke(bContext *C, Object *ob = CTX_data_active_object(C); Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - int active_vertex = SCULPT_active_vertex_get(ss); + SculptVertRef active_vertex = SCULPT_active_vertex_get(ss); const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex); if (!active_vertex_color) { return OPERATOR_CANCELLED; @@ -8840,10 +10032,14 @@ enum { SCULPT_TOPOLOGY_ID_DEFAULT, }; -static int SCULPT_vertex_get_connected_component(SculptSession *ss, int index) +static int SCULPT_vertex_get_connected_component(SculptSession *ss, SculptVertRef vertex) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + vertex.i = BM_elem_index_get((BMVert *)vertex.i); + } + if (ss->vertex_info.connected_component) { - return ss->vertex_info.connected_component[index]; + return ss->vertex_info.connected_component[vertex.i]; } return SCULPT_TOPOLOGY_ID_DEFAULT; } @@ -8852,19 +10048,28 @@ static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist) { const int totvert = SCULPT_vertex_count_get(ss); ss->fake_neighbors.fake_neighbor_index = MEM_malloc_arrayN( - totvert, sizeof(int), "fake neighbor"); + totvert, sizeof(SculptVertRef), "fake neighbor"); for (int i = 0; i < totvert; i++) { - ss->fake_neighbors.fake_neighbor_index[i] = FAKE_NEIGHBOR_NONE; + ss->fake_neighbors.fake_neighbor_index[i].i = FAKE_NEIGHBOR_NONE; } ss->fake_neighbors.current_max_distance = max_dist; } -static void SCULPT_fake_neighbor_add(SculptSession *ss, int v_index_a, int v_index_b) +static void SCULPT_fake_neighbor_add(SculptSession *ss, + SculptVertRef v_index_a, + SculptVertRef v_index_b) { - if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) { - ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b; - ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a; + int tablea = (int)v_index_a.i, tableb = (int)v_index_b.i; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + tablea = BM_elem_index_get((BMVert *)v_index_a.i); + tableb = BM_elem_index_get((BMVert *)v_index_b.i); + } + + if (ss->fake_neighbors.fake_neighbor_index[tablea].i == FAKE_NEIGHBOR_NONE) { + ss->fake_neighbors.fake_neighbor_index[tablea] = v_index_b; + ss->fake_neighbors.fake_neighbor_index[tableb] = v_index_a; } } @@ -8875,6 +10080,7 @@ static void sculpt_pose_fake_neighbors_free(SculptSession *ss) typedef struct NearestVertexFakeNeighborTLSData { int nearest_vertex_index; + SculptVertRef nearest_vertex; float nearest_vertex_distance_squared; int current_topology_id; } NearestVertexFakeNeighborTLSData; @@ -8888,14 +10094,17 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata, NearestVertexFakeNeighborTLSData *nvtd = tls->userdata_chunk; PBVHVertexIter vd; + SCULPT_vertex_random_access_ensure(ss); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index); + int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.vertex); if (vd_topology_id != nvtd->current_topology_id && - ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) { + ss->fake_neighbors.fake_neighbor_index[vd.index].i == FAKE_NEIGHBOR_NONE) { float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { nvtd->nearest_vertex_index = vd.index; + nvtd->nearest_vertex = vd.vertex; nvtd->nearest_vertex_distance_squared = distance_squared; } } @@ -8909,17 +10118,23 @@ static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata), { NearestVertexFakeNeighborTLSData *join = chunk_join; NearestVertexFakeNeighborTLSData *nvtd = chunk; + if (join->nearest_vertex_index == -1) { join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } } -static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, float max_distance) +static SculptVertRef SCULPT_fake_neighbor_search(Sculpt *sd, + Object *ob, + const SculptVertRef index, + float max_distance) { SculptSession *ss = ob->sculpt; PBVHNode **nodes = NULL; @@ -8934,7 +10149,7 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); if (totnode == 0) { - return -1; + return BKE_pbvh_make_vref(-1); } SculptThreadedTaskData task_data = { @@ -8948,6 +10163,7 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, NearestVertexFakeNeighborTLSData nvtd; nvtd.nearest_vertex_index = -1; + nvtd.nearest_vertex.i = -1; nvtd.nearest_vertex_distance_squared = FLT_MAX; nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, index); @@ -8960,19 +10176,24 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, MEM_SAFE_FREE(nodes); - return nvtd.nearest_vertex_index; + return nvtd.nearest_vertex; } typedef struct SculptTopologyIDFloodFillData { int next_id; } SculptTopologyIDFloodFillData; -static bool SCULPT_connected_components_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool SCULPT_connected_components_floodfill_cb(SculptSession *ss, + SculptVertRef from_v, + SculptVertRef to_v, + bool UNUSED(is_duplicate), + void *userdata) { SculptTopologyIDFloodFillData *data = userdata; - ss->vertex_info.connected_component[from_v] = data->next_id; - ss->vertex_info.connected_component[to_v] = data->next_id; + ss->vertex_info.connected_component[BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v)] = + data->next_id; + ss->vertex_info.connected_component[BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v)] = + data->next_id; return true; } @@ -8980,7 +10201,10 @@ void SCULPT_connected_components_ensure(Object *ob) { SculptSession *ss = ob->sculpt; - /* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild. + SCULPT_vertex_random_access_ensure(ss); + + /* Topology IDs already initialized. They only need to be recalculated when the PBVH is + * rebuild. */ if (ss->vertex_info.connected_component) { return; @@ -8995,10 +10219,16 @@ void SCULPT_connected_components_ensure(Object *ob) int next_id = 0; for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { + continue; + } + if (ss->vertex_info.connected_component[i] == SCULPT_TOPOLOGY_ID_NONE) { SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); - SCULPT_floodfill_add_initial(&flood, i); + SCULPT_floodfill_add_initial(&flood, vertex); SculptTopologyIDFloodFillData data; data.next_id = next_id; SCULPT_floodfill_execute(ss, &flood, SCULPT_connected_components_floodfill_cb, &data); @@ -9011,32 +10241,39 @@ void SCULPT_connected_components_ensure(Object *ob) void SCULPT_boundary_info_ensure(Object *object) { SculptSession *ss = object->sculpt; - if (ss->vertex_info.boundary) { + + // PBVH_BMESH now handles itself + if (ss->bm) { return; } + else { + if (ss->vertex_info.boundary) { + return; + } - Mesh *base_mesh = BKE_mesh_from_object(object); - ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); - int *adjacent_faces_edge_count = MEM_calloc_arrayN( - base_mesh->totedge, sizeof(int), "Adjacent face edge count"); + Mesh *base_mesh = BKE_mesh_from_object(object); + ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); + int *adjacent_faces_edge_count = MEM_calloc_arrayN( + base_mesh->totedge, sizeof(int), "Adjacent face edge count"); - for (int p = 0; p < base_mesh->totpoly; p++) { - MPoly *poly = &base_mesh->mpoly[p]; - for (int l = 0; l < poly->totloop; l++) { - MLoop *loop = &base_mesh->mloop[l + poly->loopstart]; - adjacent_faces_edge_count[loop->e]++; + for (int p = 0; p < base_mesh->totpoly; p++) { + MPoly *poly = &base_mesh->mpoly[p]; + for (int l = 0; l < poly->totloop; l++) { + MLoop *loop = &base_mesh->mloop[l + poly->loopstart]; + adjacent_faces_edge_count[loop->e]++; + } } - } - for (int e = 0; e < base_mesh->totedge; e++) { - if (adjacent_faces_edge_count[e] < 2) { - MEdge *edge = &base_mesh->medge[e]; - BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true); - BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true); + for (int e = 0; e < base_mesh->totedge; e++) { + if (adjacent_faces_edge_count[e] < 2) { + MEdge *edge = &base_mesh->medge[e]; + BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true); + BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true); + } } - } - MEM_freeN(adjacent_faces_edge_count); + MEM_freeN(adjacent_faces_edge_count); + } } void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) @@ -9044,7 +10281,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - /* Fake neighbors were already initialized with the same distance, so no need to be recalculated. + /* Fake neighbors were already initialized with the same distance, so no need to be + * recalculated. */ if (ss->fake_neighbors.fake_neighbor_index && ss->fake_neighbors.current_max_distance == max_dist) { @@ -9055,12 +10293,13 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) SCULPT_fake_neighbor_init(ss, max_dist); for (int i = 0; i < totvert; i++) { - const int from_v = i; + const SculptVertRef from_v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); - /* This vertex does not have a fake neighbor yet, search one for it. */ - if (ss->fake_neighbors.fake_neighbor_index[from_v] == FAKE_NEIGHBOR_NONE) { - const int to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist); - if (to_v != -1) { + /* This vertex does not have a fake neighbor yet, seach one for it. */ + if (ss->fake_neighbors.fake_neighbor_index[i].i == FAKE_NEIGHBOR_NONE) { + const SculptVertRef to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist); + + if (to_v.i != -1) { /* Add the fake neighbor if available. */ SCULPT_fake_neighbor_add(ss, from_v, to_v); } @@ -9177,16 +10416,19 @@ static void do_mask_by_color_contiguous_update_nodes_cb( } static bool sculpt_mask_by_color_contiguous_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata) { MaskByColorContiguousFloodFillData *data = userdata; + int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v); + int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v); + const float *current_color = SCULPT_vertex_color_get(ss, to_v); float new_vertex_mask = sculpt_mask_by_color_delta_get( current_color, data->initial_color, data->threshold, data->invert); - data->new_mask[to_v] = new_vertex_mask; + data->new_mask[to_v_i] = new_vertex_mask; if (is_duplicate) { - data->new_mask[to_v] = data->new_mask[from_v]; + data->new_mask[to_v_i] = data->new_mask[from_v_i]; } float len = len_v3v3(current_color, data->initial_color); @@ -9195,7 +10437,7 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb( } static void sculpt_mask_by_color_contiguous(Object *object, - const int vertex, + const SculptVertRef vertex, const float threshold, const bool invert, const bool preserve_mask) @@ -9284,7 +10526,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, } static void sculpt_mask_by_color_full_mesh(Object *object, - const int vertex, + const SculptVertRef vertex, const float threshold, const bool invert, const bool preserve_mask) @@ -9330,8 +10572,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven SCULPT_vertex_random_access_ensure(ss); - /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, - * so it needs to be updated here. */ + /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse + * move, so it needs to be updated here. */ SculptCursorGeometryInfo sgi; float mouse[2]; mouse[0] = event->mval[0]; @@ -9340,7 +10582,7 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven SCULPT_undo_push_begin(ob, "Mask by color"); - const int active_vertex = SCULPT_active_vertex_get(ss); + const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss); const float threshold = RNA_float_get(op->ptr, "threshold"); const bool invert = RNA_boolean_get(op->ptr, "invert"); const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask"); @@ -9395,6 +10637,338 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot) 1.0f); } +#if 0 +/* -------------------------------------------------------------------- */ +/** \name Dyntopo Detail Size Edit Operator + * \{ */ + +/* Defines how much the mouse movement will modify the detail size value. */ +# define DETAIL_SIZE_DELTA_SPEED 0.08f +# define DETAIL_SIZE_DELTA_ACCURATE_SPEED 0.004f + +typedef struct DyntopoDetailSizeEditCustomData { + void *draw_handle; + Object *active_object; + + float init_mval[2]; + float accurate_mval[2]; + + float outline_col[4]; + + bool accurate_mode; + bool sample_mode; + + float init_detail_size; + float accurate_detail_size; + float detail_size; + float radius; + + float preview_tri[3][3]; + float gizmo_mat[4][4]; +} DyntopoDetailSizeEditCustomData; + +static void dyntopo_detail_size_parallel_lines_draw(uint pos3d, + DyntopoDetailSizeEditCustomData *cd, + const float start_co[3], + const float end_co[3], + bool flip, + const float angle) +{ + float object_space_constant_detail = 1.0f / + (cd->detail_size * mat4_to_scale(cd->active_object->obmat)); + + /* The constant detail represents the maximum edge length allowed before subdividing it. If the + * triangle grid preview is created with this value it will represent an ideal mesh density where + * all edges have the exact maximum length, which never happens in practice. As the minimum edge + * length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the average + * between max and min edge length so the preview is more accurate. */ + object_space_constant_detail *= 0.7f; + + const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]); + const int tot_lines = (int)(total_len / object_space_constant_detail) + 1; + const float tot_lines_fl = total_len / object_space_constant_detail; + float spacing_disp[3]; + sub_v3_v3v3(spacing_disp, end_co, start_co); + normalize_v3(spacing_disp); + + float line_disp[3]; + rotate_v2_v2fl(line_disp, spacing_disp, DEG2RAD(angle)); + mul_v3_fl(spacing_disp, total_len / tot_lines_fl); + + immBegin(GPU_PRIM_LINES, (uint)tot_lines * 2); + for (int i = 0; i < tot_lines; i++) { + float line_length; + if (flip) { + line_length = total_len * ((float)i / (float)tot_lines_fl); + } + else { + line_length = total_len * (1.0f - ((float)i / (float)tot_lines_fl)); + } + float line_start[3]; + copy_v3_v3(line_start, start_co); + madd_v3_v3v3fl(line_start, line_start, spacing_disp, i); + float line_end[3]; + madd_v3_v3v3fl(line_end, line_start, line_disp, line_length); + immVertex3fv(pos3d, line_start); + immVertex3fv(pos3d, line_end); + } + immEnd(); +} + +static void dyntopo_detail_size_edit_draw(const bContext *UNUSED(C), + ARegion *UNUSED(ar), + void *arg) +{ + DyntopoDetailSizeEditCustomData *cd = arg; + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_smooth(true); + + uint pos3d = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + GPU_matrix_push(); + GPU_matrix_mul(cd->gizmo_mat); + + /* Draw Cursor */ + immUniformColor4fv(cd->outline_col); + GPU_line_width(3.0f); + + imm_draw_circle_wire_3d(pos3d, 0, 0, cd->radius, 80); + + /* Draw Triangle. */ + immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f); + immBegin(GPU_PRIM_LINES, 6); + immVertex3fv(pos3d, cd->preview_tri[0]); + immVertex3fv(pos3d, cd->preview_tri[1]); + + immVertex3fv(pos3d, cd->preview_tri[1]); + immVertex3fv(pos3d, cd->preview_tri[2]); + + immVertex3fv(pos3d, cd->preview_tri[2]); + immVertex3fv(pos3d, cd->preview_tri[0]); + immEnd(); + + /* Draw Grid */ + GPU_line_width(1.0f); + dyntopo_detail_size_parallel_lines_draw( + pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], false, 60.0f); + dyntopo_detail_size_parallel_lines_draw( + pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], true, 120.0f); + dyntopo_detail_size_parallel_lines_draw( + pos3d, cd, cd->preview_tri[0], cd->preview_tri[2], false, -60.0f); + + immUnbindProgram(); + GPU_matrix_pop(); + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); +} + +static void dyntopo_detail_size_edit_cancel(bContext *C, wmOperator *op) +{ + Object *active_object = CTX_data_active_object(C); + SculptSession *ss = active_object->sculpt; + ARegion *region = CTX_wm_region(C); + DyntopoDetailSizeEditCustomData *cd = op->customdata; + ED_region_draw_cb_exit(region->type, cd->draw_handle); + ss->draw_faded_cursor = false; + MEM_freeN(op->customdata); + ED_workspace_status_text(C, NULL); +} + +static void dyntopo_detail_size_sample_from_surface(Object *ob, + DyntopoDetailSizeEditCustomData *cd) +{ + SculptSession *ss = ob->sculpt; + const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss); + + float len_accum = 0; + int num_neighbors = 0; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { + len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex), + SCULPT_vertex_co_get(ss, ni.vertex)); + num_neighbors++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (num_neighbors > 0) { + const float avg_edge_len = len_accum / num_neighbors; + /* Use 0.7 as the average of min and max dyntopo edge length. */ + const float detail_size = 0.7f / (avg_edge_len * mat4_to_scale(cd->active_object->obmat)); + cd->detail_size = clamp_f(detail_size, 1.0f, 500.0f); + } +} + +static void dyntopo_detail_size_update_from_mouse_delta(DyntopoDetailSizeEditCustomData *cd, + const wmEvent *event) +{ + const float mval[2] = {event->mval[0], event->mval[1]}; + + float detail_size_delta; + if (cd->accurate_mode) { + detail_size_delta = mval[0] - cd->accurate_mval[0]; + cd->detail_size = cd->accurate_detail_size + + detail_size_delta * DETAIL_SIZE_DELTA_ACCURATE_SPEED; + } + else { + detail_size_delta = mval[0] - cd->init_mval[0]; + cd->detail_size = cd->init_detail_size + detail_size_delta * DETAIL_SIZE_DELTA_SPEED; + } + + if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) { + cd->accurate_mode = true; + copy_v2_v2(cd->accurate_mval, mval); + cd->accurate_detail_size = cd->detail_size; + } + if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) { + cd->accurate_mode = false; + cd->accurate_detail_size = 0.0f; + } + + cd->detail_size = clamp_f(cd->detail_size, 1.0f, 500.0f); +} + +static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *active_object = CTX_data_active_object(C); + SculptSession *ss = active_object->sculpt; + ARegion *region = CTX_wm_region(C); + DyntopoDetailSizeEditCustomData *cd = op->customdata; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + /* Cancel modal operator */ + if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) || + (event->type == RIGHTMOUSE && event->val == KM_PRESS)) { + dyntopo_detail_size_edit_cancel(C, op); + ED_region_tag_redraw(region); + return OPERATOR_FINISHED; + } + + /* Finish modal operator */ + if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) || + (event->type == EVT_RETKEY && event->val == KM_PRESS) || + (event->type == EVT_PADENTER && event->val == KM_PRESS)) { + ED_region_draw_cb_exit(region->type, cd->draw_handle); + sd->constant_detail = cd->detail_size; + ss->draw_faded_cursor = false; + MEM_freeN(op->customdata); + ED_region_tag_redraw(region); + ED_workspace_status_text(C, NULL); + return OPERATOR_FINISHED; + } + + ED_region_tag_redraw(region); + + if (event->type == EVT_LEFTCTRLKEY && event->val == KM_PRESS) { + cd->sample_mode = true; + } + if (event->type == EVT_LEFTCTRLKEY && event->val == KM_RELEASE) { + cd->sample_mode = false; + } + + /* Sample mode sets the detail size sampling the average edge length under the surface. */ + if (cd->sample_mode) { + dyntopo_detail_size_sample_from_surface(active_object, cd); + return OPERATOR_RUNNING_MODAL; + } + /* Regular mode, changes the detail size by moving the cursor. */ + dyntopo_detail_size_update_from_mouse_delta(cd, event); + + return OPERATOR_RUNNING_MODAL; +} + +static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + Object *active_object = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + DyntopoDetailSizeEditCustomData *cd = MEM_callocN(sizeof(DyntopoDetailSizeEditCustomData), + "Dyntopo Detail Size Edit OP Custom Data"); + + /* Initial operator Custom Data setup. */ + cd->draw_handle = ED_region_draw_cb_activate( + region->type, dyntopo_detail_size_edit_draw, cd, REGION_DRAW_POST_VIEW); + cd->active_object = active_object; + cd->init_mval[0] = event->mval[0]; + cd->init_mval[1] = event->mval[1]; + cd->detail_size = sd->constant_detail; + cd->init_detail_size = sd->constant_detail; + copy_v4_v4(cd->outline_col, brush->add_col); + op->customdata = cd; + + SculptSession *ss = active_object->sculpt; + cd->radius = ss->cursor_radius; + + /* Generates the matrix to position the gizmo in the surface of the mesh using the same location + * and orientation as the brush cursor. */ + float cursor_trans[4][4], cursor_rot[4][4]; + const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; + float quat[4]; + copy_m4_m4(cursor_trans, active_object->obmat); + translate_m4( + cursor_trans, ss->cursor_location[0], ss->cursor_location[1], ss->cursor_location[2]); + + float cursor_normal[3]; + if (!is_zero_v3(ss->cursor_sampled_normal)) { + copy_v3_v3(cursor_normal, ss->cursor_sampled_normal); + } + else { + copy_v3_v3(cursor_normal, ss->cursor_normal); + } + + rotation_between_vecs_to_quat(quat, z_axis, cursor_normal); + quat_to_mat4(cursor_rot, quat); + copy_m4_m4(cd->gizmo_mat, cursor_trans); + mul_m4_m4_post(cd->gizmo_mat, cursor_rot); + + /* Initialize the position of the triangle vertices. */ + const float y_axis[3] = {0.0f, cd->radius, 0.0f}; + for (int i = 0; i < 3; i++) { + zero_v3(cd->preview_tri[i]); + rotate_v2_v2fl(cd->preview_tri[i], y_axis, DEG2RAD(120.0f * i)); + } + + SCULPT_vertex_random_access_ensure(ss); + + WM_event_add_modal_handler(C, op); + ED_region_tag_redraw(region); + + ss->draw_faded_cursor = true; + + const char *status_str = TIP_( + "Move the mouse to change the dyntopo detail size. LMB: confirm size, ESC/RMB: cancel"); + ED_workspace_status_text(C, status_str); + + return OPERATOR_RUNNING_MODAL; +} + +static bool dyntopo_detail_size_edit_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + return SCULPT_mode_poll(C) && ob->sculpt->bm && (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT); +} + +static void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Edit Dyntopo Detail Size"; + ot->description = "Modify the constant detail size of dyntopo interactively"; + ot->idname = "SCULPT_OT_dyntopo_detail_size_edit"; + + /* api callbacks */ + ot->poll = dyntopo_detail_size_edit_poll; + ot->invoke = dyntopo_detail_size_edit_invoke; + ot->modal = dyntopo_detail_size_edit_modal; + ot->cancel = dyntopo_detail_size_edit_cancel; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +#endif + void ED_operatortypes_sculpt(void) { WM_operatortype_append(SCULPT_OT_brush_stroke); diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c index 35f48400fe2..135d9af1af2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.c +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c @@ -85,9 +85,11 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br) { + /* if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) { return false; - } + }*/ + if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) { return true; } @@ -100,6 +102,13 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { return true; } + if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_CONCAVITY)) { + if (br && br->concave_mask_factor == 0.0f) { + return false; + } + return true; + } + return false; } @@ -111,6 +120,18 @@ static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Br return sculpt->automasking_flags; } +static float sculpt_concavity_factor(AutomaskingCache *automasking, float fac) +{ + if (automasking->settings.flags & BRUSH_AUTOMASKING_INVERT_CONCAVITY) { + fac = 1.0 - fac; + } + + fac = pow(fac * 1.5f, (0.5f + automasking->settings.concave_factor) * 8.0); + CLAMP(fac, 0.0f, 1.0f); + + return fac; +} + static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush) { @@ -127,16 +148,37 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush return false; } -float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert) +float SCULPT_automasking_factor_get(AutomaskingCache *automasking, + SculptSession *ss, + SculptVertRef vert) { + float mask = 1.0f; + bool do_concave; + if (!automasking) { - return 1.0f; + return mask; } + + do_concave = ss->cache && ss->cache->brush && ss->cache->brush->concave_mask_factor > 0.0f && + (automasking->settings.flags & BRUSH_AUTOMASKING_CONCAVITY); + /* If the cache is initialized with valid info, use the cache. This is used when the * automasking information can't be computed in real time per vertex and needs to be * initialized for the whole mesh when the stroke starts. */ - if (automasking->factor) { - return automasking->factor[vert]; + if (automasking->factorlayer) { + mask = *(float *)SCULPT_temp_cdata_get(vert, automasking->factorlayer); + } + + // don't used cached automasking factors for facesets or concave in + // dyntopo + if (automasking->factorlayer && !ss->bm) { + return mask; + } + + if (do_concave) { + float fac = SCULPT_calc_concavity(ss, vert); + + mask *= sculpt_concavity_factor(automasking, fac); } if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { @@ -157,7 +199,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession } } - return 1.0f; + return mask; } void SCULPT_automasking_cache_free(AutomaskingCache *automasking) @@ -166,7 +208,10 @@ void SCULPT_automasking_cache_free(AutomaskingCache *automasking) return; } - MEM_SAFE_FREE(automasking->factor); + if (automasking->factorlayer) { + MEM_SAFE_FREE(automasking->factorlayer); + } + MEM_SAFE_FREE(automasking); } @@ -184,26 +229,32 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br) } typedef struct AutomaskFloodFillData { - float *automask_factor; + SculptCustomLayer *factorlayer; float radius; bool use_radius; float location[3]; char symm; } AutomaskFloodFillData; -static bool automask_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool automask_floodfill_cb(SculptSession *ss, + SculptVertRef from_vref, + SculptVertRef to_vref, + bool UNUSED(is_duplicate), + void *userdata) { AutomaskFloodFillData *data = userdata; - data->automask_factor[to_v] = 1.0f; - data->automask_factor[from_v] = 1.0f; + *(float *)SCULPT_temp_cdata_get(to_vref, data->factorlayer) = 1.0f; + *(float *)SCULPT_temp_cdata_get(from_vref, data->factorlayer) = 1.0f; + return (!data->use_radius || SCULPT_is_vertex_inside_brush_radius_symm( - SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); + SCULPT_vertex_co_get(ss, to_vref), data->location, data->radius, data->symm)); } -static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor) +static void SCULPT_topology_automasking_init(Sculpt *sd, + Object *ob, + SculptCustomLayer *factorlayer) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -215,7 +266,10 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au const int totvert = SCULPT_vertex_count_get(ss); for (int i = 0; i < totvert; i++) { - automask_factor[i] = 0.0f; + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + float *fac = SCULPT_temp_cdata_get(vertex, factorlayer); + *fac = 0.0f; } /* Flood fill automask to connected vertices. Limited to vertices inside @@ -226,7 +280,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius); AutomaskFloodFillData fdata = { - .automask_factor = automask_factor, + .factorlayer = factorlayer, .radius = radius, .use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush), .symm = SCULPT_mesh_symmetry_xyz_get(ob), @@ -234,17 +288,17 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss)); SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); - - return automask_factor; } -static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor) +static void sculpt_face_sets_automasking_init(Sculpt *sd, + Object *ob, + SculptCustomLayer *factorlayer) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); if (!SCULPT_is_automasking_enabled(sd, ss, brush)) { - return NULL; + return; } if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { @@ -255,20 +309,22 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a int tot_vert = SCULPT_vertex_count_get(ss); int active_face_set = SCULPT_active_face_set_get(ss); for (int i = 0; i < tot_vert; i++) { - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { - automask_factor[i] *= 0.0f; + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { + *(float *)SCULPT_temp_cdata_get(vertex, factorlayer) = 0.0f; } } - return automask_factor; + return; } #define EDGE_DISTANCE_INF -1 -float *SCULPT_boundary_automasking_init(Object *ob, - eBoundaryAutomaskMode mode, - int propagation_steps, - float *automask_factor) +void SCULPT_boundary_automasking_init(Object *ob, + eBoundaryAutomaskMode mode, + int propagation_steps, + SculptCustomLayer *factorlayer) { SculptSession *ss = ob->sculpt; @@ -281,15 +337,17 @@ float *SCULPT_boundary_automasking_init(Object *ob, int *edge_distance = MEM_callocN(sizeof(int) * totvert, "automask_factor"); for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + edge_distance[i] = EDGE_DISTANCE_INF; switch (mode) { case AUTOMASK_INIT_BOUNDARY_EDGES: - if (SCULPT_vertex_is_boundary(ss, i)) { + if (SCULPT_vertex_is_boundary(ss, vertex)) { edge_distance[i] = 0; } break; case AUTOMASK_INIT_BOUNDARY_FACE_SETS: - if (!SCULPT_vertex_has_unique_face_set(ss, i)) { + if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) { edge_distance[i] = 0; } break; @@ -298,11 +356,13 @@ float *SCULPT_boundary_automasking_init(Object *ob, for (int propagation_it = 0; propagation_it < propagation_steps; propagation_it++) { for (int i = 0; i < totvert; i++) { + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + if (edge_distance[i] != EDGE_DISTANCE_INF) { continue; } SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) { if (edge_distance[ni.index] == propagation_it) { edge_distance[i] = propagation_it + 1; } @@ -312,16 +372,18 @@ float *SCULPT_boundary_automasking_init(Object *ob, } for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + if (edge_distance[i] == EDGE_DISTANCE_INF) { continue; } const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps); const float edge_boundary_automask = pow2f(p); - automask_factor[i] *= (1.0f - edge_boundary_automask); + + *(float *)SCULPT_temp_cdata_get(vertex, factorlayer) *= (1.0f - edge_boundary_automask); } MEM_SAFE_FREE(edge_distance); - return automask_factor; } static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking, @@ -330,7 +392,65 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski Brush *brush) { automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush); + automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss); + automasking->settings.concave_factor = brush ? brush->concave_mask_factor : 0.0f; +} + +float SCULPT_calc_concavity(SculptSession *ss, SculptVertRef vref) +{ + SculptVertexNeighborIter ni; + float co[3], tot = 0.0, elen = 0.0; + const float *vco = SCULPT_vertex_co_get(ss, vref); + + zero_v3(co); + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) { + const float *vco2 = SCULPT_vertex_co_get(ss, ni.vertex); + + elen += len_v3v3(vco, vco2); + add_v3_v3(co, vco2); + tot += 1.0f; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (!tot) { + return 0.5f; + } + + elen /= tot; + mul_v3_fl(co, 1.0 / tot); + sub_v3_v3(co, vco); + mul_v3_fl(co, -1.0 / elen); + + float no[3]; + SCULPT_vertex_normal_get(ss, vref, no); + + float f = dot_v3v3(co, no) * 0.5 + 0.5; + return 1.0 - f; +} + +static void SCULPT_concavity_automasking_init(Object *ob, + Brush *brush, + AutomaskingCache *automasking, + SculptCustomLayer *factorlayer) +{ + SculptSession *ss = ob->sculpt; + + if (!ss) { + return; + } + + const int totvert = SCULPT_vertex_count_get(ss); + + for (int i = 0; i < totvert; i++) { + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + float f = SCULPT_calc_concavity(ss, vref); + f = sculpt_concavity_factor(automasking, f); + + *(float *)SCULPT_temp_cdata_get(vref, factorlayer) *= f; + } + // BKE_pbvh_vertex_iter_begin } AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob) @@ -350,9 +470,29 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object return automasking; } - automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor"); + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); + + automasking->factorlayer = MEM_callocN(sizeof(*automasking->factorlayer), + "automasking->factorlayer"); + + if (!SCULPT_temp_customlayer_get(ss, + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + "__sculpt_mask_factor", + automasking->factorlayer)) { + // failed + MEM_freeN(automasking->factorlayer); + return automasking; + } + + // automasking->factorlayer = SCULPT_temp_customlayer_ensure() + // automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor"); for (int i = 0; i < totvert; i++) { - automasking->factor[i] = 1.0f; + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + float *f = SCULPT_temp_cdata_get(vertex, automasking->factorlayer); + + *f = 1.0f; } const int boundary_propagation_steps = brush ? @@ -361,22 +501,35 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) { SCULPT_vertex_random_access_ensure(ss); - SCULPT_topology_automasking_init(sd, ob, automasking->factor); + SCULPT_topology_automasking_init(sd, ob, automasking->factorlayer); + } + + if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { + SCULPT_vertex_random_access_ensure(ss); + SCULPT_boundary_automasking_init(ob, + AUTOMASK_INIT_BOUNDARY_FACE_SETS, + boundary_propagation_steps, + automasking->factorlayer); } + + // for dyntopo, only topology and fset boundary area initialized here + if (ss->bm) { + return automasking; + } + if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) { SCULPT_vertex_random_access_ensure(ss); - sculpt_face_sets_automasking_init(sd, ob, automasking->factor); + sculpt_face_sets_automasking_init(sd, ob, automasking->factorlayer); } if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) { SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_automasking_init( - ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor); + ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factorlayer); } - if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) { + if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CONCAVITY)) { SCULPT_vertex_random_access_ensure(ss); - SCULPT_boundary_automasking_init( - ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor); + SCULPT_concavity_automasking_init(ob, brush, automasking, automasking->factorlayer); } return automasking; diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index 37678ec276a..8262eb4a486 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -58,23 +58,47 @@ #include <math.h> #include <stdlib.h> +#if 1 +# ifdef NDEBUG +# define NDEBUG_UNDEFD +# undef NDEBUG +# endif + +# include "BLI_assert.h" + +# ifdef NDEBUG_UNDEFD +# define NDEBUG 1 +# endif +#endif + #define BOUNDARY_VERTEX_NONE -1 #define BOUNDARY_STEPS_NONE -1 +#define TSTN 4 + typedef struct BoundaryInitialVertexFloodFillData { - int initial_vertex; + SculptVertRef initial_vertex; + int initial_vertex_index; int boundary_initial_vertex_steps; - int boundary_initial_vertex; + + SculptVertRef boundary_initial_vertex; + int *floodfill_steps; float radius_sq; } BoundaryInitialVertexFloodFillData; -static bool boundary_initial_vertex_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) +static bool boundary_initial_vertex_floodfill_cb(SculptSession *ss, + SculptVertRef from_vref, + SculptVertRef to_vref, + bool is_duplicate, + void *userdata) { BoundaryInitialVertexFloodFillData *data = userdata; - if (!SCULPT_vertex_visible_get(ss, to_v)) { + int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref); + int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref); + + if (!SCULPT_vertex_visible_get(ss, to_vref)) { return false; } @@ -85,25 +109,26 @@ static bool boundary_initial_vertex_floodfill_cb( data->floodfill_steps[to_v] = data->floodfill_steps[from_v]; } - if (SCULPT_vertex_is_boundary(ss, to_v)) { + if (SCULPT_vertex_is_boundary(ss, to_vref)) { if (data->floodfill_steps[to_v] < data->boundary_initial_vertex_steps) { data->boundary_initial_vertex_steps = data->floodfill_steps[to_v]; - data->boundary_initial_vertex = to_v; + data->boundary_initial_vertex = to_vref; } } const float len_sq = len_squared_v3v3(SCULPT_vertex_co_get(ss, data->initial_vertex), - SCULPT_vertex_co_get(ss, to_v)); + SCULPT_vertex_co_get(ss, to_vref)); return len_sq < data->radius_sq; } /* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside * the given radius, if it exists. */ -static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, - const int initial_vertex, - const float radius) +static SculptVertRef sculpt_boundary_get_closest_boundary_vertex( + SculptSession *ss, + const SculptVertRef initial_vertex, + const int initial_vertex_index, + const float radius) { - if (SCULPT_vertex_is_boundary(ss, initial_vertex)) { return initial_vertex; } @@ -114,13 +139,14 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, BoundaryInitialVertexFloodFillData fdata = { .initial_vertex = initial_vertex, - .boundary_initial_vertex = BOUNDARY_VERTEX_NONE, + .initial_vertex_index = initial_vertex_index, + .boundary_initial_vertex = {BOUNDARY_VERTEX_NONE}, .boundary_initial_vertex_steps = INT_MAX, .radius_sq = radius * radius, }; fdata.floodfill_steps = MEM_calloc_arrayN( - SCULPT_vertex_count_get(ss), sizeof(int), "floodfill steps"); + SCULPT_vertex_count_get(ss), sizeof(int) * TSTN, "floodfill steps"); SCULPT_floodfill_execute(ss, &flood, boundary_initial_vertex_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); @@ -134,28 +160,39 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, * deformations usually need in the boundary. */ static int BOUNDARY_INDICES_BLOCK_SIZE = 300; -static void sculpt_boundary_index_add(SculptBoundary *boundary, - const int new_index, +static void sculpt_boundary_index_add(SculptSession *ss, + SculptBoundary *boundary, + const SculptVertRef new_index, const float distance, GSet *included_vertices) { boundary->vertices[boundary->num_vertices] = new_index; + boundary->vertex_indices[boundary->num_vertices] = BKE_pbvh_vertex_index_to_table(ss->pbvh, + new_index); + if (boundary->distance) { - boundary->distance[new_index] = distance; + boundary->distance[BKE_pbvh_vertex_index_to_table(ss->pbvh, new_index)] = distance; } if (included_vertices) { - BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index)); + BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index.i)); } boundary->num_vertices++; if (boundary->num_vertices >= boundary->vertices_capacity) { boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE; - boundary->vertices = MEM_reallocN_id( - boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices"); + boundary->vertices = MEM_reallocN_id(boundary->vertices, + boundary->vertices_capacity * sizeof(SculptVertRef) * + TSTN, + "boundary vertrefs"); + boundary->vertex_indices = MEM_reallocN_id(boundary->vertex_indices, + boundary->vertices_capacity * sizeof(int) * TSTN, + "boundary indices"); } }; -static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2) +static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, + const SculptVertRef v1, + const SculptVertRef v2) { boundary->edges[boundary->num_edges].v1 = v1; @@ -165,7 +202,8 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int if (boundary->num_edges >= boundary->edges_capacity) { boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE; boundary->edges = MEM_reallocN_id(boundary->edges, - boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge), + boundary->edges_capacity * + sizeof(SculptBoundaryPreviewEdge) * TSTN, "boundary edges"); } }; @@ -175,7 +213,7 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int * as well as to check if the initial vertex is valid. */ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, - const int initial_vertex) + const SculptVertRef initial_vertex) { if (!SCULPT_vertex_visible_get(ss, initial_vertex)) { @@ -186,9 +224,9 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, int boundary_vertex_count = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vertex, ni) { - if (SCULPT_vertex_visible_get(ss, ni.index)) { + if (SCULPT_vertex_visible_get(ss, ni.vertex)) { neighbor_count++; - if (SCULPT_vertex_is_boundary(ss, ni.index)) { + if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { boundary_vertex_count++; } } @@ -218,44 +256,51 @@ typedef struct BoundaryFloodFillData { GSet *included_vertices; EdgeSet *preview_edges; - int last_visited_vertex; + SculptVertRef last_visited_vertex; } BoundaryFloodFillData; static bool boundary_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata) { BoundaryFloodFillData *data = userdata; SculptBoundary *boundary = data->boundary; + int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v); + if (!SCULPT_vertex_is_boundary(ss, to_v)) { return false; } const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v), SCULPT_vertex_co_get(ss, to_v)); const float distance_boundary_to_dst = boundary->distance ? - boundary->distance[from_v] + edge_len : + boundary->distance[from_v_i] + edge_len : 0.0f; - sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices); - if (!is_duplicate) { - sculpt_boundary_preview_edge_add(boundary, from_v, to_v); - } + sculpt_boundary_index_add(ss, boundary, to_v, distance_boundary_to_dst, data->included_vertices); + // if (!is_duplicate) { + sculpt_boundary_preview_edge_add(boundary, from_v, to_v); + //} + return sculpt_boundary_is_vertex_in_editable_boundary(ss, to_v); } static void sculpt_boundary_indices_init(SculptSession *ss, SculptBoundary *boundary, const bool init_boundary_distances, - const int initial_boundary_index) + const SculptVertRef initial_boundary_index) { const int totvert = SCULPT_vertex_count_get(ss); boundary->vertices = MEM_malloc_arrayN( - BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices"); + BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptVertRef) * TSTN, "boundary vrefs"); + boundary->vertex_indices = MEM_malloc_arrayN( + BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int) * TSTN, "boundary indices"); + if (init_boundary_distances) { - boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances"); + boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float) * TSTN, "boundary distances"); } boundary->edges = MEM_malloc_arrayN( - BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges"); + BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge) * TSTN, "boundary edges"); GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE); SculptFloodFill flood; @@ -264,13 +309,13 @@ static void sculpt_boundary_indices_init(SculptSession *ss, boundary->initial_vertex = initial_boundary_index; copy_v3_v3(boundary->initial_vertex_position, SCULPT_vertex_co_get(ss, boundary->initial_vertex)); - sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices); + sculpt_boundary_index_add(ss, boundary, initial_boundary_index, 0.0f, included_vertices); SCULPT_floodfill_add_initial(&flood, initial_boundary_index); BoundaryFloodFillData fdata = { .boundary = boundary, .included_vertices = included_vertices, - .last_visited_vertex = BOUNDARY_VERTEX_NONE, + .last_visited_vertex = {BOUNDARY_VERTEX_NONE}, }; @@ -278,13 +323,13 @@ static void sculpt_boundary_indices_init(SculptSession *ss, SCULPT_floodfill_free(&flood); /* Check if the boundary loops into itself and add the extra preview edge to close the loop. */ - if (fdata.last_visited_vertex != BOUNDARY_VERTEX_NONE && + if (fdata.last_visited_vertex.i != BOUNDARY_VERTEX_NONE && sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) { SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) { - if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) && - sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) { - sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index); + if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.vertex.i)) && + sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.vertex)) { + sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.vertex); boundary->forms_loop = true; } } @@ -302,7 +347,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, */ static void sculpt_boundary_edit_data_init(SculptSession *ss, SculptBoundary *boundary, - const int initial_vertex, + const SculptVertRef initial_vertex, const float radius) { const int totvert = SCULPT_vertex_count_get(ss); @@ -310,22 +355,25 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, const bool has_duplicates = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; boundary->edit_info = MEM_malloc_arrayN( - totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info"); + totvert, sizeof(SculptBoundaryEditInfo) * TSTN, "Boundary edit info"); for (int i = 0; i < totvert; i++) { - boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE; + boundary->edit_info[i].original_vertex.i = BOUNDARY_VERTEX_NONE; + boundary->edit_info[i].original_vertex_i = BOUNDARY_VERTEX_NONE; boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE; } - GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int)); - GSQueue *next_iteration = BLI_gsqueue_new(sizeof(int)); + GSQueue *current_iteration = BLI_gsqueue_new(sizeof(SculptVertRef)); + GSQueue *next_iteration = BLI_gsqueue_new(sizeof(SculptVertRef)); /* Initialized the first iteration with the vertices already in the boundary. This is propagation * step 0. */ BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); for (int i = 0; i < boundary->num_vertices; i++) { - boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i]; - boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0; + boundary->edit_info[boundary->vertex_indices[i]].original_vertex = boundary->vertices[i]; + boundary->edit_info[boundary->vertex_indices[i]].original_vertex_i = + boundary->vertex_indices[i]; + boundary->edit_info[boundary->vertex_indices[i]].num_propagation_steps = 0; /* This ensures that all duplicate vertices in the boundary have the same original_vertex * index, so the deformation for them will be the same. */ @@ -333,7 +381,10 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, SculptVertexNeighborIter ni_duplis; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) { if (ni_duplis.is_duplicate) { - boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i]; + int index = ni_duplis.index; + + boundary->edit_info[index].original_vertex = boundary->vertices[i]; + boundary->edit_info[index].original_vertex_i = boundary->vertex_indices[i]; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); @@ -354,31 +405,36 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, } while (!BLI_gsqueue_is_empty(current_iteration)) { - int from_v; + SculptVertRef from_v; BLI_gsqueue_pop(current_iteration, &from_v); + const int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v); SculptVertexNeighborIter ni; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - const bool is_visible = SCULPT_vertex_visible_get(ss, ni.index); + const bool is_visible = SCULPT_vertex_visible_get(ss, ni.vertex); + if (!is_visible || boundary->edit_info[ni.index].num_propagation_steps != BOUNDARY_STEPS_NONE) { continue; } boundary->edit_info[ni.index].original_vertex = - boundary->edit_info[from_v].original_vertex; + boundary->edit_info[from_v_i].original_vertex; + + boundary->edit_info[ni.index].original_vertex_i = + boundary->edit_info[from_v_i].original_vertex_i; BLI_BITMAP_ENABLE(visited_vertices, ni.index); if (ni.is_duplicate) { /* Grids duplicates handling. */ boundary->edit_info[ni.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps; + boundary->edit_info[from_v_i].num_propagation_steps; } else { boundary->edit_info[ni.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps + 1; + boundary->edit_info[from_v_i].num_propagation_steps + 1; - BLI_gsqueue_push(next_iteration, &ni.index); + BLI_gsqueue_push(next_iteration, &ni.vertex); /* When copying the data to the neighbor for the next iteration, it has to be copied to * all its duplicates too. This is because it is not possible to know if the updated @@ -386,12 +442,14 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, * copy the data in the from_v neighbor iterator. */ if (has_duplicates) { SculptVertexNeighborIter ni_duplis; - SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) { + SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni_duplis) { if (ni_duplis.is_duplicate) { boundary->edit_info[ni_duplis.index].original_vertex = - boundary->edit_info[from_v].original_vertex; + boundary->edit_info[from_v_i].original_vertex; + boundary->edit_info[ni_duplis.index].original_vertex_i = + boundary->edit_info[from_v_i].original_vertex_i; boundary->edit_info[ni_duplis.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps + 1; + boundary->edit_info[from_v_i].num_propagation_steps + 1; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); @@ -399,11 +457,11 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* Check the distance using the vertex that was propagated from the initial vertex that * was used to initialize the boundary. */ - if (boundary->edit_info[from_v].original_vertex == initial_vertex) { - boundary->pivot_vertex = ni.index; - copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index)); + if (boundary->edit_info[from_v_i].original_vertex.i == initial_vertex.i) { + boundary->pivot_vertex = ni.vertex; + copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.vertex)); accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v), - SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_vertex_co_get(ss, ni.vertex)); } } } @@ -412,7 +470,7 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* Copy the new vertices to the queue to be processed in the next iteration. */ while (!BLI_gsqueue_is_empty(next_iteration)) { - int next_v; + SculptVertRef next_v; BLI_gsqueue_pop(next_iteration, &next_v); BLI_gsqueue_push(current_iteration, &next_v); } @@ -443,7 +501,7 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps); } - if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) { + if (boundary->edit_info[i].original_vertex.i == boundary->initial_vertex.i) { /* All vertices that are propagated from the original vertex won't be affected by the * boundary falloff, so there is no need to calculate anything else. */ continue; @@ -455,7 +513,8 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, continue; } - const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex]; + const float boundary_distance = boundary->distance[BKE_pbvh_vertex_index_to_table( + ss->pbvh, boundary->edit_info[i].original_vertex)]; float falloff_distance = 0.0f; float direction = 1.0f; @@ -491,22 +550,27 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, * return NULL if there is no boundary from the given vertex using the given radius. */ SculptBoundary *SCULPT_boundary_data_init(Object *object, Brush *brush, - const int initial_vertex, + const SculptVertRef initial_vertex, const float radius) { SculptSession *ss = object->sculpt; - if (initial_vertex == BOUNDARY_VERTEX_NONE) { + if (initial_vertex.i == BOUNDARY_VERTEX_NONE) { return NULL; } + // XXX force update of BMVert->head.index + if (ss->bm) { + ss->bm->elem_index_dirty |= BM_VERT; + } + SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(object); - const int boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex( - ss, initial_vertex, radius); + const SculptVertRef boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex( + ss, initial_vertex, BKE_pbvh_vertex_index_to_table(ss->pbvh, initial_vertex), radius); - if (boundary_initial_vertex == BOUNDARY_VERTEX_NONE) { + if (boundary_initial_vertex.i == BOUNDARY_VERTEX_NONE) { return NULL; } @@ -516,7 +580,7 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object, return NULL; } - SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data"); + SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary) * TSTN, "Boundary edit data"); const bool init_boundary_distances = brush ? brush->boundary_falloff_type != BRUSH_BOUNDARY_FALLOFF_CONSTANT : @@ -532,9 +596,12 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object, void SCULPT_boundary_data_free(SculptBoundary *boundary) { + printf(" ======================= boundary free!\n\n"); + MEM_SAFE_FREE(boundary->vertices); MEM_SAFE_FREE(boundary->edges); MEM_SAFE_FREE(boundary->distance); + MEM_SAFE_FREE(boundary->edit_info); MEM_SAFE_FREE(boundary->bend.pivot_positions); MEM_SAFE_FREE(boundary->bend.pivot_rotation_axis); @@ -550,51 +617,187 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo { const int totvert = SCULPT_vertex_count_get(ss); boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN( - totvert, 3 * sizeof(float), "pivot rotation axis"); + totvert, 3 * sizeof(float) * TSTN, "pivot rotation axis"); boundary->bend.pivot_positions = MEM_calloc_arrayN( - totvert, 3 * sizeof(float), "pivot positions"); + totvert, 4 * sizeof(float) * TSTN, "pivot positions"); - for (int i = 0; i < totvert; i++) { - if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { - continue; + for (int step = 0; step < 1; step++) { + bool ok = true; + + for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + bool bad = boundary->edit_info[i].num_propagation_steps != + boundary->max_propagation_steps - step; + if (step == 2) { + bad = false; + } + + bad = bad || boundary->edit_info[i].original_vertex_i == BOUNDARY_VERTEX_NONE; + bad = bad || + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] != 0.0f; + + if (bad && (step != 3 || boundary->edit_info[i].original_vertex_i == i)) { + continue; + } + + float dir[3]; + float normal[3]; + SCULPT_vertex_normal_get(ss, vertex, normal); + sub_v3_v3v3(dir, + SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), + SCULPT_vertex_co_get(ss, vertex)); + + cross_v3_v3v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i], + dir, + normal); + normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); + copy_v3_v3(boundary->bend.pivot_positions[i], SCULPT_vertex_co_get(ss, vertex)); + + add_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i], + SCULPT_vertex_co_get(ss, vertex)); + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] += 1.0f; + } + + for (int i = 0; i < totvert; i++) { + if (boundary->edit_info[i].num_propagation_steps == 0 && + boundary->bend.pivot_positions[i][3] == 0.0f) { + + printf("eek!\n"); + SculptVertexNeighborIter ni; + SculptVertexNeighborIter ni2; + const int maxstack = 32; + SculptVertRef stack[32]; + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + const float *co1 = SCULPT_vertex_co_get(ss, vertex); + int si = 0; + stack[si++] = vertex; + + float mindis = 1e17; + float pivota[3], pivotb[3]; + float rota[3], rotb[3]; + SculptVertRef v_a = {-1}, v_b = {-1}; + + copy_v3_v3(pivota, co1); + copy_v3_v3(pivotb, co1); + zero_v3(rota); + zero_v3(rotb); + + float mindis2 = 1e17; + + // do a short walk on mesh to find pivot + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni2) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.vertex, ni) { + SculptVertRef v2 = ni.vertex; + + bool ok = ni.vertex.i != vertex.i; + ok = ok && boundary->edit_info[ni.index].num_propagation_steps == 0; + ok = ok && boundary->bend.pivot_positions[ni.index][3] > 0.0f; + + if (ok) { + const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); + float dis = len_squared_v3v3(co1, co2); + + if (dis < mindis) { + copy_v3_v3(pivotb, pivota); + copy_v3_v3(rotb, rota); + + v_b = v_a; + mindis2 = mindis; + + copy_v3_v3(pivota, boundary->bend.pivot_positions[ni.index]); + copy_v3_v3(rota, boundary->bend.pivot_rotation_axis[ni.index]); + + v_a = ni.vertex; + mindis = dis; + } + else if (dis < mindis2) { + v_b = ni.vertex; + copy_v3_v3(pivotb, boundary->bend.pivot_positions[ni.index]); + copy_v3_v3(rotb, boundary->bend.pivot_rotation_axis[ni.index]); + mindis2 = dis; + } + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni2); + + float len = len_v3v3(pivota, pivotb); + if (len > 0.0) { + float vec1[3], vec2[3]; + + sub_v3_v3v3(vec1, pivotb, pivota); + normalize_v3(vec1); + + sub_v3_v3v3(vec2, co1, pivota); + + float t = dot_v3v3(vec2, vec1) / len; + + interp_v3_v3v3(pivota, pivota, pivotb, t); + interp_v3_v3v3(rota, rota, rotb, t); + + copy_v3_v3(boundary->bend.pivot_positions[i], pivota); + boundary->bend.pivot_positions[i][3] = 1.0f; + copy_v3_v3(boundary->bend.pivot_rotation_axis[i], rota); + } + } + + if (boundary->bend.pivot_positions[i][3] > 0.0f) { + float mul = 1.0f / boundary->bend.pivot_positions[i][3]; + boundary->bend.pivot_positions[i][0] *= mul; + boundary->bend.pivot_positions[i][1] *= mul; + boundary->bend.pivot_positions[i][2] *= mul; + boundary->bend.pivot_positions[i][3] = 1.0f; + } + else { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + const float *co = SCULPT_vertex_co_get(ss, vertex); + + // boundary->bend.pivot_positions[i][0] = co[0]; + // boundary->bend.pivot_positions[i][1] = co[1]; + // boundary->bend.pivot_positions[i][2] = co[2]; + // boundary->bend.pivot_positions[i][3] = 0.0f; + } + } + + if (ok) { + break; } - float dir[3]; - float normal[3]; - SCULPT_vertex_normal_get(ss, i, normal); - sub_v3_v3v3(dir, - SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, i)); - cross_v3_v3v3( - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex], dir, normal); - normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); - copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex], - SCULPT_vertex_co_get(ss, i)); } for (int i = 0; i < totvert; i++) { if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) { continue; } + copy_v3_v3(boundary->bend.pivot_positions[i], - boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]); + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]); + + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] = 1.0f; + copy_v3_v3(boundary->bend.pivot_rotation_axis[i], - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); } } static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary) { const int totvert = SCULPT_vertex_count_get(ss); - boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions"); + boundary->slide.directions = MEM_calloc_arrayN( + totvert, 3 * sizeof(float) * TSTN, "slide directions"); for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { continue; } - sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex], + + sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i], SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, i)); - normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]); + SCULPT_vertex_co_get(ss, vertex)); + normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i]); } for (int i = 0; i < totvert; i++) { @@ -602,7 +805,7 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b continue; } copy_v3_v3(boundary->slide.directions[i], - boundary->slide.directions[boundary->edit_info[i].original_vertex]); + boundary->slide.directions[boundary->edit_info[i].original_vertex_i]); } } @@ -610,7 +813,7 @@ static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *b { zero_v3(boundary->twist.pivot_position); float(*poly_verts)[3] = MEM_malloc_arrayN( - boundary->num_vertices, sizeof(float) * 3, "poly verts"); + boundary->num_vertices, sizeof(float) * 3 * TSTN, "poly verts"); for (int i = 0; i < boundary->num_vertices; i++) { add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i])); copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i])); @@ -657,7 +860,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); float angle_factor = disp / ss->cache->radius; @@ -679,9 +882,10 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float t_orig_co[3]; float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]); rotate_v3_v3v3fl(target_co, t_orig_co, @@ -711,7 +915,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); @@ -727,7 +931,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -757,7 +961,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); @@ -773,7 +977,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float normal[3]; normal_short_to_float_v3(normal, orig_data.no); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); @@ -805,7 +1009,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].num_propagation_steps == -1) { @@ -819,7 +1023,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -848,7 +1052,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); float angle_factor = disp / ss->cache->radius; @@ -870,7 +1074,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float t_orig_co[3]; float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position); @@ -902,7 +1106,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].num_propagation_steps == -1) { @@ -919,9 +1123,9 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, int total_neighbors = 0; const int current_propagation_steps = boundary->edit_info[vd.index].num_propagation_steps; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { if (current_propagation_steps == boundary->edit_info[ni.index].num_propagation_steps) { - add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.vertex)); total_neighbors++; } } @@ -955,7 +1159,8 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn const int symm_area = ss->cache->mirror_symmetry_pass; if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - int initial_vertex; + SculptVertRef initial_vertex; + if (ss->cache->mirror_symmetry_pass == 0) { initial_vertex = SCULPT_active_vertex_get(ss); } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index a53a2126af4..a6d23131fdb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -219,26 +219,32 @@ static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim) static void cloth_brush_add_length_constraint(SculptSession *ss, SculptClothSimulation *cloth_sim, const int node_index, - const int v1, - const int v2, + const int v1i, + const int v2i, const bool use_persistent) { SculptClothLengthConstraint *length_constraint = &cloth_sim->length_constraints[cloth_sim->tot_length_constraints]; - length_constraint->elem_index_a = v1; - length_constraint->elem_index_b = v2; + SculptVertRef v1, v2; + + v1 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v1i); + v2 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v2i); + + length_constraint->elem_index_a = v1i; + length_constraint->elem_index_b = v2i; length_constraint->node = node_index; - length_constraint->elem_position_a = cloth_sim->pos[v1]; - length_constraint->elem_position_b = cloth_sim->pos[v2]; + length_constraint->elem_position_a = cloth_sim->pos[v1i]; + length_constraint->elem_position_b = cloth_sim->pos[v2i]; length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL; if (use_persistent) { - length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), - SCULPT_vertex_persistent_co_get(ss, v2)); + length_constraint->length = len_v3v3( + SCULPT_vertex_persistent_co_get(ss, v1, cloth_sim->cd_pers_co), + SCULPT_vertex_persistent_co_get(ss, v2, cloth_sim->cd_pers_no)); } else { length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1), @@ -252,7 +258,7 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, cloth_brush_reallocate_constraints(cloth_sim); /* Add the constraint to the #GSet to avoid creating it again. */ - BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2); + BLI_edgeset_add(cloth_sim->created_length_constraints, v1i, v2i); } static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim, @@ -386,7 +392,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex( int tot_indices = 0; build_indices[tot_indices] = vd.index; tot_indices++; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { build_indices[tot_indices] = ni.index; tot_indices++; } @@ -556,7 +562,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float brush_disp[3]; @@ -803,7 +809,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor); const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) * - SCULPT_automasking_factor_get(automasking, ss, vd.index); + SCULPT_automasking_factor_get(automasking, ss, vd.vertex); madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v); madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v); @@ -849,6 +855,9 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, const int v1 = constraint->elem_index_a; const int v2 = constraint->elem_index_b; + const SculptVertRef v1ref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v1); + const SculptVertRef v2ref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v2); + float v1_to_v2[3]; sub_v3_v3v3(v1_to_v2, constraint->elem_position_b, constraint->elem_position_a); const float current_distance = len_v3(v1_to_v2); @@ -871,10 +880,10 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f); - const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) * - SCULPT_automasking_factor_get(automasking, ss, v1); - const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) * - SCULPT_automasking_factor_get(automasking, ss, v2); + const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1ref)) * + SCULPT_automasking_factor_get(automasking, ss, v1ref); + const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2ref)) * + SCULPT_automasking_factor_get(automasking, ss, v2ref); float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); @@ -1065,6 +1074,13 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss, cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints"); + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + cloth_sim->cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO); + cloth_sim->cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO); + cloth_sim->cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); + } + + //cloth_sim->cd_pers_co = SCULPT_dyntopo_get_templayer cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) * CLOTH_LENGTH_CONSTRAINTS_BLOCK, "cloth length constraints"); @@ -1147,16 +1163,20 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation const int totverts = SCULPT_vertex_count_get(ss); const bool has_deformation_pos = cloth_sim->deformation_pos != NULL; const bool has_softbody_pos = cloth_sim->softbody_pos != NULL; + SCULPT_vertex_random_access_ensure(ss); + for (int i = 0; i < totverts; i++) { - copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, vertex)); + copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, vertex)); + copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, vertex)); if (has_deformation_pos) { - copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, vertex)); cloth_sim->deformation_strength[i] = 1.0f; } if (has_softbody_pos) { - copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, vertex)); } } } @@ -1165,7 +1185,9 @@ void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSim { const int totverts = SCULPT_vertex_count_get(ss); for (int i = 0; i < totverts; i++) { - copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex)); } } @@ -1448,13 +1470,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { float fade = vd.mask ? *vd.mask : 0.0f; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); fade = 1.0f - fade; float force[3] = {0.0f, 0.0f, 0.0f}; float disp[3], temp[3], transform[3][3]; if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) { - if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) { + if (!SCULPT_vertex_has_face_set(ss, vd.vertex, ss->filter_cache->active_face_set)) { continue; } } @@ -1473,7 +1495,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, break; case CLOTH_FILTER_INFLATE: { float normal[3]; - SCULPT_vertex_normal_get(ss, vd.index, normal); + SCULPT_vertex_normal_get(ss, vd.vertex, normal); mul_v3_v3fl(force, normal, fade * data->filter_strength); } break; case CLOTH_FILTER_EXPAND: @@ -1538,7 +1560,9 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent const int totverts = SCULPT_vertex_count_get(ss); for (int i = 0; i < totverts; i++) { - copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex)); } SculptThreadedTaskData data = { diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.c b/source/blender/editors/sculpt_paint/sculpt_curvature.c new file mode 100644 index 00000000000..a23afb93407 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.c @@ -0,0 +1,239 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 by Joseph Eagar + * All rights reserved. + * Implements curvature analysis for sculpt tools + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_dial_2d.h" +#include "BLI_ghash.h" +#include "BLI_gsqueue.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_math_color_blend.h" +#include "BLI_math_solvers.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "PIL_time.h" + +#include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_kelvinlet.h" +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_mirror.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" +#include "BKE_pbvh.h" +#include "BKE_pointcache.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subsurf.h" + +#include "DEG_depsgraph.h" + +#include "IMB_colormanagement.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_space_api.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +/* +If you're working with uniform triangle tesselations, the math for +calculating principle curvatures reduces to doing an eigen decomposition +of the smoothed normal covariance matrix. + +The normal covariance matrix is just: + +nx*nx nx*ny nx*nz +ny*nx ny*ny ny*nz +nz*nx nz*ny nz*nz + +To find principle curvatures, simply subtract neighboring covariance matrices. +You can do this over any number of neighborhood rings to get more accurate result + +*/ + +BLI_INLINE void normal_covariance(float mat[3][3], float no[3]) +{ + mat[0][0] = no[0] * no[0]; + mat[0][1] = no[0] * no[1]; + mat[0][2] = no[0] * no[2]; + mat[1][0] = no[1] * no[0]; + mat[1][1] = no[1] * no[1]; + mat[1][2] = no[1] * no[2]; + mat[2][0] = no[2] * no[0]; + mat[2][1] = no[2] * no[1]; + mat[2][2] = no[2] * no[2]; +} + +bool SCULPT_calc_principle_curvatures(SculptSession *ss, + SculptVertRef vertex, + SculptCurvatureData *out, + bool useAccurateSolver) +{ + SculptVertexNeighborIter ni; + float nmat[3][3], nmat2[3][3]; + float no[3], no2[3]; + + memset(out, 0, sizeof(SculptCurvatureData)); + + SCULPT_vertex_normal_get(ss, vertex, no); + normal_covariance(nmat, no); + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + SCULPT_vertex_normal_get(ss, ni.vertex, no2); + sub_v3_v3(no2, no); + + normal_covariance(nmat2, no2); + + add_m3_m3m3(nmat, nmat, nmat2); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (!useAccurateSolver || !BLI_eigen_solve_selfadjoint_m3(nmat, out->ks, out->principle)) { + // do simple power solve in one direction + + float t[3]; + float t2[3]; + + SCULPT_vertex_normal_get(ss, vertex, no); + copy_v3_v3(t, no); + + for (int i = 0; i < 15; i++) { + if (i > 0) { + normalize_v3(t); + + if (i > 1 && len_squared_v3v3(t, t2) < 0.0001) { + break; + } + + copy_v3_v3(t2, t); + } + + mul_m3_v3(nmat, t); + } + + out->ks[1] = normalize_v3(t); + copy_v3_v3(out->principle[1], t); + + cross_v3_v3v3(out->principle[0], out->principle[1], no); + normalize_v3(out->principle[0]); + } + + return true; +} + +void SCULPT_curvature_dir_get(SculptSession *ss, + SculptVertRef v, + float dir[3], + bool useAccurateSolver) +{ + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + SculptCurvatureData curv; + SCULPT_calc_principle_curvatures(ss, v, &curv, useAccurateSolver); + + copy_v3_v3(dir, curv.principle[0]); + return; + } + + BMVert *bv = (BMVert *)v.i; + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, bv); + + copy_v3_v3(dir, mv->curvature_dir); +} + +void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver) +{ + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + // caching only happens for bmesh for now + return; + } + + if (BKE_pbvh_curvature_update_get(node)) { + PBVHVertexIter vi; + + BKE_pbvh_curvature_update_set(node, false); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vi, PBVH_ITER_UNIQUE) { + BMVert *v = (BMVert *)vi.vertex.i; + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + SculptCurvatureData curv; + SCULPT_calc_principle_curvatures(ss, vi.vertex, &curv, useAccurateSolver); + + copy_v3_v3(mv->curvature_dir, curv.principle[0]); + } + BKE_pbvh_vertex_iter_end; + } +} diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index 188bb0a88eb..f94cdccae52 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -64,6 +64,7 @@ typedef struct { float edge_length; struct IsectRayPrecalc isect_precalc; + SculptSession *ss; } SculptDetailRaycastData; static bool sculpt_and_constant_or_manual_detail_poll(bContext *C) @@ -110,18 +111,35 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) /* Update topology size. */ float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, sd->detail_range); SCULPT_undo_push_begin(ob, "Dynamic topology flood fill"); SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); - while (BKE_pbvh_bmesh_update_topology( - ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, size, false, false)) { + DyntopoMaskCB mask_cb; + void *mask_cb_data; + + SCULPT_dyntopo_automasking_init(ss, NULL, sd, &mask_cb, &mask_cb_data); + + while (BKE_pbvh_bmesh_update_topology(ss->pbvh, + PBVH_Collapse | PBVH_Subdivide, + center, + NULL, + size, + false, + false, + -1, + false, + mask_cb, + mask_cb_data)) { + for (int i = 0; i < totnodes; i++) { BKE_pbvh_node_mark_topology_update(nodes[i]); } } + SCULPT_dyntopo_automasking_end(mask_cb_data); + MEM_SAFE_FREE(nodes); SCULPT_undo_push_end(); @@ -174,13 +192,13 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my) BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); /* Average the edge length of the connected edges to the active vertex. */ - int active_vertex = SCULPT_active_vertex_get(ss); + SculptVertRef active_vertex = SCULPT_active_vertex_get(ss); const float *active_vertex_co = SCULPT_active_vertex_co_get(ss); float edge_length = 0.0f; int tot = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { - edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.index)); + edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.vertex)); tot += 1; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -193,8 +211,13 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) { if (BKE_pbvh_node_get_tmin(node) < *tmin) { SculptDetailRaycastData *srd = data_v; - if (BKE_pbvh_bmesh_node_raycast_detail( - node, srd->ray_start, &srd->isect_precalc, &srd->depth, &srd->edge_length)) { + + if (BKE_pbvh_bmesh_node_raycast_detail(srd->ss->pbvh, + node, + srd->ray_start, + &srd->isect_precalc, + &srd->depth, + &srd->edge_length)) { srd->hit = true; *tmin = srd->depth; } @@ -215,12 +238,20 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region, SculptDetailRaycastData srd; srd.hit = 0; + srd.ss = ob->sculpt; + srd.ray_start = ray_start; srd.depth = depth; srd.edge_length = 0.0f; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); - BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, ray_start, ray_normal, false); + BKE_pbvh_raycast(ob->sculpt->pbvh, + sculpt_raycast_detail_cb, + &srd, + ray_start, + ray_normal, + false, + srd.ss->stroke_id); if (srd.hit && srd.edge_length > 0.0f) { /* Convert edge length to world space detail resolution. */ @@ -569,14 +600,14 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob, DyntopoDetailSizeEditCustomData *cd) { SculptSession *ss = ob->sculpt; - const int active_vertex = SCULPT_active_vertex_get(ss); + const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss); float len_accum = 0; int num_neighbors = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex), - SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_vertex_co_get(ss, ni.vertex)); num_neighbors++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 87e0ea7f6a9..5461d74cb47 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -23,9 +23,15 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_blenlib.h" +#include "BLI_compiler_attrs.h" #include "BLI_hash.h" +#include "BLI_linklist.h" #include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" #include "BLI_task.h" #include "BLT_translation.h" @@ -35,6 +41,7 @@ #include "DNA_modifier_types.h" #include "BKE_brush.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" @@ -76,12 +83,126 @@ #include <math.h> #include <stdlib.h> -void SCULPT_dynamic_topology_triangulate(BMesh *bm) +/* +Copies the bmesh, but orders the elements +according to PBVH node to improve memory locality +*/ +void SCULPT_reorder_bmesh(SculptSession *ss) { - if (bm->totloop != bm->totface * 3) { - BM_mesh_triangulate( - bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL); +#if 0 + SCULPT_face_random_access_ensure(ss); + SCULPT_vertex_random_access_ensure(ss); + + int actv = ss->active_vertex_index.i ? + BKE_pbvh_vertex_index_to_table(ss->pbvh, ss->active_vertex_index) : + -1; + int actf = ss->active_face_index.i ? + BKE_pbvh_face_index_to_table(ss->pbvh, ss->active_face_index) : + -1; + + if (ss->bm_log) { + BM_log_full_mesh(ss->bm, ss->bm_log); + } + + ss->bm = BKE_pbvh_reorder_bmesh(ss->pbvh); + + SCULPT_face_random_access_ensure(ss); + SCULPT_vertex_random_access_ensure(ss); + + if (actv >= 0) { + ss->active_vertex_index = BKE_pbvh_table_index_to_vertex(ss->pbvh, actv); + } + if (actf >= 0) { + ss->active_face_index = BKE_pbvh_table_index_to_face(ss->pbvh, actf); + } + + SCULPT_dyntopo_node_layers_update_offsets(ss); + + if (ss->bm_log) { + BM_log_set_bm(ss->bm, ss->bm_log); + } +#endif +} + +void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm) +{ + if (bm->totloop == bm->totface * 3) { + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; + + return; + } + + BMIter iter; + BMFace *f; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + } + + MemArena *pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__); + LinkNode *f_double = NULL; + + BMFace **faces_array = NULL; + BLI_array_declare(faces_array); + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (f->len <= 3) { + continue; + } + + bool sel = BM_elem_flag_test(f, BM_ELEM_SELECT); + + int faces_array_tot = f->len; + BLI_array_clear(faces_array); + BLI_array_grow_items(faces_array, faces_array_tot); + // BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot); + + BM_face_triangulate(bm, + f, + faces_array, + &faces_array_tot, + NULL, + NULL, + &f_double, + MOD_TRIANGULATE_QUAD_BEAUTY, + MOD_TRIANGULATE_NGON_EARCLIP, + true, + pf_arena, + NULL); + + for (int i = 0; i < faces_array_tot; i++) { + BMFace *f2 = faces_array[i]; + + // forcibly copy selection state + if (sel) { + BM_face_select_set(bm, f2, true); + + // restore original face selection state too, triangulate code unset it + BM_face_select_set(bm, f, true); + } + + // paranoia check that tag flag wasn't copied over + BM_elem_flag_disable(f2, BM_ELEM_TAG); + } } + + while (f_double) { + LinkNode *next = f_double->next; + BM_face_kill(bm, f_double->link); + MEM_freeN(f_double); + f_double = next; + } + + BLI_memarena_free(pf_arena); + MEM_SAFE_FREE(faces_array); + + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; + + // BM_mesh_triangulate( + // bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, + // NULL); } void SCULPT_pbvh_clear(Object *ob) @@ -110,19 +231,99 @@ void SCULPT_pbvh_clear(Object *ob) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } +void SCULPT_dyntopo_save_origverts(SculptSession *ss) +{ + BMIter iter; + BMVert *v; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + copy_v3_v3(mv->origco, v->co); + copy_v3_v3(mv->origno, v->no); + + if (ss->cd_vcol_offset >= 0) { + MPropCol *mp = (MPropCol *)BM_ELEM_CD_GET_VOID_P(v, ss->cd_vcol_offset); + copy_v4_v4(mv->origcolor, mp->color); + } + } +} + +char dyntopop_node_idx_layer_id[] = "_dyntopo_node_id"; + +void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss) +{ + SCULPT_dyntopo_node_layers_add(ss); + if (ss->pbvh) { + BKE_pbvh_update_offsets( + ss->pbvh, ss->cd_vert_node_offset, ss->cd_face_node_offset, ss->cd_dyn_vert); + } + if (ss->bm_log) { + BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert); + } +} + +bool SCULPT_dyntopo_has_templayer(SculptSession *ss, int type, const char *name) +{ + return CustomData_get_named_layer_index(&ss->bm->vdata, type, name) >= 0; +} + +void SCULPT_dyntopo_ensure_templayer(SculptSession *ss, int type, const char *name) +{ + int li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name); + + if (li < 0) { + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, type, name); + SCULPT_dyntopo_node_layers_update_offsets(ss); + + li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name); + ss->bm->vdata.layers[li].flag |= CD_FLAG_TEMPORARY; + } +} + +int SCULPT_dyntopo_get_templayer(SculptSession *ss, int type, const char *name) +{ + int li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name); + + if (li < 0) { + return -1; + } + + return CustomData_get_n_offset( + &ss->bm->vdata, type, li - CustomData_get_layer_index(&ss->bm->vdata, type)); +} + void SCULPT_dyntopo_node_layers_add(SculptSession *ss) { - int cd_node_layer_index; + int cd_node_layer_index, cd_face_node_layer_index; + + int cd_origco_index, cd_origno_index, cd_origvcol_index = -1; + bool have_vcol = CustomData_has_layer(&ss->bm->vdata, CD_PROP_COLOR); - char layer_id[] = "_dyntopo_node_id"; + BMCustomLayerReq vlayers[] = {{CD_PAINT_MASK, NULL, 0}, + {CD_DYNTOPO_VERT, NULL, CD_FLAG_TEMPORARY}, + {CD_PROP_INT32, dyntopop_node_idx_layer_id, CD_FLAG_TEMPORARY}}; - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT32, layer_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, layer_id); - cd_node_layer_index = CustomData_get_named_layer_index( - &ss->bm->vdata, CD_PROP_INT32, layer_id); + BM_data_layers_ensure(ss->bm, &ss->bm->vdata, vlayers, 3); + + cd_face_node_layer_index = CustomData_get_named_layer_index( + &ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id); + if (cd_face_node_layer_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id); } + // get indices again, as they might have changed after adding new layers + cd_node_layer_index = CustomData_get_named_layer_index( + &ss->bm->vdata, CD_PROP_INT32, dyntopop_node_idx_layer_id); + cd_face_node_layer_index = CustomData_get_named_layer_index( + &ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id); + + ss->cd_origvcol_offset = -1; + + ss->cd_dyn_vert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT); + + ss->cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR); + ss->cd_vert_node_offset = CustomData_get_n_offset( &ss->bm->vdata, CD_PROP_INT32, @@ -130,21 +331,143 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss) ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT32, layer_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, layer_id); - cd_node_layer_index = CustomData_get_named_layer_index( - &ss->bm->pdata, CD_PROP_INT32, layer_id); - } - ss->cd_face_node_offset = CustomData_get_n_offset( &ss->bm->pdata, CD_PROP_INT32, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32)); + cd_face_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32)); - ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; + ss->bm->pdata.layers[cd_face_node_layer_index].flag |= CD_FLAG_TEMPORARY; + ss->cd_faceset_offset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS); } +/** + Syncs customdata layers with internal bmesh, but ignores deleted layers. +*/ +void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me) +{ + SculptSession *ss = ob->sculpt; + + if (!ss || !ss->bm) { + return; + } + + bool modified = false; + BMesh *bm = ss->bm; + + CustomData *cd1[4] = {&me->vdata, &me->edata, &me->ldata, &me->pdata}; + CustomData *cd2[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + int types[4] = {BM_VERT, BM_EDGE, BM_LOOP, BM_FACE}; + int badmask = CD_MASK_MLOOP | CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MPOLY | CD_MASK_ORIGINDEX | + CD_MASK_ORIGSPACE | CD_MASK_MFACE; + + for (int i = 0; i < 4; i++) { + CustomDataLayer **newlayers = NULL; + BLI_array_declare(newlayers); + + CustomData *data1 = cd1[i]; + CustomData *data2 = cd2[i]; + + if (!data1->layers) { + modified |= data2->layers != NULL; + continue; + } + + for (int j = 0; j < data1->totlayer; j++) { + CustomDataLayer *cl1 = data1->layers + j; + + if ((1 << cl1->type) & badmask) { + continue; + } + + int idx = CustomData_get_named_layer_index(data2, cl1->type, cl1->name); + if (idx < 0) { + BLI_array_append(newlayers, cl1); + } + } + + for (int j = 0; j < BLI_array_len(newlayers); j++) { + BM_data_layer_add_named(bm, data2, newlayers[j]->type, newlayers[j]->name); + modified = true; + } + + bool typemap[CD_NUMTYPES] = {0}; + + for (int j = 0; j < data1->totlayer; j++) { + CustomDataLayer *cl1 = data1->layers + j; + + if ((1 << cl1->type) & badmask) { + continue; + } + + if (typemap[cl1->type]) { + continue; + } + + typemap[cl1->type] = true; + + // find first layer + int baseidx = CustomData_get_layer_index(data2, cl1->type); + + if (baseidx < 0) { + modified |= true; + continue; + } + + CustomDataLayer *cl2 = data2->layers + baseidx; + + int idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active].name); + if (idx >= 0) { + modified |= idx - baseidx != cl2->active; + cl2->active = idx - baseidx; + } + + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_rnd].name); + if (idx >= 0) { + modified |= idx - baseidx != cl2->active_rnd; + cl2->active_rnd = idx - baseidx; + } + + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_mask].name); + if (idx >= 0) { + modified |= idx - baseidx != cl2->active_mask; + cl2->active_mask = idx - baseidx; + } + + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_clone].name); + if (idx >= 0) { + modified |= idx - baseidx != cl2->active_clone; + cl2->active_clone = idx - baseidx; + } + + for (int k = baseidx; k < data2->totlayer; k++) { + CustomDataLayer *cl3 = data2->layers + k; + + if (cl3->type != cl2->type) { + break; + } + + // based off of how CustomData_set_layer_XXXX_index works + + cl3->active = (cl2->active + baseidx) - k; + cl3->active_rnd = (cl2->active_rnd + baseidx) - k; + cl3->active_mask = (cl2->active_mask + baseidx) - k; + cl3->active_clone = (cl2->active_clone + baseidx) - k; + } + } + + BLI_array_free(newlayers); + } + + if (modified) { + SCULPT_dyntopo_node_layers_update_offsets(ss); + } +} + +BMesh *BM_mesh_bm_from_me_threaded(BMesh *bm, + Object *ob, + const Mesh *me, + const struct BMeshFromMeshParams *params); + void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { SculptSession *ss = ob->sculpt; @@ -160,21 +483,101 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene BKE_mesh_mselect_clear(me); /* Create triangles-only BMesh. */ +#if 1 ss->bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); + &((struct BMeshCreateParams){.use_toolflags = false, + .use_unique_ids = true, + .use_id_elem_mask = BM_VERT | BM_FACE, + .use_id_map = true})); - BM_mesh_bm_from_me(ss->bm, + BM_mesh_bm_from_me(NULL, + ss->bm, me, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, .use_shapekey = true, .active_shapekey = ob->shapenr, })); - SCULPT_dynamic_topology_triangulate(ss->bm); - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); +#else + ss->bm = BM_mesh_bm_from_me_threaded(NULL, + NULL, + me, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + .use_shapekey = true, + .active_shapekey = ob->shapenr, + })); +#endif + SCULPT_dynamic_topology_triangulate(ss, ss->bm); + SCULPT_dyntopo_node_layers_add(ss); + SCULPT_dyntopo_save_origverts(ss); + + BMIter iter; + BMVert *v; + int cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR); + + int cd_pers_co = -1, cd_pers_no = -1, cd_pers_disp = -1; + int cd_layer_disp = -1; + + // convert layer brush data + if (ss->persistent_base) { + BMCustomLayerReq layers[] = {{CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO, CD_FLAG_TEMPORARY}, + {CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO, CD_FLAG_TEMPORARY}, + {CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP, CD_FLAG_TEMPORARY}, + {CD_PROP_FLOAT, SCULPT_LAYER_DISP, CD_FLAG_TEMPORARY}}; + + BM_data_layers_ensure(ss->bm, &ss->bm->vdata, layers, 4); + + cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO); + cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO); + cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); + cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP); + + SCULPT_dyntopo_node_layers_update_offsets(ss); + + cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR); + } + else { + cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); + } + + int i = 0; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + if (BM_vert_is_boundary(v)) { + mv->flag |= DYNVERT_BOUNDARY; + } + + // persistent base + if (cd_pers_co >= 0) { + float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co); + float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no); + float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp); + + copy_v3_v3(co, ss->persistent_base[i].co); + copy_v3_v3(no, ss->persistent_base[i].no); + *disp = ss->persistent_base[i].disp; + } + + if (cd_layer_disp >= 0) { + float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_layer_disp); + *disp = 0.0f; + } + + copy_v3_v3(mv->origco, v->co); + copy_v3_v3(mv->origno, v->no); + + if (ss->cd_vcol_offset >= 0) { + MPropCol *color = (MPropCol *)BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset); + copy_v4_v4(mv->origcolor, color->color); + } + + i++; + } + /* Make sure the data for existing faces are initialized. */ if (me->totpoly != ss->bm->totface) { BM_mesh_normals_update(ss->bm); @@ -184,14 +587,44 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; /* Enable logging for undo/redo. */ - ss->bm_log = BM_log_create(ss->bm); + ss->bm_log = BM_log_create(ss->bm, ss->cd_dyn_vert); /* Update dependency graph, so modifiers that depend on dyntopo being enabled * are re-evaluated and the PBVH is re-created. */ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + // TODO: this line here is being slow, do we need it? - joeedh BKE_scene_graph_update_tagged(depsgraph, bmain); } +void SCULPT_dyntopo_save_persistent_base(SculptSession *ss) +{ + int cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO); + int cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO); + int cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); + + if (cd_pers_co >= 0) { + BMIter iter; + + MEM_SAFE_FREE(ss->persistent_base); + ss->persistent_base = MEM_callocN(sizeof(*ss->persistent_base) * ss->bm->totvert, + "ss->persistent_base"); + BMVert *v; + int i = 0; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co); + float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no); + float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp); + + copy_v3_v3(ss->persistent_base[i].co, co); + copy_v3_v3(ss->persistent_base[i].no, no); + ss->persistent_base[i].disp = *disp; + + i++; + } + } +} /* Free the sculpt BMesh and BMLog * * If 'unode' is given, the BMesh's data is copied out to the unode @@ -233,16 +666,6 @@ static void SCULPT_dynamic_topology_disable_ex( else { BKE_sculptsession_bm_to_me(ob, true); - /* Reset Face Sets as they are no longer valid. */ - if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) { - CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, me->totpoly); - } - ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); - for (int i = 0; i < me->totpoly; i++) { - ss->face_sets[i] = 1; - } - me->face_sets_color_default = 1; - /* Sync the visibility to vertices manually as the pmap is still not initialized. */ for (int i = 0; i < me->totvert; i++) { me->mvert[i].flag &= ~ME_HIDE; @@ -253,15 +676,30 @@ static void SCULPT_dynamic_topology_disable_ex( /* Clear data. */ me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + bool disp_saved = false; + + if (ss->bm_log) { + if (ss->bm) { + disp_saved = true; + + // rebuild ss->persistent_base if necassary + SCULPT_dyntopo_save_persistent_base(ss); + } + + BM_log_free(ss->bm_log, true); + ss->bm_log = NULL; + } + /* Typically valid but with global-undo they can be NULL, see: T36234. */ if (ss->bm) { + if (!disp_saved) { + // rebuild ss->persistent_base if necassary + SCULPT_dyntopo_save_persistent_base(ss); + } + BM_mesh_free(ss->bm); ss->bm = NULL; } - if (ss->bm_log) { - BM_log_free(ss->bm_log); - ss->bm_log = NULL; - } BKE_particlesystem_reset_all(ob); BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED); @@ -298,6 +736,8 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, if (use_undo) { SCULPT_undo_push_end(); } + + ss->active_vertex_index.i = ss->active_face_index.i = 0; } } @@ -307,6 +747,7 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, Object *ob) { SculptSession *ss = ob->sculpt; + if (ss->bm == NULL) { /* May be false in background mode. */ const bool use_undo = G.background ? (ED_undo_stack_get() != NULL) : true; @@ -318,6 +759,8 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); SCULPT_undo_push_end(); } + + ss->active_vertex_index.i = ss->active_face_index.i = 0; } } @@ -384,6 +827,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) BLI_assert(ss->bm == NULL); UNUSED_VARS_NDEBUG(ss); +#ifndef DYNTOPO_CD_INTERP for (int i = 0; i < CD_NUMTYPES; i++) { if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) { if (CustomData_has_layer(&me->vdata, i)) { @@ -397,6 +841,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) } } } +#endif { VirtualModifierData virtualModifierData; @@ -453,3 +898,708 @@ void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +#define MAXUVLOOPS 32 +#define MAXUVNEIGHBORS 32 + +typedef struct UVSmoothVert { + double uv[2]; + float co[3]; // world co + BMVert *v; + double w; + int totw; + bool pinned, boundary; + BMLoop *ls[MAXUVLOOPS]; + struct UVSmoothVert *neighbors[MAXUVNEIGHBORS]; + int totloop, totneighbor; +} UVSmoothVert; + +typedef struct UVSmoothTri { + UVSmoothVert *vs[3]; + float area2d, area3d; +} UVSmoothTri; + +#define CON_MAX_VERTS 16 +typedef struct UVSmoothConstraint { + int type; + double k; + UVSmoothVert *vs[CON_MAX_VERTS]; + UVSmoothTri *tri; + double gs[CON_MAX_VERTS][2]; + int totvert; + double params[8]; +} UVSmoothConstraint; + +enum { CON_ANGLES = 0, CON_AREA = 1 }; + +typedef struct UVSolver { + BLI_mempool *verts; + BLI_mempool *tris; + int totvert, tottri; + float snap_limit; + BLI_mempool *constraints; + GHash *vhash; + GHash *fhash; + int cd_uv; + + double totarea3d; + double totarea2d; + + double strength; +} UVSolver; + +/*that that currently this tool is *not* threaded*/ + +typedef struct SculptUVThreadData { + SculptThreadedTaskData data; + UVSolver *solver; +} SculptUVThreadData; + +static UVSolver *uvsolver_new(int cd_uv) +{ + UVSolver *solver = MEM_callocN(sizeof(*solver), "solver"); + + solver->strength = 1.0; + solver->cd_uv = cd_uv; + solver->snap_limit = 0.0025; + + solver->verts = BLI_mempool_create(sizeof(UVSmoothVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + solver->tris = BLI_mempool_create(sizeof(UVSmoothTri), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + solver->constraints = BLI_mempool_create( + sizeof(UVSmoothConstraint), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + + solver->vhash = BLI_ghash_ptr_new("uvsolver"); + solver->fhash = BLI_ghash_ptr_new("uvsolver"); + + return solver; +} + +static void uvsolver_free(UVSolver *solver) +{ + BLI_mempool_destroy(solver->verts); + BLI_mempool_destroy(solver->tris); + BLI_mempool_destroy(solver->constraints); + + BLI_ghash_free(solver->vhash, NULL, NULL); + BLI_ghash_free(solver->fhash, NULL, NULL); + + MEM_freeN(solver); +} + +void *uvsolver_calc_loop_key(UVSolver *solver, BMLoop *l) +{ + // return (void *)l->v; + MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv); + + float u = floorf(uv->uv[0] / solver->snap_limit) * solver->snap_limit; + float v = floorf(uv->uv[1] / solver->snap_limit) * solver->snap_limit; + + intptr_t x = (intptr_t)(uv->uv[0] * 16384.0); + intptr_t y = (intptr_t)(uv->uv[1] * 16384.0); + intptr_t key = y * 16384LL + x; + + return POINTER_FROM_INT(key); +} + +static UVSmoothVert *uvsolver_get_vert(UVSolver *solver, BMLoop *l) +{ + MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv); + + void *pkey = uvsolver_calc_loop_key(solver, l); + void **entry = NULL; + UVSmoothVert *v; + + if (!BLI_ghash_ensure_p(solver->vhash, pkey, &entry)) { + v = BLI_mempool_alloc(solver->verts); + memset(v, 0, sizeof(*v)); + + // copy_v2_v2(v->uv, uv->uv); + v->uv[0] = (double)uv->uv[0]; + v->uv[1] = (double)uv->uv[1]; + + copy_v3_v3(v->co, l->v->co); + v->v = l->v; + + *entry = (void *)v; + } + + v = (UVSmoothVert *)*entry; + + if (v->totloop < MAXUVLOOPS) { + v->ls[v->totloop++] = l; + } + + return v; +} + +MINLINE double area_tri_signed_v2_db(const double v1[2], const double v2[2], const double v3[2]) +{ + return 0.5f * ((v1[0] - v2[0]) * (v2[1] - v3[1]) + (v1[1] - v2[1]) * (v3[0] - v2[0])); +} + +MINLINE double area_tri_v2_db(const double v1[2], const double v2[2], const double v3[2]) +{ + return fabsf(area_tri_signed_v2_db(v1, v2, v3)); +} + +void cross_tri_v3_db(double n[3], const double v1[3], const double v2[3], const double v3[3]) +{ + double n1[3], n2[3]; + + n1[0] = v1[0] - v2[0]; + n2[0] = v2[0] - v3[0]; + n1[1] = v1[1] - v2[1]; + n2[1] = v2[1] - v3[1]; + n1[2] = v1[2] - v2[2]; + n2[2] = v2[2] - v3[2]; + n[0] = n1[1] * n2[2] - n1[2] * n2[1]; + n[1] = n1[2] * n2[0] - n1[0] * n2[2]; + n[2] = n1[0] * n2[1] - n1[1] * n2[0]; +} + +double area_tri_v3_db(const double v1[3], const double v2[3], const double v3[3]) +{ + double n[3]; + cross_tri_v3_db(n, v1, v2, v3); + return len_v3_db(n) * 0.5; +} + +static UVSmoothTri *uvsolver_ensure_face(UVSolver *solver, BMFace *f) +{ + void **entry = NULL; + + if (BLI_ghash_ensure_p(solver->fhash, (void *)f, &entry)) { + return (UVSmoothTri *)*entry; + } + + UVSmoothTri *tri = BLI_mempool_alloc(solver->tris); + memset((void *)tri, 0, sizeof(*tri)); + *entry = (void *)tri; + + BMLoop *l = f->l_first; + + bool nocon = false; + int i = 0; + do { + UVSmoothVert *sv = uvsolver_get_vert(solver, l); + + if (BM_elem_flag_test(l->e, BM_ELEM_SEAM)) { + nocon = true; + } + + tri->vs[i] = sv; + + if (i > 3) { + // bad! + break; + } + + i++; + } while ((l = l->next) != f->l_first); + + double area3d = (double)area_tri_v3(tri->vs[0]->co, tri->vs[1]->co, tri->vs[2]->co); + double area2d = area_tri_v2_db(tri->vs[0]->uv, tri->vs[1]->uv, tri->vs[2]->uv); + + if (area2d < 0.000001) { + tri->vs[0]->uv[0] -= 0.0001; + tri->vs[0]->uv[1] -= 0.0001; + tri->vs[1]->uv[0] += 0.0001; + tri->vs[2]->uv[1] += 0.0001; + } + + solver->totarea2d += area2d; + solver->totarea3d += area3d; + + tri->area2d = area2d; + tri->area3d = area3d; + + for (int i = 0; !nocon && i < 3; i++) { + + UVSmoothConstraint *con = BLI_mempool_alloc(solver->constraints); + memset((void *)con, 0, sizeof(*con)); + con->type = CON_ANGLES; + con->k = 0.5; + + UVSmoothVert *v0 = tri->vs[(i + 2) % 3]; + UVSmoothVert *v1 = tri->vs[i]; + UVSmoothVert *v2 = tri->vs[(i + 1) % 3]; + + con->vs[0] = v0; + con->vs[1] = v1; + con->vs[2] = v2; + con->totvert = 3; + + float t1[3], t2[3]; + + sub_v3_v3v3(t1, v0->co, v1->co); + sub_v3_v3v3(t2, v2->co, v1->co); + + normalize_v3(t1); + normalize_v3(t2); + + float th3d = saacosf(dot_v3v3(t1, t2)); + + con->params[0] = (double)th3d; + + // area constraint + con = BLI_mempool_alloc(solver->constraints); + memset((void *)con, 0, sizeof(*con)); + + con->vs[0] = v0; + con->vs[1] = v1; + con->vs[2] = v2; + con->totvert = 3; + con->tri = tri; + con->type = CON_AREA; + con->k = 1.0; + } + +#if 1 + for (int i = 0; i < 3; i++) { + UVSmoothVert *v1 = tri->vs[i]; + UVSmoothVert *v2 = tri->vs[(i + 1) % 3]; + + bool ok = true; + + for (int j = 0; j < v1->totneighbor; j++) { + if (v1->neighbors[j] == v2) { + ok = false; + break; + } + } + + ok = ok && v1->totneighbor < MAXUVNEIGHBORS && v2->totneighbor < MAXUVNEIGHBORS; + + if (!ok) { + continue; + } + + v1->neighbors[v1->totneighbor++] = v2; + v2->neighbors[v2->totneighbor++] = v1; + } +#endif + + return tri; +} + +static double normalize_v2_db(double v[2]) +{ + double len = v[0] * v[0] + v[1] * v[1]; + + if (len < 0.0000001) { + v[0] = v[1] = 0.0; + return 0.0; + } + + len = sqrt(len); + + double mul = 1.0 / len; + + v[0] *= mul; + v[1] *= mul; + + return len; +} + +static double uvsolver_eval_constraint(UVSolver *solver, UVSmoothConstraint *con) +{ + switch (con->type) { + case CON_ANGLES: { + UVSmoothVert *v0 = con->vs[0]; + UVSmoothVert *v1 = con->vs[1]; + UVSmoothVert *v2 = con->vs[2]; + double t1[2], t2[2]; + + sub_v2_v2v2_db(t1, v0->uv, v1->uv); + sub_v2_v2v2_db(t2, v2->uv, v1->uv); + + normalize_v2_db(t1); + normalize_v2_db(t2); + + double th = saacos(dot_v2v2_db(t1, t2)); + + double wind = t1[0] * t2[1] - t1[1] * t2[0]; + + if (wind >= 0.0) { + th = M_PI - th; + } + + return th - con->params[0]; + } + case CON_AREA: { + UVSmoothVert *v0 = con->vs[0]; + UVSmoothVert *v1 = con->vs[1]; + UVSmoothVert *v2 = con->vs[2]; + + if (con->tri->area3d == 0.0 || solver->totarea3d == 0.0) { + return 0.0; + } + + double area2d = area_tri_signed_v2_db(v0->uv, v1->uv, v2->uv); + double goal = con->tri->area3d * solver->totarea2d / solver->totarea3d; + + con->tri->area2d = area2d; + return (area2d - goal) * 1024.0; + } + default: + return 0.0f; + } +} + +BLI_INLINE float uvsolver_vert_weight(UVSmoothVert *sv) +{ + double w = 1.0; + + if (sv->pinned || sv->boundary) { + w = 100000.0; + } + + return w; +} + +static void uvsolver_solve_begin(UVSolver *solver) +{ + UVSmoothVert *sv; + BLI_mempool_iter iter; + + BLI_mempool_iternew(solver->verts, &iter); + sv = BLI_mempool_iterstep(&iter); + BMIter liter; + + for (; sv; sv = BLI_mempool_iterstep(&iter)) { + BMLoop *l; + sv->pinned = false; + + BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { + if (!BLI_ghash_haskey(solver->fhash, (void *)l->f)) { + sv->pinned = true; + } + } + } +} + +static void uvsolver_simple_relax(UVSolver *solver, float strength) +{ + BLI_mempool_iter iter; + + UVSmoothVert *sv1; + BLI_mempool_iternew(solver->verts, &iter); + + sv1 = BLI_mempool_iterstep(&iter); + for (; sv1; sv1 = BLI_mempool_iterstep(&iter)) { + double uv[2] = {0.0, 0.0}; + double tot = 0.0; + + if (!sv1->totneighbor || sv1->pinned) { + continue; + } + + for (int i = 0; i < sv1->totneighbor; i++) { + UVSmoothVert *sv2 = sv1->neighbors[i]; + + if (!sv2 || (sv1->boundary && !sv2->boundary)) { + continue; + } + + uv[0] += sv2->uv[0]; + uv[1] += sv2->uv[1]; + tot += 1.0; + } + + if (tot < 2.0) { + continue; + } + + uv[0] /= tot; + uv[1] /= tot; + + sv1->uv[0] += (uv[0] - sv1->uv[0]) * strength; + sv1->uv[1] += (uv[1] - sv1->uv[1]) * strength; + } + + // update real uvs + + const int cd_uv = solver->cd_uv; + + BLI_mempool_iternew(solver->verts, &iter); + UVSmoothVert *sv = BLI_mempool_iterstep(&iter); + for (; sv; sv = BLI_mempool_iterstep(&iter)) { + for (int i = 0; i < sv->totloop; i++) { + BMLoop *l = sv->ls[i]; + MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, cd_uv); + + uv->uv[0] = (float)sv->uv[0]; + uv->uv[1] = (float)sv->uv[1]; + } + } +} + +static float uvsolver_solve_step(UVSolver *solver) +{ + BLI_mempool_iter iter; + + if (solver->strength < 0) { + uvsolver_simple_relax(solver, fabs(solver->strength)); + return 0.0f; + } + else { + uvsolver_simple_relax(solver, solver->strength * 0.1f); + } + + double error = 0.0; + + const double eval_limit = 0.00001; + const double df = 0.0001; + int totcon = 0; + + BLI_mempool_iternew(solver->constraints, &iter); + UVSmoothConstraint *con = BLI_mempool_iterstep(&iter); + for (; con; con = BLI_mempool_iterstep(&iter)) { + double r1 = uvsolver_eval_constraint(solver, con); + + if (fabs(r1) < eval_limit) { + totcon++; + continue; + } + + error += fabs(r1); + totcon++; + + double totg = 0.0; + double totw = 0.0; + + for (int i = 0; i < con->totvert; i++) { + UVSmoothVert *sv = con->vs[i]; + + for (int j = 0; j < 2; j++) { + double orig = sv->uv[j]; + sv->uv[j] += df; + + double r2 = uvsolver_eval_constraint(solver, con); + double g = (r2 - r1) / df; + + con->gs[i][j] = g; + totg += g * g; + + sv->uv[j] = orig; + + totw += 1.0 / uvsolver_vert_weight(sv); + } + } + + if (totg < eval_limit) { + continue; + } + + r1 *= -solver->strength * 0.75 * con->k / totg; + // totw = 1.0 / totw; + + for (int i = 0; i < con->totvert; i++) { + UVSmoothVert *sv = con->vs[i]; + double w = 1.0 / (uvsolver_vert_weight(sv) * totw); + + for (int j = 0; j < 2; j++) { + sv->uv[j] += r1 * con->gs[i][j] * w; + } + } + } + + // update real uvs + + const int cd_uv = solver->cd_uv; + + BLI_mempool_iternew(solver->verts, &iter); + UVSmoothVert *sv = BLI_mempool_iterstep(&iter); + for (; sv; sv = BLI_mempool_iterstep(&iter)) { + for (int i = 0; i < sv->totloop; i++) { + BMLoop *l = sv->ls[i]; + MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, cd_uv); + + uv->uv[0] = (float)sv->uv[0]; + uv->uv[1] = (float)sv->uv[1]; + } + } + + return (float)error / (float)totcon; +} + +static void sculpt_uv_brush_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptUVThreadData *data1 = userdata; + SculptThreadedTaskData *data = &data1->data; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *offset = data->offset; + + PBVHVertexIter vd; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + + PBVHNode *node = data->nodes[n]; + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + const int cd_uv = CustomData_get_offset(&ss->bm->ldata, CD_MLOOPUV); + + if (cd_uv < 0) { + return; // no uv layers + } + + float bstrength = ss->cache->bstrength; + const int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); + + BKE_pbvh_node_mark_update_color(node); + + TGSET_ITER (f, faces) { + BMLoop *l = f->l_first; + // float mask = 0.0f; + float cent[3] = {0}; + int tot = 0; + + // uvsolver_get_vert + do { + add_v3_v3(cent, l->v->co); + tot++; + } while ((l = l->next) != f->l_first); + + mul_v3_fl(cent, 1.0f / (float)tot); + + if (!sculpt_brush_test_sq_fn(&test, cent)) { + continue; + } + + BM_log_face_modified(ss->bm_log, f); + uvsolver_ensure_face(data1->solver, f); + + do { + BMIter iter; + BMLoop *l2; + int tot2 = 0; + float uv[2] = {0}; + bool ok = true; + UVSmoothVert *lastv = NULL; + + BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) { + if (l2->v != l->v) { + l2 = l2->prev->v == l->v ? l2->prev : l2->next; + } + + UVSmoothVert *sv = uvsolver_get_vert(data1->solver, l2); + + if (lastv && lastv != sv) { + ok = false; + lastv->boundary = true; + sv->boundary = true; + } + + lastv = sv; + + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l2, cd_uv); + + add_v2_v2(uv, luv->uv); + tot2++; + + if (BM_elem_flag_test(l2->e, BM_ELEM_SEAM)) { + ok = false; + sv->boundary = true; + } + } + + ok = ok && tot2; + + if (ok) { + mul_v2_fl(uv, 1.0f / (float)tot2); + + BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) { + if (l2->v != l->v) { + l2 = l2->next; + } + + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l2, cd_uv); + + if (len_v2v2(luv->uv, uv) < 0.02) { + copy_v2_v2(luv->uv, uv); + } + } + } + } while ((l = l->next) != f->l_first); + +#if 0 + do { + if (!sculpt_brush_test_sq_fn(&test, l->v->co)) { + continue; + } + + if (cd_mask >= 0) { + mask = BM_ELEM_CD_GET_FLOAT(l->v, cd_mask); + } + + SculptVertRef vertex = {(intptr_t)l->v}; + + float direction2[3]; + const float fade = + bstrength * + SCULPT_brush_strength_factor( + ss, brush, vd.co, sqrtf(test.dist), NULL, l->v->no, mask, vertex, thread_id) * + ss->cache->pressure; + + } while ((l = l->next) != f->l_first); +#endif + } + TGSET_ITER_END; +} + +void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + const float bstrength = ss->cache->bstrength; + + if (!ss->bm || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + // dyntopo only + return; + } + + const int cd_uv = CustomData_get_offset(&ss->bm->ldata, CD_MLOOPUV); + if (cd_uv < 0) { + return; // no uv layer? + } + + // add undo log subentry + BM_log_entry_add_ex(ss->bm, ss->bm_log, true); + + BKE_curvemapping_init(brush->curve); + + UVSolver *solver = uvsolver_new(cd_uv); + solver->strength = ss->cache->bstrength; + + /* Threaded loop over nodes. */ + SculptUVThreadData data = {.solver = solver, + .data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }}; + + TaskParallelSettings settings; + + // for now, be single-threaded + BKE_pbvh_parallel_range_settings(&settings, false, totnode); + BLI_task_parallel_range(0, totnode, &data, sculpt_uv_brush_cb, &settings); + + uvsolver_solve_begin(solver); + + for (int i = 0; i < 5; i++) { + uvsolver_solve_step(solver); + } + + // tear down solver + uvsolver_free(solver); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 40874375772..c7ad2bb20bd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -156,10 +156,12 @@ enum { */ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const SculptVertRef v) { + const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v); + for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { - if (ss->vertex_info.connected_component[v] == expand_cache->active_connected_components[i]) { + if (ss->vertex_info.connected_component[v_i] == expand_cache->active_connected_components[i]) { return true; } } @@ -171,10 +173,20 @@ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss, */ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, ExpandCache *expand_cache, - const int f) + const SculptFaceRef f) { - const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart]; - return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v); + if (ss->bm) { + BMFace *bf = (BMFace *)f.i; + BMLoop *l = bf->l_first; + SculptVertRef v = {(intptr_t)l->v}; + + return sculpt_expand_is_vert_in_active_component(ss, expand_cache, v); + } + else { + const MLoop *loop = &ss->mloop[ss->mpoly[f.i].loopstart]; + return sculpt_expand_is_vert_in_active_component( + ss, expand_cache, BKE_pbvh_table_index_to_vertex(ss->pbvh, loop->v)); + } } /** @@ -183,14 +195,16 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, */ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const SculptVertRef v) { + const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v); + if (expand_cache->texture_distortion_strength == 0.0f) { - return expand_cache->vert_falloff[v]; + return expand_cache->vert_falloff[v_i]; } if (!expand_cache->brush->mtex.tex) { - return expand_cache->vert_falloff[v]; + return expand_cache->vert_falloff[v_i]; } float rgba[4]; @@ -200,7 +214,7 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength * expand_cache->max_vert_falloff; - return expand_cache->vert_falloff[v] + distortion; + return expand_cache->vert_falloff[v_i] + distortion; } /** @@ -225,7 +239,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache) * Main function to get the state of a vertex for the current state and settings of a #ExpandCache. * Returns true when the target data should be modified by expand. */ -static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v) +static bool sculpt_expand_state_get(SculptSession *ss, + ExpandCache *expand_cache, + const SculptVertRef v) { if (!SCULPT_vertex_visible_get(ss, v)) { return false; @@ -271,9 +287,13 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache * Main function to get the state of a face for the current state and settings of a #ExpandCache. * Returns true when the target data should be modified by expand. */ -static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f) +static bool sculpt_expand_face_state_get(SculptSession *ss, + ExpandCache *expand_cache, + const SculptFaceRef f) { - if (expand_cache->original_face_sets[f] <= 0) { + const int f_i = BKE_pbvh_face_index_to_table(ss->pbvh, f); + + if (expand_cache->original_face_sets[f_i] <= 0) { return false; } @@ -288,7 +308,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_ bool enabled = false; if (expand_cache->snap_enabled_face_sets) { - const int face_set = expand_cache->original_face_sets[f]; + const int face_set = expand_cache->original_face_sets[f_i]; enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set)); } else { @@ -296,12 +316,12 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_ SCULPT_EXPAND_LOOP_THRESHOLD; const float active_factor = fmod(expand_cache->active_falloff, loop_len); - const float falloff_factor = fmod(expand_cache->face_falloff[f], loop_len); + const float falloff_factor = fmod(expand_cache->face_falloff[f_i], loop_len); enabled = falloff_factor < active_factor; } if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET) { - if (ss->face_sets[f] == expand_cache->initial_active_face_set) { + if (SCULPT_face_set_get(ss, f) == expand_cache->initial_active_face_set) { enabled = false; } } @@ -319,7 +339,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_ */ static float sculpt_expand_gradient_value_get(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const SculptVertRef v) { if (!expand_cache->falloff_gradient) { return 1.0f; @@ -363,7 +383,8 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa const int totvert = SCULPT_vertex_count_get(ss); BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); for (int i = 0; i < totvert; i++) { - const bool enabled = sculpt_expand_state_get(ss, expand_cache, i); + const bool enabled = sculpt_expand_state_get( + ss, expand_cache, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)); BLI_BITMAP_SET(enabled_vertices, i, enabled); } return enabled_vertices; @@ -381,20 +402,22 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, const int totvert = SCULPT_vertex_count_get(ss); BLI_bitmap *boundary_vertices = BLI_BITMAP_NEW(totvert, "boundary vertices"); for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + if (!BLI_BITMAP_TEST(enabled_vertices, i)) { continue; } bool is_expand_boundary = false; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { if (!BLI_BITMAP_TEST(enabled_vertices, ni.index)) { is_expand_boundary = true; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, i)) { + if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, vertex)) { is_expand_boundary = true; } @@ -410,12 +433,12 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, * Utility function to get the closet vertex after flipping an original vertex position based on * an symmetry pass iteration index. */ -static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, - const char symm_it, - const int original_vertex) +static SculptVertRef sculpt_expand_get_vertex_index_for_symmetry_pass( + Object *ob, const char symm_it, const SculptVertRef original_vertex) { SculptSession *ss = ob->sculpt; - int symm_vertex = SCULPT_EXPAND_VERTEX_NONE; + SculptVertRef symm_vertex = {SCULPT_EXPAND_VERTEX_NONE}; + if (symm_it == 0) { symm_vertex = original_vertex; } @@ -431,7 +454,7 @@ static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, * Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking * symmetry into account. */ -static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v) +static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const SculptVertRef v) { return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX); } @@ -448,20 +471,23 @@ typedef struct ExpandFloodFillData { } ExpandFloodFillData; static bool expand_topology_floodfill_cb( - SculptSession *UNUSED(ss), int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata) { ExpandFloodFillData *data = userdata; + int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v); + if (!is_duplicate) { - const float to_it = data->dists[from_v] + 1.0f; - data->dists[to_v] = to_it; + const float to_it = data->dists[from_v_i] + 1.0f; + data->dists[to_v_i] = to_it; } else { - data->dists[to_v] = data->dists[from_v]; + data->dists[to_v_i] = data->dists[from_v_i]; } return true; } -static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int v) +static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const SculptVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -486,23 +512,26 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons * This creates falloff patterns that follow and snap to the hard edges of the object. */ static bool mask_expand_normal_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata) { ExpandFloodFillData *data = userdata; + int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v); + if (!is_duplicate) { float current_normal[3], prev_normal[3]; SCULPT_vertex_normal_get(ss, to_v, current_normal); SCULPT_vertex_normal_get(ss, from_v, prev_normal); - const float from_edge_factor = data->edge_factor[from_v]; - data->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * from_edge_factor; - data->dists[to_v] = dot_v3v3(data->original_normal, current_normal) * - powf(from_edge_factor, data->edge_sensitivity); - CLAMP(data->dists[to_v], 0.0f, 1.0f); + const float from_edge_factor = data->edge_factor[from_v_i]; + data->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) * from_edge_factor; + data->dists[to_v_i] = dot_v3v3(data->original_normal, current_normal) * + powf(from_edge_factor, data->edge_sensitivity); + CLAMP(data->dists[to_v_i], 0.0f, 1.0f); } else { /* PBVH_GRIDS duplicate handling. */ - data->edge_factor[to_v] = data->edge_factor[from_v]; - data->dists[to_v] = data->dists[from_v]; + data->edge_factor[to_v_i] = data->edge_factor[from_v_i]; + data->dists[to_v_i] = data->dists[from_v_i]; } return true; @@ -510,7 +539,7 @@ static bool mask_expand_normal_floodfill_cb( static float *sculpt_expand_normal_falloff_create(Sculpt *sd, Object *ob, - const int v, + const SculptVertRef v, const float edge_sensitivity) { SculptSession *ss = ob->sculpt; @@ -537,8 +566,10 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, for (int repeat = 0; repeat < 2; repeat++) { for (int i = 0; i < totvert; i++) { float avg = 0.0f; + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) { avg += dists[ni.index]; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -555,7 +586,7 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, * Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into * account. */ -static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) +static float *sculpt_expand_spherical_falloff_create(Object *ob, const SculptVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -570,11 +601,14 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); - if (symm_vertex != -1) { + const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); + if (symm_vertex.i != -1) { const float *co = SCULPT_vertex_co_get(ss, symm_vertex); for (int i = 0; i < totvert; i++) { - dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i))); + dists[i] = min_ff( + dists[i], + len_v3v3(co, SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)))); } } } @@ -587,13 +621,13 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) * boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it * stays parallel to the boundary, increasing the falloff value by 1 on each step. */ -static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const int v) +static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const SculptVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist"); BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); - GSQueue *queue = BLI_gsqueue_new(sizeof(int)); + GSQueue *queue = BLI_gsqueue_new(sizeof(SculptVertRef)); /* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */ const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -602,7 +636,8 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX); if (!boundary) { @@ -611,7 +646,8 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i for (int i = 0; i < boundary->num_vertices; i++) { BLI_gsqueue_push(queue, &boundary->vertices[i]); - BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]); + BLI_BITMAP_ENABLE(visited_vertices, + BKE_pbvh_vertex_index_to_table(ss->pbvh, boundary->vertices[i])); } SCULPT_boundary_data_free(boundary); } @@ -623,7 +659,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i /* Propagate the values from the boundaries to the rest of the mesh. */ while (!BLI_gsqueue_is_empty(queue)) { - int v_next; + SculptVertRef v_next; BLI_gsqueue_pop(queue, &v_next); SculptVertexNeighborIter ni; @@ -631,7 +667,10 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i if (BLI_BITMAP_TEST(visited_vertices, ni.index)) { continue; } - dists[ni.index] = dists[v_next] + 1.0f; + + const int v_next_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_next); + + dists[ni.index] = dists[v_next_i] + 1.0f; BLI_BITMAP_ENABLE(visited_vertices, ni.index); BLI_gsqueue_push(queue, &ni.index); } @@ -648,32 +687,38 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i * the base mesh faces when checking a vertex neighbor. For this reason, this is not implement * using the general flood-fill and sculpt neighbors accessors. */ -static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) +static float *sculpt_expand_diagonals_falloff_create(Object *ob, const SculptVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist"); /* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialized for - * Multires. It also does not make sense to implement it for dyntopo as the result will be the - * same as Topology falloff. */ - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + * Multires. Also supports non-tri PBVH_BMESH, though untested until we implement that properly*/ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES || + (ss->bm && ss->bm->totloop != ss->bm->totvert * 3)) { return dists; } + if (ss->bm) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + BM_mesh_elem_table_ensure(ss->bm, BM_VERT); + } + /* Search and mask as visited the initial vertices using the enabled symmetry passes. */ BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); - GSQueue *queue = BLI_gsqueue_new(sizeof(int)); + GSQueue *queue = BLI_gsqueue_new(sizeof(SculptVertRef)); const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char symm_it = 0; symm_it <= symm; symm_it++) { if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); BLI_gsqueue_push(queue, &symm_vertex); - BLI_BITMAP_ENABLE(visited_vertices, symm_vertex); + BLI_BITMAP_ENABLE(visited_vertices, BKE_pbvh_vertex_index_to_table(ss->pbvh, symm_vertex)); } if (BLI_gsqueue_is_empty(queue)) { @@ -683,18 +728,50 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) /* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */ Mesh *mesh = ob->data; while (!BLI_gsqueue_is_empty(queue)) { - int v_next; + SculptVertRef v_next; BLI_gsqueue_pop(queue, &v_next); - for (int j = 0; j < ss->pmap[v_next].count; j++) { - MPoly *p = &ss->mpoly[ss->pmap[v_next].indices[j]]; - for (int l = 0; l < p->totloop; l++) { - const int neighbor_v = mesh->mloop[p->loopstart + l].v; - if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) { - continue; + + int v_next_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_next); + + if (ss->bm) { + BMIter iter; + BMFace *f; + BMVert *v = (BMVert *)v_next.i; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + BMLoop *l = f->l_first; + + do { + BMVert *neighbor_v = l->next->v; + const int neighbor_v_i = BM_elem_index_get(neighbor_v); + + if (BLI_BITMAP_TEST(visited_vertices, neighbor_v_i)) { + l = l->next; + continue; + } + + dists[neighbor_v_i] = dists[v_next_i] + 1.0f; + BLI_BITMAP_ENABLE(visited_vertices, neighbor_v_i); + BLI_gsqueue_push(queue, &neighbor_v); + + l = l->next; + } while (l != f->l_first); + } + } + else { + for (int j = 0; j < ss->pmap[v_next_i].count; j++) { + MPoly *p = &ss->mpoly[ss->pmap[v_next_i].indices[j]]; + for (int l = 0; l < p->totloop; l++) { + const int neighbor_v = mesh->mloop[p->loopstart + l].v; + + if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) { + continue; + } + + dists[neighbor_v] = dists[v_next_i] + 1.0f; + BLI_BITMAP_ENABLE(visited_vertices, neighbor_v); + BLI_gsqueue_push(queue, &neighbor_v); } - dists[neighbor_v] = dists[v_next] + 1.0f; - BLI_BITMAP_ENABLE(visited_vertices, neighbor_v); - BLI_gsqueue_push(queue, &neighbor_v); } } } @@ -716,12 +793,15 @@ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss, { const int totvert = SCULPT_vertex_count_get(ss); expand_cache->max_vert_falloff = -FLT_MAX; + for (int i = 0; i < totvert; i++) { if (expand_cache->vert_falloff[i] == FLT_MAX) { continue; } - if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) { + SculptVertRef v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) { continue; } @@ -740,11 +820,13 @@ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss, const int totface = ss->totfaces; expand_cache->max_face_falloff = -FLT_MAX; for (int i = 0; i < totface; i++) { + SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, i); + if (expand_cache->face_falloff[i] == FLT_MAX) { continue; } - if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, f)) { continue; } @@ -792,6 +874,23 @@ static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expan } } +static void sculpt_expand_vertex_to_faces_falloff_bmesh(BMesh *bm, ExpandCache *expand_cache) +{ + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + + float accum = 0.0f; + + do { + accum += expand_cache->vert_falloff[BM_elem_index_get(l->v)]; + l = l->next; + } while (l != f->l_first); + + expand_cache->face_falloff[BM_elem_index_get(f)] = accum / f->len; + } +} /** * Main function to update the faces falloff from a already calculated vertex falloff. */ @@ -806,14 +905,16 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s mesh->totpoly, sizeof(float), "face falloff factors"); } - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - sculpt_expand_vertex_to_faces_falloff(mesh, expand_cache); - } - else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - sculpt_expand_grids_to_faces_falloff(ss, mesh, expand_cache); - } - else { - BLI_assert(false); + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + sculpt_expand_vertex_to_faces_falloff(mesh, expand_cache); + break; + case PBVH_GRIDS: + sculpt_expand_grids_to_faces_falloff(ss, mesh, expand_cache); + break; + case PBVH_BMESH: + sculpt_expand_vertex_to_faces_falloff_bmesh(ss->bm, expand_cache); + break; } } @@ -829,7 +930,7 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, BLI_bitmap *enabled_vertices) { SculptSession *ss = ob->sculpt; - BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES); + BLI_assert(ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH)); GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); @@ -872,7 +973,8 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob, if (!BLI_BITMAP_TEST(boundary_vertices, i)) { continue; } - SCULPT_floodfill_add_and_skip_initial(&flood, i); + + SCULPT_floodfill_add_and_skip_initial(ss, &flood, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)); } MEM_freeN(boundary_vertices); @@ -937,16 +1039,18 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); for (int i = 0; i < totvert; i++) { - if (!SCULPT_vertex_has_unique_face_set(ss, i)) { + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_unique_face_set(ss, vref)) { continue; } - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + if (!SCULPT_vertex_has_face_set(ss, vref, active_face_set)) { continue; } BLI_BITMAP_ENABLE(enabled_vertices, i); } - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH)) { sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); } else { @@ -957,8 +1061,10 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, if (internal_falloff) { for (int i = 0; i < totvert; i++) { - if (!(SCULPT_vertex_has_face_set(ss, i, active_face_set) && - SCULPT_vertex_has_unique_face_set(ss, i))) { + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!(SCULPT_vertex_has_face_set(ss, vref, active_face_set) && + SCULPT_vertex_has_unique_face_set(ss, vref))) { continue; } expand_cache->vert_falloff[i] *= -1.0f; @@ -976,7 +1082,9 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, } else { for (int i = 0; i < totvert; i++) { - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_face_set(ss, vref, active_face_set)) { continue; } expand_cache->vert_falloff[i] = 0.0f; @@ -992,14 +1100,14 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( ExpandCache *expand_cache, Sculpt *sd, Object *ob, - const int v, + const SculptVertRef v, eSculptExpandFalloffType falloff_type) { MEM_SAFE_FREE(expand_cache->vert_falloff); expand_cache->falloff_type = falloff_type; SculptSession *ss = ob->sculpt; - const bool has_topology_info = BKE_pbvh_type(ss->pbvh) == PBVH_FACES; + const bool has_topology_info = ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH); switch (falloff_type) { case SCULPT_EXPAND_FALLOFF_GEODESIC: @@ -1131,7 +1239,9 @@ static void sculpt_expand_restore_face_set_data(SculptSession *ss, ExpandCache * } MEM_freeN(nodes); for (int i = 0; i < ss->totfaces; i++) { - ss->face_sets[i] = expand_cache->original_face_sets[i]; + SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, i); + + SCULPT_face_set_set(ss, f, expand_cache->original_face_sets[i]); } } @@ -1231,12 +1341,12 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { const float initial_mask = *vd.mask; - const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); + const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex); float new_mask; if (enabled) { - new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index); + new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex); } else { new_mask = 0.0f; @@ -1268,16 +1378,20 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expand_cache) { const int totface = ss->totfaces; - for (int f = 0; f < totface; f++) { + + for (int f_i = 0; f_i < totface; f_i++) { + SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, f_i); + int fset = SCULPT_face_set_get(ss, f); + const bool enabled = sculpt_expand_face_state_get(ss, expand_cache, f); if (!enabled) { continue; } if (expand_cache->preserve) { - ss->face_sets[f] += expand_cache->next_face_set; + SCULPT_face_set_set(ss, f, fset + expand_cache->next_face_set); } else { - ss->face_sets[f] = expand_cache->next_face_set; + SCULPT_face_set_set(ss, f, expand_cache->next_face_set); } } @@ -1305,11 +1419,11 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, float initial_color[4]; copy_v4_v4(initial_color, vd.col); - const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); + const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex); float fade; if (enabled) { - fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index); + fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex); } else { fade = 0.0f; @@ -1371,22 +1485,28 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c /* Face Sets are always stored as they are needed for snapping. */ expand_cache->initial_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "initial face set"); expand_cache->original_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "original face set"); + for (int i = 0; i < totface; i++) { - expand_cache->initial_face_sets[i] = ss->face_sets[i]; - expand_cache->original_face_sets[i] = ss->face_sets[i]; + const SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i); + const int fset = SCULPT_face_set_get(ss, fref); + + expand_cache->initial_face_sets[i] = fset; + expand_cache->original_face_sets[i] = fset; } if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { expand_cache->original_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask"); for (int i = 0; i < totvert; i++) { - expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, i); + expand_cache->original_mask[i] = SCULPT_vertex_mask_get( + ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)); } } if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) { expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors"); for (int i = 0; i < totvert; i++) { - copy_v4_v4(expand_cache->original_colors[i], SCULPT_vertex_color_get(ss, i)); + copy_v4_v4(expand_cache->original_colors[i], + SCULPT_vertex_color_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i))); } } } @@ -1397,26 +1517,32 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expand_cache) { const int totfaces = ss->totfaces; + for (int i = 0; i < totfaces; i++) { + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i); + if (expand_cache->original_face_sets[i] <= 0) { /* Do not modify hidden Face Sets, even when restoring the IDs state. */ continue; } - if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, fref)) { continue; } - ss->face_sets[i] = expand_cache->initial_face_sets[i]; + + SCULPT_face_set_set(ss, fref, expand_cache->initial_face_sets[i]); } } -static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int vertex) +static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const SculptVertRef vertex) { SculptSession *ss = ob->sculpt; + const int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; ExpandCache *expand_cache = ss->expand_cache; /* Update the active factor in the cache. */ - if (vertex == SCULPT_EXPAND_VERTEX_NONE) { + if (vertex.i == SCULPT_EXPAND_VERTEX_NONE) { /* This means that the cursor is not over the mesh, so a valid active falloff can't be * determined. In this situations, don't evaluate enabled states and default all vertices in * connected components to enabled. */ @@ -1424,7 +1550,7 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v expand_cache->all_enabled = true; } else { - expand_cache->active_falloff = expand_cache->vert_falloff[vertex]; + expand_cache->active_falloff = expand_cache->vert_falloff[vertex_i]; expand_cache->all_enabled = false; } @@ -1465,16 +1591,18 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v * Updates the #SculptSession cursor data and gets the active vertex * if the cursor is over the mesh. */ -static int sculpt_expand_target_vertex_update_and_get(bContext *C, - Object *ob, - const float mouse[2]) +static SculptVertRef sculpt_expand_target_vertex_update_and_get(bContext *C, + Object *ob, + const float mouse[2]) { SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { return SCULPT_active_vertex_get(ss); } - return SCULPT_EXPAND_VERTEX_NONE; + + SculptVertRef ret = {SCULPT_EXPAND_VERTEX_NONE}; + return ret; } /** @@ -1494,8 +1622,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache /* For boundary topology, position the pivot using only the boundary of the enabled vertices, * without taking mesh boundary into account. This allows to create deformations like bending the * mesh from the boundary of the mask that was just created. */ - const float use_mesh_boundary = expand_cache->falloff_type != - SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; + const bool use_mesh_boundary = expand_cache->falloff_type != + SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled( ss, enabled_vertices, use_mesh_boundary); @@ -1514,11 +1642,13 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache continue; } - if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) { + SculptVertRef v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) { continue; } - const float *vertex_co = SCULPT_vertex_co_get(ss, i); + const float *vertex_co = SCULPT_vertex_co_get(ss, v); if (!SCULPT_check_vertex_pivot_symmetry(vertex_co, expand_init_co, symm)) { continue; @@ -1573,9 +1703,8 @@ static void sculpt_expand_finish(bContext *C) * Finds and stores in the #ExpandCache the sculpt connected component index for each symmetry pass * needed for expand. */ -static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, - ExpandCache *expand_cache, - const int initial_vertex) +static void sculpt_expand_find_active_connected_components_from_vert( + Object *ob, ExpandCache *expand_cache, const SculptVertRef initial_vertex) { SculptSession *ss = ob->sculpt; for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { @@ -1588,11 +1717,12 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( ob, symm_it, initial_vertex); + const int symm_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, symm_vertex); expand_cache->active_connected_components[(int)symm_it] = - ss->vertex_info.connected_component[symm_vertex]; + ss->vertex_info.connected_component[symm_vertex_i]; } } @@ -1606,8 +1736,9 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C, const float mouse[2]) { SculptSession *ss = ob->sculpt; - int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); - if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) { + SculptVertRef initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); + + if (initial_vertex.i == SCULPT_EXPAND_VERTEX_NONE) { /* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active * vertex in the sculpt session. */ initial_vertex = SCULPT_active_vertex_get(ss); @@ -1680,17 +1811,15 @@ static void sculpt_expand_ensure_sculptsession_data(Object *ob) static int sculpt_expand_active_face_set_id_get(SculptSession *ss, ExpandCache *expand_cache) { switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: case PBVH_FACES: - return expand_cache->original_face_sets[ss->active_face_index]; + return expand_cache + ->original_face_sets[BKE_pbvh_vertex_index_to_table(ss->pbvh, ss->active_face_index)]; case PBVH_GRIDS: { const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, ss->active_grid_index); return expand_cache->original_face_sets[face_index]; } - case PBVH_BMESH: { - /* Dyntopo does not support Face Set functionality. */ - BLI_assert(false); - } } return SCULPT_FACE_SET_NONE; } @@ -1713,7 +1842,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event /* Update and get the active vertex (and face) from the cursor. */ const float mouse[2] = {event->mval[0], event->mval[1]}; - const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); + const SculptVertRef target_expand_vertex = sculpt_expand_target_vertex_update_and_get( + C, ob, mouse); /* Handle the modal keymap state changes. */ ExpandCache *expand_cache = ss->expand_cache; @@ -1899,12 +2029,133 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event * The faces that were using the `delete_id` Face Set are filled * using the content from their neighbors. */ +static void sculpt_expand_delete_face_set_id_bmesh(int *r_face_sets, + SculptSession *ss, + ExpandCache *expand_cache, + const int delete_id) +{ + BMIter iter; + BMFace *f; + int i = 0; + const int totface = ss->totpoly; + + /* Check that all the face sets IDs in the mesh are not equal to `delete_id` + * before attempting to delete it. */ + bool all_same_id = true; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + SculptFaceRef fref = {(intptr_t)f}; + i++; + + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, fref)) { + continue; + } + + if (r_face_sets[i] != delete_id) { + all_same_id = false; + break; + } + } + + if (all_same_id) { + return; + } + + BLI_LINKSTACK_DECLARE(queue, BMFace *); + BLI_LINKSTACK_DECLARE(queue_next, BMFace *); + + BLI_LINKSTACK_INIT(queue); + BLI_LINKSTACK_INIT(queue_next); + + for (int i = 0; i < totface; i++) { + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i); + + if (r_face_sets[i] == delete_id) { + BLI_LINKSTACK_PUSH(queue, (BMFace *)(fref.i)); + } + } + + while (BLI_LINKSTACK_SIZE(queue)) { + bool any_updated = false; + + while (BLI_LINKSTACK_SIZE(queue)) { + const SculptFaceRef f = {(intptr_t)(BLI_LINKSTACK_POP(queue))}; + BMFace *bf = (BMFace *)f.i; + const int f_index = BM_elem_index_get(bf); + + int other_id = delete_id; + BMLoop *l = bf->l_first; + do { + BMLoop *l2 = l->radial_next; + do { + const int neighbor_face_index = BM_elem_index_get(l2->f); + + if (expand_cache->original_face_sets[neighbor_face_index] <= 0) { + /* Skip picking IDs from hidden Face Sets. */ + continue; + } + + if (r_face_sets[neighbor_face_index] != delete_id) { + other_id = r_face_sets[neighbor_face_index]; + } + + l2 = l2->radial_next; + } while (l2 != l); + + l = l->next; + } while (l != bf->l_first); + + if (other_id != delete_id) { + any_updated = true; + r_face_sets[f_index] = other_id; + } + else { + BLI_LINKSTACK_PUSH(queue_next, bf); + } + } + + if (!any_updated) { + /* No Face Sets where updated in this iteration, which means that no more content to keep + * filling the polys of the deleted Face Set was found. Break to avoid entering an infinite + * loop trying to search for those polys again. */ + break; + } + + BLI_LINKSTACK_SWAP(queue, queue_next); + } + + BLI_LINKSTACK_FREE(queue); + BLI_LINKSTACK_FREE(queue_next); + + /* Ensure that the visibility state of the modified Face Sets is the same as the original ones. + */ + for (int i = 0; i < totface; i++) { + if (expand_cache->original_face_sets[i] >= 0) { + r_face_sets[i] = abs(r_face_sets[i]); + } + else { + r_face_sets[i] = -abs(r_face_sets[i]); + } + } +} + +/** + * Deletes the `delete_id` Face Set ID from the mesh Face Sets + * and stores the result in `r_face_set`. + * The faces that were using the `delete_id` Face Set are filled + * using the content from their neighbors. + */ static void sculpt_expand_delete_face_set_id(int *r_face_sets, SculptSession *ss, ExpandCache *expand_cache, Mesh *mesh, const int delete_id) { + if (ss->bm) { + sculpt_expand_delete_face_set_id_bmesh(r_face_sets, ss, expand_cache, delete_id); + return; + } + const int totface = ss->totfaces; MeshElemMap *pmap = ss->pmap; @@ -1912,7 +2163,8 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets, * before attempting to delete it. */ bool all_same_id = true; for (int i = 0; i < totface; i++) { - if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_face_in_active_component( + ss, expand_cache, BKE_pbvh_table_index_to_face(ss->pbvh, i))) { continue; } if (r_face_sets[i] != delete_id) { @@ -2060,6 +2312,9 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); + /* Create and configure the Expand Cache. */ ss->expand_cache = MEM_callocN(sizeof(ExpandCache), "expand cache"); sculpt_expand_cache_initial_config_set(C, op, ss->expand_cache); @@ -2083,13 +2338,6 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even return OPERATOR_CANCELLED; } - /* Face Set operations are not supported in dyntopo. */ - if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS && - BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - sculpt_expand_cache_free(ss); - return OPERATOR_CANCELLED; - } - sculpt_expand_ensure_sculptsession_data(ob); /* Initialize undo. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index bdbdb75732a..f0f15fe3e3f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -23,7 +23,9 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.h" #include "BLI_blenlib.h" +#include "BLI_compiler_attrs.h" #include "BLI_hash.h" #include "BLI_math.h" #include "BLI_task.h" @@ -72,6 +74,78 @@ #include <math.h> #include <stdlib.h> +int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face) +{ + if (ss->bm) { + BMFace *f = (BMFace *)face.i; + return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + } + + return ss->face_sets[face.i]; +} + +// returns previous face set +int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset) +{ + int ret = 0; + + if (ss->bm) { + BMFace *f = (BMFace *)face.i; + ret = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); + } + else { + ret = ss->face_sets[face.i]; + ss->face_sets[face.i] = fset; + } + + return ret; +} + +int SCULPT_face_set_flag_get(SculptSession *ss, SculptFaceRef face, char flag) +{ + if (ss->bm) { + BMFace *f = (BMFace *)face.i; + + flag = BM_face_flag_from_mflag(flag); + return f->head.hflag & flag; + } + else { + return ss->mpoly[face.i].flag & flag; + } +} + +int SCULPT_face_set_flag_set(SculptSession *ss, SculptFaceRef face, char flag, bool state) +{ + int ret; + + if (ss->bm) { + BMFace *f = (BMFace *)face.i; + + flag = BM_face_flag_from_mflag(flag); + ret = f->head.hflag & flag; + + if (state) { + f->head.hflag |= flag; + } + else { + f->head.hflag &= ~flag; + } + } + else { + ret = ss->mpoly[face.i].flag & flag; + + if (state) { + ss->mpoly[face.i].flag |= flag; + } + else { + ss->mpoly[face.i].flag &= ~flag; + } + } + + return ret; +} /* Utils. */ int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh) { @@ -118,6 +192,34 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo return SCULPT_active_face_set_get(ss); } +static BMesh *sculpt_faceset_bm_begin(SculptSession *ss, Mesh *mesh) +{ + if (ss->bm) { + return ss->bm; + } + + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); + BMesh *bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + + BM_mesh_bm_from_me(NULL, + bm, + mesh, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); + return bm; +} + +static void sculpt_faceset_bm_end(SculptSession *ss, BMesh *bm) +{ + if (bm != ss->bm) { + BM_mesh_free(bm); + } +} + /* Draw Face Sets Brush. */ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, @@ -135,6 +237,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + const int active_fset = abs(ss->cache->paint_face_set); MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); @@ -157,7 +260,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) { @@ -165,7 +268,34 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, } } } + else if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BMVert *v = vd.bm_vert; + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + float poly_center[3]; + BM_face_calc_center_median(f, poly_center); + + if (sculpt_brush_test_sq_fn(&test, poly_center)) { + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.vertex, + thread_id); + + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + if (fade > 0.05f && fset > 0) { + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, active_fset); + } + } + } + } else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -178,11 +308,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (fade > 0.05f) { - SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); + SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set); } } } @@ -217,7 +347,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } - if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) { + if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) { continue; } @@ -228,7 +358,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co); @@ -316,13 +446,11 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) const int mode = RNA_enum_get(op->ptr, "mode"); - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false); + SCULPT_face_random_access_ensure(ss); + SCULPT_vertex_random_access_ensure(ss); + const int tot_vert = SCULPT_vertex_count_get(ss); float threshold = 0.5f; @@ -342,8 +470,11 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) if (mode == SCULPT_FACE_SET_MASKED) { for (int i = 0; i < tot_vert; i++) { - if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (SCULPT_vertex_mask_get(ss, vertex) >= threshold && + SCULPT_vertex_visible_get(ss, vertex)) { + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } } @@ -355,7 +486,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) * sets and the performance hit of rendering the overlay. */ bool all_visible = true; for (int i = 0; i < tot_vert; i++) { - if (!SCULPT_vertex_visible_get(ss, i)) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { all_visible = false; break; } @@ -369,41 +502,40 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } for (int i = 0; i < tot_vert; i++) { - if (SCULPT_vertex_visible_get(ss, i)) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (SCULPT_vertex_visible_get(ss, vertex)) { + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } } if (mode == SCULPT_FACE_SET_ALL) { for (int i = 0; i < tot_vert; i++) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } if (mode == SCULPT_FACE_SET_SELECTION) { Mesh *mesh = ob->data; BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); + BMFace *f; + BMIter iter; + const int totface = ss->totfaces; - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); + for (int i = 0; i < totface; i++) { + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i); - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - ss->face_sets[BM_elem_index_get(f)] = next_face_set; + // XXX check hidden? + int ok = !SCULPT_face_set_flag_get(ss, fref, ME_HIDE); + ok = ok && SCULPT_face_set_flag_get(ss, fref, ME_FACE_SEL); + + if (ok) { + SCULPT_face_set_set(ss, fref, next_face_set); } } - BM_mesh_free(bm); } for (int i = 0; i < totnode; i++) { @@ -579,25 +711,19 @@ static void sculpt_face_sets_init_flood_fill(Object *ob, SculptSession *ss = ob->sculpt; Mesh *mesh = ob->data; BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); - BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces"); - const int totfaces = mesh->totpoly; + bm = sculpt_faceset_bm_begin(ss, mesh); - int *face_sets = ss->face_sets; + BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces"); + const int totfaces = ss->totfaces; // mesh->totpoly; - BM_mesh_elem_table_init(bm, BM_FACE); - BM_mesh_elem_table_ensure(bm, BM_FACE); + if (!ss->bm) { + BM_mesh_elem_index_ensure(bm, BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_FACE); + } int next_face_set = 1; @@ -608,7 +734,9 @@ static void sculpt_face_sets_init_flood_fill(Object *ob, GSQueue *queue; queue = BLI_gsqueue_new(sizeof(int)); - face_sets[i] = next_face_set; + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i); + SCULPT_face_set_set(ss, fref, next_face_set); + BLI_BITMAP_ENABLE(visited_faces, i); BLI_gsqueue_push(queue, &i); @@ -635,7 +763,9 @@ static void sculpt_face_sets_init_flood_fill(Object *ob, continue; } - face_sets[neighbor_face_index] = next_face_set; + SculptFaceRef fref2 = BKE_pbvh_table_index_to_face(ss->pbvh, neighbor_face_index); + SCULPT_face_set_set(ss, fref2, next_face_set); + BLI_BITMAP_ENABLE(visited_faces, neighbor_face_index); BLI_gsqueue_push(queue, &neighbor_face_index); } @@ -649,44 +779,37 @@ static void sculpt_face_sets_init_flood_fill(Object *ob, MEM_SAFE_FREE(visited_faces); - BM_mesh_free(bm); + sculpt_faceset_bm_end(ss, bm); } static void sculpt_face_sets_init_loop(Object *ob, const int mode) { Mesh *mesh = ob->data; SculptSession *ss = ob->sculpt; - BMesh *bm; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); + BMesh *bm = sculpt_faceset_bm_begin(ss, mesh); - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); BMIter iter; BMFace *f; const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP); BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + SculptFaceRef fref = {(intptr_t)f}; + if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) { - ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1); + SCULPT_face_set_set(ss, fref, (int)(f->mat_nr + 1)); } else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) { if (cd_fmaps_offset != -1) { - ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2; + SCULPT_face_set_set(ss, fref, BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2); } else { - ss->face_sets[BM_elem_index_get(f)] = 1; + SCULPT_face_set_set(ss, fref, 1); } } } - BM_mesh_free(bm); + + sculpt_faceset_bm_end(ss, bm); } static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) @@ -697,11 +820,6 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) const int mode = RNA_enum_get(op->ptr, "mode"); - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); PBVH *pbvh = ob->sculpt->pbvh; @@ -851,11 +969,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); const int tot_vert = SCULPT_vertex_count_get(ss); @@ -884,17 +997,31 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) * be synced from face sets to non-manifold vertices. */ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { for (int i = 0; i < tot_vert; i++) { - if (!SCULPT_vertex_visible_get(ss, i)) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { hidden_vertex = true; break; } } } + else if (ss->bm) { + BMIter iter; + BMFace *f; - for (int i = 0; i < ss->totfaces; i++) { - if (ss->face_sets[i] <= 0) { - hidden_vertex = true; - break; + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + if (BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset) <= 0) { + hidden_vertex = true; + break; + } + } + } + else { + for (int i = 0; i < ss->totfaces; i++) { + if (ss->face_sets[i] <= 0) { + hidden_vertex = true; + break; + } } } @@ -1001,22 +1128,21 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; Mesh *mesh = ob->data; + SCULPT_face_random_access_ensure(ss); + mesh->face_sets_color_seed += 1; - if (ss->face_sets) { + if (ss->face_sets || (ss->bm && ss->cd_faceset_offset >= 0)) { const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed), 0, max_ii(0, ss->totfaces - 1)); - mesh->face_sets_color_default = ss->face_sets[random_index]; + + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, random_index); + mesh->face_sets_color_default = SCULPT_face_set_get(ss, fref); } BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default); @@ -1095,12 +1221,60 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { {0, NULL, 0, NULL, NULL}, }; +static void sculpt_face_set_grow_bmesh(Object *ob, + SculptSession *ss, + const int *prev_face_sets, + const int active_face_set_id, + const bool modify_hidden) +{ + BMesh *bm = ss->bm; + BMIter iter; + BMFace *f; + BMFace **faces = NULL; + BLI_array_declare(faces); + + if (ss->cd_faceset_offset < 0) { + return; + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) && !modify_hidden) { + continue; + } + + int fset = abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)); + + if (fset == active_face_set_id) { + BLI_array_append(faces, f); + } + } + + for (int i = 0; i < BLI_array_len(faces); i++) { + BMFace *f = faces[i]; + BMLoop *l = f->l_first; + + do { + if (l->radial_next != l) { + BM_ELEM_CD_SET_INT(l->radial_next->f, ss->cd_faceset_offset, active_face_set_id); + } + l = l->next; + } while (l != f->l_first); + } + + BLI_array_free(faces); +} + static void sculpt_face_set_grow(Object *ob, SculptSession *ss, const int *prev_face_sets, const int active_face_set_id, const bool modify_hidden) { + if (ss && ss->bm) { + sculpt_face_set_grow_bmesh(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); + return; + } + Mesh *mesh = BKE_mesh_from_object(ob); for (int p = 0; p < mesh->totpoly; p++) { if (!modify_hidden && prev_face_sets[p] <= 0) { @@ -1123,12 +1297,70 @@ static void sculpt_face_set_grow(Object *ob, } } +static void sculpt_face_set_shrink_bmesh(Object *ob, + SculptSession *ss, + const int *prev_face_sets, + const int active_face_set_id, + const bool modify_hidden) +{ + BMesh *bm = ss->bm; + BMIter iter; + BMFace *f; + BMFace **faces = NULL; + BLI_array_declare(faces); + + if (ss->cd_faceset_offset < 0) { + return; + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) && !modify_hidden) { + continue; + } + + int fset = abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)); + + if (fset == active_face_set_id) { + BLI_array_append(faces, f); + } + } + + for (int i = 0; i < BLI_array_len(faces); i++) { + BMFace *f = faces[i]; + BMLoop *l = f->l_first; + + do { + if (!modify_hidden && BM_elem_flag_test(l->radial_next->f, BM_ELEM_HIDDEN)) { + l = l->next; + continue; + } + + if (l->radial_next != l && + abs(BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset)) != + abs(active_face_set_id)) { + BM_ELEM_CD_SET_INT(f, + ss->cd_faceset_offset, + BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset)); + break; + } + l = l->next; + } while (l != f->l_first); + } + + BLI_array_free(faces); +} + static void sculpt_face_set_shrink(Object *ob, SculptSession *ss, const int *prev_face_sets, const int active_face_set_id, const bool modify_hidden) { + if (ss && ss->bm) { + sculpt_face_set_shrink_bmesh(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); + return; + } + Mesh *mesh = BKE_mesh_from_object(ob); for (int p = 0; p < mesh->totpoly; p++) { if (!modify_hidden && prev_face_sets[p] <= 0) { @@ -1153,20 +1385,28 @@ static void sculpt_face_set_shrink(Object *ob, } } -static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only) +static bool check_single_face_set(SculptSession *ss, const bool check_visible_only) { + if (!ss->totfaces) { + return true; + } int first_face_set = SCULPT_FACE_SET_NONE; + if (check_visible_only) { for (int f = 0; f < ss->totfaces; f++) { - if (face_sets[f] > 0) { - first_face_set = face_sets[f]; + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, f); + int fset = SCULPT_face_set_get(ss, fref); + + if (fset > 0) { + first_face_set = fset; break; } } } else { - first_face_set = abs(face_sets[0]); + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, 0); + first_face_set = abs(SCULPT_face_set_get(ss, fref)); } if (first_face_set == SCULPT_FACE_SET_NONE) { @@ -1174,8 +1414,12 @@ static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool } for (int f = 0; f < ss->totfaces; f++) { - const int face_set_id = check_visible_only ? face_sets[f] : abs(face_sets[f]); - if (face_set_id != first_face_set) { + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, f); + + int fset = SCULPT_face_set_get(ss, fref); + fset = check_visible_only ? abs(fset) : fset; + + if (fset != first_face_set) { return false; } } @@ -1190,39 +1434,65 @@ static void sculpt_face_set_delete_geometry(Object *ob, Mesh *mesh = ob->data; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - BMesh *bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, + if (ss->bm) { + BMFace **faces = NULL; + BLI_array_declare(faces); + + BMIter iter; + BMFace *f; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + const int face_set_id = modify_hidden ? abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)) : + BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + if (face_set_id == active_face_set_id) { + BLI_array_append(faces, f); + } + } + + for (int i = 0; i < BLI_array_len(faces); i++) { + BKE_pbvh_bmesh_remove_face(ss->pbvh, faces[i], true); + } + + BLI_array_free(faces); + } + else { + BMesh *bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + + BM_mesh_bm_from_me(ob, + bm, + mesh, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); + + BM_mesh_elem_table_init(bm, BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_FACE); + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + const int face_index = BM_elem_index_get(f); + const int face_set_id = modify_hidden ? abs(ss->face_sets[face_index]) : + ss->face_sets[face_index]; + BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id); + } + BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + + BM_mesh_bm_to_me(NULL, + ob, + bm, + ob->data, + (&(struct BMeshToMeshParams){ + .calc_object_remap = false, })); - BM_mesh_elem_table_init(bm, BM_FACE); - BM_mesh_elem_table_ensure(bm, BM_FACE); - BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - const int face_index = BM_elem_index_get(f); - const int face_set_id = modify_hidden ? abs(ss->face_sets[face_index]) : - ss->face_sets[face_index]; - BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id); - } - BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); - BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); - - BM_mesh_bm_to_me(NULL, - bm, - ob->data, - (&(struct BMeshToMeshParams){ - .calc_object_remap = false, - })); - - BM_mesh_free(bm); + BM_mesh_free(bm); + } } static void sculpt_face_set_edit_fair_face_set(Object *ob, @@ -1230,21 +1500,31 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, const int fair_order) { SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); Mesh *mesh = ob->data; bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices"); + SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); for (int i = 0; i < totvert; i++) { - fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) && - SCULPT_vertex_has_face_set(ss, i, active_face_set_id) && - SCULPT_vertex_has_unique_face_set(ss, i); + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, vref) && + SCULPT_vertex_has_face_set(ss, vref, active_face_set_id) && + SCULPT_vertex_has_unique_face_set(ss, vref); + } + + if (ss->bm) { + BKE_bmesh_prefair_and_fair_vertices(ss->bm, fair_vertices, fair_order); + } + else { + MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); + BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order); } - MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); - BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order); MEM_freeN(fair_vertices); } @@ -1257,13 +1537,13 @@ static void sculpt_face_set_apply_edit(Object *ob, switch (mode) { case SCULPT_FACE_SET_EDIT_GROW: { - int *prev_face_sets = MEM_dupallocN(ss->face_sets); + int *prev_face_sets = ss->face_sets ? MEM_dupallocN(ss->face_sets) : NULL; sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); MEM_SAFE_FREE(prev_face_sets); break; } case SCULPT_FACE_SET_EDIT_SHRINK: { - int *prev_face_sets = MEM_dupallocN(ss->face_sets); + int *prev_face_sets = ss->face_sets ? MEM_dupallocN(ss->face_sets) : NULL; sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); MEM_SAFE_FREE(prev_face_sets); break; @@ -1284,10 +1564,8 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss, const eSculptFaceSetEditMode mode, const bool modify_hidden) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - /* Dyntopo is not supported. */ - return false; - } + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); if (mode == SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY) { if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { @@ -1297,7 +1575,7 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss, * data remapping as what happens in the mesh edit mode. */ return false; } - if (check_single_face_set(ss, ss->face_sets, !modify_hidden)) { + if (check_single_face_set(ss, !modify_hidden)) { /* Cancel the operator if the mesh only contains one Face Set to avoid deleting the * entire object. */ return false; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index de9511bab6f..472ade36f7f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -107,7 +107,7 @@ static void color_filter_task_cb(void *__restrict userdata, const int mode = data->filter_type; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -118,7 +118,7 @@ static void color_filter_task_cb(void *__restrict userdata, float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); if (fade == 0.0f) { continue; } @@ -196,7 +196,7 @@ static void color_filter_task_cb(void *__restrict userdata, case COLOR_FILTER_SMOOTH: { fade = clamp_f(fade, -1.0f, 1.0f); float smooth_color[4]; - SCULPT_neighbor_color_average(ss, smooth_color, vd.index); + SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); blend_color_interpolate_float(final_color, vd.col, smooth_color, fade); break; } @@ -281,7 +281,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent if (!ss->pbvh) { return OPERATOR_CANCELLED; } - if (BKE_pbvh_type(pbvh) != PBVH_FACES) { + if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index 10f141e2311..4c6c156e361 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -119,7 +119,7 @@ static void mask_filter_task_cb(void *__restrict userdata, switch (mode) { case MASK_FILTER_SMOOTH: case MASK_FILTER_SHARPEN: { - float val = SCULPT_neighbor_mask_average(ss, vd.index); + float val = SCULPT_neighbor_mask_average(ss, vd.vertex); val -= *vd.mask; @@ -139,7 +139,7 @@ static void mask_filter_task_cb(void *__restrict userdata, } case MASK_FILTER_GROW: max = 0.0f; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; if (vmask_f > max) { max = vmask_f; @@ -150,7 +150,7 @@ static void mask_filter_task_cb(void *__restrict userdata, break; case MASK_FILTER_SHRINK: min = 1.0f; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; if (vmask_f < min) { min = vmask_f; @@ -232,7 +232,9 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask"); for (int j = 0; j < num_verts; j++) { - prev_mask[j] = SCULPT_vertex_mask_get(ss, j); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, j); + + prev_mask[j] = SCULPT_vertex_mask_get(ss, vertex); } } @@ -323,9 +325,9 @@ static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd) zero_v3(avg); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { float normalized[3]; - sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.index), vd->co); + sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); normalize_v3(normalized); add_v3_v3(avg, normalized); total++; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 3fc1a7674f7..c117a3a41f3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -293,7 +293,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, const eSculptMeshFilterType filter_type = data->filter_type; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); /* When using the relax face sets meshes filter, * each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */ @@ -308,7 +308,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) { /* Surface Smooth can't skip the loop for this vertex as it needs to calculate its @@ -326,7 +326,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, } if (filter_type == MESH_FILTER_RELAX_FACE_SETS) { - if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) { + if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) { continue; } } @@ -334,7 +334,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, switch (filter_type) { case MESH_FILTER_SMOOTH: fade = clamp_f(fade, -1.0f, 1.0f); - SCULPT_neighbor_coords_average_interior(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, 0.0f); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -397,9 +397,9 @@ static void mesh_filter_task_cb(void *__restrict userdata, disp, vd.co, ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + vd.vertex, orig_data.co, - ss->filter_cache->surface_smooth_shape_preservation); + ss->filter_cache->surface_smooth_shape_preservation, 0.0f); break; } case MESH_FILTER_SHARPEN: { @@ -411,10 +411,10 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_sharpen[3] = {0.0f, 0.0f, 0.0f}; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float disp_n[3]; sub_v3_v3v3( - disp_n, SCULPT_vertex_co_get(ss, ni.index), SCULPT_vertex_co_get(ss, vd.index)); + disp_n, SCULPT_vertex_co_get(ss, ni.vertex), SCULPT_vertex_co_get(ss, vd.vertex)); mul_v3_fl(disp_n, ss->filter_cache->sharpen_factor[ni.index]); add_v3_v3(disp_sharpen, disp_n); } @@ -424,7 +424,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_avg[3]; float avg_co[3]; - SCULPT_neighbor_coords_average(ss, avg_co, vd.index); + SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex, 0.0f); sub_v3_v3v3(disp_avg, avg_co, vd.co); mul_v3_v3fl( disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index])); @@ -486,9 +486,11 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss) filter_cache->detail_directions = MEM_malloc_arrayN( totvert, sizeof(float[3]), "detail directions"); for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -512,8 +514,10 @@ static void mesh_filter_init_limit_surface_co(SculptSession *ss) filter_cache->limit_surface_co = MEM_malloc_arrayN( sizeof(float[3]), totvert, "limit surface co"); + for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + SCULPT_vertex_limit_surface_get(ss, vertex, filter_cache->limit_surface_co[i]); } } @@ -534,8 +538,10 @@ static void mesh_filter_sharpen_init(SculptSession *ss, for (int i = 0; i < totvert; i++) { float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } @@ -558,11 +564,12 @@ static void mesh_filter_sharpen_init(SculptSession *ss, smooth_iterations++) { for (int i = 0; i < totvert; i++) { float direction_avg[3] = {0.0f, 0.0f, 0.0f}; + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); float sharpen_avg = 0; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]); sharpen_avg += filter_cache->sharpen_factor[ni.index]; total++; @@ -589,7 +596,7 @@ static void mesh_filter_surface_smooth_displace_task_cb( float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); if (fade == 0.0f) { continue; } @@ -597,7 +604,7 @@ static void mesh_filter_surface_smooth_displace_task_cb( SCULPT_surface_smooth_displace_step(ss, vd.co, ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + vd.vertex, ss->filter_cache->surface_smooth_current_vertex, clamp_f(fade, 0.0f, 1.0f)); } diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c index d86d0938300..23efb04b316 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c @@ -112,6 +112,48 @@ static bool sculpt_geodesic_mesh_test_dist_add( return false; } +#define BMESH_INITIAL_VERT_TAG BM_ELEM_TAG_ALT + +static bool sculpt_geodesic_mesh_test_dist_add_bmesh( + BMVert *v0, BMVert *v1, BMVert *v2, float *dists, GSet *initial_vertices) +{ + const int v0_i = BM_elem_index_get(v0); + const int v1_i = BM_elem_index_get(v1); + const int v2_i = v2 ? BM_elem_index_get(v2) : SCULPT_GEODESIC_VERTEX_NONE; + + if (BM_elem_flag_test(v0, BMESH_INITIAL_VERT_TAG)) { + return false; + } + + BLI_assert(dists[v1_i] != FLT_MAX); + if (dists[v0_i] <= dists[v1_i]) { + return false; + } + + float dist0; + if (v2_i != SCULPT_GEODESIC_VERTEX_NONE) { + BLI_assert(dists[v2_i] != FLT_MAX); + if (dists[v0_i] <= dists[v2_i]) { + return false; + } + + dist0 = geodesic_distance_propagate_across_triangle( + v0->co, v1->co, v2->co, dists[v1_i], dists[v2_i]); + } + else { + float vec[3]; + sub_v3_v3v3(vec, v1->co, v0->co); + dist0 = dists[v1_i] + len_v3(vec); + } + + if (dist0 < dists[v0_i]) { + dists[v0_i] = dist0; + return true; + } + + return false; +} + static float *SCULPT_geodesic_mesh_create(Object *ob, GSet *initial_vertices, const float limit_radius) @@ -271,6 +313,190 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, return dists; } +static float *SCULPT_geodesic_bmesh_create(Object *ob, + GSet *initial_vertices, + const float limit_radius) +{ + SculptSession *ss = ob->sculpt; + + if (!ss->bm) { + return NULL; + } + + const int totvert = ss->bm->totvert; + const int totedge = ss->bm->totedge; + + const float limit_radius_sq = limit_radius * limit_radius; + + float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances"); + BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag"); + + BLI_LINKSTACK_DECLARE(queue, BMEdge *); + BLI_LINKSTACK_DECLARE(queue_next, BMEdge *); + + BLI_LINKSTACK_INIT(queue); + BLI_LINKSTACK_INIT(queue_next); + + for (int i = 0; i < totvert; i++) { + if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) { + dists[i] = 0.0f; + } + else { + dists[i] = FLT_MAX; + } + } + + /* Masks vertices that are further than limit radius from an initial vertex. As there is no need + * to define a distance to them the algorithm can stop earlier by skipping them. */ + BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex"); + GSetIterator gs_iter; + + SCULPT_vertex_random_access_ensure(ss); + + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + BM_elem_flag_disable(v, BMESH_INITIAL_VERT_TAG); + } + + if (limit_radius == FLT_MAX) { + /* In this case, no need to loop through all initial vertices to check distances as they are + * all going to be affected. */ + BLI_bitmap_set_all(affected_vertex, true, totvert); + } + else { + /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When + * this optimization is needed, it is expected for the tool to request the distance to a low + * number of vertices (usually just 1 or 2). */ + GSET_ITER (gs_iter, initial_vertices) { + const int v_i = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); + BMVert *v = (BMVert *)BKE_pbvh_table_index_to_vertex(ss->pbvh, v_i).i; + + BM_elem_flag_enable(v, BMESH_INITIAL_VERT_TAG); + + for (int i = 0; i < totvert; i++) { + BMVert *v2 = (BMVert *)BKE_pbvh_table_index_to_vertex(ss->pbvh, i).i; + + if (len_squared_v3v3(v->co, v2->co) <= limit_radius_sq) { + BLI_BITMAP_ENABLE(affected_vertex, i); + } + } + } + } + + BMEdge *e; + int i = 0; + + BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) { + const int v1_i = BM_elem_index_get(e->v1); + const int v2_i = BM_elem_index_get(e->v2); + + if (!BLI_BITMAP_TEST(affected_vertex, v1_i) && !BLI_BITMAP_TEST(affected_vertex, v2_i)) { + i++; + continue; + } + if (dists[v1_i] != FLT_MAX || dists[v2_i] != FLT_MAX) { + BLI_LINKSTACK_PUSH(queue, e); + } + + i++; + } + + do { + while (BLI_LINKSTACK_SIZE(queue)) { + BMEdge *e = BLI_LINKSTACK_POP(queue); + + BMVert *v1 = e->v1, *v2 = e->v2; + int v1_i = BM_elem_index_get(e->v1); + int v2_i = BM_elem_index_get(e->v2); + + if (dists[v1_i] == FLT_MAX || dists[v2_i] == FLT_MAX) { + if (dists[v1_i] > dists[v2_i]) { + SWAP(BMVert *, v1, v2); + SWAP(int, v1_i, v2_i); + } + sculpt_geodesic_mesh_test_dist_add_bmesh(v2, v1, NULL, dists, initial_vertices); + } + + BMLoop *l = e->l; + if (l) { + do { + BMFace *f = l->f; + BMLoop *l2 = f->l_first; + + if (BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset) < 0) { + l = l->radial_next; + continue; + } + + do { + BMVert *v_other = l2->v; + + if (ELEM(v_other, v1, v2)) { + l2 = l2->next; + continue; + } + + const int v_other_i = BM_elem_index_get(v_other); + + if (sculpt_geodesic_mesh_test_dist_add_bmesh( + v_other, v1, v2, dists, initial_vertices)) { + BMIter eiter; + BMEdge *e_other; + + BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) { + BMVert *ev_other; + + if (e_other->v1 == v_other) { + ev_other = e_other->v2; + } + else { + ev_other = e_other->v1; + } + + const int ev_other_i = BM_elem_index_get(ev_other); + const int e_other_i = BM_elem_index_get(e_other); + + bool ok = e_other != e && !BLI_BITMAP_TEST(edge_tag, e_other_i); + ok = ok && (!e_other->l || dists[ev_other_i] != FLT_MAX); + ok = ok && (BLI_BITMAP_TEST(affected_vertex, v_other_i) || + BLI_BITMAP_TEST(affected_vertex, ev_other_i)); + + if (ok) { + BLI_BITMAP_ENABLE(edge_tag, e_other_i); + BLI_LINKSTACK_PUSH(queue_next, e_other); + } + } + } + + l2 = l2->next; + } while (l2 != f->l_first); + + l = l->radial_next; + } while (l != e->l); + } + } + + for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) { + BMEdge *e = (BMEdge *)lnk->link; + const int e_i = BM_elem_index_get(e); + + BLI_BITMAP_DISABLE(edge_tag, e_i); + } + + BLI_LINKSTACK_SWAP(queue, queue_next); + + } while (BLI_LINKSTACK_SIZE(queue)); + + BLI_LINKSTACK_FREE(queue); + BLI_LINKSTACK_FREE(queue_next); + MEM_SAFE_FREE(edge_tag); + MEM_SAFE_FREE(affected_vertex); + + return dists; +} + /* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the * distance to each vertex. In this case, only one of the initial vertices will be used to * calculate the distance. */ @@ -281,14 +507,15 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices Mesh *mesh = BKE_object_get_original_mesh(ob); const int totvert = mesh->totvert; float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances"); - int first_affected = SCULPT_GEODESIC_VERTEX_NONE; + SculptVertRef first_affected = {SCULPT_GEODESIC_VERTEX_NONE}; + GSetIterator gs_iter; GSET_ITER (gs_iter, initial_vertices) { - first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); + first_affected.i = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); break; } - if (first_affected == SCULPT_GEODESIC_VERTEX_NONE) { + if (first_affected.i == SCULPT_GEODESIC_VERTEX_NONE) { for (int i = 0; i < totvert; i++) { dists[i] = FLT_MAX; } @@ -297,7 +524,8 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices const float *first_affected_co = SCULPT_vertex_co_get(ss, first_affected); for (int i = 0; i < totvert; i++) { - dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, i)); + dists[i] = len_v3v3(first_affected_co, + SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i))); } return dists; @@ -312,6 +540,7 @@ float *SCULPT_geodesic_distances_create(Object *ob, case PBVH_FACES: return SCULPT_geodesic_mesh_create(ob, initial_vertices, limit_radius); case PBVH_BMESH: + return SCULPT_geodesic_bmesh_create(ob, initial_vertices, limit_radius); case PBVH_GRIDS: return SCULPT_geodesic_fallback_create(ob, initial_vertices); } @@ -321,7 +550,7 @@ float *SCULPT_geodesic_distances_create(Object *ob, float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, Object *ob, - const int vertex, + const SculptVertRef vertex, const float limit_radius) { SculptSession *ss = ob->sculpt; @@ -330,7 +559,8 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char i = 0; i <= symm; ++i) { if (SCULPT_is_symmetry_iteration_valid(i, symm)) { - int v = -1; + SculptVertRef v = {-1}; + if (i == 0) { v = vertex; } @@ -339,8 +569,11 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i); v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false); } - if (v != -1) { - BLI_gset_add(initial_vertices, POINTER_FROM_INT(v)); + + const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v); + + if (v_i != -1) { + BLI_gset_add(initial_vertices, POINTER_FROM_INT(v_i)); } } } @@ -350,10 +583,19 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, return dists; } -float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius) +float *SCULPT_geodesic_from_vertex(Object *ob, + const SculptVertRef vertex, + const float limit_radius) { + SculptSession *ss = ob->sculpt; + + SCULPT_vertex_random_access_ensure(ss); + GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); - BLI_gset_add(initial_vertices, POINTER_FROM_INT(vertex)); + + BLI_gset_add(initial_vertices, + POINTER_FROM_INT(BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex))); + float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); BLI_gset_free(initial_vertices, NULL); return dists; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 696c3332a2b..f966e52ae02 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -30,12 +30,16 @@ #include "DNA_vec_types.h" #include "BLI_bitmap.h" +#include "BLI_compiler_compat.h" #include "BLI_gsqueue.h" #include "BLI_threads.h" +#include "BKE_attribute.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "bmesh.h" + struct AutomaskingCache; struct KeyBlock; struct Object; @@ -44,6 +48,22 @@ struct bContext; enum ePaintSymmetryFlags; +typedef struct SculptCustomLayer { + bool is_cdlayer; // false for multires data + void *data; // only valid for multires and face + int elemsize; + int cd_offset; // for bmesh + CustomDataLayer *layer; // not for multires +} SculptCustomLayer; + +/* +maximum symmetry passes returned by SCULPT_get_symmetry_pass. +enough for about ~30 radial symmetry passes, which seems like plenty + +used by various code that needs to statically store per-pass state. +*/ +#define SCULPT_MAX_SYMMETRY_PASSES 255 + bool SCULPT_mode_poll(struct bContext *C); bool SCULPT_mode_poll_view3d(struct bContext *C); /* checks for a brush, not just sculpt mode */ @@ -51,6 +71,7 @@ bool SCULPT_poll(struct bContext *C); bool SCULPT_poll_view3d(struct bContext *C); bool SCULPT_vertex_colors_poll(struct bContext *C); +bool SCULPT_vertex_colors_poll_no_bmesh(struct bContext *C); /* Updates */ @@ -96,22 +117,28 @@ char SCULPT_mesh_symmetry_xyz_get(Object *object); /* Sculpt PBVH abstraction API */ void SCULPT_vertex_random_access_ensure(struct SculptSession *ss); +void SCULPT_face_random_access_ensure(struct SculptSession *ss); int SCULPT_vertex_count_get(struct SculptSession *ss); -const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index); -void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]); -float SCULPT_vertex_mask_get(struct SculptSession *ss, int index); -const float *SCULPT_vertex_color_get(SculptSession *ss, int index); - -const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index); -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); +const float *SCULPT_vertex_co_get(struct SculptSession *ss, SculptVertRef index); +void SCULPT_vertex_normal_get(SculptSession *ss, SculptVertRef index, float no[3]); +float SCULPT_vertex_mask_get(struct SculptSession *ss, SculptVertRef index); +const float *SCULPT_vertex_color_get(SculptSession *ss, SculptVertRef index); + +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, + SculptVertRef index, + int cd_pers_co); +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, + SculptVertRef index, + float no[3], + int cd_pers_no); /* Coordinates used for manipulating the base mesh when Grab Active Vertex is enabled. */ -const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index); +const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, SculptVertRef index); /* Returns the info of the limit surface when Multires is available, otherwise it returns the * current coordinate of the vertex. */ -void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]); +void SCULPT_vertex_limit_surface_get(SculptSession *ss, SculptVertRef index, float r_co[3]); /* Returns the pointer to the coordinates that should be edited from a brush tool iterator * depending on the given deformation target. */ @@ -122,22 +149,26 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 typedef struct SculptVertexNeighborIter { /* Storage */ - int *neighbors; + SculptVertRef *neighbors; + int *neighbor_indices; + int size; int capacity; - int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + SculptVertRef neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + int neighbor_indices_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; /* Internal iterator. */ int num_duplicates; int i; /* Public */ + SculptVertRef vertex; int index; bool is_duplicate; } SculptVertexNeighborIter; void SCULPT_vertex_neighbors_get(struct SculptSession *ss, - const int index, + const SculptVertRef vref, const bool include_duplicates, SculptVertexNeighborIter *iter); @@ -146,7 +177,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ neighbor_iterator.i++) { \ - neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i]; + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; /* Iterate over neighboring and duplicate vertices (for PBVH_GRIDS). Duplicates come * first since they are nearest for floodfill. */ @@ -154,7 +186,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, SCULPT_vertex_neighbors_get(ss, v_index, true, &neighbor_iterator); \ for (neighbor_iterator.i = neighbor_iterator.size - 1; neighbor_iterator.i >= 0; \ neighbor_iterator.i--) { \ - neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; \ neighbor_iterator.is_duplicate = (neighbor_iterator.i >= \ neighbor_iterator.size - neighbor_iterator.num_duplicates); @@ -165,8 +198,9 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, } \ ((void)0) -int SCULPT_active_vertex_get(SculptSession *ss); +SculptVertRef SCULPT_active_vertex_get(SculptSession *ss); const float *SCULPT_active_vertex_co_get(SculptSession *ss); +const float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex); void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]); /* Returns PBVH deformed vertices array if shape keys or deform modifiers are used, otherwise @@ -185,14 +219,14 @@ void SCULPT_fake_neighbors_free(struct Object *ob); /* Vertex Info. */ void SCULPT_boundary_info_ensure(Object *object); /* Boundary Info needs to be initialized in order to use this function. */ -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index); +bool SCULPT_vertex_is_boundary(const SculptSession *ss, const SculptVertRef index); void SCULPT_connected_components_ensure(Object *ob); /* Sculpt Visibility API */ -void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible); -bool SCULPT_vertex_visible_get(SculptSession *ss, int index); +void SCULPT_vertex_visible_set(SculptSession *ss, SculptVertRef index, bool visible); +bool SCULPT_vertex_visible_get(SculptSession *ss, SculptVertRef index); void SCULPT_visibility_sync_all_face_sets_to_vertices(struct Object *ob); void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); @@ -200,21 +234,28 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); /* Face Sets API */ int SCULPT_active_face_set_get(SculptSession *ss); -int SCULPT_vertex_face_set_get(SculptSession *ss, int index); -void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set); +int SCULPT_vertex_face_set_get(SculptSession *ss, SculptVertRef index); +void SCULPT_vertex_face_set_set(SculptSession *ss, SculptVertRef index, int face_set); -bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set); -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index); +bool SCULPT_vertex_has_face_set(SculptSession *ss, SculptVertRef index, int face_set); +bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, SculptVertRef index); int SCULPT_face_set_next_available_get(SculptSession *ss); void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible); -bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index); -bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index); +bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, SculptVertRef index); +bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, SculptVertRef index); void SCULPT_face_sets_visibility_invert(SculptSession *ss); void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible); +int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face); + +// returns previous face set +int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset); +int SCULPT_face_set_flag_get(SculptSession *ss, SculptFaceRef face, char flag); +int SCULPT_face_set_flag_set(SculptSession *ss, SculptFaceRef face, char flag, bool state); + bool SCULPT_stroke_is_main_symmetry_pass(struct StrokeCache *cache); bool SCULPT_stroke_is_first_brush_step(struct StrokeCache *cache); bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cache); @@ -224,24 +265,22 @@ typedef struct { struct BMLog *bm_log; struct SculptUndoNode *unode; + int datatype; float (*coords)[3]; short (*normals)[3]; const float *vmasks; float (*colors)[4]; + short _no[3]; /* Original coordinate, normal, and mask. */ const float *co; const short *no; float mask; const float *col; + struct PBVH *pbvh; + struct SculptSession *ss; } SculptOrigVertData; -void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node); -void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter); -void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, - Object *ob, - struct SculptUndoNode *unode); - /* Utils. */ void SCULPT_calc_brush_plane(struct Sculpt *sd, struct Object *ob, @@ -253,11 +292,11 @@ void SCULPT_calc_brush_plane(struct Sculpt *sd, void SCULPT_calc_area_normal( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]); -int SCULPT_nearest_vertex_get(struct Sculpt *sd, - struct Object *ob, - const float co[3], - float max_distance, - bool use_original); +SculptVertRef SCULPT_nearest_vertex_get(struct Sculpt *sd, + struct Object *ob, + const float co[3], + float max_distance, + bool use_original); int SCULPT_plane_point_side(const float co[3], const float plane[4]); int SCULPT_plane_trim(const struct StrokeCache *cache, @@ -299,15 +338,21 @@ void SCULPT_floodfill_add_initial_with_symmetry(struct Sculpt *sd, struct Object *ob, struct SculptSession *ss, SculptFloodFill *flood, - int index, + SculptVertRef index, float radius); -void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index); -void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index); -void SCULPT_floodfill_execute( - struct SculptSession *ss, - SculptFloodFill *flood, - bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata), - void *userdata); + +void SCULPT_floodfill_add_initial(SculptFloodFill *flood, SculptVertRef index); +void SCULPT_floodfill_add_and_skip_initial(struct SculptSession *ss, + SculptFloodFill *flood, + SculptVertRef vertex); +void SCULPT_floodfill_execute(struct SculptSession *ss, + SculptFloodFill *flood, + bool (*func)(SculptSession *ss, + SculptVertRef from_v, + SculptVertRef to_v, + bool is_duplicate, + void *userdata), + void *userdata); void SCULPT_floodfill_free(SculptFloodFill *flood); /* Dynamic topology */ @@ -319,6 +364,11 @@ enum eDynTopoWarnFlag { DYNTOPO_WARN_MODIFIER = (1 << 3), }; +struct Mesh; + +void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss); +void SCULPT_dynamic_topology_sync_layers(Object *ob, struct Mesh *me); + void SCULPT_dynamic_topology_enable_ex(struct Main *bmain, struct Depsgraph *depsgraph, Scene *scene, @@ -331,8 +381,9 @@ void sculpt_dynamic_topology_disable_with_undo(struct Main *bmain, bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush); -void SCULPT_dynamic_topology_triangulate(struct BMesh *bm); +void SCULPT_dynamic_topology_triangulate(struct SculptSession *ss, struct BMesh *bm); void SCULPT_dyntopo_node_layers_add(struct SculptSession *ss); +void SCULPT_dyntopo_save_origverts(struct SculptSession *ss); enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); @@ -341,7 +392,7 @@ void SCULPT_pbvh_clear(Object *ob); /* Auto-masking. */ float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking, SculptSession *ss, - int vert); + SculptVertRef vert); /* Returns the automasking cache depending on the active tool. Used for code that can run both for * brushes and filter. */ @@ -359,25 +410,28 @@ typedef enum eBoundaryAutomaskMode { AUTOMASK_INIT_BOUNDARY_EDGES = 1, AUTOMASK_INIT_BOUNDARY_FACE_SETS = 2, } eBoundaryAutomaskMode; -float *SCULPT_boundary_automasking_init(Object *ob, - eBoundaryAutomaskMode mode, - int propagation_steps, - float *automask_factor); + +void SCULPT_boundary_automasking_init(Object *ob, + eBoundaryAutomaskMode mode, + int propagation_steps, + SculptCustomLayer *factorlayer); /* Geodesic distances. */ /* Returns an array indexed by vertex index containing the geodesic distance to the closest vertex in the initial vertex set. The caller is responsible for freeing the array. -Geodesic distances will only work when used with PBVH_FACES, for other types of PBVH it will -fallback to euclidean distances to one of the initial vertices in the set. */ +Geodesic distances will only work when used with PBVH_FACES or PBVH_BMESH, for other types of PBVH +it will fallback to euclidean distances to one of the initial vertices in the set. */ float *SCULPT_geodesic_distances_create(struct Object *ob, struct GSet *initial_vertices, const float limit_radius); float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd, struct Object *ob, - const int vertex, + const SculptVertRef vertex, const float limit_radius); -float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius); +float *SCULPT_geodesic_from_vertex(Object *ob, + const SculptVertRef vertex, + const float limit_radius); /* Filters. */ void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, const int undo_type); @@ -515,7 +569,7 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain); /* Boundary Brush. */ struct SculptBoundary *SCULPT_boundary_data_init(Object *object, Brush *brush, - const int initial_vertex, + const SculptVertRef initial_vertex, const float radius); void SCULPT_boundary_data_free(struct SculptBoundary *boundary); void SCULPT_do_boundary_brush(struct Sculpt *sd, @@ -545,23 +599,39 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode /* Smear Brush. */ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -/* Smooth Brush. */ -void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct BMVert *v); +/* Topology rake */ +void SCULPT_bmesh_four_neighbor_average(float avg[3], + float direction[3], + struct BMVert *v, + float projection); -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index); -float SCULPT_neighbor_mask_average(SculptSession *ss, int index); -void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index); +/* Smoothing api */ +void SCULPT_neighbor_coords_average(SculptSession *ss, + float result[3], + SculptVertRef index, + float projection); +float SCULPT_neighbor_mask_average(SculptSession *ss, SculptVertRef index); +void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], SculptVertRef index); /* Mask the mesh boundaries smoothing only the mesh surface without using automasking. */ -void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index); +void SCULPT_neighbor_coords_average_interior(SculptSession *ss, + float result[3], + SculptVertRef index, + float projection); + +void SCULPT_smooth_vcol_boundary( + Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength); void SCULPT_smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength, - const bool smooth_mask); -void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); + const bool smooth_mask, + float projection); + +void SCULPT_do_smooth_brush( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection); /* Surface Smooth Brush. */ @@ -569,13 +639,15 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], float (*laplacian_disp)[3], - const int v_index, + const SculptVertRef v_index, const float origco[3], - const float alpha); + const float alpha, + const float projection); + void SCULPT_surface_smooth_displace_step(SculptSession *ss, float *co, float (*laplacian_disp)[3], - const int v_index, + const SculptVertRef v_index, const float beta, const float fade); void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); @@ -634,8 +706,8 @@ typedef struct SculptUndoNode { int totvert; /* non-multires */ - int maxvert; /* to verify if totvert it still the same */ - int *index; /* to restore into right location */ + int maxvert; /* to verify if totvert it still the same */ + SculptVertRef *index; /* to restore into right location */ BLI_bitmap *vert_hidden; /* multires */ @@ -672,7 +744,11 @@ typedef struct SculptUndoNode { /* Sculpt Face Sets */ int *face_sets; + bool *nodemap; + int nodemap_size; + size_t undo_size; + // int gen, lasthash; } SculptUndoNode; /* Factor of brush to have rake point following behind @@ -786,7 +862,7 @@ typedef struct SculptThreadedTaskData { bool mask_by_color_preserve_mask; /* Index of the vertex that is going to be used as a reference for the colors. */ - int mask_by_color_vertex; + SculptVertRef mask_by_color_vertex; float *mask_by_color_floodfill; int face_set; @@ -797,6 +873,13 @@ typedef struct SculptThreadedTaskData { ThreadMutex mutex; + // Layer brush + int cd_pers_co, cd_pers_no, cd_pers_disp; + int cd_layer_disp; + + float smooth_projection; + float rake_projection; + SculptCustomLayer *scl; } SculptThreadedTaskData; /*************** Brush testing declarations ****************/ @@ -830,6 +913,8 @@ typedef struct { bool original; /* This ignores fully masked and fully hidden nodes. */ bool ignore_fully_ineffective; + struct Object *ob; + struct Brush *brush; } SculptSearchSphereData; typedef struct { @@ -866,7 +951,7 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss, const short vno[3], const float fno[3], const float mask, - const int vertex_index, + const SculptVertRef vertex_index, const int thread_id); /* Tilts a normal by the x and y tilt values using the view axis. */ @@ -897,13 +982,15 @@ typedef struct AutomaskingSettings { /* Flags from eAutomasking_flag. */ int flags; int initial_face_set; + float concave_factor; } AutomaskingSettings; typedef struct AutomaskingCache { AutomaskingSettings settings; /* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and * initialized in #SCULPT_automasking_cache_init when needed. */ - float *factor; + // float *factor; + SculptCustomLayer *factorlayer; } AutomaskingCache; typedef struct StrokeCache { @@ -1066,6 +1153,17 @@ typedef struct StrokeCache { rcti previous_r; /* previous redraw rectangle */ rcti current_r; /* current redraw rectangle */ + float stroke_distance; // copy of PaintStroke->stroke_distance + float stroke_distance_t; // copy of PaintStroke->stroke_distance_t + + float last_dyntopo_t; + float last_smooth_t[SCULPT_MAX_SYMMETRY_PASSES]; + float last_rake_t[SCULPT_MAX_SYMMETRY_PASSES]; + + int layer_disp_map_size; + BLI_bitmap *layer_disp_map; + + struct PaintStroke *stroke; } StrokeCache; /* Sculpt Filters */ @@ -1141,7 +1239,7 @@ typedef struct ExpandCache { * during the execution of Expand by moving the origin. */ float initial_mouse_move[2]; float initial_mouse[2]; - int initial_active_vertex; + SculptVertRef initial_active_vertex; int initial_active_face_set; /* Maximum number of vertices allowed in the SculptSession for previewing the falloff using @@ -1302,8 +1400,17 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, const float angle); void SCULPT_cache_free(StrokeCache *cache); +void SCULPT_orig_vert_data_init(SculptOrigVertData *data, + Object *ob, + PBVHNode *node, + SculptUndoType type); +void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter); +void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, + Object *ob, + struct SculptUndoNode *unode); + SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type); -SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node); +SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type); SculptUndoNode *SCULPT_undo_get_first_node(void); void SCULPT_undo_push_begin(struct Object *ob, const char *name); void SCULPT_undo_push_end(void); @@ -1370,3 +1477,99 @@ void SCULPT_OT_dyntopo_detail_size_edit(struct wmOperatorType *ot); /* Dyntopo. */ void SCULPT_OT_dynamic_topology_toggle(struct wmOperatorType *ot); +bool SCULPT_ensure_dyntopo_node_undo(struct Object *ob, + struct PBVHNode *node, + SculptUndoType type, + int extraType); + +float SCULPT_calc_concavity(SculptSession *ss, SculptVertRef vref); + +typedef struct SculptCurvatureData { + float ks[3]; + float principle[3][3]; // normalized +} SculptCurvatureData; + +/* +If useAccurateSolver is false, a faster but less accurate +power solver will be used. If true then BLI_eigen_solve_selfadjoint_m3 +will be called. +*/ +bool SCULPT_calc_principle_curvatures(SculptSession *ss, + SculptVertRef vertex, + SculptCurvatureData *out, + bool useAccurateSolver); + +void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver); +void SCULPT_curvature_dir_get(SculptSession *ss, + SculptVertRef v, + float dir[3], + bool useAccurateSolver); + +/* +Ensure a named temporary layer exists, creating it if necassary. +The layer will be marked with CD_FLAG_TEMPORARY. +*/ +void SCULPT_dyntopo_ensure_templayer(SculptSession *ss, int type, const char *name); + +bool SCULPT_dyntopo_has_templayer(SculptSession *ss, int type, const char *name); + +/* Get a named temporary vertex customdata layer offset, if it exists. If not + -1 is returned.*/ +int SCULPT_dyntopo_get_templayer(SculptSession *ss, int type, const char *name); + +void SCULPT_dyntopo_save_persistent_base(SculptSession *ss); + +#define SCULPT_LAYER_PERS_CO "__dyntopo_layer_pers_co" +#define SCULPT_LAYER_PERS_NO "__dyntopo_layer_pers_no" +#define SCULPT_LAYER_PERS_DISP "__dyntopo_layer_pers_disp" +#define SCULPT_LAYER_DISP "__dyntopo_layer_disp" + +// these tools don't support dynamic pbvh splitting during the stroke +#define DYNTOPO_HAS_DYNAMIC_SPLIT(tool) \ + (ELEM(tool, SCULPT_TOOL_DRAW_SHARP, SCULPT_TOOL_LAYER) == 0) + +/*get current symmetry pass index inclusive of both + mirror and radial symmetry*/ +int SCULPT_get_symmetry_pass(const SculptSession *ss); + +void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss); +void SCULPT_reorder_bmesh(SculptSession *ss); + +// TODO: support faces +static inline void *SCULPT_temp_cdata_get(SculptVertRef vertex, SculptCustomLayer *scl) +{ + if (scl->data) { + char *p = (char *)scl->data; + return p + scl->elemsize * (int)vertex.i; + } + else { + BMVert *v = (BMVert *)vertex.i; + return BM_ELEM_CD_GET_VOID_P(v, scl->cd_offset); + } +} + +/* +create a custom vertex or face attribute. +always create all of your attributes together with SCULPT_temp_customlayer_ensure, + +then initialize their SculptCustomLayer's with SCULPT_temp_customlayer_get +afterwards. Otherwise customdata offsets will be wrong (for PBVH_BMESH). + +return true on success. if false, layer was not created. + +Access per element data with SCULPT_temp_cdata_get. +*/ +bool SCULPT_temp_customlayer_ensure(SculptSession *ss, + AttributeDomain domain, + int proptype, + char *name); +bool SCULPT_temp_customlayer_get( + SculptSession *ss, AttributeDomain domain, int proptype, char *name, SculptCustomLayer *scl); + +bool SCULPT_dyntopo_automasking_init(const SculptSession *ss, + const Brush *br, + const Sculpt *sd, + DyntopoMaskCB *r_mask_cb, + void **r_mask_cb_data); +void SCULPT_dyntopo_automasking_end(void *mask_data); +void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 9b06b2ee5d5..cf34fdc1506 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -117,7 +117,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata, int vi = vd.index; float final_mask = *vd.mask; if (data->mask_expand_use_normals) { - if (ss->filter_cache->normal_factor[SCULPT_active_vertex_get(ss)] < + if (ss->filter_cache->normal_factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss))] < ss->filter_cache->normal_factor[vd.index]) { final_mask = 1.0f; } @@ -137,7 +137,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata, if (data->mask_expand_create_face_set) { if (final_mask == 1.0f) { - SCULPT_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set); + SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set); } BKE_pbvh_node_mark_redraw(node); } @@ -188,7 +188,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * mouse[1] = event->mval[1]; if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { /* The cursor is over the mesh, get the update iteration from the updated active vertex. */ - mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)]; + int vi = BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss)); + mask_expand_update_it = ss->filter_cache->mask_update_it[vi]; } else { /* When the cursor is outside the mesh, affect the entire connected component. */ @@ -309,10 +310,13 @@ typedef struct MaskExpandFloodFillData { } MaskExpandFloodFillData; static bool mask_expand_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, SculptVertRef from_vref, SculptVertRef to_vref, bool is_duplicate, void *userdata) { MaskExpandFloodFillData *data = userdata; + int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref); + int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref); + if (!is_duplicate) { int to_it = ss->filter_cache->mask_update_it[from_v] + 1; ss->filter_cache->mask_update_it[to_v] = to_it; @@ -322,8 +326,8 @@ static bool mask_expand_floodfill_cb( if (data->use_normals) { float current_normal[3], prev_normal[3]; - SCULPT_vertex_normal_get(ss, to_v, current_normal); - SCULPT_vertex_normal_get(ss, from_v, prev_normal); + SCULPT_vertex_normal_get(ss, to_vref, current_normal); + SCULPT_vertex_normal_get(ss, from_vref, prev_normal); const float from_edge_factor = ss->filter_cache->edge_factor[from_v]; ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * from_edge_factor; @@ -412,13 +416,15 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent else { ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask"); for (int i = 0; i < vertex_count; i++) { - ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, i); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, vertex); } } ss->filter_cache->mask_update_last_it = 1; ss->filter_cache->mask_update_current_it = 1; - ss->filter_cache->mask_update_it[SCULPT_active_vertex_get(ss)] = 0; + ss->filter_cache->mask_update_it[BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss))] = 0; copy_v3_v3(ss->filter_cache->mask_expand_initial_co, SCULPT_active_vertex_co_get(ss)); @@ -437,9 +443,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent if (use_normals) { for (int repeat = 0; repeat < 2; repeat++) { for (int i = 0; i < vertex_count; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + float avg = 0.0f; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { avg += ss->filter_cache->normal_factor[ni.index]; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c index 0c383cdf035..349f781c7a1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c @@ -115,7 +115,7 @@ static void mask_init_task_cb(void *__restrict userdata, *vd.mask = BLI_hash_int_01(vd.index + seed); break; case SCULPT_MASK_INIT_RANDOM_PER_FACE_SET: { - const int face_set = SCULPT_vertex_face_set_get(ss, vd.index); + const int face_set = SCULPT_vertex_face_set_get(ss, vd.vertex); *vd.mask = BLI_hash_int_01(face_set + seed); break; } diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index f78f30a2cfd..a7f42349777 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -107,7 +107,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); /* Sample the normal and area of the +X and -X axis individually. */ @@ -166,7 +166,6 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -215,7 +214,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index c3977b28178..12934af943f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -96,11 +96,11 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float smooth_color[4]; - SCULPT_neighbor_color_average(ss, smooth_color, vd.index); + SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade); if (vd.mvert) { @@ -123,7 +123,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, PBVHColorBufferNode *color_buffer; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); + orig_data.datatype = SCULPT_UNDO_COLOR; color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]); @@ -163,7 +164,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); /* Density. */ @@ -363,6 +364,10 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range(0, totnode, &data, do_paint_brush_task_cb_ex, &settings); + + if (brush->vcol_boundary_factor > 0.0f) { + SCULPT_smooth_vcol_boundary(sd, ob, nodes, totnode, brush->vcol_boundary_factor); + } } static void do_smear_brush_task_cb_exec(void *__restrict userdata, @@ -392,7 +397,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; @@ -415,10 +420,10 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vertex_disp[3]; float vertex_disp_norm[3]; - sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co); const float *neighbor_color = ss->cache->prev_colors[ni.index]; normalize_v3_v3(vertex_disp_norm, vertex_disp); if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) { @@ -451,7 +456,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.index)); + copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.vertex)); } BKE_pbvh_vertex_iter_end; } @@ -465,13 +470,17 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode return; } + SCULPT_vertex_random_access_ensure(ss); + const int totvert = SCULPT_vertex_count_get(ss); if (SCULPT_stroke_is_first_brush_step(ss->cache)) { if (!ss->cache->prev_colors) { ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors"); for (int i = 0; i < totvert; i++) { - copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, i)); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, vertex)); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index 587ce346428..07f0f763ff9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -172,7 +172,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, float final_pos[3]; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -198,7 +198,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, /* Apply the vertex mask to the displacement. */ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); mul_v3_fl(disp, mask * automask); /* Accumulate the displacement. */ @@ -237,7 +237,7 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, float max = 0.0f; /* Grow the factor. */ - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; max = MAX2(vmask_f, max); } @@ -383,7 +383,8 @@ typedef struct PoseFloodFillData { int current_face_set; int next_face_set; int prev_face_set; - int next_vertex; + SculptVertRef next_vertex; + int next_vertex_index; bool next_face_set_found; @@ -413,11 +414,16 @@ typedef struct PoseFloodFillData { int target_face_set; } PoseFloodFillData; -static bool pose_topology_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) +static bool pose_topology_floodfill_cb(SculptSession *ss, + SculptVertRef UNUSED(from_v), + SculptVertRef to_vref, + bool is_duplicate, + void *userdata) { PoseFloodFillData *data = userdata; - const float *co = SCULPT_vertex_co_get(ss, to_v); + int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref); + + const float *co = SCULPT_vertex_co_get(ss, to_vref); if (data->pose_factor) { data->pose_factor[to_v] = 1.0f; @@ -442,15 +448,18 @@ static bool pose_topology_floodfill_cb( return false; } -static bool pose_face_sets_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) +static bool pose_face_sets_floodfill_cb(SculptSession *ss, + SculptVertRef UNUSED(from_v), + SculptVertRef to_v, + bool is_duplicate, + void *userdata) { PoseFloodFillData *data = userdata; - const int index = to_v; + const int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v); bool visit_next = false; - const float *co = SCULPT_vertex_co_get(ss, index); + const float *co = SCULPT_vertex_co_get(ss, to_v); const bool symmetry_check = SCULPT_check_vertex_pivot_symmetry( co, data->pose_initial_co, data->symm) && !is_duplicate; @@ -464,11 +473,11 @@ static bool pose_face_sets_floodfill_cb( if (sculpt_pose_brush_is_vertex_inside_brush_radius( co, data->pose_initial_co, data->radius, data->symm)) { - const int visited_face_set = SCULPT_vertex_face_set_get(ss, index); + const int visited_face_set = SCULPT_vertex_face_set_get(ss, to_v); BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(visited_face_set)); } else if (symmetry_check) { - data->current_face_set = SCULPT_vertex_face_set_get(ss, index); + data->current_face_set = SCULPT_vertex_face_set_get(ss, to_v); BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(data->current_face_set)); } return true; @@ -482,11 +491,11 @@ static bool pose_face_sets_floodfill_cb( GSetIterator gs_iter; GSET_ITER (gs_iter, data->visited_face_sets) { const int visited_face_set = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); - is_vertex_valid |= SCULPT_vertex_has_face_set(ss, index, visited_face_set); + is_vertex_valid |= SCULPT_vertex_has_face_set(ss, to_v, visited_face_set); } } else { - is_vertex_valid = SCULPT_vertex_has_face_set(ss, index, data->current_face_set); + is_vertex_valid = SCULPT_vertex_has_face_set(ss, to_v, data->current_face_set); } if (!is_vertex_valid) { @@ -501,11 +510,11 @@ static bool pose_face_sets_floodfill_cb( /* Fallback origin accumulation. */ if (symmetry_check) { - add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, index)); + add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, to_v)); data->fallback_count++; } - if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, index)) { + if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, to_v)) { return visit_next; } @@ -514,15 +523,15 @@ static bool pose_face_sets_floodfill_cb( bool count_as_boundary = false; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.index); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, to_v, ni) { + int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.vertex); /* Check if we can get a valid face set for the next iteration from this neighbor. */ - if (SCULPT_vertex_has_unique_face_set(ss, ni.index) && + if (SCULPT_vertex_has_unique_face_set(ss, ni.vertex) && !BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(next_face_set_candidate))) { if (!data->next_face_set_found) { data->next_face_set = next_face_set_candidate; - data->next_vertex = ni.index; + data->next_vertex = ni.vertex; data->next_face_set_found = true; } count_as_boundary = true; @@ -532,7 +541,7 @@ static bool pose_face_sets_floodfill_cb( /* Origin accumulation. */ if (count_as_boundary) { - add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, index)); + add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, to_v)); data->tot_co++; } return visit_next; @@ -556,8 +565,6 @@ void SCULPT_pose_calc_pose_data(Sculpt *sd, float *r_pose_origin, float *r_pose_factor) { - SCULPT_vertex_random_access_ensure(ss); - /* Calculate the pose rotation point based on the boundaries of the brush factor. */ SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); @@ -608,7 +615,7 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata, SculptVertexNeighborIter ni; float avg = 0.0f; int total = 0; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { avg += data->pose_factor[ni.index]; total++; } @@ -678,12 +685,15 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd, const float initial_location[3], const float radius) { + SCULPT_vertex_random_access_ensure(ss); const float chain_segment_len = radius * (1.0f + br->pose_offset); float next_chain_segment_target[3]; int totvert = SCULPT_vertex_count_get(ss); - int nearest_vertex_index = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true); + SculptVertRef nearest_vertex = SCULPT_nearest_vertex_get( + sd, ob, initial_location, FLT_MAX, true); + int nearest_vertex_index = BKE_pbvh_vertex_index_to_table(ss->pbvh, nearest_vertex); /* Init the buffers used to keep track of the changes in the pose factors as more segments are * added to the IK chain. */ @@ -768,7 +778,8 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( int current_face_set = SCULPT_FACE_SET_NONE; int prev_face_set = SCULPT_FACE_SET_NONE; - int current_vertex = SCULPT_active_vertex_get(ss); + SculptVertRef current_vertex = SCULPT_active_vertex_get(ss); + int current_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, current_vertex); for (int s = 0; s < ik_chain->tot_segments; s++) { @@ -797,6 +808,13 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( SCULPT_floodfill_execute(ss, &flood, pose_face_sets_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); + if (!fdata.next_face_set_found) { + for (int i = s; i < ik_chain->tot_segments; i++) { + zero_v3(ik_chain->segments[i].orig); + } + break; + } + if (fdata.tot_co > 0) { mul_v3_fl(fdata.pose_origin, 1.0f / (float)fdata.tot_co); copy_v3_v3(ik_chain->segments[s].orig, fdata.pose_origin); @@ -823,10 +841,15 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( return ik_chain; } -static bool pose_face_sets_fk_find_masked_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) +static bool pose_face_sets_fk_find_masked_floodfill_cb(SculptSession *ss, + SculptVertRef from_vr, + SculptVertRef to_vr, + bool is_duplicate, + void *userdata) { PoseFloodFillData *data = userdata; + int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vr); + int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vr); if (!is_duplicate) { data->floodfill_it[to_v] = data->floodfill_it[from_v] + 1; @@ -835,11 +858,11 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb( data->floodfill_it[to_v] = data->floodfill_it[from_v]; } - const int to_face_set = SCULPT_vertex_face_set_get(ss, to_v); + const int to_face_set = SCULPT_vertex_face_set_get(ss, to_vr); if (!BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(to_face_set))) { - if (SCULPT_vertex_has_unique_face_set(ss, to_v) && - !SCULPT_vertex_has_unique_face_set(ss, from_v) && - SCULPT_vertex_has_face_set(ss, from_v, to_face_set)) { + if (SCULPT_vertex_has_unique_face_set(ss, to_vr) && + !SCULPT_vertex_has_unique_face_set(ss, from_vr) && + SCULPT_vertex_has_face_set(ss, from_vr, to_face_set)) { BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(to_face_set)); @@ -854,14 +877,17 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb( } } - return SCULPT_vertex_has_face_set(ss, to_v, data->initial_face_set); + return SCULPT_vertex_has_face_set(ss, to_vr, data->initial_face_set); } -static bool pose_face_sets_fk_set_weights_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool pose_face_sets_fk_set_weights_floodfill_cb(SculptSession *ss, + SculptVertRef UNUSED(from_v), + SculptVertRef to_v, + bool UNUSED(is_duplicate), + void *userdata) { PoseFloodFillData *data = userdata; - data->fk_weights[to_v] = 1.0f; + data->fk_weights[BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v)] = 1.0f; return !SCULPT_vertex_has_face_set(ss, to_v, data->masked_face_set); } @@ -872,7 +898,9 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( SculptPoseIKChain *ik_chain = pose_ik_chain_new(1, totvert); - const int active_vertex = SCULPT_active_vertex_get(ss); + const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss); + const int active_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, active_vertex); + const int active_face_set = SCULPT_active_face_set_get(ss); SculptFloodFill flood; @@ -880,7 +908,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( SCULPT_floodfill_add_initial(&flood, active_vertex); PoseFloodFillData fdata; fdata.floodfill_it = MEM_calloc_arrayN(totvert, sizeof(int), "floodfill iteration"); - fdata.floodfill_it[active_vertex] = 1; + fdata.floodfill_it[active_vertex_i] = 1; fdata.initial_face_set = active_face_set; fdata.masked_face_set = SCULPT_FACE_SET_NONE; fdata.target_face_set = SCULPT_FACE_SET_NONE; @@ -893,9 +921,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( int origin_count = 0; float origin_acc[3] = {0.0f}; for (int i = 0; i < totvert; i++) { - if (fdata.floodfill_it[i] != 0 && SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) && - SCULPT_vertex_has_face_set(ss, i, fdata.masked_face_set)) { - add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, i)); + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (fdata.floodfill_it[i] != 0 && + SCULPT_vertex_has_face_set(ss, vref, fdata.initial_face_set) && + SCULPT_vertex_has_face_set(ss, vref, fdata.masked_face_set)) { + add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, vref)); origin_count++; } } @@ -904,10 +935,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( float target_acc[3] = {0.0f}; if (fdata.target_face_set != fdata.masked_face_set) { for (int i = 0; i < totvert; i++) { + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + if (fdata.floodfill_it[i] != 0 && - SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) && - SCULPT_vertex_has_face_set(ss, i, fdata.target_face_set)) { - add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, i)); + SCULPT_vertex_has_face_set(ss, vref, fdata.initial_face_set) && + SCULPT_vertex_has_face_set(ss, vref, fdata.target_face_set)) { + add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, vref)); target_count++; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index eabbfe43e03..6a37fd55562 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -24,6 +24,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_compiler_attrs.h" #include "BLI_hash.h" #include "BLI_math.h" #include "BLI_task.h" @@ -32,6 +33,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_attribute.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_mesh.h" @@ -58,53 +60,177 @@ #include "RNA_define.h" #include "bmesh.h" - +#ifdef PROXY_ADVANCED +/* clang-format off */ +#include "BKE_DerivedMesh.h" +#include "../../blenkernel/intern/pbvh_intern.h" +/* clang-format on */ +#endif #include <math.h> #include <stdlib.h> -void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index) +void SCULPT_neighbor_coords_average_interior(SculptSession *ss, + float result[3], + SculptVertRef vertex, + float projection) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; int neighbor_count = 0; - const bool is_boundary = SCULPT_vertex_is_boundary(ss, index); + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex); + const float *co = SCULPT_vertex_co_get(ss, vertex); + float no[3]; + + if (projection > 0.0f) { + SCULPT_vertex_normal_get(ss, vertex, no); + } SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { neighbor_count++; + + float tmp[3]; + bool ok = false; + if (is_boundary) { /* Boundary vertices use only other boundary vertices. */ - if (SCULPT_vertex_is_boundary(ss, ni.index)) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); - total++; + if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { + copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex)); + ok = true; } } else { /* Interior vertices use all neighbors. */ - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); - total++; + copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex)); + ok = true; + } + + if (!ok) { + continue; } + + if (projection > 0.0f) { + sub_v3_v3(tmp, co); + float fac = dot_v3v3(tmp, no); + madd_v3_v3fl(tmp, no, -fac * projection); + add_v3_v3(avg, tmp); + } + else { + add_v3_v3(avg, tmp); + } + + total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); /* Do not modify corner vertices. */ if (neighbor_count <= 2) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); return; } /* Avoid division by 0 when there are no neighbors. */ if (total == 0) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); return; } mul_v3_v3fl(result, avg, 1.0f / total); + + if (projection > 0.0f) { + add_v3_v3(result, co); + } } +void SCULPT_neighbor_coords_average_interior_velocity(SculptSession *ss, + float result[3], + SculptVertRef vertex, + float projection, + SculptCustomLayer *scl) +{ + float avg[3] = {0.0f, 0.0f, 0.0f}; + int total = 0; + int neighbor_count = 0; + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex); + const float *co = SCULPT_vertex_co_get(ss, vertex); + float no[3]; + + if (projection > 0.0f) { + SCULPT_vertex_normal_get(ss, vertex, no); + } + + float vel[3]; + + copy_v3_v3(vel, (float *)SCULPT_temp_cdata_get(vertex, scl)); + mul_v3_fl(vel, 0.4f); + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + neighbor_count++; + + float tmp[3]; + bool ok = false; + + float *vel2 = SCULPT_temp_cdata_get(ni.vertex, scl); + + // propegate smooth velocities a bit + madd_v3_v3fl(vel2, vel, 1.0f / (float)ni.size); + + if (is_boundary) { + /* Boundary vertices use only other boundary vertices. */ + if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { + copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex)); + ok = true; + } + } + else { + /* Interior vertices use all neighbors. */ + copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex)); + ok = true; + } + + if (!ok) { + continue; + } + + if (projection > 0.0f) { + sub_v3_v3(tmp, co); + float fac = dot_v3v3(tmp, no); + madd_v3_v3fl(tmp, no, -fac * projection); + add_v3_v3(avg, tmp); + } + else { + add_v3_v3(avg, tmp); + } + + total++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + /* Do not modify corner vertices. */ + if (neighbor_count <= 2) { + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); + return; + } + + /* Avoid division by 0 when there are no neighbors. */ + if (total == 0) { + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); + return; + } + + mul_v3_v3fl(result, avg, 1.0f / total); + + if (projection > 0.0f) { + add_v3_v3(result, co); + } +} /* For bmesh: Average surrounding verts based on an orthogonality measure. * Naturally converges to a quad-like structure. */ -void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v) +void SCULPT_bmesh_four_neighbor_average(float avg[3], + float direction[3], + BMVert *v, + float projection) { float avg_co[3] = {0.0f, 0.0f, 0.0f}; @@ -121,7 +247,8 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; float vec[3]; sub_v3_v3v3(vec, v_other->co, v->co); - madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no)); + + madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no) * projection); normalize_v3(vec); /* fac is a measure of how orthogonal or parallel the edge is @@ -140,7 +267,7 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert /* Preserve volume. */ float vec[3]; sub_v3_v3(avg, v->co); - mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); + mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no) * projection); sub_v3_v3(avg, vec); add_v3_v3(avg, v->co); } @@ -152,34 +279,60 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert /* Generic functions for laplacian smoothing. These functions do not take boundary vertices into * account. */ -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index) +void SCULPT_neighbor_coords_average(SculptSession *ss, + float result[3], + SculptVertRef vertex, + float projection) { float avg[3] = {0.0f, 0.0f, 0.0f}; + float *co, no[3]; int total = 0; + if (projection > 0.0f) { + co = (float *)SCULPT_vertex_co_get(ss, vertex); + SCULPT_vertex_normal_get(ss, vertex, no); + } + SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); + + if (projection > 0.0f) { + float tmp[3]; + + sub_v3_v3v3(tmp, co2, co); + float fac = dot_v3v3(tmp, no); + madd_v3_v3fl(tmp, no, -fac * projection); + + add_v3_v3(avg, tmp); + } + else { + add_v3_v3(avg, co2); + } total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { mul_v3_v3fl(result, avg, 1.0f / total); + + if (projection > 0.0) { + add_v3_v3(result, co); + } } else { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); } } -float SCULPT_neighbor_mask_average(SculptSession *ss, int index) +float SCULPT_neighbor_mask_average(SculptSession *ss, SculptVertRef index) { float avg = 0.0f; int total = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - avg += SCULPT_vertex_mask_get(ss, ni.index); + avg += SCULPT_vertex_mask_get(ss, ni.vertex); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -190,14 +343,14 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index) return SCULPT_vertex_mask_get(ss, index); } -void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index) +void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], SculptVertRef index) { float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f}; int total = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index)); + add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.vertex)); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -241,7 +394,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp[3]; @@ -273,8 +426,10 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, for (int i = 0; i < totvert; i++) { float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f); + sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -290,6 +445,88 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings); } +#ifdef PROXY_ADVANCED +static void do_smooth_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const bool smooth_mask = data->smooth_mask; + float bstrength = data->strength; + + PBVHVertexIter vd; + + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + const int thread_id = BLI_task_parallel_thread_id(tls); + + PBVHNode **nodes = data->nodes; + ProxyVertArray *p = &nodes[n]->proxyverts; + + for (int i = 0; i < p->size; i++) { + float co[3] = {0.0f, 0.0f, 0.0f}; + int ni = 0; + +# if 1 + if (sculpt_brush_test_sq_fn(&test, p->co[i])) { + const float fade = bstrength * SCULPT_brush_strength_factor( + ss, + brush, + p->co[i], + sqrtf(test.dist), + p->no[i], + p->fno[i], + smooth_mask ? 0.0f : (p->mask ? p->mask[i] : 0.0f), + p->index[i], + thread_id); +# else + if (1) { + const float fade = 1.0; +# endif + + while (ni < MAX_PROXY_NEIGHBORS && p->neighbors[i][ni].node >= 0) { + ProxyKey *key = p->neighbors[i] + ni; + PBVHNode *n2 = ss->pbvh->nodes + key->node; + + // printf("%d %d %d %p\n", key->node, key->pindex, ss->pbvh->totnode, n2); + + if (key->pindex < 0 || key->pindex >= n2->proxyverts.size) { + printf("corruption!\n"); + fflush(stdout); + ni++; + continue; + } + + if (n2->proxyverts.co) { + add_v3_v3(co, n2->proxyverts.co[key->pindex]); + ni++; + } + } + + // printf("ni %d\n", ni); + + if (ni > 2) { + mul_v3_fl(co, 1.0f / (float)ni); + } + else { + copy_v3_v3(co, p->co[i]); + } + + // printf("%f %f %f ", co[0], co[1], co[2]); + + interp_v3_v3v3(p->co[i], p->co[i], co, fade); + } + } +} + +#else static void do_smooth_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -300,6 +537,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, const Brush *brush = data->brush; const bool smooth_mask = data->smooth_mask; float bstrength = data->strength; + float projection = data->smooth_projection; PBVHVertexIter vd; @@ -323,17 +561,17 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), - vd.index, + vd.vertex, thread_id); if (smooth_mask) { - float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask; + float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask; val *= fade * bstrength; *vd.mask += val; CLAMP(*vd.mask, 0.0f, 1.0f); } else { float avg[3], val[3]; - SCULPT_neighbor_coords_average_interior(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); @@ -344,13 +582,72 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } BKE_pbvh_vertex_iter_end; } +#endif + +static void do_smooth_brush_task_cb_ex_scl(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + float bstrength = data->strength; + float projection = data->smooth_projection; + + SculptCustomLayer *scl = data->scl; + + PBVHVertexIter vd; + + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + const int thread_id = BLI_task_parallel_thread_id(tls); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + (vd.mask ? *vd.mask : 0.0f), + vd.vertex, + thread_id); + + float avg[3], val[3]; + + SCULPT_neighbor_coords_average_interior_velocity(ss, avg, vd.vertex, projection, scl); + + sub_v3_v3v3(val, avg, vd.co); + + float *vel = (float *)SCULPT_temp_cdata_get(vd.vertex, scl); + interp_v3_v3v3(vel, vel, val, 0.5); + + madd_v3_v3v3fl(val, vd.co, vel, fade); + + SCULPT_clip(sd, ss, vd.co, val); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} void SCULPT_smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength, - const bool smooth_mask) + const bool smooth_mask, + float projection) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -366,6 +663,18 @@ void SCULPT_smooth(Sculpt *sd, count = (int)(bstrength * max_iterations); last = max_iterations * (bstrength - count * fract); + SculptCustomLayer scl; +#if 0 + bool have_scl = smooth_mask ? false : + SCULPT_temp_customlayer_ensure( + ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel"); + if (have_scl) { + SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &scl); + } +#else + bool have_scl = false; +#endif + if (type == PBVH_FACES && !ss->pmap) { BLI_assert_msg(0, "sculpt smooth: pmap missing"); return; @@ -374,25 +683,41 @@ void SCULPT_smooth(Sculpt *sd, SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); +#ifdef PROXY_ADVANCED + int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK; + BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask); + + BKE_pbvh_load_proxyarrays(ss->pbvh, nodes, totnode, PV_CO | PV_NO | PV_MASK); +#endif for (iteration = 0; iteration <= count; iteration++) { const float strength = (iteration != count) ? 1.0f : last; - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .smooth_mask = smooth_mask, - .strength = strength, - }; + SculptThreadedTaskData data = {.sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .smooth_mask = smooth_mask, + .strength = strength, + .smooth_projection = projection, + .scl = have_scl ? &scl : NULL}; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings); + if (have_scl) { + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex_scl, &settings); + } + else { + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings); + } + +#ifdef PROXY_ADVANCED + BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode); +#endif } } -void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +void SCULPT_do_smooth_brush( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection) { SculptSession *ss = ob->sculpt; if (ss->cache->bstrength <= 0.0f) { @@ -401,7 +726,7 @@ void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod } else { /* Regular mode, smooth. */ - SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); + SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false, projection); } } @@ -412,18 +737,21 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], float (*laplacian_disp)[3], - const int v_index, + const SculptVertRef v_index, const float origco[3], - const float alpha) + const float alpha, + const float projection) { float laplacian_smooth_co[3]; float weigthed_o[3], weigthed_q[3], d[3]; - SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index); + SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index, projection); + + int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_index); mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); add_v3_v3v3(d, weigthed_o, weigthed_q); - sub_v3_v3v3(laplacian_disp[v_index], laplacian_smooth_co, d); + sub_v3_v3v3(laplacian_disp[index], laplacian_smooth_co, d); sub_v3_v3v3(disp, laplacian_smooth_co, co); } @@ -431,22 +759,25 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, void SCULPT_surface_smooth_displace_step(SculptSession *ss, float *co, float (*laplacian_disp)[3], - const int v_index, + const SculptVertRef v_index, const float beta, const float fade) { float b_avg[3] = {0.0f, 0.0f, 0.0f}; float b_current_vertex[3]; int total = 0; + int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_index); + SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_index, ni) { add_v3_v3(b_avg, laplacian_disp[ni.index]); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + if (total > 0) { mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total); - madd_v3_v3fl(b_current_vertex, laplacian_disp[v_index], beta); + madd_v3_v3fl(b_current_vertex, laplacian_disp[index], beta); mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f)); sub_v3_v3(co, b_current_vertex); } @@ -469,10 +800,11 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -483,12 +815,18 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp[3]; - SCULPT_surface_smooth_laplacian_step( - ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, orig_data.co, alpha); + SCULPT_surface_smooth_laplacian_step(ss, + disp, + vd.co, + ss->cache->surface_smooth_laplacian_disp, + vd.vertex, + orig_data.co, + alpha, + data->smooth_projection); madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; @@ -524,10 +862,10 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_surface_smooth_displace_step( - ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade); + ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade); } BKE_pbvh_vertex_iter_end; } @@ -544,12 +882,11 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in } /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; + SculptThreadedTaskData data = {.sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .smooth_projection = brush->autosmooth_projection}; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -560,3 +897,190 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in 0, totnode, &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings); } } + +static void do_smooth_vcol_boundary_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const bool smooth_mask = data->smooth_mask; + float bstrength = data->strength; + + PBVHVertexIter vd; + + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + const int thread_id = BLI_task_parallel_thread_id(tls); + + float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float tot = 0.0f; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!vd.col) { + continue; + } + + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * SCULPT_brush_strength_factor( + ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), + vd.vertex, + thread_id); + + madd_v3_v3fl(avg, vd.col, fade); + tot += fade; + } + } + BKE_pbvh_vertex_iter_end; + + if (tot == 0.0f) { + return; + } + tot = 1.0f / tot; + + mul_v3_fl(avg, tot); + + float exp = brush->vcol_boundary_exponent; + // detect bad value + + if (exp == 0.0f) { + exp = 1.0f; + } + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * SCULPT_brush_strength_factor( + ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), + vd.vertex, + thread_id); + if (!vd.col) { + continue; + } + + float avg2[3], avg3[3], val[3]; + float tot2 = 0.0f, tot4 = 0.0f; + + copy_v4_v4(avg, vd.col); + + zero_v3(avg2); + zero_v3(avg3); + + madd_v3_v3fl(avg2, vd.co, 0.5f); + tot2 += 0.5f; + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { + const float *col = SCULPT_vertex_color_get(ss, ni.vertex); + const float *co = SCULPT_vertex_co_get(ss, ni.vertex); + + // simple color metric. TODO: plug in appropriate color space code? + float dv[4]; + sub_v4_v4v4(dv, col, avg); + float w = (fabs(dv[0]) + fabs(dv[1]) + fabs(dv[2]) + fabs(dv[3])) / 4.0; + + w = powf(w, exp); + + madd_v3_v3fl(avg3, co, 1.0f); + tot4 += 1.0f; + + madd_v3_v3fl(avg2, co, w); + tot2 += w; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (tot2 == 0.0f) { + continue; + } + + if (tot4 > 0.0f) { + mul_v3_fl(avg3, 1.0f / tot4); + } + + /* try to avoid perfectly colinear triangles, and the normal discontinuities they create, + by blending slightly with unweighted smoothed position */ + mul_v3_fl(avg2, 1.0f / tot2); + interp_v3_v3v3(avg2, avg2, avg3, 0.025); + + sub_v3_v3v3(val, avg2, vd.co); + madd_v3_v3v3fl(val, vd.co, val, fade); + SCULPT_clip(sd, ss, vd.co, val); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BKE_pbvh_vertex_iter_end; +} + +void SCULPT_smooth_vcol_boundary( + Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) +{ + SculptSession *ss = ob->sculpt; + + Brush *brush = BKE_paint_brush(&sd->paint); + + const int max_iterations = 4; + const float fract = 1.0f / max_iterations; + PBVHType type = BKE_pbvh_type(ss->pbvh); + int iteration, count; + float last; + + CLAMP(bstrength, 0.0f, 1.0f); + + count = (int)(bstrength * max_iterations); + last = max_iterations * (bstrength - count * fract); + + if (type == PBVH_FACES && !ss->pmap) { + BLI_assert(!"sculpt smooth: pmap missing"); + return; + } + + SCULPT_vertex_random_access_ensure(ss); + SCULPT_boundary_info_ensure(ob); + +#ifdef PROXY_ADVANCED + int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK; + BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask); + + BKE_pbvh_load_proxyarrays(ss->pbvh, nodes, totnode, PV_CO | PV_NO | PV_MASK); +#endif + for (iteration = 0; iteration <= count; iteration++) { + const float strength = (iteration != count) ? 1.0f : last; + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .smooth_mask = false, + .strength = strength, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range( + 0, totnode, &data, do_smooth_vcol_boundary_brush_task_cb_ex, &settings); + +#ifdef PROXY_ADVANCED + BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode); +#endif + } +} diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 3c0a591e8a7..8d9497f6bef 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -159,7 +159,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata, PBVHNode *node = data->nodes[i]; SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS); PBVHVertexIter vd; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 501a1e53276..df4b43d21a2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -113,9 +113,11 @@ typedef struct UndoSculpt { ListBase nodes; size_t undo_size; + BMLog *bm_restore; } UndoSculpt; static UndoSculpt *sculpt_undo_get_nodes(void); +void sculpt_undo_print_nodes(void *active); static void update_cb(PBVHNode *node, void *rebuild) { @@ -133,6 +135,8 @@ struct PartialUpdateData { char *modified_grids; }; +static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p); + /** * A version of #update_cb that tests for 'ME_VERT_PBVH_UPDATE' */ @@ -170,6 +174,8 @@ static bool test_swap_v3_v3(float a[3], float b[3]) return false; } +void pbvh_bmesh_check_nodes(PBVH *pbvh); + static bool sculpt_undo_restore_deformed( const SculptSession *ss, SculptUndoNode *unode, int uindex, int oindex, float coord[3]) { @@ -187,7 +193,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; MVert *mvert; - int *index; + SculptVertRef *index; if (unode->maxvert) { /* Regular mesh restore. */ @@ -221,18 +227,18 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt if (unode->orig_co) { if (ss->deform_modifiers_active) { for (int i = 0; i < unode->totvert; i++) { - sculpt_undo_restore_deformed(ss, unode, i, index[i], vertCos[index[i]]); + sculpt_undo_restore_deformed(ss, unode, i, index[i].i, vertCos[index[i].i]); } } else { for (int i = 0; i < unode->totvert; i++) { - swap_v3_v3(vertCos[index[i]], unode->orig_co[i]); + swap_v3_v3(vertCos[index[i].i], unode->orig_co[i]); } } } else { for (int i = 0; i < unode->totvert; i++) { - swap_v3_v3(vertCos[index[i]], unode->co[i]); + swap_v3_v3(vertCos[index[i].i], unode->co[i]); } } @@ -249,21 +255,21 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt if (unode->orig_co) { if (ss->deform_modifiers_active) { for (int i = 0; i < unode->totvert; i++) { - sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co); - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + sculpt_undo_restore_deformed(ss, unode, i, index[i].i, mvert[index[i].i].co); + mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE; } } else { for (int i = 0; i < unode->totvert; i++) { - swap_v3_v3(mvert[index[i]].co, unode->orig_co[i]); - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + swap_v3_v3(mvert[index[i].i].co, unode->orig_co[i]); + mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE; } } } else { for (int i = 0; i < unode->totvert; i++) { - swap_v3_v3(mvert[index[i]].co, unode->co[i]); - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + swap_v3_v3(mvert[index[i].i].co, unode->co[i]); + mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE; } } } @@ -303,7 +309,7 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode) MVert *mvert = ss->mvert; for (int i = 0; i < unode->totvert; i++) { - MVert *v = &mvert[unode->index[i]]; + MVert *v = &mvert[unode->index[i].i]; if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) { BLI_BITMAP_FLIP(unode->vert_hidden, i); v->flag ^= ME_HIDE; @@ -330,13 +336,13 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode) if (unode->maxvert) { /* regular mesh restore */ - int *index = unode->index; + SculptVertRef *index = unode->index; MVert *mvert = ss->mvert; MPropCol *vcol = ss->vcol; for (int i = 0; i < unode->totvert; i++) { - copy_v4_v4(vcol[index[i]].color, unode->col[i]); - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + swap_v4_v4(vcol[index[i].i].color, unode->col[i]); + mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE; } } return true; @@ -350,7 +356,7 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) SubdivCCG *subdiv_ccg = ss->subdiv_ccg; MVert *mvert; float *vmask; - int *index; + SculptVertRef *index; if (unode->maxvert) { /* Regular mesh restore. */ @@ -360,9 +366,9 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) vmask = ss->vmask; for (int i = 0; i < unode->totvert; i++) { - if (vmask[index[i]] != unode->mask[i]) { - SWAP(float, vmask[index[i]], unode->mask[i]); - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + if (vmask[index[i].i] != unode->mask[i]) { + SWAP(float, vmask[index[i].i], unode->mask[i]); + mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE; } } } @@ -397,7 +403,7 @@ static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode) Mesh *me = BKE_object_get_original_mesh(ob); int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); for (int i = 0; i < me->totpoly; i++) { - face_sets[i] = unode->face_sets[i]; + SWAP(int, face_sets[i], unode->face_sets[i]); } return false; } @@ -410,18 +416,152 @@ static void sculpt_undo_bmesh_restore_generic_task_cb( BKE_pbvh_node_mark_redraw(nodes[n]); } +extern const char dyntopop_node_idx_layer_id[]; + +typedef struct BmeshUndoData { + PBVH *pbvh; + BMesh *bm; + bool do_full_recalc; + bool balance_pbvh; + int cd_face_node_offset, cd_vert_node_offset; +} BmeshUndoData; + +static void bmesh_undo_on_vert_kill(BMVert *v, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + // data->do_full_recalc = true; + + if (BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset) < 0) { + // something went wrong + printf("pbvh bmesh undo error\n"); + data->do_full_recalc = true; + return; + } + else { + int ni = BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset); + + /* + regenerate bm_unique_verts, which can end up with + freed verts for some reason. I've run this through + ASAN and fixed one likely cause, but it still happens. + - joeedh + */ + if (ni >= 0) { + PBVHNode *node = BKE_pbvh_get_node(data->pbvh, ni); + BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node); + } + } + + BKE_pbvh_bmesh_remove_vertex(data->pbvh, v, false); + data->balance_pbvh = true; +} +static void bmesh_undo_on_vert_add(BMVert *v, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); + // data->do_full_recalc = true; + data->balance_pbvh = true; +} +static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + int ni = BM_ELEM_CD_GET_INT(f, data->cd_face_node_offset); + + /* + regenerate bm_unique_verts, which can end up with + freed verts for some reason. I've run this through + ASAN and fixed one likely cause, but it still happens. + - joeedh + */ + if (ni >= 0) { + PBVHNode *node = BKE_pbvh_get_node(data->pbvh, ni); + BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node); + } + + BKE_pbvh_bmesh_remove_face(data->pbvh, f, false); + + // data->do_full_recalc = true; + data->balance_pbvh = true; +} +static void bmesh_undo_on_face_add(BMFace *f, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + // data->do_full_recalc = true; + + BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, -1); + BKE_pbvh_bmesh_add_face(data->pbvh, f, false, true); + + data->balance_pbvh = true; +} +static void bmesh_undo_full_mesh(void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + data->do_full_recalc = true; +} + +static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_customdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + if (!old_customdata) { + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); + return; + } + + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); + return; + // preserve pbvh node references + + BMVert h; + h.head.data = old_customdata; + + int oldnode_i = BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset); + + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, oldnode_i); + + if (oldnode_i >= 0) { + PBVHNode *node = BKE_pbvh_node_from_index(data->pbvh, oldnode_i); + BKE_pbvh_node_mark_update(node); + } +} + +static void bmesh_undo_on_face_change(BMFace *f, void *userdata, void *old_customdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + // vert will be added back to pbvh when its owning faces are + BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, -1); +} + static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, SculptSession *ss) { + BmeshUndoData data = { + ss->pbvh, ss->bm, false, false, ss->cd_face_node_offset, ss->cd_vert_node_offset}; + + BMLogCallbacks callbacks = {bmesh_undo_on_vert_add, + bmesh_undo_on_vert_kill, + bmesh_undo_on_vert_change, + bmesh_undo_on_face_add, + bmesh_undo_on_face_kill, + bmesh_undo_on_face_change, + bmesh_undo_full_mesh, + (void *)&data}; + if (unode->applied) { - BM_log_undo(ss->bm, ss->bm_log); + BM_log_undo(ss->bm, ss->bm_log, &callbacks, dyntopop_node_idx_layer_id); unode->applied = false; } else { - BM_log_redo(ss->bm, ss->bm_log); + BM_log_redo(ss->bm, ss->bm_log, &callbacks, dyntopop_node_idx_layer_id); unode->applied = true; } - if (unode->type == SCULPT_UNDO_MASK) { + BKE_pbvh_bmesh_regen_node_verts(ss->pbvh); + pbvh_bmesh_check_nodes(ss->pbvh); + + if (!data.do_full_recalc || unode->type == SCULPT_UNDO_MASK || + unode->type == SCULPT_UNDO_COLOR) { int totnode; PBVHNode **nodes; @@ -435,6 +575,12 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, if (nodes) { MEM_freeN(nodes); } + + SCULPT_dyntopo_node_layers_update_offsets(ss); + + if (data.balance_pbvh) { + BKE_pbvh_bmesh_after_stroke(ss->pbvh); + } } else { SCULPT_pbvh_clear(ob); @@ -448,18 +594,27 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) Mesh *me = ob->data; SCULPT_pbvh_clear(ob); + ss->active_face_index.i = ss->active_vertex_index.i = 0; /* Create empty BMesh and enable logging. */ ss->bm = BM_mesh_create(&bm_mesh_allocsize_default, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + &((struct BMeshCreateParams){.use_toolflags = false, + .use_unique_ids = true, + .use_id_elem_mask = BM_VERT | BM_FACE, + .use_id_map = true})); SCULPT_dyntopo_node_layers_add(ss); + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - /* Restore the BMLog using saved entries. */ - ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry); + ss->bm_log = BM_log_unfreeze(ss->bm, unode->bm_entry); + + if (!ss->bm_log) { + /* Restore the BMLog using saved entries. */ + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry); + } + + SCULPT_dyntopo_node_layers_update_offsets(ss); + BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert); } static void sculpt_undo_bmesh_restore_begin(bContext *C, @@ -475,7 +630,7 @@ static void sculpt_undo_bmesh_restore_begin(bContext *C, sculpt_undo_bmesh_enable(ob, unode); /* Restore the mesh from the first log entry. */ - BM_log_redo(ss->bm, ss->bm_log); + BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); unode->applied = true; } @@ -486,11 +641,12 @@ static void sculpt_undo_bmesh_restore_end(bContext *C, Object *ob, SculptSession *ss) { + if (unode->applied) { sculpt_undo_bmesh_enable(ob, unode); /* Restore the mesh from the last log entry. */ - BM_log_undo(ss->bm, ss->bm_log); + BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); unode->applied = false; } @@ -499,6 +655,10 @@ static void sculpt_undo_bmesh_restore_end(bContext *C, SCULPT_dynamic_topology_disable(C, NULL); unode->applied = true; } + + if (ss->bm) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + } } static void sculpt_undo_geometry_store_data(SculptUndoNodeGeometry *geometry, Object *object) @@ -590,23 +750,91 @@ static int sculpt_undo_bmesh_restore(bContext *C, Object *ob, SculptSession *ss) { + if (ss->bm_log && ss->bm && + !ELEM(unode->type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) { + SCULPT_dyntopo_node_layers_update_offsets(ss); + BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert); + + if (ss->active_face_index.i && ss->active_face_index.i != -1LL) { + ss->active_face_index.i = (intptr_t)BM_log_face_id_get(ss->bm_log, + (BMFace *)ss->active_face_index.i); + } + else { + ss->active_face_index.i = -1; + } + + if (ss->active_vertex_index.i && ss->active_vertex_index.i != -1LL) { + ss->active_vertex_index.i = (intptr_t)BM_log_vert_id_get( + ss->bm_log, (BMVert *)ss->active_vertex_index.i); + } + else { + ss->active_vertex_index.i = -1; + } + } + else { + ss->active_face_index.i = ss->active_vertex_index.i = -1; + } + + bool ret = false; + switch (unode->type) { case SCULPT_UNDO_DYNTOPO_BEGIN: sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); - return true; - + SCULPT_vertex_random_access_ensure(ss); + ss->active_face_index.i = ss->active_vertex_index.i = 0; + ret = true; + break; case SCULPT_UNDO_DYNTOPO_END: - sculpt_undo_bmesh_restore_end(C, unode, ob, ss); - return true; + ss->active_face_index.i = ss->active_vertex_index.i = 0; + + if (ss->bm) { + sculpt_undo_bmesh_restore_end(C, unode, ob, ss); + } + SCULPT_vertex_random_access_ensure(ss); + ret = true; + break; default: if (ss->bm_log) { sculpt_undo_bmesh_restore_generic(unode, ob, ss); - return true; + SCULPT_vertex_random_access_ensure(ss); + ret = true; } break; } - return false; + if (ss->bm_log && ss->bm) { + if (ss->active_face_index.i != -1) { + BMFace *f = BM_log_id_face_get(ss->bm_log, (uint)ss->active_face_index.i); + if (f && f->head.htype == BM_FACE) { + ss->active_face_index.i = (intptr_t)f; + } + else { + ss->active_face_index.i = 0LL; + } + } + else { + ss->active_face_index.i = 0LL; + } + + if (ss->active_vertex_index.i != -1) { + BMVert *v = BM_log_id_vert_get(ss->bm_log, (uint)ss->active_vertex_index.i); + + if (v && v->head.htype == BM_VERT) { + ss->active_vertex_index.i = (intptr_t)v; + } + else { + ss->active_vertex_index.i = 0LL; + } + } + else { + ss->active_vertex_index.i = 0LL; + } + } + else { + ss->active_face_index.i = ss->active_vertex_index.i = 0; + } + + return ret; } /* Geometry updates (such as Apply Base, for example) will re-evaluate the object and refine its @@ -647,8 +875,36 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase bool update = false, rebuild = false, update_mask = false, update_visibility = false; bool need_mask = false; bool need_refine_subdiv = false; + bool did_first_hack = false; for (unode = lb->first; unode; unode = unode->next) { + if (unode->bm_entry && !ss->bm) { + // file loading breaks undo because the stack isn't initialized + // detect that case and try to fix it + + did_first_hack = true; + + ss->active_face_index.i = ss->active_vertex_index.i = 0; + SCULPT_dynamic_topology_enable_ex(CTX_data_main(C), depsgraph, scene, ob); + + // see if we have a saved log in the entry + BMLog *log = BM_log_unfreeze(ss->bm, unode->bm_entry); + + if (log) { + if (ss->bm_log) { + BM_log_free(ss->bm_log, false); + } + + ss->bm_log = log; + + SCULPT_dyntopo_node_layers_update_offsets(ss); + BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert); + } + + // PBVH is corrupted at this point, destroy it + SCULPT_pbvh_clear(ob); + } + /* Restore pivot. */ copy_v3_v3(ss->pivot_pos, unode->pivot_pos); copy_v3_v3(ss->pivot_rot, unode->pivot_rot); @@ -664,7 +920,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - if (lb->first) { + sculpt_undo_print_nodes(NULL); + + if (!ss->bm && lb->first) { unode = lb->first; if (unode->type == SCULPT_UNDO_FACE_SETS) { sculpt_undo_restore_face_sets(C, unode); @@ -719,6 +977,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * continue. */ if (unode->maxvert) { if (ss->totvert != unode->maxvert) { + printf("error! %s\n", __func__); continue; } } @@ -859,6 +1118,9 @@ static void sculpt_undo_free_list(ListBase *lb) if (unode->co) { MEM_freeN(unode->co); } + if (unode->nodemap) { + MEM_freeN(unode->nodemap); + } if (unode->no) { MEM_freeN(unode->no); } @@ -888,6 +1150,7 @@ static void sculpt_undo_free_list(ListBase *lb) if (unode->bm_entry) { BM_log_entry_drop(unode->bm_entry); + unode->bm_entry = NULL; } sculpt_undo_geometry_free_data(&unode->geometry_original); @@ -927,7 +1190,28 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) } #endif -SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node) +static int hash_sculpt_colors(SculptUndoNode *node) +{ + if (!node->col) { + return -1; + } + + int i = 0; + int hash = 0; + + for (i = 0; i < node->totvert; i++) { + float *col = node->col[i]; + + for (int j = 0; j < 4; j++) { + hash = hash ^ (int)(col[j] * 2048.0f * 2048.0f); + hash += (1 << 23) - 1; + } + } + + return hash; +} + +SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); @@ -935,7 +1219,19 @@ SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node) return NULL; } - return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node)); + if (type < 0) { + return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node)); + } + + SculptUndoNode *unode; + + for (unode = usculpt->nodes.first; unode; unode = unode->next) { + if (unode->node == node && type == unode->type) { + return unode; + } + } + + return NULL; } SculptUndoNode *SCULPT_undo_get_first_node() @@ -1104,6 +1400,9 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; + SculptOrigVertData orig_data; + SCULPT_orig_vert_data_unode_init(&orig_data, ob, unode); + BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) { copy_v3_v3(unode->co[vd.i], vd.co); if (vd.no) { @@ -1114,7 +1413,11 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) } if (ss->deform_modifiers_active) { - copy_v3_v3(unode->orig_co[vd.i], ss->orig_cos[unode->index[vd.i]]); + SCULPT_orig_vert_data_update(&orig_data, &vd); + + int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, unode->index[vd.i]); + + copy_v3_v3(unode->orig_co[vd.i], orig_data.co); } } BKE_pbvh_vertex_iter_end; @@ -1157,6 +1460,8 @@ static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; + // unode->gen++; + BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) { copy_v4_v4(unode->col[vd.i], vd.col); } @@ -1217,7 +1522,10 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt SculptUndoNode *unode = usculpt->nodes.first; + bool new_node = false; + if (unode == NULL) { + new_node = true; unode = MEM_callocN(sizeof(*unode), __func__); BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); @@ -1225,8 +1533,10 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt unode->applied = true; if (type == SCULPT_UNDO_DYNTOPO_END) { - unode->bm_entry = BM_log_entry_add(ss->bm_log); - BM_log_before_all_removed(ss->bm, ss->bm_log); + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); + BM_log_full_mesh(ss->bm, ss->bm_log); + + // BM_log_before_all_removed(ss->bm, ss->bm_log); } else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { /* Store a copy of the mesh's current vertices, loops, and @@ -1237,56 +1547,140 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; sculpt_undo_geometry_store_data(geometry, ob); - unode->bm_entry = BM_log_entry_add(ss->bm_log); - BM_log_all_added(ss->bm, ss->bm_log); + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); + // BM_log_all_added(ss->bm, ss->bm_log); + BM_log_full_mesh(ss->bm, ss->bm_log); } else { - unode->bm_entry = BM_log_entry_add(ss->bm_log); + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); } BLI_addtail(&usculpt->nodes, unode); } if (node) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + unode->bm_entry = BM_log_entry_check_customdata(ss->bm, ss->bm_log); + } + switch (type) { case SCULPT_UNDO_COORDS: case SCULPT_UNDO_MASK: - /* Before any vertex values get modified, ensure their - * original positions are logged. */ - BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { - BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + float *dummy; + BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, false); } BKE_pbvh_vertex_iter_end; break; case SCULPT_UNDO_HIDDEN: { - GSetIterator gs_iter; - GSet *faces = BKE_pbvh_bmesh_node_faces(node); - BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { - BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, true); } BKE_pbvh_vertex_iter_end; - GSET_ITER (gs_iter, faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + TGSET_ITER (f, faces) { BM_log_face_modified(ss->bm_log, f); } + TGSET_ITER_END + break; + } + + case SCULPT_UNDO_COLOR: { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + float *dummy; + BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, true); + } + BKE_pbvh_vertex_iter_end; break; } + case SCULPT_UNDO_FACE_SETS: { + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + TGSET_ITER (f, faces) { + BM_log_face_modified(ss->bm_log, f); + } + TGSET_ITER_END + + break; + } case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: case SCULPT_UNDO_GEOMETRY: - case SCULPT_UNDO_FACE_SETS: - case SCULPT_UNDO_COLOR: break; } } + else { + switch (type) { + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + case SCULPT_UNDO_GEOMETRY: + BM_log_full_mesh(ss->bm, ss->bm_log); + break; + } + } + + if (new_node) { + sculpt_undo_print_nodes(NULL); + } return unode; } +bool SCULPT_ensure_dyntopo_node_undo(Object *ob, + PBVHNode *node, + SculptUndoType type, + int extraType) +{ + SculptSession *ss = ob->sculpt; + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + SculptUndoNode *unode = usculpt->nodes.first; + + if (!unode || unode->type != type) { + unode = sculpt_undo_alloc_node_type(ob, type); + + BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); + + unode->type = type; + unode->applied = true; + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); + + return SCULPT_ensure_dyntopo_node_undo(ob, node, type, extraType); + } + + int n = BKE_pbvh_get_node_id(ss->pbvh, node); + + if (unode->nodemap_size <= n) { + int newsize = (n + 1) * 2; + + if (!unode->nodemap) { + unode->nodemap = MEM_callocN(sizeof(*unode->nodemap) * newsize, "unode->nodemap"); + } + else { + unode->nodemap = MEM_recallocN(unode->nodemap, sizeof(*unode->nodemap) * newsize); + } + + unode->nodemap_size = newsize; + } + + if (unode->nodemap[n]) { + return false; + } + + unode->nodemap[n] = 1; + sculpt_undo_bmesh_push(ob, node, type); + + if (extraType >= 0) { + sculpt_undo_bmesh_push(ob, node, extraType); + } + + return true; +} + SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) { SculptSession *ss = ob->sculpt; @@ -1301,20 +1695,24 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType /* Dynamic topology stores only one undo node per stroke, * regardless of the number of PBVH nodes modified. */ unode = sculpt_undo_bmesh_push(ob, node, type); + sculpt_undo_print_nodes(NULL); BLI_thread_unlock(LOCK_CUSTOM1); return unode; } if (type == SCULPT_UNDO_GEOMETRY) { unode = sculpt_undo_geometry_push(ob, type); + sculpt_undo_print_nodes(NULL); BLI_thread_unlock(LOCK_CUSTOM1); return unode; } if (type == SCULPT_UNDO_FACE_SETS) { unode = sculpt_undo_face_sets_push(ob, type); + sculpt_undo_print_nodes(NULL); BLI_thread_unlock(LOCK_CUSTOM1); return unode; } - if ((unode = SCULPT_undo_get_node(node))) { + if ((unode = SCULPT_undo_get_node(node, type))) { + sculpt_undo_print_nodes(NULL); BLI_thread_unlock(LOCK_CUSTOM1); return unode; } @@ -1336,7 +1734,11 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType int allvert; BKE_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert); BKE_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL); - memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert); + + for (int i = 0; i < unode->totvert; i++) { + unode->index[i].i = vert_indices[i]; + } + // memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert); } switch (type) { @@ -1373,6 +1775,8 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType unode->shapeName[0] = '\0'; } + sculpt_undo_print_nodes(NULL); + BLI_thread_unlock(LOCK_CUSTOM1); return unode; @@ -1434,6 +1838,7 @@ typedef struct SculptUndoStep { UndoStep step; /* NOTE: will split out into list for multi-object-sculpt-mode. */ UndoSculpt data; + int id; } SculptUndoStep; static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) @@ -1472,6 +1877,8 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, BLI_assert(us->step.is_applied == true); sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = false; + + sculpt_undo_print_nodes(us); } static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, @@ -1481,6 +1888,8 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, BLI_assert(us->step.is_applied == false); sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = true; + + sculpt_undo_print_nodes(us); } static void sculpt_undosys_step_decode_undo(struct bContext *C, @@ -1632,6 +2041,10 @@ static UndoSculpt *sculpt_undo_get_nodes(void) return sculpt_undosys_step_get_nodes(us); } +void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss) +{ +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1726,3 +2139,106 @@ void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str) } /** \} */ + +#ifdef _ +# undef _ +#endif +#define _(type) \ + case type: \ + return #type; +static char *undo_type_to_str(int type) +{ + switch (type) { + _(SCULPT_UNDO_DYNTOPO_BEGIN) + _(SCULPT_UNDO_DYNTOPO_END) + _(SCULPT_UNDO_COORDS) + _(SCULPT_UNDO_GEOMETRY) + _(SCULPT_UNDO_DYNTOPO_SYMMETRIZE) + _(SCULPT_UNDO_FACE_SETS) + _(SCULPT_UNDO_HIDDEN) + _(SCULPT_UNDO_MASK) + _(SCULPT_UNDO_COLOR) + default: + return "unknown node type"; + } +} +#undef _ + +static int nodeidgen = 1; + +static void print_sculpt_node(SculptUndoNode *node) +{ + int hash = hash_sculpt_colors(node); + + // if (node->lasthash == 0) { + // node->lasthash = hash; + // } + + printf(" %s:%s {applied=%d gen=%d hash=%d}\n", + undo_type_to_str(node->type), + node->idname, + node->applied, + 0, // node->gen, + hash /*- node->lasthash*/); +} + +static void print_sculpt_undo_step(UndoStep *us, UndoStep *active, int i) +{ + SculptUndoNode *node; + + if (us->type != BKE_UNDOSYS_TYPE_SCULPT) { + return; + } + + int id = -1; + + SculptUndoStep *su = (SculptUndoStep *)us; + if (!su->id) { + su->id = nodeidgen++; + } + + id = su->id; + + printf("id=%d %s %d %s\n", id, us == active ? "->" : " ", i, us->name); + + if (us->type == BKE_UNDOSYS_TYPE_SCULPT) { + UndoSculpt *usculpt = sculpt_undosys_step_get_nodes(us); + + for (node = usculpt->nodes.first; node; node = node->next) { + print_sculpt_node(node); + } + } +} +void sculpt_undo_print_nodes(void *active) +{ +#if 0 + UndoStack *ustack = ED_undo_stack_get(); + UndoStep *us = ustack->steps.first; + if (active == NULL) { + active = ustack->step_active; + } + + SculptUndoNode *node; + + if (!us) { + return; + } + + printf("\n"); + if (ustack->step_init) { + printf("===undo init===\n"); + print_sculpt_undo_step(ustack->step_init, active, -1); + printf("===============\n"); + } + + int i = 0; + for (; us; us = us->next, i++) { + print_sculpt_undo_step(us, active, i); + } + + if (ustack->step_active) { + print_sculpt_undo_step(ustack->step_active, active, i); + } + +#endif +} diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index d7671a372c6..4f11765b10b 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -382,8 +382,10 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats) stats->totfacesculpt = ss->totfaces; break; case PBVH_BMESH: - stats->totvertsculpt = ob->sculpt->bm->totvert; - stats->tottri = ob->sculpt->bm->totface; + if (ob->sculpt->bm) { + stats->totvertsculpt = ob->sculpt->bm->totvert; + stats->tottri = ob->sculpt->bm->totface; + } break; case PBVH_GRIDS: stats->totvertsculpt = BKE_pbvh_get_grid_num_vertices(ss->pbvh); @@ -443,15 +445,7 @@ static void stats_update(Depsgraph *depsgraph, FOREACH_OBJECT_END; } else if (ob && (ob->mode & OB_MODE_SCULPT)) { - /* Sculpt Mode. */ - if (stats_is_object_dynamic_topology_sculpt(ob)) { - /* Dynamic topology. Do not count all vertices, - * dynamic topology stats are initialized later as part of sculpt stats. */ - } - else { - /* When dynamic topology is not enabled both sculpt stats and scene stats are collected. */ - stats_object_sculpt(ob, stats); - } + stats_object_sculpt(ob, stats); } else { /* Objects. */ diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 3d5dabda23d..84be69b7c4f 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -2897,7 +2897,8 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) ED_mesh_uv_texture_ensure(me, NULL); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, me, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -2908,7 +2909,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) /* Set the margin really quickly before the packing operation. */ scene->toolsettings->uvcalc_margin = 0.001f; uvedit_pack_islands(scene, ob, bm); - BM_mesh_bm_to_me(bmain, bm, me, (&(struct BMeshToMeshParams){0})); + BM_mesh_bm_to_me(bmain, NULL, bm, me, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm); if (sync_selection) { |