diff options
Diffstat (limited to 'source/blender/editors')
55 files changed, 13426 insertions, 1698 deletions
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 702fd2e375a..5f25114eaf3 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -750,7 +750,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 @@ -763,15 +762,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/ED_object.h b/source/blender/editors/include/ED_object.h index 5397cd95ace..65e13b29015 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -266,7 +266,8 @@ void ED_object_sculptmode_enter_ex(struct Main *bmain, struct Scene *scene, struct Object *ob, const bool force_dyntopo, - struct ReportList *reports); + struct ReportList *reports, + bool do_undo); void ED_object_sculptmode_enter(struct bContext *C, struct Depsgraph *depsgraph, struct ReportList *reports); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index ddd9ca4a98c..0ae3e61293c 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/interface/interface.c b/source/blender/editors/interface/interface.c index fd75be5b847..ebfc3f307cb 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -88,8 +88,27 @@ #include "DEG_depsgraph_query.h" +#include "BLI_asan.h" #include "interface_intern.h" +void poison_ui_but(struct uiBut *but) +{ + BLI_asan_poison(but->poison1, sizeof(but->poison1)); + BLI_asan_poison(but->poison2, sizeof(but->poison2)); + BLI_asan_poison(but->poison3, sizeof(but->poison3)); + BLI_asan_poison(but->poison4, sizeof(but->poison4)); + BLI_asan_poison(but->poison5, sizeof(but->poison5)); +} + +void unpoison_ui_but(struct uiBut *but) +{ + BLI_asan_unpoison(but->poison1, sizeof(but->poison1)); + BLI_asan_unpoison(but->poison2, sizeof(but->poison2)); + BLI_asan_unpoison(but->poison3, sizeof(but->poison3)); + BLI_asan_unpoison(but->poison4, sizeof(but->poison4)); + BLI_asan_unpoison(but->poison5, sizeof(but->poison5)); +} + /* prototypes. */ static void ui_but_to_pixelrect(struct rcti *rect, const struct ARegion *region, @@ -3370,9 +3389,16 @@ static void ui_but_free_type_specific(uiBut *but) } } +#include "BLI_compiler_attrs.h" +#include "BLI_threads.h" + /* can be called with C==NULL */ static void ui_but_free(const bContext *C, uiBut *but) { + if (!BLI_thread_is_main()) { + printf("Evil!\n"); + } + if (but->opptr) { WM_operator_properties_free(but->opptr); MEM_freeN(but->opptr); @@ -3420,6 +3446,7 @@ static void ui_but_free(const bContext *C, uiBut *but) BLI_assert(UI_butstore_is_registered(but->block, but) == false); + unpoison_ui_but(but); MEM_freeN(but); } @@ -3966,7 +3993,11 @@ static uiBut *ui_but_alloc(const eButType type) const char *alloc_str; ui_but_alloc_info(type, &alloc_size, &alloc_str, NULL); - return MEM_callocN(alloc_size, alloc_str); + uiBut *ret = MEM_callocN(alloc_size, alloc_str); + + poison_ui_but(ret); + + return ret; } /** @@ -4000,7 +4031,10 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type) const bool has_str_ptr_to_self = but->str == but->strdata; const bool has_poin_ptr_to_self = but->poin == (char *)but; + unpoison_ui_but(but); but = MEM_recallocN_id(but, alloc_size, alloc_str); + poison_ui_but(but); + but->type = new_type; if (has_str_ptr_to_self) { but->str = but->strdata; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 77ae16d7cc7..d4eff9f1151 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -8566,7 +8566,11 @@ static void button_activate_exit( #ifdef USE_ALLSELECT { /* only RNA from this button is used */ + + unpoison_ui_but(but); uiBut but_temp = *but; + poison_ui_but(but); + uiSelectContextStore *selctx_data = &data->select_others; for (int i = 0; i < selctx_data->elems_len; i++) { uiSelectContextElem *other = &selctx_data->elems[i]; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index d61104f094e..601227b75a3 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -239,6 +239,7 @@ struct uiBut { short modifier_key; short iconadd; + char poison1[512]; /** #UI_BTYPE_BLOCK data */ uiBlockCreateFunc block_create_func; @@ -253,6 +254,7 @@ struct uiBut { int rnaindex; /* Operator data */ + char poison2[512]; struct wmOperatorType *optype; struct PointerRNA *opptr; short opcontext; @@ -262,6 +264,8 @@ struct uiBut { ListBase extra_op_icons; /** #uiButExtraOpIcon */ + char poison3[512]; + /* Drag-able data, type is WM_DRAG_... */ char dragtype; short dragflag; @@ -269,6 +273,8 @@ struct uiBut { struct ImBuf *imb; float imb_scale; + char poison4[512]; + /** Active button data (set when the user is hovering or interacting with a button). */ struct uiHandleButtonData *active; @@ -279,6 +285,8 @@ struct uiBut { double *editval; float *editvec; + char poison5[512]; + uiButPushedStateFunc pushed_state_func; const void *pushed_state_arg; @@ -1273,6 +1281,8 @@ bool ui_jump_to_target_button_poll(struct bContext *C); /* interface_queries.c */ void ui_interface_tag_script_reload_queries(void); +void poison_ui_but(struct uiBut *but); +void unpoison_ui_but(struct uiBut *but); #ifdef __cplusplus } 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_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index 303cf41df0d..e9594e62add 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -221,7 +221,7 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; if (BM_vert_is_edge_pair(v_act) && !BM_vert_is_wire(v_act)) { - BM_edge_collapse(bm, v_act->e, v_act, true, true); + BM_edge_collapse(bm, v_act->e, v_act, true, true, false); changed = true; } else { @@ -570,7 +570,7 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C, else if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; if (BM_vert_is_edge_pair(v_act)) { - BM_edge_collapse(bm, v_act->e, v_act, true, true); + BM_edge_collapse(bm, v_act->e, v_act, true, true, false); } else { /* too involved to do inline */ diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 122214b87d5..e90f38be727 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4396,7 +4396,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; @@ -4462,7 +4463,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; @@ -4645,6 +4647,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){ @@ -4738,7 +4741,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: @@ -4754,6 +4757,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) if (changed) { BM_mesh_bm_to_me(bmain, + ob, bm_old, me, (&(struct BMeshToMeshParams){ @@ -5943,6 +5947,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"); @@ -7019,7 +7133,7 @@ static void sort_bmelem_flag(bContext *C, } } - BM_mesh_remap(em->bm, map[0], map[1], map[2]); + BM_mesh_remap(em->bm, map[0], map[1], map[2], NULL); DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index f52cd94b8dc..171d8862861 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 c6a8e771362..1bfe4ef254d 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 03c99e40d1e..cd166a96e00 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -262,6 +262,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); @@ -288,3 +289,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 94823b92c44..6df29316c07 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 b9942bc563a..26589019b0b 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; @@ -1923,8 +1936,8 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)(RNA_enum_get( - op->ptr, "mode")); + const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)( + RNA_enum_get(op->ptr, "mode")); multiresModifier_subdivide(object, mmd, subdivide_mode); ED_object_iter_other( @@ -2594,7 +2607,8 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, MVertSkin *mvert_skin = CustomData_get_layer(&me->vdata, CD_MVERT_SKIN); int *emap_mem; MeshElemMap *emap; - BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge); + BKE_mesh_vert_edge_map_create( + &emap, &emap_mem, me->mvert, me->medge, me->totvert, me->totedge, false); BLI_bitmap *edges_visited = BLI_BITMAP_NEW(me->totedge, "edge_visited"); diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index d56cb3c7548..577cb63587d 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -763,7 +763,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 = {{nullptr}}; mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; @@ -784,8 +784,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(nullptr, mesh_bisect_temp); } @@ -853,7 +855,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(bisect_mesh, qj->target_faces, diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index f0ab082cd9c..becb815f8b1 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -1963,7 +1963,8 @@ static void vgroup_smooth_subset(Object *ob, emap_mem = NULL; } else { - BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge); + BKE_mesh_vert_edge_map_create( + &emap, &emap_mem, me->mvert, me->medge, me->totvert, me->totedge, false); } weight_accum_prev = MEM_mallocN(sizeof(*weight_accum_prev) * dvert_tot, __func__); diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 3b668a1bd4c..77a633f0932 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 @@ -76,6 +77,14 @@ set(SRC sculpt_transform.c sculpt_undo.c sculpt_uv.c + sculpt_displacement.c + sculpt_displacement.h + sculpt_replay.c + + sculpt.cc + sculpt.hh + + sculpt_brush_machine.c paint_intern.h sculpt_intern.h 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 a58b1947b0c..75834e019f5 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1081,10 +1081,11 @@ 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) { return false; @@ -1092,9 +1093,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 +1103,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 e65d6ce2d48..09f4d7a5ffb 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,32 @@ 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); - BKE_mesh_normals_tag_dirty(result); - 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, + sgcontext->ss->bm_smooth_shading, + sgcontext->ss->bm_log, + sgcontext->ss->cd_vert_node_offset, + sgcontext->ss->cd_face_node_offset, + sgcontext->ss->cd_dyn_vert, + sgcontext->ss->cd_face_areas, + sgcontext->ss->fast_draw); + } + 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_normals_tag_dirty(result); + 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 +1367,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 +1580,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/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 9387b84f437..33d8be08c0c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -1151,18 +1151,24 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) gmap->vert_to_poly = NULL; BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop, &gmap->vert_map_mem, + me->mvert, + me->medge, me->mpoly, me->mloop, me->totvert, me->totpoly, - me->totloop); + me->totloop, + false); BKE_mesh_vert_poly_map_create(&gmap->vert_to_poly, &gmap->poly_map_mem, + me->mvert, + me->medge, me->mpoly, me->mloop, me->totvert, me->totpoly, - me->totloop); + me->totloop, + false); } /* Create average brush arrays */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 7bde864e73f..fcece692172 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -24,15 +24,21 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.h" #include "BLI_blenlib.h" #include "BLI_dial_2d.h" #include "BLI_ghash.h" #include "BLI_gsqueue.h" #include "BLI_hash.h" +#include "BLI_link_utils.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" +#include "BLI_rand.h" #include "BLI_task.h" #include "BLI_utildefines.h" +#include "atomic_ops.h" #include "BLT_translation.h" @@ -40,12 +46,14 @@ #include "DNA_brush_types.h" #include "DNA_customdata_types.h" +#include "DNA_listBase.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_attribute.h" #include "BKE_brush.h" #include "BKE_ccg.h" #include "BKE_colortools.h" @@ -101,6 +109,9 @@ #include <stdlib.h> #include <string.h> +static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, + const SculptVertRef index); + /* Sculpt PBVH abstraction API * * This is read-only, for writing use PBVH vertex iterators. There vd.index matches @@ -111,12 +122,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 +164,64 @@ int SCULPT_vertex_count_get(SculptSession *ss) return 0; } -const float *SCULPT_vertex_co_get(SculptSession *ss, int index) +MDynTopoVert *SCULPT_vertex_get_mdyntopo(SculptSession *ss, SculptVertRef vertex) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + return BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + } + + case PBVH_GRIDS: + case PBVH_FACES: { + return ss->mdyntopo_verts + vertex.i; + } + } + + return NULL; +} + +float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + return BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v)->origco; + } + + case PBVH_GRIDS: + case PBVH_FACES: { + MDynTopoVert *mv = ss->mdyntopo_verts + vertex.i; + + return ss->mdyntopo_verts[vertex.i].origco; + } + } + + return NULL; +} + +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 +229,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 +283,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 +333,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 +345,177 @@ 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; + out->from_bmesh = true; + + 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; + + out->from_bmesh = false; + + 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; + + out->from_bmesh = false; + + 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 +524,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 +597,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 +666,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 +700,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 +738,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 +847,37 @@ 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 && fset != abs(face_set)) { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + mv->flag |= DYNVERT_NEED_BOUNDARY; + 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 +887,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 +927,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 +968,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 +1030,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 +1095,110 @@ 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 SculptCornerType sculpt_check_corner_in_base_mesh(const SculptSession *ss, + SculptVertRef vertex, + bool check_facesets) { + int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex); + + MeshElemMap *vert_map = &ss->pmap[index]; + int face_set = -1; + int last1 = -1; + int last2 = -1; + + int ret = 0; + + if (sculpt_check_boundary_vertex_in_base_mesh(ss, vertex) && ss->pmap[index].count < 4) { + ret |= SCULPT_CORNER_MESH; + } + + if (check_facesets) { + for (int i = 0; i < ss->pmap[index].count; i++) { + if (check_facesets) { + if (last2 != last1) { + last2 = last1; + } + if (last1 != face_set) { + last1 = face_set; + } + face_set = abs(ss->face_sets[vert_map->indices[i]]); + + bool corner = last1 != -1 && last2 != -1 && face_set != -1; + corner = corner && last1 != last2 && last1 != face_set; + + if (corner) { + ret |= SCULPT_CORNER_FACE_SET; + } + } + } + } + + return ret; +} + +static bool sculpt_check_unique_face_set_in_base_mesh(const 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++) { @@ -638,7 +1218,9 @@ static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int ind * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2 * in the base mesh are equal. */ -static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2) +static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(const SculptSession *ss, + int v1, + int v2) { MeshElemMap *vert_map = &ss->pmap[v1]; int p1 = -1, p2 = -1; @@ -666,18 +1248,50 @@ 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(const 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; + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + if (mv->flag & DYNVERT_NEED_BOUNDARY) { + BKE_pbvh_update_vert_boundary( + ss->cd_dyn_vert, ss->cd_faceset_offset, v, ss->boundary_symmetry); + } + + return !(mv->flag & DYNVERT_FSET_BOUNDARY); + +#if 0 + 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; +#endif + } 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 +1300,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 +1325,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 +1351,13 @@ 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, + SculptEdgeRef edge, + int neighbor_index) { for (int i = 0; i < iter->size; i++) { - if (iter->neighbors[i] == neighbor_index) { + if (iter->neighbors[i].vertex.i == neighbor.i) { return; } } @@ -733,51 +1366,123 @@ 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(struct _SculptNeighborRef), + "neighbor array"); + iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); + + memcpy( + iter->neighbors, iter->neighbors_fixed, sizeof(struct _SculptNeighborRef) * 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(struct _SculptNeighborRef), "neighbor array"); + iter->neighbor_indices = MEM_reallocN_id( + iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array"); + } + } + + iter->neighbors[iter->size].vertex = neighbor; + iter->neighbors[iter->size].edge = edge; + iter->neighbor_indices[iter->size] = neighbor_index; + iter->size++; +} + +static void sculpt_vertex_neighbor_add_nocheck(SculptVertexNeighborIter *iter, + SculptVertRef neighbor, + SculptEdgeRef edge, + 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(struct _SculptNeighborRef), + "neighbor array"); + iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); + + memcpy( + iter->neighbors, iter->neighbors_fixed, sizeof(struct _SculptNeighborRef) * 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(struct _SculptNeighborRef), "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].vertex = neighbor; + iter->neighbors[iter->size].edge = edge; + iter->neighbor_indices[iter->size] = neighbor_index; iter->size++; } -static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss, - int index, +static void sculpt_vertex_neighbors_get_bmesh(const SculptSession *ss, + SculptVertRef index, SculptVertexNeighborIter *iter) { - BMVert *v = BM_vert_at_index(ss->bm, index); - BMIter liter; - BMLoop *l; + BMVert *v = (BMVert *)index.i; + + 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; + } + + BMEdge *e2 = NULL; + + do { + BMVert *v2; + e2 = BM_DISK_EDGE_NEXT(e, v); + v2 = v == e->v1 ? e->v2 : e->v1; + + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v2); + + if (!(mv->flag & DYNVERT_VERT_FSET_HIDDEN)) { // && (e->head.hflag & BM_ELEM_DRAW)) { + sculpt_vertex_neighbor_add_nocheck(iter, + BKE_pbvh_make_vref((intptr_t)v2), + BKE_pbvh_make_eref((intptr_t)e), + BM_elem_index_get(v2)); + } + } while ((e = e2) != v->e); + + if (ss->fake_neighbors.use_fake_neighbors) { + int index = BM_elem_index_get(v); + + BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); + if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add(iter, + ss->fake_neighbors.fake_neighbor_index[index], + BKE_pbvh_make_eref(SCULPT_REF_NONE), + ss->fake_neighbors.fake_neighbor_index[index].i); } } } -static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, - int index, +static void sculpt_vertex_neighbors_get_faces(const SculptSession *ss, + 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) { @@ -788,8 +1493,21 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, uint f_adj_v[2]; 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) { + int e = 0; + if (f_adj_v[j] != index) { - sculpt_vertex_neighbor_add(iter, f_adj_v[j]); + int loopidx = p->loopstart; + + for (int k = 0; k < p->totloop; k++, loopidx++) { + const MEdge *e2 = &ss->medge[ss->mloop[loopidx].e]; + if ((e2->v1 == index && e2->v2 == f_adj_v[j]) || + (e2->v2 == index && e2->v1 == f_adj_v[j])) { + e = e2 - ss->medge; + } + } + + sculpt_vertex_neighbor_add( + iter, BKE_pbvh_make_vref(f_adj_v[j]), BKE_pbvh_make_eref(e), f_adj_v[j]); } } } @@ -797,17 +1515,61 @@ 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], + BKE_pbvh_make_eref(SCULPT_REF_NONE), + ss->fake_neighbors.fake_neighbor_index[index].i); } } } -static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, - const int index, +static void sculpt_vertex_neighbors_get_faces_vemap(const SculptSession *ss, + SculptVertRef vertex, + SculptVertexNeighborIter *iter) +{ + int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex); + + MeshElemMap *vert_map = &ss->vemap[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 < vert_map->count; i++) { + const MEdge *me = &ss->medge[vert_map->indices[i]]; + + unsigned int v = me->v1 == (unsigned int)vertex.i ? me->v2 : me->v1; + + if (ss->face_sets[v] < 0) { + /* Skip connectivity from hidden faces. */ + continue; + } + + sculpt_vertex_neighbor_add( + iter, BKE_pbvh_make_vref(v), BKE_pbvh_make_eref(vert_map->indices[i]), v); + } + + if (ss->fake_neighbors.use_fake_neighbors) { + BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); + if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add(iter, + ss->fake_neighbors.fake_neighbor_index[index], + BKE_pbvh_make_eref(SCULPT_REF_NONE), + ss->fake_neighbors.fake_neighbor_index[index].i); + } + } +} + +static void sculpt_vertex_neighbors_get_grids(const SculptSession *ss, + 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 +1584,29 @@ 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), BKE_pbvh_make_eref(SCULPT_REF_NONE), 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], + BKE_pbvh_make_eref(SCULPT_REF_NONE), + ss->fake_neighbors.fake_neighbor_index[index].i); } } @@ -845,45 +1615,286 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, } } -void SCULPT_vertex_neighbors_get(SculptSession *ss, - const int index, +void SCULPT_vertex_neighbors_get(const SculptSession *ss, + 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); + // use vemap if it exists, so result is in disk cycle order + if (ss->vemap) { + sculpt_vertex_neighbors_get_faces_vemap(ss, vertex, iter); + } + else { + 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) +SculptBoundaryType SCULPT_edge_is_boundary(const SculptSession *ss, + const SculptEdgeRef edge, + SculptBoundaryType typemask) { - BLI_assert(ss->vertex_info.boundary); - return BLI_BITMAP_TEST(ss->vertex_info.boundary, index); + + int ret = 0; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMEdge *e = (BMEdge *)edge.i; + + if (typemask & SCULPT_BOUNDARY_MESH) { + ret |= (!e->l || e->l == e->l->radial_next) ? SCULPT_BOUNDARY_MESH : 0; + } + + if ((typemask & SCULPT_BOUNDARY_FACE_SET) && e->l && e->l != e->l->radial_next) { + if (ss->boundary_symmetry) { + // TODO: calc and cache this properly + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, e->v1); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, e->v2); + + return (mv1->flag & DYNVERT_FSET_BOUNDARY) && (mv2->flag & DYNVERT_FSET_BOUNDARY); + } + else { + int fset1 = BM_ELEM_CD_GET_INT(e->l->f, ss->cd_faceset_offset); + int fset2 = BM_ELEM_CD_GET_INT(e->l->radial_next->f, ss->cd_faceset_offset); + + bool ok = (fset1 < 0) != (fset2 < 0); + + ok = ok || fset1 != fset2; + + ret |= ok ? SCULPT_BOUNDARY_FACE_SET : 0; + } + } + + if (typemask & SCULPT_BOUNDARY_SHARP) { + ret |= !BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? SCULPT_BOUNDARY_SHARP : 0; + } + + if (typemask & SCULPT_BOUNDARY_SEAM) { + ret |= BM_elem_flag_test(e, BM_ELEM_SEAM) ? SCULPT_BOUNDARY_SEAM : 0; + } + + break; + } + case PBVH_FACES: { + int mask = typemask & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET); + SculptVertRef v1, v2; + + SCULPT_edge_get_verts(ss, edge, &v1, &v2); + + if (mask) { // use less accurate approximation for now + SculptBoundaryType a = SCULPT_vertex_is_boundary(ss, v1, mask); + SculptBoundaryType b = SCULPT_vertex_is_boundary(ss, v2, mask); + + ret |= a & b; + } + + if (typemask & SCULPT_BOUNDARY_SHARP) { + ret |= ss->medge[edge.i].flag & ME_SHARP ? SCULPT_BOUNDARY_SHARP : 0; + } + + if (typemask & SCULPT_BOUNDARY_SEAM) { + ret |= ss->medge[edge.i].flag & ME_SEAM ? SCULPT_BOUNDARY_SEAM : 0; + } + + break; + } + case PBVH_GRIDS: { + // not implemented + break; + } + } + + return (SculptBoundaryType)ret; } -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index) +void SCULPT_edge_get_verts(const SculptSession *ss, + const SculptEdgeRef edge, + SculptVertRef *r_v1, + SculptVertRef *r_v2) + { switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMEdge *e = (BMEdge *)edge.i; + r_v1->i = (intptr_t)e->v1; + r_v2->i = (intptr_t)e->v2; + break; + } + case PBVH_FACES: { - if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) { - return true; + r_v1->i = (intptr_t)ss->medge[edge.i].v1; + r_v2->i = (intptr_t)ss->medge[edge.i].v2; + break; + } + case PBVH_GRIDS: + // not supported yet + r_v1->i = r_v2->i = SCULPT_REF_NONE; + break; + } +} + +SculptVertRef SCULPT_edge_other_vertex(const SculptSession *ss, + const SculptEdgeRef edge, + const SculptVertRef vertex) +{ + SculptVertRef v1, v2; + + SCULPT_edge_get_verts(ss, edge, &v1, &v2); + + return v1.i == vertex.i ? v2 : v1; +} + +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, + BKE_pbvh_vertex_index_to_table(ss->pbvh, index)); +} + +SculptCornerType SCULPT_vertex_is_corner(const SculptSession *ss, + const SculptVertRef vertex, + SculptCornerType cornertype) +{ + bool check_facesets = cornertype & SCULPT_CORNER_FACE_SET; + SculptCornerType ret = 0; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + if (mv->flag & DYNVERT_NEED_BOUNDARY) { + BKE_pbvh_update_vert_boundary( + ss->cd_dyn_vert, ss->cd_faceset_offset, (BMVert *)vertex.i, ss->boundary_symmetry); + } + + ret = 0; + + if (cornertype & SCULPT_CORNER_MESH) { + ret |= (mv->flag & DYNVERT_CORNER) ? SCULPT_CORNER_MESH : 0; + } + if (cornertype & SCULPT_CORNER_FACE_SET) { + ret |= (mv->flag & DYNVERT_FSET_CORNER) ? SCULPT_CORNER_FACE_SET : 0; + } + if (cornertype & SCULPT_CORNER_SEAM) { + ret |= (mv->flag & DYNVERT_SEAM_CORNER) ? SCULPT_CORNER_SEAM : 0; + } + if (cornertype & SCULPT_CORNER_SHARP) { + ret |= (mv->flag & DYNVERT_SHARP_CORNER) ? SCULPT_CORNER_SHARP : 0; + } + + break; + } + case PBVH_FACES: + if (ss->pmap) { + // sculpt_check_corner_face_set_in_base_mesh + ret = sculpt_check_corner_in_base_mesh(ss, vertex, check_facesets); + } + else { + // approximate + if (SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_MESH) && + SCULPT_vertex_valence_get(ss, vertex) < 4) { + ret = SCULPT_CORNER_MESH; + } + + // can't check face sets in this case + } + break; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.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}; + int v1, v2; + const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( + ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); + switch (adjacency) { + case SUBDIV_CCG_ADJACENT_VERTEX: + return sculpt_check_corner_in_base_mesh(ss, BKE_pbvh_make_vref(v1), check_facesets); + case SUBDIV_CCG_ADJACENT_EDGE: + return false; // sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2); + case SUBDIV_CCG_ADJACENT_NONE: + return false; + break; } - return sculpt_check_boundary_vertex_in_base_mesh(ss, index); } + } + + return ret; +} + +SculptBoundaryType SCULPT_vertex_is_boundary(const SculptSession *ss, + const SculptVertRef vertex, + SculptBoundaryType boundary_types) +{ + bool check_facesets = boundary_types & SCULPT_BOUNDARY_FACE_SET; + + switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { - BMVert *v = BM_vert_at_index(ss->bm, index); - return BM_vert_is_boundary(v); + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, ((BMVert *)(vertex.i))); + + if (mv->flag & DYNVERT_NEED_BOUNDARY) { + BKE_pbvh_update_vert_boundary( + ss->cd_dyn_vert, ss->cd_faceset_offset, (BMVert *)vertex.i, ss->boundary_symmetry); + } + + int flag = 0; + if (boundary_types & SCULPT_BOUNDARY_MESH) { + flag |= (mv->flag & DYNVERT_BOUNDARY) ? SCULPT_BOUNDARY_MESH : 0; + } + if (boundary_types & SCULPT_BOUNDARY_FACE_SET) { + flag |= (mv->flag & DYNVERT_FSET_BOUNDARY) ? SCULPT_BOUNDARY_FACE_SET : 0; + } + if (boundary_types & SCULPT_BOUNDARY_SHARP) { + flag |= (mv->flag & DYNVERT_SHARP_BOUNDARY) ? SCULPT_BOUNDARY_SHARP : 0; + } + if (boundary_types & SCULPT_BOUNDARY_SEAM) { + flag |= (mv->flag & DYNVERT_SEAM_BOUNDARY) ? SCULPT_BOUNDARY_SEAM : 0; + } + + return flag; + } + case PBVH_FACES: { + int flag = 0; + + if (!SCULPT_vertex_all_face_sets_visible_get(ss, vertex)) { + flag |= SCULPT_BOUNDARY_MESH; + } + + if (check_facesets) { + bool ret = sculpt_check_boundary_vertex_in_base_mesh(ss, vertex); + + if (ret) { + flag |= SCULPT_BOUNDARY_MESH; + } + + if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) { + flag |= SCULPT_BOUNDARY_FACE_SET; + } + + return flag; + } + else if (sculpt_check_boundary_vertex_in_base_mesh(ss, vertex)) { + return SCULPT_BOUNDARY_MESH; + } + + return 0; } 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,17 +1906,21 @@ 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)) ? + SCULPT_BOUNDARY_MESH : + 0; case SUBDIV_CCG_ADJACENT_EDGE: - return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2); + if (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))) { + return SCULPT_BOUNDARY_MESH; + } case SUBDIV_CCG_ADJACENT_NONE: - return false; + return 0; } } } - return false; + return 0; } /* Utilities */ @@ -924,8 +1939,8 @@ bool SCULPT_stroke_is_main_symmetry_pass(StrokeCache *cache) * Return true only once per stroke on the first symmetry pass, regardless of the symmetry passes * enabled. * - * This should be used for functionality that needs to be computed once per stroke of a particular - * tool (allocating memory, updating random seeds...). + * This should be used for functionality that needs to be computed once per stroke of a + * particular tool (allocating memory, updating random seeds...). */ bool SCULPT_stroke_is_first_brush_step(StrokeCache *cache) { @@ -962,6 +1977,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 +1995,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 +2010,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 +2035,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 +2048,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 +2060,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 +2111,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 +2141,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 +2167,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 +2179,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 +2211,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,11 +2246,13 @@ 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); } -static bool sculpt_tool_is_proxy_used(const char sculpt_tool) +bool sculpt_tool_is_proxy_used(const char sculpt_tool) { return ELEM(sculpt_tool, SCULPT_TOOL_SMOOTH, @@ -1280,19 +2315,23 @@ typedef enum StrokeFlags { void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode) { SculptSession *ss = ob->sculpt; + + // do nothing + BMesh *bm = ss->bm; 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->coords = data->unode->co; - data->normals = data->unode->no; - data->vmasks = data->unode->mask; - data->colors = data->unode->col; + // data->datatype = data->unode->type; } } @@ -1300,37 +2339,66 @@ 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; + + // don't need undo node here anymore + if (!ob->sculpt->bm) { + // unode = SCULPT_undo_push_node(ob, node, type); + } + SCULPT_orig_vert_data_unode_init(data, ob, unode); + data->datatype = type; +} + +void SCULPT_vertex_check_origdata(SculptSession *ss, SculptVertRef vertex) +{ + // check if we need to update original data for current stroke + MDynTopoVert *mv = ss->bm ? BKE_PBVH_DYNVERT(ss->cd_dyn_vert, (BMVert *)vertex.i) : + ss->mdyntopo_verts + vertex.i; + + if (mv->stroke_id != ss->stroke_id) { + mv->stroke_id = ss->stroke_id; + + copy_v3_v3(mv->origco, SCULPT_vertex_co_get(ss, vertex)); + SCULPT_vertex_normal_get(ss, vertex, mv->origno); + + const float *color = SCULPT_vertex_color_get(ss, vertex); + if (color) { + copy_v4_v4(mv->origcolor, color); + } + + mv->origmask = SCULPT_vertex_mask_get(ss, vertex); + } } /** - * Update a #SculptOrigVertData for a particular vertex from the PBVH iterator. + * DEPRECATED use Update a #SculptOrigVertData for a particular vertex from the PBVH iterator. */ -void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter) +void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, SculptVertRef vertex) { - if (orig_data->unode->type == 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); - } - else { - orig_data->co = orig_data->coords[iter->i]; - orig_data->no = orig_data->normals[iter->i]; - } + // check if we need to update original data for current stroke + MDynTopoVert *mv = SCULPT_vertex_get_mdyntopo(orig_data->ss, vertex); + + SCULPT_vertex_check_origdata(orig_data->ss, vertex); + + if (orig_data->datatype == SCULPT_UNDO_COORDS) { + float *no = mv->origno; + normal_float_to_short_v3(orig_data->_no, no); + + orig_data->no = orig_data->_no; + orig_data->co = mv->origco; } - 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) { + orig_data->col = mv->origcolor; } - else if (orig_data->unode->type == SCULPT_UNDO_MASK) { - if (orig_data->bm_log) { - orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); - } - else { - orig_data->mask = orig_data->vmasks[iter->i]; - } + else if (orig_data->datatype == SCULPT_UNDO_MASK) { + orig_data->mask = mv->origmask; } } @@ -1450,15 +2518,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) && - - (!ss->cache || (!ss->cache->alt_smooth)) && + return ( + (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && - /* Requires mesh restore, which doesn't work with - * dynamic-topology. */ - !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && + (!ss->cache || (!ss->cache->alt_smooth)) && - 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 +2548,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) { @@ -1491,7 +2561,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (orig_data.unode->type == SCULPT_UNDO_COORDS) { copy_v3_v3(vd.co, orig_data.co); @@ -1997,17 +3067,18 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, /* When the mesh is edited we can't rely on original coords * (original mesh may not even have verts in brush radius). */ if (use_original && data->has_bm_orco) { - float(*orco_coords)[3]; - 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); - - 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]], + 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]]; + + 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 +3130,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]); @@ -2359,13 +3430,13 @@ static float brush_strength(const Sculpt *sd, return root_alpha * feather * pressure * overlap; } else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_EXPAND) { - /* Expand is more sensible to strength as it keeps expanding the cloth when sculpting over - * the same vertices. */ + /* Expand is more sensible to strength as it keeps expanding the cloth when sculpting + * over the same vertices. */ return 0.1f * alpha * flip * pressure * overlap * feather; } else { - /* Multiply by 10 by default to get a larger range of strength depending on the size of the - * brush and object. */ + /* Multiply by 10 by default to get a larger range of strength depending on the size of + * the brush and object. */ return 10.0f * alpha * flip * pressure * overlap * feather; } case SCULPT_TOOL_DRAW_FACE_SETS: @@ -2427,7 +3498,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 +3544,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; @@ -2684,7 +3758,8 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, SculptSession *ss = ob->sculpt; PBVHNode **nodes = NULL; - /* Build a list of all nodes that are potentially within the cursor or brush's area of influence. + /* Build a list of all nodes that are potentially within the cursor or brush's area of + * influence. */ if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { SculptSearchSphereData data = { @@ -2874,10 +3949,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 +3974,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,26 +3997,92 @@ 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; + int check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS; + check_fsets = check_fsets ? SCULPT_BOUNDARY_FACE_SET : 0; + + if (use_curvature) { + SCULPT_curvature_begin(ss, node, false); + } + + const bool have_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH; + + const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + if (weighted || ss->cache->brush->boundary_smooth_factor > 0.0f) { + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + } + 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; } + + /* ignore boundary verts + might want to call normal smooth with + rake's projection in this case, I'm not entirely sure + - joeedh + */ + if (have_bmesh) { + BMVert *v = (BMVert *)vd.vertex.i; + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + // if (mv->flag & (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY)) { + // 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); + } - sub_v3_v3v3(val, avg, vd.co); +#if 0 + if (SCULPT_vertex_is_boundary( + ss, vd.vertex, SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_MESH | check_fsets)) { + continue; + } - madd_v3_v3v3fl(val, vd.co, val, fade); + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, vd.bm_vert); + if (check_fsets && (mv->flag & (DYNVERT_FSET_CORNER))) { + continue; + } - SCULPT_clip(sd, ss, vd.co, val); + if (mv->flag & (DYNVERT_CORNER | DYNVERT_SHARP_CORNER)) { + continue; + } +#endif + + int steps = data->do_origco ? 2 : 1; + + for (int step = 0; step < steps; step++) { + float *co = step ? (float *)SCULPT_vertex_origco_get(ss, vd.vertex) : vd.co; + + SCULPT_bmesh_four_neighbor_average(ss, + avg, + direction2, + vd.bm_vert, + data->rake_projection, + check_fsets, + data->cd_temp, + data->cd_dyn_vert, + step); + + sub_v3_v3v3(val, avg, co); + madd_v3_v3v3fl(val, co, val, fade); + SCULPT_clip(sd, ss, co, val); + } if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; @@ -2952,11 +4094,63 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, static void bmesh_topology_rake( Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) { + SculptSession *ss = ob->sculpt; 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; + + // vector4, nto color + SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_COLOR, "_rake_temp"); + int cd_temp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_COLOR, "_rake_temp"); + +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + // reset edge flags, single threaded + for (int i = 0; i < totnode; i++) { + PBVHNode *node = nodes[i]; + PBVHVertexIter vd; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + BMVert *v = vd.bm_vert; + BMEdge *e = v->e; + + if (!e) { + continue; + } + + do { + e->head.hflag |= BM_ELEM_DRAW; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + BKE_pbvh_vertex_iter_end; + } +#endif + + if (SCULPT_stroke_is_first_brush_step(ss->cache) && + (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) { + BKE_pbvh_update_all_tri_areas(ss->pbvh); + } + + 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 +4158,15 @@ 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, + .cd_temp = cd_temp, + .cd_dyn_vert = ss->cd_dyn_vert, + .rake_projection = brush->topology_rake_projection, + .do_origco = SCULPT_stroke_needs_original(brush)}; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -3000,7 +4196,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 +4240,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, false); break; } } @@ -3081,12 +4277,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 +4342,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 +4369,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 +4413,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 +4425,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 +4490,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 +4547,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; @@ -3357,7 +4557,8 @@ static void do_draw_sharp_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) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3369,7 +4570,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 +4630,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; @@ -3439,7 +4640,8 @@ static void do_topology_slide_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) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3450,7 +4652,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 +4674,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 +4707,42 @@ 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); + + int bset = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP; + if (ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS) { + bset |= SCULPT_BOUNDARY_FACE_SET; + } + + if (SCULPT_vertex_is_corner(ss, vd->vertex, (SculptCornerType)bset)) { + copy_v3_v3(r_final_pos, vd->co); + return; + } + + const int is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex, bset); 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. */ + /* 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, bset)) { 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++; } } @@ -3554,11 +4767,11 @@ void SCULPT_relax_vertex(SculptSession *ss, float smooth_closest_plane[3]; float vno[3]; - if (is_boundary && avg_count == 2) { + if ((is_boundary & SCULPT_BOUNDARY_MESH) && avg_count == 2) { 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 +4799,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]); @@ -3596,7 +4809,8 @@ static void do_topology_relax_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) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3607,7 +4821,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 +4979,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 +4988,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 +5034,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; @@ -3833,8 +5048,8 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod flippedbstrength *= -1.0f; } - /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single - * point. Without this we get a 'flat' surface surrounding the pinch. */ + /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a + * single point. Without this we get a 'flat' surface surrounding the pinch. */ sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); /* Threaded loop over nodes. */ @@ -3889,7 +5104,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 +5196,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; @@ -3993,7 +5208,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -4005,7 +5220,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 +5284,7 @@ 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); proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; @@ -4094,7 +5309,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, ¶ms, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); float final_disp[3]; switch (brush->elastic_deform_type) { case BRUSH_ELASTIC_DEFORM_GRAB: @@ -4125,13 +5340,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 +5374,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 +5554,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 +5639,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 +5687,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 +5750,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; @@ -4540,7 +5760,7 @@ static void do_thumb_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) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -4552,7 +5772,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 +5823,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; @@ -4613,7 +5833,7 @@ static void do_rotate_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) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -4626,7 +5846,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); @@ -4663,6 +5883,8 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); } +//#define LAYER_FACE_SET_MODE + static void do_layer_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -4672,12 +5894,41 @@ 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( @@ -4685,7 +5936,7 @@ static void do_layer_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) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -4697,13 +5948,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 +5994,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); @@ -4755,27 +6019,157 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, } } BKE_pbvh_vertex_iter_end; + +#ifdef LAYER_FACE_SET_MODE + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + TableGSet *bm_faces = BKE_pbvh_bmesh_node_faces(data->nodes[n]); + TableGSet *bm_unique_verts = BKE_pbvh_bmesh_node_unique_verts(data->nodes[n]); + BMFace *f; + + const int cd_vcol = ss->cd_vcol_offset; + + int fset2 = 1; // data->face_set; + const int cd_disp = use_persistent_base ? data->cd_pers_disp : data->cd_layer_disp; + int f_ni = BKE_pbvh_get_node_index(ss->pbvh, data->nodes[n]); + BMVert *v; + + TGSET_ITER (v, bm_unique_verts) { + BMIter iter; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + if (BM_ELEM_CD_GET_INT(f, ss->cd_face_node_offset) != f_ni) { + continue; + } + if (BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset) < 0) { + // continue; + } + + fset2 = 1; + float height = 0.0; + int tot = 0; + BMLoop *l = f->l_first; + + int inside = 0; + + do { + int v_ni = BM_ELEM_CD_GET_INT(l->v, ss->cd_vert_node_offset); + + float *disp_factor = BM_ELEM_CD_GET_VOID_P(l->v, cd_disp); + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, l->v); + mv->flag |= DYNVERT_NEED_BOUNDARY; + + if (cd_vcol >= 0) { + MPropCol *col = BM_ELEM_CD_GET_VOID_P(l->v, cd_vcol); + col->color[0] = MAX2(*disp_factor, 0.0f); + col->color[1] = MAX2(-(*disp_factor), 0.0f); + col->color[2] = 0.0f; + col->color[3] = 1.0f; + } + + if (sculpt_brush_test_sq_fn(&test, l->v->co)) { // mv->origco)) { + inside++; + } + + if (*disp_factor < 0.9) { + fset2 = data->face_set2; + } + + height += *disp_factor; + tot++; + } while ((l = l->next) != f->l_first); + + if (!inside) { + continue; + } + + int *ptr = BM_ELEM_CD_GET_VOID_P(f, ss->cd_faceset_offset); + int old = *ptr; + atomic_cas_int32(ptr, old, fset2); + + // BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset2); + } + } + TGSET_ITER_END; + } + +#endif } 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; + +#ifdef LAYER_FACE_SET_MODE + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + ss->cache->paint_face_set = SCULPT_face_set_next_available_get(ss); + } + + const int fset = ss->cache->paint_face_set; +#endif + + 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); - if (ss->cache->layer_displacement_factor == NULL) { + 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); + + // 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"); } - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, + 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, +#ifdef LAYER_FACE_SET_MODE + .face_set = fset, + .face_set2 = fset + 1 + +#endif }; TaskParallelSettings settings; +#ifdef LAYER_FACE_SET_MODE + BKE_pbvh_parallel_range_settings(&settings, false, totnode); +#else BKE_pbvh_parallel_range_settings(&settings, true, totnode); +#endif BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); } @@ -4809,7 +6203,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 +6316,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 +6470,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 +6596,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) { @@ -5260,14 +6652,14 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t mul_v3_fl(temp, displace); add_v3_v3(area_co, temp); - /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the - * vertices. When in Add mode, vertices that are below the plane and inside the cube are move - * towards the plane. In this situation, there may be cases where a vertex is outside the cube - * but below the plane, so won't be deformed, causing artifacts. In order to prevent these + /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform + * the vertices. When in Add mode, vertices that are below the plane and inside the cube are + * move towards the plane. In this situation, there may be cases where a vertex is outside the + * cube but below the plane, so won't be deformed, causing artifacts. In order to prevent these * artifacts, this displaces the test cube space in relation to the plane in order to * deform more vertices that may be below it. */ - /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were set - * by doing multiple tests using the default "Clay Strips" brush preset. */ + /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were + * set by doing multiple tests using the default "Clay Strips" brush preset. */ float area_co_displaced[3]; madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f); @@ -5286,8 +6678,8 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t scale_m4_fl(scale, ss->cache->radius); mul_m4_m4m4(tmat, mat, scale); - /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in - * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices + /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff + * in Z this does not produce artifacts in the falloff cube and allows to deform extra vertices * during big deformation while keeping the surface as uniform as possible. */ mul_v3_fl(tmat[2], 1.25f); @@ -5356,7 +6748,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 +6846,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 +6962,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); @@ -5625,8 +7017,8 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to return; } - /* Simulate the clay accumulation by increasing the plane angle as more samples are added to the - * stroke. */ + /* Simulate the clay accumulation by increasing the plane angle as more samples are added to + * the stroke. */ if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { ss->cache->clay_thumb_front_angle += 0.8f; ss->cache->clay_thumb_front_angle = clamp_f(ss->cache->clay_thumb_front_angle, 0.0f, 60.0f); @@ -5709,7 +7101,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 +7183,108 @@ 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; + AutomaskingCache _fixed; + bool free_automasking; +} 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, + Sculpt *sd, + const Brush *br, + Object *ob, + 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"); + + if (!ss->cache) { + state->cache = SCULPT_automasking_cache_init(sd, br, ob); + } + else { + 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"); + if (!ss->cache) { + state->cache = SCULPT_automasking_cache_init(sd, br, ob); + state->free_automasking = true; + } + else { + 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,65 +7294,97 @@ static void sculpt_topology_update(Sculpt *sd, { SculptSession *ss = ob->sculpt; - int n, totnode; + /* 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); + } + + /* multiply with primary radius scale instead of max */ + if (brush->cached_dyntopo.radius_scale > 0.0f) { + radius_scale *= brush->cached_dyntopo.radius_scale; + } + /* 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. */ + /* Free index based vertex info as it will become invalid after modifying the topology during + * the stroke. */ MEM_SAFE_FREE(ss->vertex_info.boundary); MEM_SAFE_FREE(ss->vertex_info.connected_component); 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; } + else if (brush->cached_dyntopo.flag & DYNTOPO_LOCAL_SUBDIVIDE) { + mode |= PBVH_LocalSubdivide | 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]); + else if (brush->cached_dyntopo.flag & DYNTOPO_LOCAL_COLLAPSE) { + mode |= PBVH_LocalCollapse | PBVH_Collapse; } } - 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)); + if (brush->cached_dyntopo.flag & DYNTOPO_CLEANUP) { + mode |= PBVH_Cleanup; } - MEM_SAFE_FREE(nodes); + 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, sd, brush, ob, &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,32 +7408,51 @@ 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]); } } -static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups) +void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups) { SculptSession *ss = ob->sculpt; 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 (sculpt_brush_use_topology_rake(ss, brush)) { + radius_scale = MAX2(radius_scale, brush->topology_rake_radius_factor); } - if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) { + /* 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 +7462,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 +7477,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 +7509,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); @@ -6006,6 +7577,8 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe bool invert = ss->cache->pen_flip || ss->cache->invert || brush->flag & BRUSH_DIR_IN; + SCULPT_replay_log_append(sd, ss, ob); + /* Apply one type of brush action. */ switch (brush->sculpt_tool) { case SCULPT_TOOL_DRAW: @@ -6013,7 +7586,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 +7689,97 @@ 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_BOUNDARY, 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, + false); } else { - SCULPT_smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, false); + SCULPT_smooth(sd, + ob, + nodes, + totnode, + brush->autosmooth_factor, + false, + brush->autosmooth_projection, + false); + } + + 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 +7863,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]); @@ -6239,7 +7890,7 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata, BKE_pbvh_node_free_proxies(data->nodes[n]); } -static void sculpt_combine_proxies(Sculpt *sd, Object *ob) +void sculpt_combine_proxies(Sculpt *sd, Object *ob) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -6304,6 +7955,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); @@ -6362,8 +8017,8 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used) MEM_SAFE_FREE(nodes); /* Modifiers could depend on mesh normals, so we should update them. - * NOTE: then if sculpting happens on locked key, normals should be re-calculate after applying - * coords from key-block on base mesh. */ + * NOTE: then if sculpting happens on locked key, normals should be re-calculate after + * applying coords from key-block on base mesh. */ BKE_mesh_calc_normals(me); } else if (ss->shapekey_active) { @@ -6587,6 +8242,22 @@ bool SCULPT_vertex_colors_poll(bContext *C) if (!U.experimental.use_sculpt_vertex_colors) { return false; } + + 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); } @@ -6674,6 +8345,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"; @@ -6693,6 +8368,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); } @@ -6700,6 +8379,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; } } @@ -6756,6 +8436,7 @@ static void sculpt_update_cache_invariants( float max_scale; int mode; + cache->C = C; ss->cache = cache; /* Set scaling adjustment. */ @@ -6932,8 +8613,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, @@ -7146,9 +8827,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); } @@ -7274,7 +8955,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po /* Returns true if any of the smoothing modes are active (currently * one of smooth brush, autosmooth, mask smooth, or shift-key * smooth). */ -static bool sculpt_needs_connectivity_info(const Sculpt *sd, +static bool sculpt_needs_connectivity_info(Sculpt *sd, const Brush *brush, SculptSession *ss, int stroke_mode) @@ -7286,6 +8967,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) || @@ -7322,7 +9006,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; } @@ -7338,7 +9022,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; } @@ -7359,7 +9044,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; } @@ -7372,7 +9057,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; } @@ -7415,8 +9101,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, @@ -7462,7 +9149,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) { @@ -7474,6 +9162,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)); @@ -7483,11 +9172,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; } @@ -7574,11 +9263,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; @@ -7591,7 +9275,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); @@ -7767,7 +9452,8 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) SCULPT_update_object_bounding_box(ob); } - if (SCULPT_get_redraw_rect(region, CTX_wm_region_view3d(C), ob, &r)) { + if (CTX_wm_region_view3d(C) && + SCULPT_get_redraw_rect(region, CTX_wm_region_view3d(C), ob, &r)) { if (ss->cache) { ss->cache->current_r = r; } @@ -7785,6 +9471,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 @@ -7836,12 +9529,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 */ @@ -7882,6 +9594,13 @@ 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++; + + if (ss->pbvh) { + BKE_pbvh_set_stroke_id(ss->pbvh, ss->stroke_id); + } + sculpt_update_cache_invariants(C, sd, ss, op, mouse); SculptCursorGeometryInfo sgi; @@ -7894,35 +9613,73 @@ 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) +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); + if (itemptr) { + 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); + int boundsym = BKE_get_fset_boundary_symflag(ob); + ss->cache->boundary_symmetry = boundsym; + ss->boundary_symmetry = boundsym; + + if (ss->pbvh) { + BKE_pbvh_set_symmetry(ss->pbvh, SCULPT_mesh_symmetry_xyz_get(ob), boundsym); } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); + + 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 (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); + + if (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) { + /* run dyntopo again for snake hook */ + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); + } + } } do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); @@ -8161,13 +9918,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; } @@ -8229,6 +10016,69 @@ static bool sculpt_no_multires_poll(bContext *C) return false; } +static bool sculpt_only_bmesh_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (SCULPT_mode_poll(C) && ob->sculpt && ob->sculpt->pbvh) { + return BKE_pbvh_type(ob->sculpt->pbvh) == PBVH_BMESH; + } + return false; +} + +static int sculpt_spatial_sort_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + PBVH *pbvh = ss->pbvh; + + if (!pbvh) { + return OPERATOR_CANCELLED; + } + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_BMESH: + SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize"); + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY); + + BKE_pbvh_reorder_bmesh(ss->pbvh); + + BKE_pbvh_bmesh_on_mesh_change(ss->pbvh); + BM_log_full_mesh(ss->bm, ss->bm_log); + + ss->active_vertex_index.i = 0; + ss->active_face_index.i = 0; + + BKE_pbvh_free(ss->pbvh); + ss->pbvh = NULL; + + /* Finish undo. */ + SCULPT_undo_push_end(); + + break; + case PBVH_FACES: + return OPERATOR_CANCELLED; + case PBVH_GRIDS: + return OPERATOR_CANCELLED; + } + + /* Redraw. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, ND_DATA | NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} +static void SCULPT_OT_spatial_sort_mesh(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Spatially Sort Mesh"; + ot->idname = "SCULPT_OT_spatial_sort_mesh"; + ot->description = "Spatially sort mesh to improve memory coherency"; + + /* API callbacks. */ + ot->exec = sculpt_spatial_sort_exec; + ot->poll = sculpt_only_bmesh_poll; +} + static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -8252,7 +10102,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 +10112,22 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) sd->symmetrize_direction, dist, true); - SCULPT_dynamic_topology_triangulate(ss->bm); - +#ifndef DYNTOPO_DYNAMIC_TESS + SCULPT_dynamic_topology_triangulate(ss, ss->bm); +#endif /* 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); + SCULT_dyntopo_flag_all_disk_sort(ss); + + // 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; @@ -8311,7 +10167,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot) RNA_def_float(ot->srna, "merge_tolerance", - 0.001f, + 0.0002f, 0.0f, FLT_MAX, "Merge Distance", @@ -8344,10 +10200,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. */ @@ -8365,7 +10221,8 @@ void ED_object_sculptmode_enter_ex(Main *bmain, Scene *scene, Object *ob, const bool force_dyntopo, - ReportList *reports) + ReportList *reports, + bool do_undo) { const int mode_flag = OB_MODE_SCULPT; Mesh *me = BKE_mesh_from_object(ob); @@ -8389,32 +10246,26 @@ void ED_object_sculptmode_enter_ex(Main *bmain, paint_cursor_start(paint, SCULPT_mode_poll_view3d); + bool has_multires = false; + /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, * As long as no data was added that is not supported. */ if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); const char *message_unsupported = NULL; - if (me->totloop != me->totpoly * 3) { - message_unsupported = TIP_("non-triangle face"); - } - else if (mmd != NULL) { + if (mmd != NULL) { message_unsupported = TIP_("multi-res modifier"); + has_multires = true; } else { enum eDynTopoWarnFlag flag = SCULPT_dynamic_topology_check(scene, ob); if (flag == 0) { /* pass */ } - else if (flag & DYNTOPO_WARN_VDATA) { - message_unsupported = TIP_("vertex data"); - } else if (flag & DYNTOPO_WARN_EDATA) { message_unsupported = TIP_("edge data"); } - else if (flag & DYNTOPO_WARN_LDATA) { - message_unsupported = TIP_("face data"); - } else if (flag & DYNTOPO_WARN_MODIFIER) { message_unsupported = TIP_("constructive modifier"); } @@ -8423,19 +10274,37 @@ void ED_object_sculptmode_enter_ex(Main *bmain, } } - if ((message_unsupported == NULL) || force_dyntopo) { + if (!has_multires && ((message_unsupported == NULL) || force_dyntopo)) { /* 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; + bool has_undo = do_undo && wm->undo_stack != NULL; + /* Undo push is needed to prevent memory leak. */ if (has_undo) { SCULPT_undo_push_begin(ob, "Dynamic topology enable"); } + + bool need_bmlog = !ob->sculpt->bm_log; + SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); + if (has_undo) { SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); SCULPT_undo_push_end(); } + else if (need_bmlog) { + if (ob->sculpt->bm_log) { + BM_log_free(ob->sculpt->bm_log, true); + ob->sculpt->bm_log = NULL; + } + + SCULPT_undo_ensure_bmlog(ob); + + // SCULPT_undo_ensure_bmlog failed to find a sculpt undo step + if (!ob->sculpt->bm_log) { + ob->sculpt->bm_log = BM_log_create(ob->sculpt->bm, ob->sculpt->cd_dyn_vert); + } + } } else { BKE_reportf( @@ -8454,7 +10323,7 @@ void ED_object_sculptmode_enter(struct bContext *C, Depsgraph *depsgraph, Report Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = OBACT(view_layer); - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports, true); } void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) @@ -8534,7 +10403,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) if (depsgraph) { depsgraph = CTX_data_ensure_evaluated_depsgraph(C); } - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports, true); BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); if (ob->mode & mode_flag) { @@ -8610,29 +10479,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 +10583,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 +10646,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 +10661,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 +10718,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 +10734,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 +10766,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 +10780,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 +10804,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 +10835,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 +10849,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 +10862,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 +10887,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 +10905,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 +10927,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 +10967,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 +10979,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 +11102,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 +11123,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 +11212,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 +11258,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 +11268,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 +11323,685 @@ 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 + +int SCULPT_vertex_valence_get(const struct SculptSession *ss, SculptVertRef vertex) +{ + SculptVertexNeighborIter ni; + int tot = 0; + int mval = -1; + +#if 0 + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BMVert *v = (BMVert *)vertex.i; + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + if (mv->flag & DYNVERT_NEED_VALENCE) { + BKE_pbvh_bmesh_update_valence(ss->cd_dyn_vert, vertex); + } + + mval = mv->valence; + +# ifdef NDEBUG + return mval; +# endif + } +#else + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BMVert *v = (BMVert *)vertex.i; + + return BM_vert_edge_count(v); + } +#endif + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + tot++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + +#ifdef NDEBUG + if (mval >= 0 && mval != tot) { + printf("Out of date vertex valence detected! old: %d, should be: %d\n", mval, tot); + } +#else + BLI_assert(mval < 0 || mval == tot); +#endif + + return tot; +} + +typedef struct BMLinkItem { + struct BMLinkItem *next, *prev; + BMVert *item; + int depth; +} BMLinkItem; + +ATTR_NO_OPT static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + Object *sculpt_get_vis_object(bContext * C, SculptSession * ss, char *name); + void sculpt_end_vis_object(bContext * C, SculptSession * ss, Object * ob, BMesh * bm); + + Object *visob = sculpt_get_vis_object(C, ss, "rakevis"); + BMesh *visbm = BM_mesh_create( + &bm_mesh_allocsize_default, + &((struct BMeshCreateParams){.create_unique_ids = false, .use_toolflags = false})); + + if (!ss) { + printf("mising sculpt session\n"); + return OPERATOR_CANCELLED; + } + if (!ss->bm) { + printf("bmesh only!\n"); + return OPERATOR_CANCELLED; + } + + BMesh *bm = ss->bm; + BMIter iter; + BMVert *v; + + PBVHNode **nodes = NULL; + int totnode; + + int idx = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_COLOR, "_rake_temp"); + if (idx < 0) { + printf("no rake temp\n"); + return OPERATOR_CANCELLED; + } + + int cd_vcol = bm->vdata.layers[idx].offset; + int cd_vcol_vis = -1; + + idx = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_COLOR, "_rake_vis"); + if (idx >= 0) { + cd_vcol_vis = bm->vdata.layers[idx].offset; + } + + for (int step = 0; step < 33; step++) { + BLI_mempool *nodepool = BLI_mempool_create(sizeof(BMLinkItem), bm->totvert, 4196, 0); + + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes, &totnode); + + SCULPT_undo_push_begin(ob, "Regularized Rake Directions"); + for (int i = 0; i < totnode; i++) { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COLOR, -1); + BKE_pbvh_node_mark_update_color(nodes[i]); + } + SCULPT_undo_push_end(); + + MEM_SAFE_FREE(nodes); + + BMVert **stack = NULL; + BLI_array_declare(stack); + + bm->elem_index_dirty |= BM_VERT; + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BLI_bitmap *visit = BLI_BITMAP_NEW(bm->totvert, "regularize rake visit bitmap"); + + BMVert **verts = MEM_malloc_arrayN(bm->totvert, sizeof(*verts), "verts"); + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + verts[v->head.index] = v; + v->head.hflag &= ~BM_ELEM_SELECT; + } + + RNG *rng = BLI_rng_new((uint)BLI_thread_rand(0)); + BLI_rng_shuffle_array(rng, verts, sizeof(void *), bm->totvert); + + for (int i = 0; i < bm->totvert; i++) { + BMVert *v = verts[i]; + + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + if (mv->flag & + (DYNVERT_CORNER | DYNVERT_FSET_CORNER | DYNVERT_SHARP_CORNER | DYNVERT_SEAM_CORNER)) { + continue; + } + + if (BLI_BITMAP_TEST(visit, v->head.index)) { + continue; + } + + // v->head.hflag |= BM_ELEM_SELECT; + + float *dir = BM_ELEM_CD_GET_VOID_P(v, cd_vcol); + normalize_v3(dir); + + BMLinkItem *node = BLI_mempool_alloc(nodepool); + node->next = node->prev = NULL; + node->item = v; + node->depth = 0; + + BLI_BITMAP_SET(visit, v->head.index, true); + + ListBase queue = {node, node}; + const int boundflag = DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_SEAM_BOUNDARY | + DYNVERT_SHARP_BOUNDARY; + while (queue.first) { + BMLinkItem *node2 = BLI_poptail(&queue); + BMVert *v2 = node2->item; + + float *dir2 = BM_ELEM_CD_GET_VOID_P(v2, cd_vcol); + + if (cd_vcol_vis >= 0) { + float *color = BM_ELEM_CD_GET_VOID_P(v2, cd_vcol_vis); + color[0] = color[1] = color[2] = (float)(node2->depth % 5) / 5.0f; + color[3] = 1.0f; + } + + if (step % 5 != 0 && node2->depth > 15) { + // break; + } + // dir2[0] = dir2[1] = dir2[2] = (float)(node2->depth % 5) / 5.0f; + // dir2[3] = 1.0f; + + BMIter viter; + BMEdge *e; + + int closest_vec_to_perp( + float dir[3], float r_dir2[3], float no[3], float *buckets, float w); + + float buckets[8] = {0}; + float tmp[3]; + float dir32[3]; + float avg[3] = {0.0f}; + float tot = 0.0f; + + // angle_on_axis_v3v3v3_v3 + float tco[3]; + zero_v3(tco); + add_v3_fl(tco, 1000.0f); + // madd_v3_v3fl(tco, v2->no, -dot_v3v3(v2->no, tco)); + + BMLoop *l; + + float tanco[3]; + add_v3_v3v3(tanco, v2->co, dir2); + + SCULPT_dyntopo_check_disk_sort(ss, (SculptVertRef){.i = (intptr_t)v2}); + + float thlast, thfirst; + float lastdir3[3]; + float firstdir3[3]; + bool first = true; + float thsum = 0.0f; + + // don't propegate across singularities + + BM_ITER_ELEM (e, &viter, v2, BM_EDGES_OF_VERT) { + // e = l->e; + BMVert *v3 = BM_edge_other_vert(e, v2); + float *dir3 = BM_ELEM_CD_GET_VOID_P(v3, cd_vcol); + float dir32[3]; + + copy_v3_v3(dir32, dir3); + + if (first) { + first = false; + copy_v3_v3(firstdir3, dir32); + } + else { + float th = saacos(dot_v3v3(dir32, lastdir3)); + thsum += th; + } + + copy_v3_v3(lastdir3, dir32); + + add_v3_v3(avg, dir32); + tot += 1.0f; + } + + thsum += saacos(dot_v3v3(lastdir3, firstdir3)); + bool sing = thsum >= M_PI * 0.5f; + + // still apply smoothing even with singularity? + if (tot > 0.0f && !(mv->flag & boundflag)) { + mul_v3_fl(avg, 1.0 / tot); + interp_v3_v3v3(dir2, dir2, avg, sing ? 0.15 : 0.25); + normalize_v3(dir2); + } + + if (sing) { + v2->head.hflag |= BM_ELEM_SELECT; + + if (node2->depth == 0) { + continue; + } + } + + BM_ITER_ELEM (e, &viter, v2, BM_EDGES_OF_VERT) { + BMVert *v3 = BM_edge_other_vert(e, v2); + float *dir3 = BM_ELEM_CD_GET_VOID_P(v3, cd_vcol); + + if (BLI_BITMAP_TEST(visit, v3->head.index)) { + continue; + } + + copy_v3_v3(dir32, dir3); + madd_v3_v3fl(dir32, v2->no, -dot_v3v3(dir3, v2->no)); + normalize_v3(dir32); + + if (dot_v3v3(dir32, dir2) < 0) { + negate_v3(dir32); + } + + cross_v3_v3v3(tmp, dir32, v2->no); + normalize_v3(tmp); + + if (dot_v3v3(tmp, dir2) < 0) { + negate_v3(tmp); + } + + float th1 = fabsf(saacos(dot_v3v3(dir2, dir32))); + float th2 = fabsf(saacos(dot_v3v3(dir2, tmp))); + + if (th2 < th1) { + copy_v3_v3(dir32, tmp); + } + + madd_v3_v3fl(dir32, v3->no, -dot_v3v3(dir32, v3->no)); + normalize_v3(dir32); + copy_v3_v3(dir3, dir32); + + // int bits = closest_vec_to_perp(dir2, dir32, v2->no, buckets, 1.0f); + + MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v3); + if (mv3->flag & boundflag) { + // continue; + } + + BLI_BITMAP_SET(visit, v3->head.index, true); + + BMLinkItem *node3 = BLI_mempool_alloc(nodepool); + node3->next = node3->prev = NULL; + node3->item = v3; + node3->depth = node2->depth + 1; + + BLI_addhead(&queue, node3); + } + + BLI_mempool_free(nodepool, node2); + } + } + + MEM_SAFE_FREE(verts); + BLI_array_free(stack); + BLI_mempool_destroy(nodepool); + MEM_SAFE_FREE(visit); + } + + BMVert *v3; + BM_ITER_MESH (v3, &iter, bm, BM_VERTS_OF_MESH) { + float visco[3]; + float *dir3 = BM_ELEM_CD_GET_VOID_P(v3, cd_vcol); + + madd_v3_v3v3fl(visco, v3->co, v3->no, 0.001); + BMVert *vis1 = BM_vert_create(visbm, visco, NULL, BM_CREATE_NOP); + + madd_v3_v3v3fl(visco, visco, dir3, 0.003); + BMVert *vis2 = BM_vert_create(visbm, visco, NULL, BM_CREATE_NOP); + BM_edge_create(visbm, vis1, vis2, NULL, BM_CREATE_NOP); + + float fisco2[3]; + float tan[3]; + cross_v3_v3v3(tan, dir3, v3->no); + madd_v3_v3fl(visco, tan, 0.001); + + vis1 = BM_vert_create(visbm, visco, NULL, BM_CREATE_NOP); + BM_edge_create(visbm, vis1, vis2, NULL, BM_CREATE_NOP); + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + sculpt_end_vis_object(C, ss, visob, visbm); + + return OPERATOR_FINISHED; +} + +void SCULPT_OT_regularize_rake_directions(wmOperatorType *ot) +{ + ot->name = "Regularize Rake Directions"; + ot->idname = "SCULPT_OT_regularize_rake_directions"; + ot->description = "Development operator"; + + /* API callbacks. */ + ot->poll = SCULPT_mode_poll; + ot->exec = sculpt_regularize_rake_exec; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + void ED_operatortypes_sculpt(void) { WM_operatortype_append(SCULPT_OT_brush_stroke); @@ -9430,6 +12037,7 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_mask_by_color); WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit); WM_operatortype_append(SCULPT_OT_mask_init); - + WM_operatortype_append(SCULPT_OT_spatial_sort_mesh); WM_operatortype_append(SCULPT_OT_expand); + WM_operatortype_append(SCULPT_OT_regularize_rake_directions); } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc new file mode 100644 index 00000000000..adfe2a1c8b1 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -0,0 +1,20 @@ +#if 0 +# include "sculpt.hh" + +typedef blender::sculpt::SculptImpl<BMVert *, + BMEdge *, + BMFace *, + blender::sculpt::BMeshBackend, + blender::sculpt::BMeshPBVH> + BMeshSculpt; + +BMeshSculpt *sculpt = new BMeshSculpt(NULL, NULL); + +void test_cxsculpt() +{ + float dir[3] = {1.0f, 2.0f, 3.0f}; + float cent[3] = {0.0f, 0.0f, 0.0f}; + + sculpt->moveVerts(cent, 5.0f, dir); +} +#endif diff --git a/source/blender/editors/sculpt_paint/sculpt.hh b/source/blender/editors/sculpt_paint/sculpt.hh new file mode 100644 index 00000000000..ca144ea6391 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt.hh @@ -0,0 +1,474 @@ +#if 0 +# pragma once + +/* + +This is a proof of concept of how a C++ sculpt system could work. + +We can't really use virtual-based polymorphism for performance reasons, +so the idea is to use templates and C++20's concepts instead. + +*/ + +# include "BKE_pbvh.h" +# include "BLI_bitmap.h" +# include "BLI_map.hh" +# include "BLI_math.h" +# include "BLI_mempool.h" +# include "BLI_task.hh" +# include "MEM_guardedalloc.h" + +# include "DNA_brush_enums.h" +# include "DNA_brush_types.h" +# include "DNA_mesh_types.h" +# include "DNA_meshdata_types.h" +# include "DNA_scene_types.h" + +# include "BKE_brush.h" +# include "BKE_mesh.h" +# include "BKE_object.h" +# include "BKE_paint.h" +# include "BKE_pbvh.h" + +# include "sculpt_intern.h" + +# include "bmesh.h" +# include <concepts> +# include <functional> +# include <iterator> + +/* clang-format off */ +#include "../../blenkernel/intern/pbvh_intern.h" +/* clang-format on */ + +extern PBVHNode *the_fake_node; + +namespace blender { +namespace sculpt { + +typedef struct BrushSearchArgs { + float *center; + float radius; + bool use_threads; + bool use_original; + void *userdata; + const Brush *brush; + SculptBrushTest *test; // may be NULL, will be pulled from brush + SculptBrushTestFn test_func; +} BrushSearchArgs; + +class BMeshPBVH { + public: + BMeshPBVH() + { + } + + struct unique_verts_iter : public std::iterator<std::forward_iterator_tag, BMVert> { + public: + ~unique_verts_iter(){}; + + unique_verts_iter(PBVHNode *node) : _node(node) + { + i = _i = 0; + + while (_i < _node->bm_unique_verts->length && _node->bm_unique_verts->elems[_i] == nullptr) { + _i++; + } + } + + unique_verts_iter(const unique_verts_iter &a) + { + i = a.i; + _i = a._i; + index = a.index; + vertex = a.vertex; + co = a.co; + no = a.no; + mask = a.mask; + color = a.color; + } + + unique_verts_iter(bool is_end_iter) + { + i = _i = index = 0; + vertex = nullptr; + co = nullptr; + no = nullptr; + mask = nullptr; + color = nullptr; + } + + unique_verts_iter() + { + i = _i = index = 0; + vertex = nullptr; + co = nullptr; + no = nullptr; + mask = nullptr; + color = nullptr; + } + + inline unique_verts_iter &operator++() + { + i++; + _i++; + + while (i < _node->bm_unique_verts->length && _node->bm_unique_verts->elems[_i] == nullptr) { + _i++; + } + + if (_i >= _node->bm_unique_verts->length) { + vertex = NULL; + } + else { + vertex = reinterpret_cast<BMVert *>(_node->bm_unique_verts->elems + _i); + } + + if (vertex) { + deprecated_vertref.i = reinterpret_cast<intptr_t>(vertex); + co = vertex->co; + no = vertex->no; + } + + return *this; + } + + inline unique_verts_iter operator++(int) + { + unique_verts_iter tmp(*this); + operator++(); + return tmp; + } + + inline void reset() + { + _i = i = 0; + while (_i < _node->bm_unique_verts->length && _node->bm_unique_verts->elems[_i] == nullptr) { + _i++; + } + } + + inline unique_verts_iter begin() + { + unique_verts_iter ret = *this; + + ret.reset(); + + return ret; + } + + inline unique_verts_iter end() + { + return unique_verts_iter(true); + } + + inline bool operator==(const unique_verts_iter &b) + { + // detect comparison with end iterator + return (!vertex && !b.vertex) || (_node == b._node && i == b.i); + } + + inline bool operator!=(const unique_verts_iter &b) + { + return !(*this == b); + } + + inline unique_verts_iter operator*() + { + return *this; + } + + SculptVertRef deprecated_vertref; + + int i; + int index; + BMVert *vertex; + float *co; + float *no; + float *mask; + float *color; + + private: + PBVHNode *_node; + int _i; + }; + + struct brush_verts_iter : unique_verts_iter { + public: + ~brush_verts_iter(){}; + + brush_verts_iter(SculptSession *ss, + PBVHNode *node, + SculptBrushTest *test, + SculptBrushTestFn test_func, + const Brush *brush, + float *center, + float radius, + int thread_id) + : _node(node), + brush(brush), + radius_scale(radius_scale), + test(*test), + test_func(test_func), + _ss(ss), + _thread_id(thread_id) + { + copy_v3_v3(this->center, center); + this->radius = radius; + this->radius_squared = radius * radius; + } + + brush_verts_iter(const brush_verts_iter &a) + { + copy_v3_v3(center, a.center); + radius = a.radius; + radius_squared = a.radius_squared; + _node = a._node; + brush = a.brush; + + _ss = a._ss; + } + + brush_verts_iter(bool is_end) + { + brush = nullptr; + } + + brush_verts_iter begin() + { + brush_verts_iter ret = *this; + unique_verts_iter &viter = static_cast<unique_verts_iter &>(ret); + + viter.reset(); + + return ret; + } + + brush_verts_iter end() + { + return brush_verts_iter(true); + } + + inline brush_verts_iter &operator++() + { + unique_verts_iter::operator++(); + + skip_outside(); + + if (!vertex) { + brush = nullptr; + } + + fade = SCULPT_brush_strength_factor( + _ss, brush, co, 3, NULL, no, mask ? *mask : 0.0f, deprecated_vertref, _thread_id); + + return *this; + } + + inline bool operator==(const brush_verts_iter &b) + { + // detect comparison with end iterator + if (!brush && !b.brush) { + return true; + } + + return unique_verts_iter::operator==(static_cast<const unique_verts_iter &>(b)); + } + + inline bool operator!=(const brush_verts_iter &b) + { + return !(*this == b); + } + + inline brush_verts_iter &operator*() + { + return *this; + } + + const Brush *brush; + float center[3]; + float radius; + float radius_scale; + float radius_squared; + + float fade; + + SculptBrushTest test; + SculptBrushTestFn test_func; + + unique_verts_iter viter; + + private: + inline void skip_outside() + { + while (vertex && !test_func(&test, co)) { + unique_verts_iter::operator*(); + } + } + + SculptSession *_ss; + PBVHNode *_node; + int _thread_id; + }; + + inline unique_verts_iter forAllUniqueVerts(PBVHNode *node) + { + unique_verts_iter ret(node); + + return ret; + } + + inline float *getVertexCo(BMVert *v) + { + return v->co; + } + + const inline float *getVertexNormal(BMVert *v) + { + return v->no; + } + + inline void setVertexNormal(BMVert *v, float *no) + { + } + + /* + * SculptSession *ss, + PBVHNode *node, + SculptBrushTest test, + SculptBrushTestFn test_func, + Brush *brush, + float center[3], + float radius, + int thread_id) + */ + void forVertsInRange( + BrushSearchArgs args, + std::function<void(brush_verts_iter biter, int node_i, void *userdata)> callback, + std::function<void(PBVHNode *node, int node_i, void *userdata)> node_callback) + { + PBVHNode **nodes = NULL; + int totnode = 0; + + SculptBrushTest default_test; + + if (!args.test) { + args.test = &default_test; + args.test_func = SCULPT_brush_test_init_with_falloff_shape( + _ss, &default_test, args.brush->falloff_shape); + } + + SculptSearchSphereData data = {.ss = _ss, + .sd = _sd, + .radius_squared = args.radius * args.radius, + .original = args.use_original, + .center = args.center}; + + BKE_pbvh_search_gather(_pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); + + SculptSession *ss = _ss; + + /*SculptSession *ss, + PBVHNode *node, + SculptBrushTest test, + SculptBrushTestFn test_func, + Brush *brush, + float *center, + float radius, + int thread_id) + */ + + blender::IndexRange range(0, totnode); + blender::threading::parallel_for( + range, 1, [&args, &nodes, &ss, &callback, &node_callback](blender::IndexRange &subrange) { + for (auto i : subrange) { + brush_verts_iter biter(ss, + nodes[i], + args.test, + args.test_func, + args.brush, + args.center, + args.radius, + (int)i); + + for (auto viter : biter) { + callback(viter, (int)i, args.userdata); + } + + if (node_callback) { + node_callback(nodes[i], (int)i, args.userdata); + } + } + }); + } + + private: + BMesh *_bm; + PBVH *_pbvh; + SculptSession *_ss; + Sculpt *_sd; +}; + +class BMeshBackend { + public: + BMeshBackend() + { + } + + private: +}; + +/* clang-format off */ + +template<class PBVHClass, class V, class E, class F> +concept PBVHBackend = requires(PBVHClass b, V v){ + {(*(b.forAllUniqueVerts(nullptr))).vertex} -> std::same_as<V>; + {b.getVertexCo(v)} -> std::same_as<float*>; + {b.getVertexNormal(v)} -> std::same_as<const float*>; +}; +/* clang-format on */ + +template<class V, class E, class F, class Backend, PBVHBackend<V, E, F> PBVHClass> +class SculptImpl { + public: + PBVHClass *pbvh; + SculptSession *ss; + + SculptImpl(SculptSession *ss, PBVHClass *pbvh) : pbvh(pbvh), ss(ss) + { + } + + /* + &((BrushSearchArgs *){0}) + */ + inline void moveVerts(float cent[3], float radius, float offset[3]) + { + /* clang-format off */ + pbvh->forVertsInRange( + { + .brush = ss->cache->brush, + .radius = radius, + .use_threads = true, + .use_original = false + }, + + [&offset](auto viter, int node_i, void *userdata) { + //add offset to vertex coordinates + + madd_v3_v3fl(viter.co, offset, viter.fade); + printf("yay"); + }, + + [](PBVHNode *node, int node_i, void *userdata) { + BKE_pbvh_node_mark_update(node); + }); + + /* clang-format on */ + } + + private: +}; + +} // namespace sculpt +} // namespace blender +#endif diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c index 35f48400fe2..09d5298f7a5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.c +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c @@ -73,9 +73,7 @@ AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss) return NULL; } -bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, - const Brush *br, - const eAutomasking_flag mode) +bool SCULPT_is_automasking_mode_enabled(Sculpt *sd, const Brush *br, const eAutomasking_flag mode) { if (br) { return br->automasking_flags & mode || sd->automasking_flags & mode; @@ -83,11 +81,13 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, return sd->automasking_flags & mode; } -bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br) +bool SCULPT_is_automasking_enabled(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,10 +100,17 @@ 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; } -static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush) +static int sculpt_automasking_mode_effective_bits(Sculpt *sculpt, const Brush *brush) { if (brush) { return sculpt->automasking_flags | brush->automasking_flags; @@ -111,7 +118,19 @@ static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Br return sculpt->automasking_flags; } -static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush) +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(Sculpt *sd, const Brush *brush) { const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush); @@ -127,16 +146,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) { @@ -146,7 +186,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession } if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) { - if (SCULPT_vertex_is_boundary(ss, vert)) { + if (SCULPT_vertex_is_boundary(ss, vert, SCULPT_BOUNDARY_MESH)) { return 0.0f; } } @@ -157,7 +197,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession } } - return 1.0f; + return mask; } void SCULPT_automasking_cache_free(AutomaskingCache *automasking) @@ -166,11 +206,14 @@ 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); } -static bool sculpt_automasking_is_constrained_by_radius(Brush *br) +static bool sculpt_automasking_is_constrained_by_radius(const Brush *br) { /* 2D falloff is not constrained by radius. */ if (br->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { @@ -184,38 +227,47 @@ 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); + const Brush *brush = BKE_paint_brush(&sd->paint); if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { BLI_assert_msg(0, "Topology masking: pmap missing"); - return NULL; + return; } 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 +278,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,62 +286,66 @@ 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) { BLI_assert_msg(0, "Face Sets automasking: pmap missing"); - return NULL; + return; } 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; if (!ss->pmap) { BLI_assert_msg(0, "Boundary Edges masking: pmap missing"); - return NULL; + return; } const int totvert = SCULPT_vertex_count_get(ss); 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, SCULPT_BOUNDARY_MESH)) { 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 +354,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,28 +370,88 @@ 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, SculptSession *ss, Sculpt *sd, - Brush *brush) + const 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; } -AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob) +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, + const 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, const Brush *brush, Object *ob) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -350,9 +468,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 +499,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..b55e362c142 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -23,6 +23,8 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_blenlib.h" #include "BLI_edgehash.h" #include "BLI_math.h" @@ -37,6 +39,10 @@ #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_multires.h" #include "BKE_node.h" @@ -55,26 +61,61 @@ #include "bmesh.h" +#include "ED_mesh.h" +#include "ED_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "WM_api.h" +#include "WM_types.h" + #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 + +static void boundary_color_vis(SculptSession *ss, SculptBoundary *boundary); +static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *boundary); + 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,26 +126,27 @@ 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, SCULPT_BOUNDARY_MESH)) { 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)) { + if (SCULPT_vertex_is_boundary(ss, initial_vertex, SCULPT_BOUNDARY_MESH)) { return initial_vertex; } @@ -114,13 +156,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 +177,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 +219,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 +230,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 +241,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, SCULPT_BOUNDARY_MESH)) { boundary_vertex_count++; } } @@ -218,73 +273,362 @@ 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; - if (!SCULPT_vertex_is_boundary(ss, to_v)) { + 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, SCULPT_BOUNDARY_MESH)) { 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, +static float *calc_boundary_tangent(SculptSession *ss, SculptBoundary *boundary) +{ + const int totvert = SCULPT_vertex_count_get(ss); + float dir[3]; + + float(*tangents)[3] = MEM_calloc_arrayN( + totvert, sizeof(float) * 3, "boundary->boundary_tangents"); + + for (int i = 0; i < totvert; i++) { + float f1 = boundary->boundary_dist[i]; + + if (f1 == FLT_MAX) { + continue; + } + + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + const float *co1 = SCULPT_vertex_co_get(ss, vertex); + + zero_v3(dir); + + SculptVertexNeighborIter ni; + + float no1[3]; + SCULPT_vertex_normal_get(ss, vertex, no1); + +#if 0 + volatile int val = SCULPT_vertex_valence_get(ss, vertex); + float *ws = BLI_array_alloca(ws, val); + float *cot1 = BLI_array_alloca(cot1, val); + float *cot2 = BLI_array_alloca(cot2, val); + float *areas = BLI_array_alloca(areas, val); + float totarea; + + SCULPT_get_cotangents(ss, vertex, ws, cot1, cot2, areas, &totarea); + + float(*cos)[3] = BLI_array_alloca(cos, val); + float *scalars = BLI_array_alloca(scalars, val); + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + scalars[ni.i] = boundary->boundary_dist[ni.index]; + copy_v3_v3(cos[ni.i], SCULPT_vertex_co_get(ss, ni.vertex)); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + for (int j1 = 0; j1 < val; j1++) { + int j2 = (j1 + 1) % val; + + float *co2 = cos[j1]; + float *co3 = cos[j2]; + float dir2[3]; + float dir3[3]; + + float f2 = scalars[j1]; + float f3 = scalars[j2]; + + if (f2 == FLT_MAX || f1 == FLT_MAX) { + continue; + } + + float du = f2 - f1; + float dv = f3 - f1; + + sub_v3_v3v3(dir2, co2, co1); + sub_v3_v3v3(dir3, co3, co1); + + mul_v3_fl(dir2, du); + mul_v3_fl(dir3, dv); + + add_v3_v3(dir2, dir3); + // normalize_v3(dir2); + + float w = 1.0; // ws[j1]; + + madd_v3_v3v3fl(dir, dir, dir2, w); + } + + normalize_v3(dir); + copy_v3_v3(tangents[i], dir); + +#else + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); + float no2[3]; + + SCULPT_vertex_normal_get(ss, ni.vertex, no2); + + // int i2 = BKE_pbvh_vertex_index_to_table(ss->pbvh, ni.vertex); + int i2 = ni.index; + + float f2 = boundary->boundary_dist[i2]; + float dir2[3]; + + sub_v3_v3v3(dir2, co2, co1); + + if (f2 == FLT_MAX) { + continue; + } + + float distsqr = len_squared_v3v3(co1, co2); + if (distsqr == 0.0f) { + continue; + } + + float w = (f2 - f1) / distsqr; + + mul_v3_fl(dir2, w); + add_v3_v3(dir, dir2); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + normalize_v3(dir); + negate_v3(dir); + + copy_v3_v3(tangents[i], dir); +#endif + } + + return (float *)tangents; +} + +static void sculpt_boundary_cotan_init(SculptSession *ss, SculptBoundary *boundary) +{ + const int totvert = SCULPT_vertex_count_get(ss); + boundary->boundary_cotangents = MEM_calloc_arrayN( + totvert, sizeof(StoredCotangentW), "StoredCotangentW"); + StoredCotangentW *cotw = boundary->boundary_cotangents; + + for (int i = 0; i < totvert; i++, cotw++) { + if (boundary->boundary_dist[i] == FLT_MAX) { + cotw->length = 0; + cotw->weights = NULL; + continue; + } + + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + const int val = SCULPT_vertex_valence_get(ss, vertex); + + cotw->length = val; + + if (val < MAX_STORED_COTANGENTW_EDGES) { + cotw->weights = cotw->static_weights; + } + else { + cotw->weights = (float *)MEM_malloc_arrayN(val, sizeof(*cotw->weights), "cotw->weights"); + } + + SCULPT_get_cotangents(ss, vertex, cotw->weights, NULL, NULL, NULL, NULL); + } +} + +static void sculpt_boundary_indices_init(Object *ob, + SculptSession *ss, SculptBoundary *boundary, const bool init_boundary_distances, - const int initial_boundary_index) + const SculptVertRef initial_boundary_index, + const float radius) { 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"); + + boundary->sculpt_totvert = totvert; + 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); + GSet *included_vertices = BLI_gset_ptr_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE); SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); 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}, }; SCULPT_floodfill_execute(ss, &flood, boundary_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); + GSet *boundary_verts; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + boundary_verts = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE); + + GSetIterator gi; + + GSET_ITER (gi, included_vertices) { + BMVert *v = (BMVert *)BLI_gsetIterator_getKey(&gi); + BLI_gset_add(boundary_verts, POINTER_FROM_INT(v->head.index)); + } + } + else { + boundary_verts = included_vertices; + } + + boundary->boundary_closest = MEM_calloc_arrayN( + totvert, sizeof(SculptVertRef), "boundary_closest"); + boundary->boundary_dist = SCULPT_geodesic_distances_create( + ob, boundary_verts, radius, boundary->boundary_closest, NULL); + + sculpt_boundary_cotan_init(ss, boundary); + +#if 0 // smooth geodesic scalar field + float *boundary_dist = MEM_calloc_arrayN(totvert, sizeof(float), "boundary_dist"); + + for (int iteration = 0; iteration < 4; iteration++) { + for (int i = 0; i < totvert; i++) { + if (boundary->boundary_dist[i] == FLT_MAX) { + boundary_dist[i] = FLT_MAX; + continue; + } + + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + float tot = 0.0f; + + StoredCotangentW *cotw = boundary->boundary_cotangents + i; + + SculptVertexNeighborIter ni; + int j = 0; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + if (boundary->boundary_dist[ni.index] == FLT_MAX) { + j++; + continue; + } + + const float w = cotw->weights[j]; + + boundary_dist[i] += boundary->boundary_dist[ni.index] * w; + + tot += w; + j++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (tot == 0.0f) { + boundary_dist[i] = FLT_MAX; + } + else { + boundary_dist[i] /= tot; + } + } + + SWAP(float *, boundary_dist, boundary->boundary_dist); + } + + MEM_SAFE_FREE(boundary_dist); +#endif + + boundary->boundary_tangents = (float(*)[3])calc_boundary_tangent(ss, boundary); + +#if 1 // smooth geodesic tangent field + float(*boundary_tangents)[3] = MEM_calloc_arrayN( + totvert, sizeof(float) * 3, "boundary_tangents"); + + for (int iteration = 0; iteration < 4; iteration++) { + for (int i = 0; i < totvert; i++) { + + if (boundary->boundary_dist[i] == FLT_MAX) { + copy_v3_v3(boundary_tangents[i], boundary->boundary_tangents[i]); + continue; + } + + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + float tot = 0.0f; + + StoredCotangentW *cotw = boundary->boundary_cotangents + i; + float tan[3] = {0.0f, 0.0f, 0.0f}; + + SculptVertexNeighborIter ni; + int j = 0; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + if (boundary->boundary_dist[ni.index] == FLT_MAX) { + j++; + continue; + } + + add_v3_v3(tan, boundary->boundary_tangents[ni.index]); + + tot += 1.0f; + j++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (tot == 0.0f) { + continue; + } + + normalize_v3(tan); + interp_v3_v3v3(boundary_tangents[i], boundary->boundary_tangents[i], tan, 0.75f); + normalize_v3(boundary_tangents[i]); + } + + float(*tmp)[3] = boundary_tangents; + boundary_tangents = boundary->boundary_tangents; + boundary->boundary_tangents = tmp; + } + + MEM_SAFE_FREE(boundary_tangents); +#endif + + boundary_color_vis(ss, boundary); + + if (boundary_verts != included_vertices) { + BLI_gset_free(boundary_verts, NULL); + } + /* 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; } } @@ -294,6 +638,54 @@ static void sculpt_boundary_indices_init(SculptSession *ss, BLI_gset_free(included_vertices, NULL); } +static void boundary_color_vis(SculptSession *ss, SculptBoundary *boundary) +{ + if (boundary->boundary_dist && G.debug_value == 890 && ss->bm && + CustomData_has_layer(&ss->bm->vdata, CD_PROP_COLOR)) { + const int cd_color = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR); + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + + BMIter iter; + BMVert *v; + int i = 0; + + float min = 1e17f, max = -1e17f; + + // calc bounds + BM_ITER_MESH_INDEX (v, &iter, ss->bm, BM_VERTS_OF_MESH, i) { + float f = boundary->boundary_dist[i]; + + if (f == FLT_MAX) { + continue; + } + + min = MIN2(min, f); + max = MAX2(max, f); + } + + float scale = max != min ? 1.0f / (max - min) : 0.0f; + + BM_ITER_MESH_INDEX (v, &iter, ss->bm, BM_VERTS_OF_MESH, i) { + MPropCol *mcol = BM_ELEM_CD_GET_VOID_P(v, cd_color); + + float f = boundary->boundary_dist[i]; + + if (f == FLT_MAX) { + mcol->color[0] = mcol->color[1] = 1.0f; + mcol->color[2] = 0.0f; + mcol->color[3] = 1.0f; + continue; + } + else { + f = (f - min) * scale; + } + + mcol->color[0] = mcol->color[1] = mcol->color[2] = f; + mcol->color[3] = 1.0f; + } + } +} + /** * This functions initializes all data needed to calculate falloffs and deformation from the * boundary into the mesh into a #SculptBoundaryEditInfo array. This includes how many steps are @@ -302,7 +694,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 +702,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 +728,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 +752,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 +789,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 +804,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 +817,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 +848,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 +860,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 +897,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,17 +927,23 @@ 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 : false; - sculpt_boundary_indices_init(ss, boundary, init_boundary_distances, boundary_initial_vertex); - const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius; + + sculpt_boundary_indices_init( + object, ss, boundary, init_boundary_distances, boundary_initial_vertex, boundary_radius); + sculpt_boundary_edit_data_init(ss, boundary, boundary_initial_vertex, boundary_radius); + if (ss->cache) { + SCULPT_boundary_build_smoothco(ss, boundary); + } + return boundary; } @@ -535,66 +952,571 @@ void SCULPT_boundary_data_free(SculptBoundary *boundary) MEM_SAFE_FREE(boundary->vertices); MEM_SAFE_FREE(boundary->edges); MEM_SAFE_FREE(boundary->distance); + + MEM_SAFE_FREE(boundary->boundary_dist); + MEM_SAFE_FREE(boundary->boundary_tangents); + MEM_SAFE_FREE(boundary->boundary_closest); + MEM_SAFE_FREE(boundary->smoothco); + MEM_SAFE_FREE(boundary->edit_info); MEM_SAFE_FREE(boundary->bend.pivot_positions); MEM_SAFE_FREE(boundary->bend.pivot_rotation_axis); MEM_SAFE_FREE(boundary->slide.directions); + + StoredCotangentW *cotw = boundary->boundary_cotangents; + + if (cotw) { + for (int i = 0; i < boundary->sculpt_totvert; i++, cotw++) { + if (cotw->weights != cotw->static_weights) { + MEM_SAFE_FREE(cotw->weights); + } + } + } + + MEM_SAFE_FREE(boundary->boundary_cotangents); MEM_SAFE_FREE(boundary); } +typedef struct ScalarFieldWalkData { + SculptVertRef v; + float co[3]; + + struct { + SculptVertRef v1, v2; + } edge; + + float t, f; + bool has_edge; +} ScalarFieldWalkData; + +static void sculpt_walk_scalar_field_init(SculptSession *ss, + SculptVertRef v, + ScalarFieldWalkData *wd, + float *field) +{ + wd->v = v; + copy_v3_v3(wd->co, SCULPT_vertex_co_get(ss, v)); + wd->has_edge = false; + wd->t = 0.0f; + + int i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v); + wd->f = field[i]; +} + +/*walk in decreasing direction of scalar field*/ +static bool sculpt_walk_scalar_field(SculptSession *ss, + ScalarFieldWalkData *wd, + float *field, + float (*dfield)[3]) +{ + SculptVertexNeighborIter ni; + SculptVertexNeighborIter ni2; + SculptVertRef v = wd->v, minv1 = {-1LL}, minv2 = {-1LL}; + float mindis1 = FLT_MAX, mindis2 = FLT_MAX; + float minf1 = 0.0, minf2 = 0.0; + float minl1 = 0.0, minl2 = 0.0; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v, ni) { + const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); + float f2 = field[ni.index]; + + if (ni.vertex.i == v.i) { + continue; + } + + if (f2 > wd->f) { + continue; + } + + float len = len_v3v3(co2, wd->co); + float dist = f2 * len; + + if (dist >= mindis1) { + continue; + } + + mindis1 = dist; + minf1 = f2; + minl1 = len; + minv1 = ni.vertex; + + mindis2 = FLT_MAX; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni2) { + if (ni2.vertex.i == ni.vertex.i) { + continue; + } + + const float *co3 = SCULPT_vertex_co_get(ss, ni2.vertex); + float f3 = field[ni2.index]; + + float len2 = len_v3v3(co3, wd->co); + float dist2 = f3 * len; // wd->f + (f2 - wd->f) * len; + + if (dist2 < mindis2) { + mindis2 = dist2; + minf2 = f3; + minl2 = len2; + minv2 = ni2.vertex; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni2); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (minv1.i == -1LL) { + // didn't find anything + return false; + } + + if (minv2.i == -1LL) { + wd->v = minv1; + copy_v3_v3(wd->co, SCULPT_vertex_co_get(ss, minv1)); + wd->has_edge = false; + wd->f = minf1; + + return true; + } + + wd->has_edge = true; + wd->edge.v1 = minv1; + wd->edge.v2 = minv2; + + /* + on factor + load_package "avector"; + + comment: relative to wd.co; + a := avec(ax, ay, az); + b := avec(bx, by, bz); + + dva := avec(dvax, dvay, dvaz); + dvb := avec(dvbx, dvby, dvbz); + + la := a dot a; + lb := b dot b; + + f2 := a + (b - a) * t; + df2 := dva + (dvb - dva)*t; + + ll := f2 dot f2; + f1 := (minf1 + (minf2 - minf1)*t) * ll; + + ff := solve(df(f1, t, 2), t); + f := part(ff, 1, 2); + + + */ + + const float *a = SCULPT_vertex_co_get(ss, minv1); + const float *b = SCULPT_vertex_co_get(ss, minv2); + + float ax = a[0] - wd->co[0]; + float ay = a[1] - wd->co[1]; + float az = a[2] - wd->co[2]; + + float bx = b[0] - wd->co[0]; + float by = b[1] - wd->co[1]; + float bz = b[2] - wd->co[2]; + + float div = (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + + (ax - 2.0 * bx) * ax); + + float t = ((ay - by) * ay + (az - bz) * az + (ax - bx) * ax) / div; + + float m1m2 = minf1 + minf2; + + float ans4 = -2.0 * (by * by + bz * bz + bx * bx) * m1m2 * az * bz * minf1 - + (2.0 * (minf1 + minf2) * bz - az * minf2) * az * az * az * minf2; + + float sqr2 = (by * by + bz * bz + bx * bx); + sqr2 = sqr2 * sqr2; + + float ans3 = + (2.0 * + ((4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * by - + (minf1 + minf2) * ay * minf2) * + ay + + (4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * bz - + (minf1 + minf2) * az * minf2) * + az - + (by * by + bz * bz + bx * bx) * m1m2 * minf1) * + bx - + (2.0 * m1m2 * bx - ax * minf2) * ax * ax * minf2 - + ((by * by + bz * bz) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - + (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * bx * bx + + 2.0 * (m1m2 * bz - az * minf2) * az * minf2 + + 2.0 * (m1m2 * by - ay * minf2) * ay * minf2) * + ax) * + ax - + (2.0 * + ((by * by + bz * bz + bx * bx) * m1m2 * minf1 - + (4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * bz - m1m2 * az * minf2) * + az) * + by + + (2.0 * (minf1 + minf2) * by - ay * minf2) * ay * ay * minf2 + + ((bx * bx + bz * bz) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - + (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * by * by + + 2.0 * (m1m2 * bz - az * minf2) * az * minf2) * + ay) * + ay - + ((bx * bx + by * by) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - + (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * bz * bz) * + az * az + + sqr2 * minf1 * minf1 + ans4; + + float ans2 = sqrtf(ans3); + + float ans1 = (by * by + bz * bz + bx * bx) * minf1 + + ((3.0 * minf1 - 2.0 * minf2) * az - 2.0 * (2.0 * minf1 - minf2) * bz) * az + + ((3.0 * minf1 - 2.0 * minf2) * ay - 2.0 * (2.0 * minf1 - minf2) * by) * ay + + ((3.0 * minf1 - 2.0 * minf2) * ax - 2.0 * (2.0 * minf1 - minf2) * bx) * ax + ans2; + + t = ans1 / (3.0 * + (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + + (ax - 2.0 * bx) * ax) * + (minf1 - minf2)); + +#if 1 + t = (((3.0 * minf1 - 2.0 * minf2) * ay - 2.0 * (2.0 * minf1 - minf2) * by) * ay + + ((3.0 * minf1 - 2.0 * minf2) * az - 2.0 * (2.0 * minf1 - minf2) * bz) * az + + ((3.0 * minf1 - 2.0 * minf2) * ax - 2.0 * (2.0 * minf1 - minf2) * bx) * ax + + (by * by + bz * bz + bx * bx) * minf1) / + (3.0 * + (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + + (ax - 2.0 * bx) * ax) * + (minf1 - minf2)); +#endif + + t = t < 0.0f ? 0.0f : t; + t = t > 1.0f ? 1.0f : t; + + t = 0.5f; + wd->t = t; + + wd->v = minv1; + wd->f = minf1 + (minf2 - minf1) * wd->t; + float co[3]; + + interp_v3_v3v3(co, SCULPT_vertex_co_get(ss, minv1), SCULPT_vertex_co_get(ss, minv2), wd->t); + + float f3 = wd->f * len_v3v3(wd->co, co); + if (f3 > mindis1 || f3 > mindis2) { + wd->f = minf1; + t = 0.0f; + interp_v3_v3v3(co, SCULPT_vertex_co_get(ss, minv1), SCULPT_vertex_co_get(ss, minv2), wd->t); + } + + copy_v3_v3(wd->co, co); + + return true; +} + +Object *sculpt_get_vis_object(bContext *C, SculptSession *ss, char *name) +{ + if (!C) { + C = ss->cache->C; + } + + Scene *scene = CTX_data_scene(C); + ViewLayer *vlayer = CTX_data_view_layer(C); + Main *bmain = CTX_data_main(C); + Object *actob = CTX_data_active_object(C); + + View3D *v3d = CTX_wm_view3d(C); + unsigned short local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + + Object *ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + + if (!ob) { + Mesh *me = BKE_mesh_add(bmain, name); + + ob = BKE_object_add_only_object(bmain, OB_MESH, name); + ob->data = (void *)me; + id_us_plus((ID *)me); + + DEG_id_tag_update_ex( + bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + + LayerCollection *layer_collection = BKE_layer_collection_get_active(vlayer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + } + + copy_v3_v3(ob->loc, actob->loc); + copy_v3_v3(ob->rot, actob->rot); + BKE_object_to_mat4(ob, ob->obmat); + + DEG_id_type_tag(bmain, ID_OB); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + DEG_id_tag_update(&scene->id, 0); + + Mesh *me = (Mesh *)ob->data; + + DEG_id_tag_update(&me->id, ID_RECALC_ALL); + return ob; +} + +void sculpt_end_vis_object(bContext *C, SculptSession *ss, Object *ob, BMesh *bm) +{ + if (!C) { + C = ss->cache->C; + } + + Scene *scene = CTX_data_scene(C); + ViewLayer *vlayer = CTX_data_view_layer(C); + Main *bmain = CTX_data_main(C); + Object *actob = CTX_data_active_object(C); + + Mesh *me = (Mesh *)ob->data; + + BM_mesh_bm_to_me(bmain, + NULL, + bm, + me, + (&(struct BMeshToMeshParams){.calc_object_remap = false, + .update_shapekey_indices = false, + .copy_temp_cdlayers = false})); + + DEG_id_tag_update(&me->id, ID_RECALC_ALL); +} + +//#define VISBM + /* These functions initialize the required vectors for the desired deformation using the * SculptBoundaryEditInfo. They calculate the data using the vertices that have the * max_propagation_steps value and them this data is copied to the rest of the vertices using the * original vertex index. */ -static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *boundary) +static void sculpt_boundary_bend_data_init(SculptSession *ss, + SculptBoundary *boundary, + float radius) { +#ifdef VISBM + Object *visob = get_vis_object(ss, "_vis_sculpt_boundary_bend_data_init"); + BMAllocTemplate alloc = {512, 512, 512, 512}; + BMesh *visbm = BM_mesh_create(&alloc, + (&(struct BMeshCreateParams){.use_unique_ids = 0, + .use_id_elem_mask = 0, + .use_id_map = 0, + .use_toolflags = 0, + .no_reuse_ids = 0})); +#endif + const int totvert = SCULPT_vertex_count_get(ss); boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN( totvert, 3 * sizeof(float), "pivot rotation axis"); boundary->bend.pivot_positions = MEM_calloc_arrayN( - totvert, 3 * sizeof(float), "pivot positions"); + totvert, 4 * sizeof(float), "pivot positions"); for (int i = 0; i < totvert; i++) { + boundary->bend.pivot_positions[i][3] = 0.0f; + } + + for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + +#ifdef VISBM + if (boundary->boundary_dist[i] != FLT_MAX) { + const float *co1 = SCULPT_vertex_co_get(ss, vertex); + float *dir = boundary->boundary_tangents[i]; + + BMVert *v1, *v2; + + float tmp[3]; + madd_v3_v3v3fl(tmp, co1, dir, 0.35); + + v1 = BM_vert_create(visbm, co1, NULL, BM_CREATE_NOP); + v2 = BM_vert_create(visbm, tmp, NULL, BM_CREATE_NOP); + BM_edge_create(visbm, v1, v2, NULL, BM_CREATE_NOP); + } +#endif + + if (boundary->boundary_closest[i].i != -1LL) { + SculptVertRef v = boundary->boundary_closest[i]; + boundary->edit_info[i].original_vertex = v; + boundary->edit_info[i].original_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v); + } + if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { continue; } + } + + for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (boundary->edit_info[i].original_vertex_i == BOUNDARY_VERTEX_NONE) { + continue; + } + + if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { + continue; + } + + const float *co1 = SCULPT_vertex_co_get(ss, vertex); + 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)); + SCULPT_vertex_normal_get(ss, vertex, normal); + sub_v3_v3v3(dir, SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), co1); + + normalize_v3(dir); + + float olddir[3]; + copy_v3_v3(olddir, dir); + + if (boundary->boundary_dist[i] != FLT_MAX) { + float f1 = boundary->boundary_dist[i]; + + zero_v3(dir); + copy_v3_v3(dir, boundary->boundary_tangents[i]); + + if (dot_v3v3(dir, dir) < 0.00001f) { + sub_v3_v3v3(dir, + SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), + SCULPT_vertex_co_get(ss, vertex)); + } + } + else { + // continue; + } + 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)); + 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]); + + const float *oco = SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex); + float pos[3]; + + copy_v3_v3(pos, co1); + + copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i], pos); + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] = 1.0f; } for (int i = 0; i < totvert; i++) { + if (boundary->bend.pivot_positions[i][3] > 1.0f) { + mul_v3_fl(boundary->bend.pivot_positions[i], 1.0f / boundary->bend.pivot_positions[i][3]); + boundary->bend.pivot_positions[i][3] = 1.0f; + } + } + + // fix any remaining boundaries without pivots + for (int vi = 0; vi < boundary->num_vertices; vi++) { + SculptVertRef v = boundary->vertices[vi]; + const float *co1 = SCULPT_vertex_co_get(ss, v); + int i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v); + + if (boundary->bend.pivot_positions[i][3] != 0.0f) { + continue; + } + + float minlen = FLT_MAX; + + // nasty inner loop here + for (int j = 0; j < totvert; j++) { + if (boundary->edit_info[j].num_propagation_steps != boundary->max_propagation_steps) { + continue; + } + + SculptVertRef v2 = BKE_pbvh_table_index_to_vertex(ss->pbvh, j); + const float *co2 = SCULPT_vertex_co_get(ss, v2); + + float len = len_v3v3(co2, co1); + + if (len < minlen) { + minlen = len; + copy_v3_v3(boundary->bend.pivot_positions[i], co2); + boundary->bend.pivot_positions[i][3] = 1.0f; + } + } + } + + for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + const float *co1 = SCULPT_vertex_co_get(ss, vertex); + float dir[3]; + 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]); - copy_v3_v3(boundary->bend.pivot_rotation_axis[i], - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); + + float pos[3], oco[3]; + copy_v3_v3(pos, boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]); + copy_v3_v3(oco, SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex)); + + if (boundary->boundary_dist[i] != FLT_MAX) { + float no[3]; + + SCULPT_vertex_normal_get(ss, vertex, no); + + // snap to radial plane + cross_v3_v3v3(dir, no, boundary->boundary_tangents[i]); + normalize_v3(dir); + //* + + sub_v3_v3(pos, oco); + normalize_v3(pos); + mul_v3_fl(pos, radius); + add_v3_v3(pos, oco); + + sub_v3_v3(pos, co1); + madd_v3_v3fl(pos, dir, -dot_v3v3(dir, pos)); + add_v3_v3(pos, co1); + + //*/ + + copy_v3_v3(boundary->bend.pivot_rotation_axis[i], dir); + } + else { + zero_v3(dir); + + // printf("boundary info missing tangent\n"); + copy_v3_v3(boundary->bend.pivot_rotation_axis[i], + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); + } + + copy_v3_v3(boundary->bend.pivot_positions[i], pos); + +#ifdef VISBM + { + BMVert *v1, *v2; + v1 = BM_vert_create(visbm, co1, NULL, BM_CREATE_NOP); + + v2 = BM_vert_create(visbm, pos, NULL, BM_CREATE_NOP); + BM_edge_create(visbm, v1, v2, NULL, BM_CREATE_NOP); + + float tmp[3]; + madd_v3_v3v3fl(tmp, co1, dir, 0.35); + + v2 = BM_vert_create(visbm, tmp, NULL, BM_CREATE_NOP); + BM_edge_create(visbm, v1, v2, NULL, BM_CREATE_NOP); + } +#endif } + +#ifdef VISBM + end_vis_object(ss, visob, visbm); +#endif } 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 +1524,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 +1532,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 +1579,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; @@ -672,16 +1594,17 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, continue; } - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; } 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 +1634,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); @@ -720,14 +1643,14 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, continue; } - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; } 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 +1680,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); @@ -766,14 +1689,14 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, continue; } - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; } 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,21 +1728,21 @@ 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) { continue; } - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; } 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 +1771,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; @@ -863,14 +1786,14 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, continue; } - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; } 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,14 +1825,14 @@ 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) { continue; } - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -919,9 +1842,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++; } } @@ -946,16 +1869,126 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } +static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *boundary) +{ + const int totvert = SCULPT_vertex_count_get(ss); + PBVHNode **nodes; + int totnode; + + const int max_iterations = 4; + const float fract = 1.0f / max_iterations; + float bstrength = ss->cache->brush->autosmooth_factor; + + CLAMP(bstrength, 0.0f, 1.0f); + + const int count = (int)(bstrength * max_iterations); + const float last = max_iterations * (bstrength - count * fract); + + const float boundary_radius = ss->cache->radius * (1.0f + ss->cache->brush->boundary_offset) * + ss->cache->brush->autosmooth_radius_factor; + + BKE_curvemapping_init(ss->cache->brush->curve); + + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes, &totnode); + + for (int iteration = 0; iteration <= count; iteration++) { + for (int i = 0; i < totnode; i++) { + const float strength = (iteration != count) ? 1.0f : last; + + PBVHNode *node = nodes[i]; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (boundary->boundary_dist[vd.index] == FLT_MAX) { + continue; + } + + if (boundary->edit_info[vd.index].num_propagation_steps == BOUNDARY_STEPS_NONE) { + continue; + } + + float fac = boundary->boundary_dist[vd.index] / boundary_radius; + + if (fac > 1.0f) { + continue; + } + + fac = BKE_brush_curve_strength(ss->cache->brush, fac, 1.0f); + + float sco[3]; + + SCULPT_neighbor_coords_average_interior( + ss, sco, vd.vertex, ss->cache->brush->autosmooth_projection, NULL, false); + + float *co = SCULPT_brush_deform_target_vertex_co_get( + ss, ss->cache->brush->deform_target, &vd); + + interp_v3_v3v3(co, co, sco, strength * fac); + BKE_pbvh_node_mark_update(node); + } + BKE_pbvh_vertex_iter_end; + } + } + + MEM_SAFE_FREE(nodes); +} + +static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *boundary) +{ + const int totvert = SCULPT_vertex_count_get(ss); + PBVHNode **nodes; + int totnode; + + boundary->smoothco = MEM_calloc_arrayN(totvert, sizeof(float) * 3, "boundary->smoothco"); + + const float projection = 0.5f; + + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes, &totnode); + + for (int iteration = 0; iteration < 3; iteration++) { + for (int i = 0; i < totnode; i++) { + PBVHNode *node = nodes[i]; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (boundary->boundary_dist[vd.index] == FLT_MAX) { + continue; + } + + float sco[3]; + + SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, NULL, false); + + float *co = SCULPT_brush_deform_target_vertex_co_get( + ss, ss->cache->brush->deform_target, &vd); + + interp_v3_v3v3(sco, sco, co, 0.25); + BKE_pbvh_node_mark_update(node); + + copy_v3_v3(boundary->smoothco[vd.index], sco); + } + BKE_pbvh_vertex_iter_end; + } + } + + MEM_SAFE_FREE(nodes); +} /* Main Brush Function. */ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + SCULPT_cotangents_begin(ob, ss); + + const float radius = ss->cache->radius; + const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius; + 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); } @@ -970,10 +2003,9 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn ob, brush, initial_vertex, ss->cache->initial_radius); if (ss->cache->boundaries[symm_area]) { - switch (brush->boundary_deform_type) { case BRUSH_BOUNDARY_DEFORM_BEND: - sculpt_boundary_bend_data_init(ss, ss->cache->boundaries[symm_area]); + sculpt_boundary_bend_data_init(ss, ss->cache->boundaries[symm_area], boundary_radius); break; case BRUSH_BOUNDARY_DEFORM_EXPAND: sculpt_boundary_slide_data_init(ss, ss->cache->boundaries[symm_area]); @@ -990,6 +2022,33 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn sculpt_boundary_falloff_factor_init( ss, ss->cache->boundaries[symm_area], brush, ss->cache->initial_radius); } + + if (ss->bm && ss->cache->boundaries[symm_area] && + ss->cache->boundaries[symm_area]->boundary_dist) { + PBVHNode **nodes2; + int totnode2 = 0; + + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes2, &totnode2); + + for (int i = 0; i < totnode2; i++) { + PBVHNode *node = nodes2[i]; + PBVHVertexIter vd; + + bool ok = false; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (ss->cache->boundaries[symm_area]->boundary_dist[vd.index] != FLT_MAX) { + ok = true; + break; + } + } + BKE_pbvh_vertex_iter_end; + + if (ok) { + SCULPT_ensure_dyntopo_node_undo(ob, node, SCULPT_UNDO_COORDS, -1); + } + } + } } /* No active boundary under the cursor. */ @@ -1027,6 +2086,12 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_smooth_task_cb_ex, &settings); break; } + + if (brush->autosmooth_factor > 0.0f) { + BKE_pbvh_update_normals(ss->pbvh, ss->subdiv_ccg); + + SCULPT_boundary_autosmooth(ss, ss->cache->boundaries[symm_area]); + } } void SCULPT_boundary_edges_preview_draw(const uint gpuattr, diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_machine.c b/source/blender/editors/sculpt_paint/sculpt_brush_machine.c new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_brush_machine.c 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..9bc2f840e3a --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.c @@ -0,0 +1,261 @@ +/* + * 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_alloca.h" +#include "BLI_array.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); + + if (useAccurateSolver) { + int val = SCULPT_vertex_valence_get(ss, vertex); + float *ws = BLI_array_alloca(ws, val); + float *cot1 = BLI_array_alloca(cot1, val); + float *cot2 = BLI_array_alloca(cot2, val); + float *areas = BLI_array_alloca(areas, val); + float totarea = 0.0f; + + SCULPT_get_cotangents(ss, vertex, ws, cot1, cot2, areas, &totarea); + + 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); + madd_m3_m3m3fl(nmat, nmat, nmat2, ws[ni.i]); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + else { + 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..4676ff50079 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,84 @@ 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)) { - for (int i = 0; i < totnodes; i++) { - BKE_pbvh_node_mark_topology_update(nodes[i]); + DyntopoMaskCB mask_cb; + void *mask_cb_data; + + SCULPT_dyntopo_automasking_init(ss, sd, NULL, ob, &mask_cb, &mask_cb_data); + + const int max_steps = 10; + const int max_dyntopo_steps_coll = 1 << 13; + const int max_dyntopo_steps_subd = 1 << 15; + + int i = 0; + bool modified = true; + + while (modified) { + modified = BKE_pbvh_bmesh_update_topology(ss->pbvh, + PBVH_Collapse, + center, + NULL, + size, + false, + false, + -1, + false, + mask_cb, + mask_cb_data, + max_dyntopo_steps_coll); + + for (int j = 0; j < totnodes; j++) { + BKE_pbvh_node_mark_topology_update(nodes[j]); } + + modified |= BKE_pbvh_bmesh_update_topology(ss->pbvh, + PBVH_Subdivide, + center, + NULL, + size, + false, + false, + -1, + false, + mask_cb, + mask_cb_data, + max_dyntopo_steps_subd); + for (int j = 0; j < totnodes; j++) { + BKE_pbvh_node_mark_topology_update(nodes[j]); + } + + if (i++ > max_steps) { + break; + } + } + + /* one more time, but with cleanup valence 3/4 verts enabled */ + for (i = 0; i < 2; i++) { + for (int j = 0; j < totnodes; j++) { + BKE_pbvh_node_mark_topology_update(nodes[j]); + } + + BKE_pbvh_bmesh_update_topology(ss->pbvh, + PBVH_Cleanup, + center, + NULL, + size, + false, + false, + -1, + false, + mask_cb, + mask_cb_data, + max_dyntopo_steps_coll); } + SCULPT_dyntopo_automasking_end(mask_cb_data); + MEM_SAFE_FREE(nodes); SCULPT_undo_push_end(); @@ -174,13 +241,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 +260,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 +287,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 +649,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_displacement.c b/source/blender/editors/sculpt_paint/sculpt_displacement.c new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_displacement.c @@ -0,0 +1 @@ + diff --git a/source/blender/editors/sculpt_paint/sculpt_displacement.h b/source/blender/editors/sculpt_paint/sculpt_displacement.h new file mode 100644 index 00000000000..6f70f09beec --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_displacement.h @@ -0,0 +1 @@ +#pragma once diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index ae6dcbdbff4..2d5d428e676 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,14 +83,417 @@ #include <math.h> #include <stdlib.h> -void SCULPT_dynamic_topology_triangulate(BMesh *bm) +BMesh *SCULPT_dyntopo_empty_bmesh() { - if (bm->totloop != bm->totface * 3) { - BM_mesh_triangulate( - bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL); + const BMAllocTemplate allocsize = { + .totvert = 2048 * 16, .totface = 2048 * 16, .totloop = 4196 * 16, .totedge = 2048 * 16}; + + BMesh *bm = BM_mesh_create( + &allocsize, + &((struct BMeshCreateParams){.use_toolflags = false, + .create_unique_ids = true, + .id_elem_mask = BM_VERT | BM_EDGE | BM_FACE, + .id_map = true, + .temporary_ids = false, + .no_reuse_ids = false})); + + return bm; +} +// TODO: check if (mathematically speaking) is it really necassary +// to sort the edge lists around verts + +// from http://rodolphe-vaillant.fr/?e=20 +static float tri_voronoi_area(float p[3], float q[3], float r[3]) +{ + float pr[3]; + float pq[3]; + + sub_v3_v3v3(pr, p, r); + sub_v3_v3v3(pq, p, q); + + float angles[3]; + + angle_tri_v3(angles, p, q, r); + + if (angles[0] > (float)M_PI * 0.5f) { + return area_tri_v3(p, q, r) / 2.0f; + } + else if (angles[1] > (float)M_PI * 0.5f || angles[2] > (float)M_PI * 0.5f) { + return area_tri_v3(p, q, r) / 4.0f; + } + else { + + float dpr = dot_v3v3(pr, pr); + float dpq = dot_v3v3(pq, pq); + + float area = (1.0f / 8.0f) * + (dpr * cotangent_tri_weight_v3(q, p, r) + dpq * cotangent_tri_weight_v3(r, q, p)); + + return area; + } +} + +static float cotangent_tri_weight_v3_proj(const float n[3], + const float v1[3], + const float v2[3], + const float v3[3]) +{ + float a[3], b[3], c[3], c_len; + + sub_v3_v3v3(a, v2, v1); + sub_v3_v3v3(b, v3, v1); + + madd_v3_v3fl(a, n, -dot_v3v3(n, a)); + madd_v3_v3fl(b, n, -dot_v3v3(n, b)); + + cross_v3_v3v3(c, a, b); + + c_len = len_v3(c); + + if (c_len > FLT_EPSILON) { + return dot_v3v3(a, b) / c_len; + } + + return 0.0f; +} + +void SCULPT_dyntopo_get_cotangents(SculptSession *ss, + SculptVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea) +{ + SCULPT_dyntopo_check_disk_sort(ss, vertex); + + BMVert *v = (BMVert *)vertex.i; + BMEdge *e = v->e; + + if (!e) { + return; + } + + int i = 0; + float totarea = 0.0; + float totw = 0.0; + + do { + BMEdge *eprev = v == e->v1 ? e->v1_disk_link.prev : e->v2_disk_link.prev; + BMEdge *enext = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; + + BMVert *v1 = BM_edge_other_vert(eprev, v); + BMVert *v2 = BM_edge_other_vert(e, v); + BMVert *v3 = BM_edge_other_vert(enext, v); + + float cot1 = cotangent_tri_weight_v3(v1->co, v->co, v2->co); + float cot2 = cotangent_tri_weight_v3(v3->co, v2->co, v->co); + + float area = tri_voronoi_area(v->co, v1->co, v2->co); + + r_ws[i] = (cot1 + cot2); + totw += r_ws[i]; + + totarea += area; + + if (r_cot1) { + r_cot1[i] = cot1; + } + + if (r_cot2) { + r_cot2[i] = cot2; + } + + if (r_area) { + r_area[i] = area; + } + + i++; + e = enext; + } while (e != v->e); + + if (r_totarea) { + *r_totarea = totarea; + } + + int count = i; + + float mul = 1.0f / (totarea * 2.0); + + for (i = 0; i < count; i++) { + r_ws[i] *= mul; } } +void SCULPT_faces_get_cotangents(SculptSession *ss, + SculptVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea) +{ + // sculpt vemap should always be sorted in disk cycle order + + float totarea = 0.0; + float totw = 0.0; + + MeshElemMap *elem = ss->vemap + vertex.i; + for (int i = 0; i < elem->count; i++) { + int i1 = (i + elem->count - 1) % elem->count; + int i2 = i; + int i3 = (i + 1) % elem->count; + + MVert *v = ss->mvert + vertex.i; + MEdge *e1 = ss->medge + elem->indices[i1]; + MEdge *e2 = ss->medge + elem->indices[i2]; + MEdge *e3 = ss->medge + elem->indices[i3]; + + MVert *v1 = (unsigned int)vertex.i == e1->v1 ? ss->mvert + e1->v2 : ss->mvert + e1->v1; + MVert *v2 = (unsigned int)vertex.i == e2->v1 ? ss->mvert + e2->v2 : ss->mvert + e2->v1; + MVert *v3 = (unsigned int)vertex.i == e3->v1 ? ss->mvert + e3->v2 : ss->mvert + e3->v1; + + float cot1 = cotangent_tri_weight_v3(v1->co, v->co, v2->co); + float cot2 = cotangent_tri_weight_v3(v3->co, v2->co, v->co); + + float area = tri_voronoi_area(v->co, v1->co, v2->co); + + r_ws[i] = (cot1 + cot2); + totw += r_ws[i]; + + totarea += area; + + if (r_cot1) { + r_cot1[i] = cot1; + } + + if (r_cot2) { + r_cot2[i] = cot2; + } + + if (r_area) { + r_area[i] = area; + } + } + + if (r_totarea) { + *r_totarea = totarea; + } + + float mul = 1.0f / (totarea * 2.0); + + for (int i = 0; i < elem->count; i++) { + r_ws[i] *= mul; + } +} + +void SCULPT_cotangents_begin(Object *ob, SculptSession *ss) +{ + SCULPT_vertex_random_access_ensure(ss); + int totvert = SCULPT_vertex_count_get(ss); + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + SCULPT_dyntopo_check_disk_sort(ss, vertex); + } + break; + } + case PBVH_FACES: { + Mesh *mesh = BKE_object_get_original_mesh(ob); + + if (!ss->vemap) { + BKE_mesh_vert_edge_map_create(&ss->vemap, + &ss->vemap_mem, + mesh->mvert, + mesh->medge, + mesh->totvert, + mesh->totedge, + true); + } + + break; + } + case PBVH_GRIDS: // not supported yet + break; + } +} + +void SCULPT_get_cotangents(SculptSession *ss, + SculptVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: + SCULPT_dyntopo_get_cotangents(ss, vertex, r_ws, r_cot1, r_cot2, r_area, r_totarea); + break; + case PBVH_FACES: + SCULPT_faces_get_cotangents(ss, vertex, r_ws, r_cot1, r_cot2, r_area, r_totarea); + break; + case PBVH_GRIDS: { + { + // not supported, return uniform weights; + + int val = SCULPT_vertex_valence_get(ss, vertex); + + for (int i = 0; i < val; i++) { + r_ws[i] = 1.0f; + } + } + break; + } + } +} + +void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss) +{ + BKE_pbvh_bmesh_flag_all_disk_sort(ss->pbvh); +} + +// returns true if edge disk list around vertex was sorted +bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, SculptVertRef vertex) +{ + BMVert *v = (BMVert *)vertex.i; + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + if (mv->flag & DYNVERT_NEED_DISK_SORT) { + mv->flag &= ~DYNVERT_NEED_DISK_SORT; + + BM_sort_disk_cycle(v); + + return true; + } + + return false; +} + +/* +Copies the bmesh, but orders the elements +according to PBVH node to improve memory locality +*/ +void SCULPT_reorder_bmesh(SculptSession *ss) +{ +#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) { SculptSession *ss = ob->sculpt; @@ -104,18 +514,103 @@ void SCULPT_pbvh_clear(Object *ob) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } -void SCULPT_dyntopo_node_layers_add(SculptSession *ss) +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, + ss->cd_face_areas); + } + 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) { - int cd_node_layer_index; + return CustomData_get_named_layer_index(&ss->bm->vdata, type, name) >= 0; +} - char layer_id[] = "_dyntopo_node_id"; +void SCULPT_dyntopo_ensure_templayer(SculptSession *ss, int type, const char *name) +{ + int li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name); - 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); + 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)); +} + +char dyntopop_faces_areas_layer_id[] = "__dyntopo_face_areas"; + +void SCULPT_dyntopo_node_layers_add(SculptSession *ss) +{ + 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); + + 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}}; + + BM_data_layers_ensure(ss->bm, &ss->bm->vdata, vlayers, 3); + + BMCustomLayerReq flayers[] = { + {CD_PROP_INT32, dyntopop_node_idx_layer_id, CD_FLAG_TEMPORARY}, + {CD_PROP_FLOAT, dyntopop_faces_areas_layer_id, CD_FLAG_TEMPORARY}, + }; + BM_data_layers_ensure(ss->bm, &ss->bm->pdata, flayers, 2); + + // 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, @@ -124,51 +619,272 @@ 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); + + ss->cd_face_areas = CustomData_get_named_layer( + &ss->bm->pdata, CD_PROP_FLOAT, dyntopop_faces_areas_layer_id); + ss->cd_face_areas = ss->bm->pdata.layers[ss->cd_face_areas].offset; } +/** + 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; Mesh *me = ob->data; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); + const BMAllocTemplate allocsize = { + .totvert = 2048 * 16, .totface = 2048 * 16, .totloop = 4196 * 16, .totedge = 2048 * 16}; SCULPT_pbvh_clear(ob); + if (ss->mdyntopo_verts) { + MEM_freeN(ss->mdyntopo_verts); + ss->mdyntopo_verts = NULL; + } + ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != 0; /* Dynamic topology doesn't ensure selection state is valid, so remove T36280. */ BKE_mesh_mselect_clear(me); - /* Create triangles-only BMesh. */ +#if 1 ss->bm = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); - - BM_mesh_bm_from_me(ss->bm, + &((struct BMeshCreateParams){.use_toolflags = false, + .create_unique_ids = true, + .id_elem_mask = BM_VERT | BM_EDGE | BM_FACE, + .id_map = true, + .temporary_ids = false, + .no_reuse_ids = false})); + + 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 + +#ifndef DYNTOPO_DYNAMIC_TESS + SCULPT_dynamic_topology_triangulate(ss, ss->bm); +#endif + SCULPT_dyntopo_node_layers_add(ss); + SCULPT_dyntopo_save_origverts(ss); + + BMIter iter; + BMVert *v; + + 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); + } + else { + cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP); + } + + int cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR); + SCULPT_dyntopo_node_layers_update_offsets(ss); + + int i = 0; + BMEdge *e; + + BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) { + e->head.hflag |= BM_ELEM_DRAW; + } + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v); + + mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE; + + BKE_pbvh_update_vert_boundary( + ss->cd_dyn_vert, ss->cd_faceset_offset, v, ss->boundary_symmetry); + BKE_pbvh_bmesh_update_valence(ss->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v}); + + // 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); @@ -178,14 +894,46 @@ 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); + if (!ss->bm_log) { + 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 @@ -198,64 +946,41 @@ static void SCULPT_dynamic_topology_disable_ex( SCULPT_pbvh_clear(ob); - if (unode) { - /* Free all existing custom data. */ - CustomData_free(&me->vdata, me->totvert); - CustomData_free(&me->edata, me->totedge); - CustomData_free(&me->fdata, me->totface); - CustomData_free(&me->ldata, me->totloop); - CustomData_free(&me->pdata, me->totpoly); - - /* Copy over stored custom data. */ - SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; - me->totvert = geometry->totvert; - me->totloop = geometry->totloop; - me->totpoly = geometry->totpoly; - me->totedge = geometry->totedge; - me->totface = 0; - CustomData_copy( - &geometry->vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, geometry->totvert); - CustomData_copy( - &geometry->edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, geometry->totedge); - CustomData_copy( - &geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop); - CustomData_copy( - &geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly); - - BKE_mesh_update_customdata_pointers(me, false); - } - 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; + BKE_sculptsession_bm_to_me(ob, true); - /* 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; - me->mvert[i].flag |= ME_VERT_PBVH_UPDATE; - } + /* 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; + me->mvert[i].flag |= ME_VERT_PBVH_UPDATE; } /* 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); @@ -292,6 +1017,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; } } @@ -301,6 +1028,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; @@ -312,6 +1040,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; } } @@ -338,14 +1068,33 @@ static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(o return OPERATOR_FINISHED; } +static int dyntopo_error_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) +{ + uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Error!"), ICON_ERROR); + uiLayout *layout = UI_popup_menu_layout(pup); + + if (flag & DYNTOPO_ERROR_MULTIRES) { + const char *msg_error = TIP_("Multires modifier detected; cannot enable dyntopo."); + const char *msg = TIP_("Dyntopo and multires cannot be mixed."); + + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) { uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); uiLayout *layout = UI_popup_menu_layout(pup); - if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { - const char *msg_error = TIP_("Vertex Data Detected!"); - const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); + if (flag & (DYNTOPO_WARN_EDATA)) { + const char *msg_error = TIP_("Edge Data Detected!"); + const char *msg = TIP_("Dyntopo will not preserve custom edge attributes"); uiItemL(layout, msg_error, ICON_INFO); uiItemL(layout, msg, ICON_NONE); uiItemS(layout); @@ -378,6 +1127,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)) { @@ -391,6 +1141,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) } } } +#endif { VirtualModifierData virtualModifierData; @@ -403,6 +1154,10 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) continue; } + if (md->type == eModifierType_Multires) { + flag |= DYNTOPO_ERROR_MULTIRES; + } + if (mti->type == eModifierTypeType_Constructive) { flag |= DYNTOPO_WARN_MODIFIER; break; @@ -424,7 +1179,10 @@ static int sculpt_dynamic_topology_toggle_invoke(bContext *C, Scene *scene = CTX_data_scene(C); enum eDynTopoWarnFlag flag = SCULPT_dynamic_topology_check(scene, ob); - if (flag) { + if (flag & DYNTOPO_ERROR_MULTIRES) { + return dyntopo_error_popup(C, op->type, flag); + } + else if (flag) { /* The mesh has customdata that will be lost, let the user confirm this is OK. */ return dyntopo_warning_popup(C, op->type, flag); } @@ -438,7 +1196,10 @@ void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot) /* Identifiers. */ ot->name = "Dynamic Topology Toggle"; ot->idname = "SCULPT_OT_dynamic_topology_toggle"; - ot->description = "Dynamic topology alters the mesh topology while sculpting"; + ot->description = + "Dynamic mode; note that you must now check the DynTopo" + "option to enable dynamic remesher (which updates topology will sculpting)" + "this is on by default."; /* API callbacks. */ ot->invoke = sculpt_dynamic_topology_toggle_invoke; @@ -447,3 +1208,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..3257b1a9c5e 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, SCULPT_BOUNDARY_MESH)) { 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_GRIDS, 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); @@ -845,7 +946,8 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, MEM_SAFE_FREE(expand_cache->vert_falloff); MEM_SAFE_FREE(expand_cache->face_falloff); - expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX); + expand_cache->vert_falloff = SCULPT_geodesic_distances_create( + ob, initial_vertices, FLT_MAX, NULL, NULL); BLI_gset_free(initial_vertices, NULL); } @@ -872,7 +974,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 +1040,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_GRIDS, PBVH_FACES, PBVH_BMESH)) { sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); } else { @@ -957,8 +1062,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 +1083,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,20 +1101,23 @@ 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: + expand_cache->vert_falloff = sculpt_expand_geodesic_falloff_create(sd, ob, v); + /* expand_cache->vert_falloff = has_topology_info ? sculpt_expand_geodesic_falloff_create(sd, ob, v) : sculpt_expand_spherical_falloff_create(ob, v); + */ break; case SCULPT_EXPAND_FALLOFF_TOPOLOGY: expand_cache->vert_falloff = sculpt_expand_topology_falloff_create(sd, ob, v); @@ -1131,7 +1243,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 +1345,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 +1382,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 +1423,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 +1489,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 +1521,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 +1554,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 +1595,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 +1626,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 +1646,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 +1707,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 +1721,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 +1740,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 +1815,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 +1846,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 +2033,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 +2167,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 +2316,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 +2342,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. */ @@ -2119,7 +2371,8 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even eSculptExpandFalloffType falloff_type = RNA_enum_get(op->ptr, "falloff_type"); /* When starting from a boundary vertex, set the initial falloff to boundary. */ - if (SCULPT_vertex_is_boundary(ss, ss->expand_cache->initial_active_vertex)) { + if (SCULPT_vertex_is_boundary( + ss, ss->expand_cache->initial_active_vertex, SCULPT_BOUNDARY_MESH)) { falloff_type = SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index bdbdb75732a..1504da20caf 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" @@ -49,6 +51,7 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_scene.h" +#include "BKE_subdiv_ccg.h" #include "DEG_depsgraph.h" @@ -72,6 +75,101 @@ #include <math.h> #include <stdlib.h> +static int sculpt_face_material_get(SculptSession *ss, SculptFaceRef face) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMFace *f = (BMFace *)face.i; + return f->mat_nr; + } + case PBVH_GRIDS: + case PBVH_FACES: + return ss->mpoly[face.i].mat_nr; + } + + return -1; +} + +int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMFace *f = (BMFace *)face.i; + return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + } + case PBVH_GRIDS: + case PBVH_FACES: + return ss->face_sets[face.i]; + } + return -1; +} + +// returns previous face set +int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset) +{ + int ret = 0; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + 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); + break; + } + case PBVH_FACES: + case PBVH_GRIDS: + ret = ss->face_sets[face.i]; + ss->face_sets[face.i] = fset; + break; + } + + 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 +216,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,8 +261,33 @@ 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); + const float test_limit = 0.05f; + int cd_mask = -1; + + if (ss->bm) { + cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); + } + + /*check if we need to sample the current face set*/ + + bool set_active_faceset = ss->cache->automasking && + (brush->automasking_flags & BRUSH_AUTOMASKING_FACE_SETS); + set_active_faceset = set_active_faceset && ss->cache->invert; + set_active_faceset = set_active_faceset && ss->cache->automasking->settings.initial_face_set == + ss->cache->automasking->settings.current_face_set; + + int automasking_fset_flag = 0; + + if (set_active_faceset) { + // temporarily clear faceset flag + automasking_fset_flag = ss->cache->automasking ? ss->cache->automasking->settings.flags & + BRUSH_AUTOMASKING_FACE_SETS : + 0; + ss->cache->automasking->settings.flags &= ~BRUSH_AUTOMASKING_FACE_SETS; + } BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { @@ -157,15 +308,146 @@ 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) { - ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set); + if (fade > test_limit && ss->face_sets[vert_map->indices[j]] > 0) { + bool ok = true; + + int fset = abs(ss->face_sets[vert_map->indices[j]]); + + // XXX kind of hackish, tries to sample faces that are within + // 8 pixels of the center of the brush, and using a crude linear + // scale at that - joeedh + if (set_active_faceset && + fset != abs(ss->cache->automasking->settings.initial_face_set)) { + + float radius = ss->cache->radius; + float pixels = 8; // TODO: multiply with DPI + radius = pixels * (radius / (float)ss->cache->dyntopo_pixel_radius); + + if (sqrtf(test.dist) < radius) { + ss->cache->automasking->settings.initial_face_set = abs(fset); + set_active_faceset = false; + ss->cache->automasking->settings.flags |= BRUSH_AUTOMASKING_FACE_SETS; + } + else { + ok = false; + } + } + + MLoop *ml = &ss->mloop[p->loopstart]; + + for (int i = 0; i < p->totloop; i++, ml++) { + MVert *v = &ss->mvert[ml->v]; + float fno[3]; + + normal_short_to_float_v3(fno, v->no); + float mask = ss->vmask ? ss->vmask[ml->v] : 0.0f; + + const float fade2 = bstrength * + SCULPT_brush_strength_factor(ss, + brush, + v->co, + sqrtf(test.dist), + v->no, + fno, + mask, + (SculptVertRef){.i = ml->v}, + thread_id); + + if (fade2 < test_limit) { + ok = false; + break; + } + } + + if (ok) { + ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set); + } } } } + 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 > test_limit && fset > 0) { + BMLoop *l = f->l_first; + + bool ok = true; + + // XXX kind of hackish, tries to sample faces that are within + // 8 pixels of the center of the brush, and using a crude linear + // scale at that - joeedh + if (set_active_faceset && + abs(fset) != abs(ss->cache->automasking->settings.initial_face_set)) { + + float radius = ss->cache->radius; + float pixels = 8; // TODO: multiple with DPI + radius = pixels * (radius / (float)ss->cache->dyntopo_pixel_radius); + + if (sqrtf(test.dist) < radius) { + ss->cache->automasking->settings.initial_face_set = abs(fset); + set_active_faceset = false; + ss->cache->automasking->settings.flags |= BRUSH_AUTOMASKING_FACE_SETS; + } + else { + ok = false; + } + } + + do { + short sno[3]; + float mask = cd_mask >= 0 ? BM_ELEM_CD_GET_FLOAT(l->v, cd_mask) : 0.0f; + + normal_float_to_short_v3(sno, l->v->no); + + const float fade2 = bstrength * SCULPT_brush_strength_factor( + ss, + brush, + l->v->co, + sqrtf(test.dist), + sno, + l->v->no, + mask, + (SculptVertRef){.i = (intptr_t)l->v}, + thread_id); + + if (fade2 < test_limit) { + ok = false; + break; + } + + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, l->v); + mv->flag |= DYNVERT_NEED_BOUNDARY; + } while ((l = l->next) != f->l_first); + + if (ok) { + 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,16 +460,21 @@ 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); } } } } BKE_pbvh_vertex_iter_end; + + // restore automasking flag + if (set_active_faceset) { + ss->cache->automasking->settings.flags |= automasking_fset_flag; + } } static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, @@ -217,7 +504,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 +515,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); @@ -254,8 +541,27 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in .nodes = nodes, }; + bool threaded = true; + + /*for ctrl invert mode we have to set the automasking initial_face_set + to the first non-current faceset that is found*/ + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + if (ss->cache->invert && ss->cache->automasking && + (brush->automasking_flags & BRUSH_AUTOMASKING_FACE_SETS)) { + ss->cache->automasking->settings.current_face_set = + ss->cache->automasking->settings.initial_face_set; + } + } + + if (ss->cache->invert && !ss->cache->alt_smooth && ss->cache->automasking && + ss->cache->automasking->settings.initial_face_set == + ss->cache->automasking->settings.current_face_set) { + threaded = false; + } + + // ctrl-click is single threaded since the tasks will set the initial face set TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BKE_pbvh_parallel_range_settings(&settings, threaded, totnode); if (ss->cache->alt_smooth) { SCULPT_boundary_info_ensure(ob); for (int i = 0; i < 4; i++) { @@ -316,13 +622,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 +646,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 +662,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 +678,36 @@ 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, - })); - - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); + const int totface = ss->totfaces; - 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; + for (int i = 0; i < totface; i++) { + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i); + + // 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 +883,21 @@ 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; + int totface = bm->totface; - BM_mesh_elem_table_init(bm, BM_FACE); - BM_mesh_elem_table_ensure(bm, BM_FACE); + BLI_bitmap *visited_faces = BLI_BITMAP_NEW(ss->totfaces, "visited faces"); + const int totfaces = ss->totfaces; // mesh->totpoly; + + 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 +908,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 +937,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 +953,63 @@ 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, - })); - BM_mesh_bm_from_me(bm, - mesh, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - })); - BMIter iter; - BMFace *f; + SCULPT_face_random_access_ensure(ss); - const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP); + int cd_fmaps_offset = -1; + if (ss->bm) { + cd_fmaps_offset = CustomData_get_offset(&ss->bm->pdata, CD_FACEMAP); + } + + Mesh *me = NULL; + int *fmaps = NULL; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + me = ob->data; + fmaps = CustomData_get_layer(&me->pdata, CD_FACEMAP); + } + else if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + fmaps = CustomData_get_layer(ss->pdata, CD_FACEMAP); + } + + for (int i = 0; i < ss->totfaces; i++) { + SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i); - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { 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)(sculpt_face_material_get(ss, fref) + 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; - } - else { - ss->face_sets[BM_elem_index_get(f)] = 1; + int fmap = 1; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMFace *f = (BMFace *)fref.i; + + if (cd_fmaps_offset >= 0) { + fmap = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2; + } + + break; + } + case PBVH_FACES: + case PBVH_GRIDS: { + if (fmaps) { + fmap = fmaps[i] + 2; + } + break; + } } + + SCULPT_face_set_set(ss, fref, fmap); } } - BM_mesh_free(bm); } static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) @@ -697,11 +1020,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 +1169,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 +1197,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; + } } } @@ -961,8 +1288,8 @@ static int sculpt_face_sets_change_visibility_invoke(bContext *C, Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint - * cursor updates. */ + /* Update the active vertex and Face Set using the cursor position to avoid relying on the + * paint cursor updates. */ SculptCursorGeometryInfo sgi; float mouse[2]; mouse[0] = event->mval[0]; @@ -1001,22 +1328,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 +1421,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 +1497,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 +1585,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 +1614,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 +1634,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 +1700,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_BOUNDARY_MESH) && + 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 +1737,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,20 +1764,18 @@ 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) { /* Modification of base mesh geometry requires special remapping of multires displacement, * which does not happen here. - * Disable delete operation. It can be supported in the future by doing similar displacement - * data remapping as what happens in the mesh edit mode. */ + * Disable delete operation. It can be supported in the future by doing similar + * displacement 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 4b49bf2cefb..dfb1f2e74dc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -107,18 +107,18 @@ 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) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); float orig_color[3], final_color[4], hsv_color[3]; int hue; float brightness, contrast, gain, delta, offset; 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..4e50b9fbd9b 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. */ @@ -303,12 +303,12 @@ static void mesh_filter_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); float orig_co[3], val[3], avg[3], normal[3], disp[3], disp2[3], transform[3][3], final_pos[3]; 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, NULL, false); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -393,13 +393,22 @@ static void mesh_filter_task_cb(void *__restrict userdata, break; } case MESH_FILTER_SURFACE_SMOOTH: { + SculptCustomLayer scl = {.cd_offset = -1, + .from_bmesh = ss->bm != NULL, + .elemsize = sizeof(float) * 3, + .data = ss->filter_cache->surface_smooth_laplacian_disp, + .is_cdlayer = false, + .layer = NULL}; + SCULPT_surface_smooth_laplacian_step(ss, disp, vd.co, - ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + &scl, + vd.vertex, orig_data.co, - ss->filter_cache->surface_smooth_shape_preservation); + ss->filter_cache->surface_smooth_shape_preservation, + 0.0f, + false); break; } case MESH_FILTER_SHARPEN: { @@ -411,10 +420,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 +433,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, false); 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 +495,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, false); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -512,8 +523,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 +547,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, false); + 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 +573,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,15 +605,22 @@ 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; } + SculptCustomLayer scl = {.cd_offset = -1, + .from_bmesh = ss->bm != NULL, + .elemsize = sizeof(float) * 3, + .data = ss->filter_cache->surface_smooth_laplacian_disp, + .is_cdlayer = false, + .layer = NULL}; + SCULPT_surface_smooth_displace_step(ss, vd.co, - ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + &scl, + 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..ecdbbe9280e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c @@ -23,10 +23,16 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_blenlib.h" #include "BLI_linklist_stack.h" #include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_sort_utils.h" #include "BLI_task.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" @@ -77,8 +83,14 @@ #define SCULPT_GEODESIC_VERTEX_NONE -1 /* Propagate distance from v1 and v2 to v0. */ -static bool sculpt_geodesic_mesh_test_dist_add( - MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_vertices) +static bool sculpt_geodesic_mesh_test_dist_add(MVert *mvert, + const int v0, + const int v1, + const int v2, + float *dists, + GSet *initial_vertices, + SculptVertRef *r_closest_verts, + float (*cos)[3]) { if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) { return false; @@ -89,23 +101,202 @@ static bool sculpt_geodesic_mesh_test_dist_add( return false; } + float *co0 = cos ? cos[v0] : mvert[v0].co; + float *co1 = cos ? cos[v1] : mvert[v1].co; + float *co2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? (cos ? cos[v2] : mvert[v2].co) : NULL; + float dist0; if (v2 != SCULPT_GEODESIC_VERTEX_NONE) { BLI_assert(dists[v2] != FLT_MAX); if (dists[v0] <= dists[v2]) { return false; } - dist0 = geodesic_distance_propagate_across_triangle( - mvert[v0].co, mvert[v1].co, mvert[v2].co, dists[v1], dists[v2]); + dist0 = geodesic_distance_propagate_across_triangle(co0, co1, co2, dists[v1], dists[v2]); } else { float vec[3]; - sub_v3_v3v3(vec, mvert[v1].co, mvert[v0].co); + sub_v3_v3v3(vec, co1, co0); dist0 = dists[v1] + len_v3(vec); } if (dist0 < dists[v0]) { dists[v0] = dist0; + + if (r_closest_verts) { + bool tag1 = r_closest_verts[v1].i != -1LL; + bool tag2 = v2 != SCULPT_GEODESIC_VERTEX_NONE && r_closest_verts[v2].i != -1LL; + + float l1 = len_v3v3(co0, co1); + float l2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? len_v3v3(co0, co2) : 0.0f; + + if (tag1 && tag2) { + if (l1 < l2) { + r_closest_verts[v0] = r_closest_verts[v1]; + } + else { + r_closest_verts[v0] = r_closest_verts[v2]; + } + } + else if (tag2) { + r_closest_verts[v0] = r_closest_verts[v2]; + } + else if (tag1) { + r_closest_verts[v0] = r_closest_verts[v1]; + } + } + return true; + } + + return false; +} + +/* Propagate distance from v1 and v2 to v0. */ +static bool sculpt_geodesic_grids_test_dist_add(SculptSession *ss, + const int v0, + const int v1, + const int v2, + float *dists, + GSet *initial_vertices, + SculptVertRef *r_closest_verts, + float (*cos)[3]) +{ + if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) { + return false; + } + + BLI_assert(dists[v1] != FLT_MAX); + if (dists[v0] <= dists[v1]) { + return false; + } + + const float *co0 = cos ? cos[v0] : + SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v0)); + const float *co1 = cos ? cos[v1] : + SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v1)); + const float *co2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? + (cos ? cos[v2] : + SCULPT_vertex_co_get( + ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v2))) : + NULL; + + float dist0; + if (v2 != SCULPT_GEODESIC_VERTEX_NONE) { + BLI_assert(dists[v2] != FLT_MAX); + if (dists[v0] <= dists[v2]) { + return false; + } + dist0 = geodesic_distance_propagate_across_triangle(co0, co1, co2, dists[v1], dists[v2]); + } + else { + float vec[3]; + sub_v3_v3v3(vec, co1, co0); + dist0 = dists[v1] + len_v3(vec); + } + + if (dist0 < dists[v0]) { + dists[v0] = dist0; + + if (r_closest_verts) { + bool tag1 = r_closest_verts[v1].i != -1LL; + bool tag2 = v2 != SCULPT_GEODESIC_VERTEX_NONE && r_closest_verts[v2].i != -1LL; + + float l1 = len_v3v3(co0, co1); + float l2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? len_v3v3(co0, co2) : 0.0f; + + if (tag1 && tag2) { + if (l1 < l2) { + r_closest_verts[v0] = r_closest_verts[v1]; + } + else { + r_closest_verts[v0] = r_closest_verts[v2]; + } + } + else if (tag2) { + r_closest_verts[v0] = r_closest_verts[v2]; + } + else if (tag1) { + r_closest_verts[v0] = r_closest_verts[v1]; + } + } + return true; + } + + 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, + SculptVertRef *r_closest_verts, + float (*cos)[3]) +{ + 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; + + const float *v0co = cos ? cos[BM_elem_index_get(v0)] : v0->co; + const float *v1co = cos ? cos[BM_elem_index_get(v1)] : v1->co; + const float *v2co = v2 ? (cos ? cos[BM_elem_index_get(v2)] : v2->co) : NULL; + + 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( + v0co, v1co, v2co, dists[v1_i], dists[v2_i]); + } + else { + float vec[3]; + sub_v3_v3v3(vec, v1co, v0co); + dist0 = dists[v1_i] + len_v3(vec); + } + + if (dist0 < dists[v0_i]) { + dists[v0_i] = dist0; + + if (r_closest_verts) { + bool tag1 = r_closest_verts[v1_i].i != -1LL; + bool tag2 = v2 && r_closest_verts[v2_i].i != -1LL; + + float l1 = len_v3v3(v0co, v1co); + float l2 = v2 ? len_v3v3(v0co, v2co) : 0.0f; + + if (!tag1 && !tag2) { + printf("bad\n"); + } + + if (tag1 && tag2) { + if (l1 < l2) { // dists[v1_i] < dists[v2_i]) { + r_closest_verts[v0_i] = r_closest_verts[v1_i]; + } + else { + r_closest_verts[v0_i] = r_closest_verts[v2_i]; + } + } + else if (tag2) { + r_closest_verts[v0_i] = r_closest_verts[v2_i]; + } + else if (tag1) { + r_closest_verts[v0_i] = r_closest_verts[v1_i]; + } + } + return true; } @@ -114,7 +305,9 @@ static bool sculpt_geodesic_mesh_test_dist_add( static float *SCULPT_geodesic_mesh_create(Object *ob, GSet *initial_vertices, - const float limit_radius) + const float limit_radius, + SculptVertRef *r_closest_verts, + float (*cos)[3]) { SculptSession *ss = ob->sculpt; Mesh *mesh = BKE_object_get_original_mesh(ob); @@ -142,7 +335,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, } if (!ss->vemap) { BKE_mesh_vert_edge_map_create( - &ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge); + &ss->vemap, &ss->vemap_mem, mesh->mvert, mesh->medge, mesh->totvert, mesh->totedge, true); } /* Both contain edge indices encoded as *void. */ @@ -154,9 +347,17 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, for (int i = 0; i < totvert; i++) { if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) { + if (r_closest_verts) { + r_closest_verts[i] = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + } + dists[i] = 0.0f; } else { + if (r_closest_verts) { + r_closest_verts[i].i = -1LL; + } + dists[i] = FLT_MAX; } } @@ -177,9 +378,10 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, * number of vertices (usually just 1 or 2). */ GSET_ITER (gs_iter, initial_vertices) { const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); - float *v_co = verts[v].co; + float *v_co = cos ? cos[v] : verts[v].co; + for (int i = 0; i < totvert; i++) { - if (len_squared_v3v3(v_co, verts[i].co) <= limit_radius_sq) { + if (len_squared_v3v3(v_co, cos ? cos[i] : verts[i].co) <= limit_radius_sq) { BLI_BITMAP_ENABLE(affected_vertex, i); } } @@ -208,8 +410,14 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, if (dists[v1] > dists[v2]) { SWAP(int, v1, v2); } - sculpt_geodesic_mesh_test_dist_add( - verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_vertices); + sculpt_geodesic_mesh_test_dist_add(verts, + v2, + v1, + SCULPT_GEODESIC_VERTEX_NONE, + dists, + initial_vertices, + r_closest_verts, + cos); } if (ss->epmap[e].count != 0) { @@ -227,7 +435,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, continue; } if (sculpt_geodesic_mesh_test_dist_add( - verts, v_other, v1, v2, dists, initial_vertices)) { + verts, v_other, v1, v2, dists, initial_vertices, r_closest_verts, cos)) { for (int edge_map_index = 0; edge_map_index < ss->vemap[v_other].count; edge_map_index++) { const int e_other = ss->vemap[v_other].indices[edge_map_index]; @@ -271,6 +479,501 @@ 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, + SculptVertRef *r_closest_verts, + float (*cos)[3]) +{ + SculptSession *ss = ob->sculpt; + + if (!ss->bm) { + return NULL; + } + + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_EDGE | BM_FACE); + + const int totvert = ss->bm->totvert; + const int totedge = ss->bm->totedge; + + if (r_closest_verts) { + for (int i = 0; i < totvert; i++) { + r_closest_verts[i].i = -1LL; + } + } + + 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; + + if (r_closest_verts) { + r_closest_verts[i] = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + } + } + 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; + + 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; + float *co1 = cos ? cos[BM_elem_index_get(v)] : v->co; + + 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; + float *co2 = cos ? cos[BM_elem_index_get(v2)] : v2->co; + + if (len_squared_v3v3(co1, co2) <= 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, r_closest_verts, cos); + } + + 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, r_closest_verts, cos)) { + 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; +} + +BLI_INLINE void *hash_edge(int v1, int v2, int totvert) +{ + if (v1 > v2) { + SWAP(int, v1, v2); + } + + intptr_t ret = (intptr_t)v1 + (intptr_t)v2 * (intptr_t)totvert; + return (void *)ret; +} + +typedef struct TempEdge { + int v1, v2; +} TempEdge; + +int find_quad(TempEdge *edges, MeshElemMap *vmap, int v1, int v2, int v3) +{ + for (int i = 0; i < vmap[v1].count; i++) { + TempEdge *te = edges + vmap[v1].indices[i]; + int v = v1 == te->v1 ? te->v2 : te->v1; + + if (v == v2) { + continue; + } + + for (int j = 0; j < vmap[v].count; j++) { + TempEdge *te2 = edges + vmap[v].indices[j]; + int v4 = v == te2->v1 ? te2->v2 : te2->v1; + + if (v4 == v3) { + return v; + } + } + } + + return -1; +} + +static float *SCULPT_geodesic_grids_create(Object *ob, + GSet *initial_vertices, + const float limit_radius, + SculptVertRef *r_closest_verts, + float (*cos)[3]) +{ + SculptSession *ss = ob->sculpt; + Mesh *mesh = BKE_object_get_original_mesh(ob); + + const int totvert = SCULPT_vertex_count_get(ss); + + const float limit_radius_sq = limit_radius * limit_radius; + + float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances"); + + /* Both contain edge indices encoded as *void. */ + BLI_LINKSTACK_DECLARE(queue, void *); + BLI_LINKSTACK_DECLARE(queue_next, void *); + + 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))) { + if (r_closest_verts) { + r_closest_verts[i] = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + } + + dists[i] = 0.0f; + } + else { + if (r_closest_verts) { + r_closest_verts[i].i = -1LL; + } + + 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; + + 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 = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, v); + const float *v_co = cos ? cos[v] : SCULPT_vertex_co_get(ss, vertex); + + for (int i = 0; i < totvert; i++) { + const float *v_co2 = cos ? cos[i] : + SCULPT_vertex_co_get( + ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)); + if (len_squared_v3v3(v_co, v_co2) <= limit_radius_sq) { + BLI_BITMAP_ENABLE(affected_vertex, i); + } + } + } + } + + SculptVertexNeighborIter ni; + + TempEdge *edges = NULL; + BLI_array_declare(edges); + GHash *ehash = BLI_ghash_ptr_new("geodesic multigrids ghash"); + + MeshElemMap *vmap = MEM_calloc_arrayN(totvert, sizeof(*vmap), "geodesic grids vmap"); + + int totedge = 0; + MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "geodesic grids memarena"); + + for (int i = 0; i < totvert; i++) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + MeshElemMap *map = vmap + i; + + int val = SCULPT_vertex_valence_get(ss, vertex); + map->count = val; + map->indices = BLI_memarena_alloc(ma, sizeof(int) * val); + + int j = 0; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + void *ekey = hash_edge(i, ni.index, totvert); + void **val; + + if (!BLI_ghash_ensure_p(ehash, ekey, &val)) { + *val = POINTER_FROM_INT(totedge); + + TempEdge te = {i, ni.index}; + BLI_array_append(edges, te); + totedge++; + } + + map->indices[j] = POINTER_AS_INT(*val); + j++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + + int(*e_otherv_map)[4] = MEM_malloc_arrayN(totedge, sizeof(*e_otherv_map), "e_otherv_map"); + + // create an edge map of opposite edge verts in (up to 2) adjacent faces + for (int i = 0; i < totedge; i++) { + int v1a = -1, v2a = -1; + int v1b = -1, v2b = -1; + + TempEdge *te = edges + i; + SculptVertexNeighborIter ni2; + + for (int j = 0; j < vmap[te->v1].count; j++) { + TempEdge *te2 = edges + vmap[te->v1].indices[j]; + int v3 = te->v1 == te2->v1 ? te2->v2 : te2->v1; + + if (v3 == te->v2) { + continue; + } + + int p = find_quad(edges, vmap, te->v1, te->v2, v3); + + if (p != -1) { + v1a = p; + v1b = v3; + } + } + + for (int j = 0; j < vmap[te->v2].count; j++) { + TempEdge *te2 = edges + vmap[te->v2].indices[j]; + int v3 = te->v2 == te2->v1 ? te2->v2 : te2->v1; + + if (v3 == te->v1) { + continue; + } + + int p = find_quad(edges, vmap, te->v1, te->v2, v3); + + if (p != -1) { + if (v1a != -1) { + v2a = p; + v2b = v3; + } + else { + v1a = p; + v1b = v3; + } + } + } + + e_otherv_map[i][0] = v1a; + e_otherv_map[i][1] = v1b; + e_otherv_map[i][2] = v2a; + e_otherv_map[i][3] = v2b; + } + + BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag"); + + /* Add edges adjacent to an initial vertex to the queue. */ + for (int i = 0; i < totedge; i++) { + const int v1 = edges[i].v1; + const int v2 = edges[i].v2; + + if (!BLI_BITMAP_TEST(affected_vertex, v1) && !BLI_BITMAP_TEST(affected_vertex, v2)) { + continue; + } + if (dists[v1] != FLT_MAX || dists[v2] != FLT_MAX) { + BLI_LINKSTACK_PUSH(queue, POINTER_FROM_INT(i)); + } + } + + do { + while (BLI_LINKSTACK_SIZE(queue)) { + const int e = POINTER_AS_INT(BLI_LINKSTACK_POP(queue)); + int v1 = edges[e].v1; + int v2 = edges[e].v2; + + if (dists[v1] == FLT_MAX || dists[v2] == FLT_MAX) { + if (dists[v1] > dists[v2]) { + SWAP(int, v1, v2); + } + sculpt_geodesic_grids_test_dist_add(ss, + v2, + v1, + SCULPT_GEODESIC_VERTEX_NONE, + dists, + initial_vertices, + r_closest_verts, + cos); + } + + TempEdge *te = edges + e; + + for (int pi = 0; pi < 4; pi++) { + int v_other = e_otherv_map[e][pi]; + + if (v_other == -1) { + continue; + } + + // XXX not sure how to handle face sets here - joeedh + // if (ss->face_sets[poly] <= 0) { + // continue; + //} + + if (sculpt_geodesic_grids_test_dist_add( + ss, v_other, v1, v2, dists, initial_vertices, r_closest_verts, cos)) { + for (int edge_map_index = 0; edge_map_index < vmap[v_other].count; edge_map_index++) { + const int e_other = vmap[v_other].indices[edge_map_index]; + int ev_other; + if (edges[e_other].v1 == (uint)v_other) { + ev_other = edges[e_other].v2; + } + else { + ev_other = edges[e_other].v1; + } + + if (e_other != e && !BLI_BITMAP_TEST(edge_tag, e_other) && + (dists[ev_other] != FLT_MAX)) { + if (BLI_BITMAP_TEST(affected_vertex, v_other) || + BLI_BITMAP_TEST(affected_vertex, ev_other)) { + BLI_BITMAP_ENABLE(edge_tag, e_other); + BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(e_other)); + } + } + } + } + } + } + + for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) { + const int e = POINTER_AS_INT(lnk->link); + BLI_BITMAP_DISABLE(edge_tag, e); + } + + 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); + + BLI_memarena_free(ma); + BLI_ghash_free(ehash, NULL, NULL); + MEM_SAFE_FREE(edges); + MEM_SAFE_FREE(vmap); + MEM_SAFE_FREE(e_otherv_map); + + 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. */ @@ -279,16 +982,17 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices SculptSession *ss = ob->sculpt; Mesh *mesh = BKE_object_get_original_mesh(ob); - const int totvert = mesh->totvert; + const int totvert = SCULPT_vertex_count_get(ss); 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 +1001,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; @@ -305,15 +1010,22 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices float *SCULPT_geodesic_distances_create(Object *ob, GSet *initial_vertices, - const float limit_radius) + const float limit_radius, + SculptVertRef *r_closest_verts, + float (*vertco_override)[3]) { SculptSession *ss = ob->sculpt; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return SCULPT_geodesic_mesh_create(ob, initial_vertices, limit_radius); + return SCULPT_geodesic_mesh_create( + ob, initial_vertices, limit_radius, r_closest_verts, vertco_override); case PBVH_BMESH: + return SCULPT_geodesic_bmesh_create( + ob, initial_vertices, limit_radius, r_closest_verts, vertco_override); case PBVH_GRIDS: - return SCULPT_geodesic_fallback_create(ob, initial_vertices); + return SCULPT_geodesic_grids_create( + ob, initial_vertices, limit_radius, r_closest_verts, vertco_override); + // return SCULPT_geodesic_fallback_create(ob, initial_vertices); } BLI_assert(false); return NULL; @@ -321,7 +1033,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 +1042,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,22 +1052,34 @@ 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)); } } } - float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); + float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius, NULL, NULL); BLI_gset_free(initial_vertices, NULL); 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)); - float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); + + 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, NULL, NULL); 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..d1f90750445 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,24 @@ 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 + bool from_bmesh; // note that layers can be fixed arrays but still from a bmesh, e.g. filter + // laplacian smooth +} 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 +73,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 */ @@ -61,12 +84,12 @@ typedef enum SculptUpdateType { SCULPT_UPDATE_COLOR = 1 << 3, } SculptUpdateType; -void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags); -void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags); +void SCULPT_flush_update_step(struct bContext *C, SculptUpdateType update_flags); +void SCULPT_flush_update_done(const struct bContext *C, Object *ob, SculptUpdateType update_flags); void SCULPT_flush_stroke_deform(struct Sculpt *sd, Object *ob, bool is_proxy_used); /* Should be used after modifying the mask or Face Sets IDs. */ -void SCULPT_tag_update_overlays(bContext *C); +void SCULPT_tag_update_overlays(struct bContext *C); /* Stroke */ @@ -96,22 +119,30 @@ 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); +int SCULPT_vertex_valence_get(const struct SculptSession *ss, SculptVertRef vertex); -const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index); -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); +int SCULPT_vertex_count_get(struct SculptSession *ss); +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. */ @@ -119,25 +150,36 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, const int deform_target, PBVHVertexIter *iter); +struct _SculptNeighborRef { + SculptVertRef vertex; + SculptEdgeRef edge; +}; + #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 typedef struct SculptVertexNeighborIter { /* Storage */ - int *neighbors; + struct _SculptNeighborRef *neighbors; + int *neighbor_indices; + int size; int capacity; - int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + struct _SculptNeighborRef 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; + SculptEdgeRef edge; int index; + bool has_edge; // does this iteration step have an edge, fake neighbors do not bool is_duplicate; } SculptVertexNeighborIter; -void SCULPT_vertex_neighbors_get(struct SculptSession *ss, - const int index, +void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, + const SculptVertRef vref, const bool include_duplicates, SculptVertexNeighborIter *iter); @@ -146,7 +188,11 @@ 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.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \ + SCULPT_REF_NONE; \ + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i].vertex; \ + neighbor_iterator.edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge; \ + 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 +200,11 @@ 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.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \ + SCULPT_REF_NONE; \ + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i].vertex; \ + neighbor_iterator.edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge; \ + 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,9 +215,11 @@ 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); +float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex); void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]); +MDynTopoVert *SCULPT_vertex_get_mdyntopo(SculptSession *ss, SculptVertRef vertex); /* Returns PBVH deformed vertices array if shape keys or deform modifiers are used, otherwise * returns mesh original vertices array. */ @@ -184,15 +236,38 @@ void SCULPT_fake_neighbors_free(struct Object *ob); /* Vertex Info. */ void SCULPT_boundary_info_ensure(Object *object); + +/* this is a bitmask */ +typedef enum SculptCornerType { + SCULPT_CORNER_NONE = 0, + SCULPT_CORNER_MESH = 1 << 0, + SCULPT_CORNER_FACE_SET = 1 << 1, + SCULPT_CORNER_SEAM = 1 << 2, + SCULPT_CORNER_SHARP = 1 << 3 +} SculptCornerType; + +SculptCornerType SCULPT_vertex_is_corner(const SculptSession *ss, + const SculptVertRef index, + SculptCornerType cornertype); + +typedef enum SculptBoundaryType { + SCULPT_BOUNDARY_MESH = 1 << 0, + SCULPT_BOUNDARY_FACE_SET = 1 << 1, + SCULPT_BOUNDARY_SEAM = 1 << 2, + SCULPT_BOUNDARY_SHARP = 1 << 3 +} SculptBoundaryType; + /* Boundary Info needs to be initialized in order to use this function. */ -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index); +SculptBoundaryType SCULPT_vertex_is_boundary(const SculptSession *ss, + const SculptVertRef index, + SculptBoundaryType boundary_types); 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 +275,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(const 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 +306,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 +333,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,26 +379,36 @@ 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 */ enum eDynTopoWarnFlag { - DYNTOPO_WARN_VDATA = (1 << 0), DYNTOPO_WARN_EDATA = (1 << 1), - DYNTOPO_WARN_LDATA = (1 << 2), DYNTOPO_WARN_MODIFIER = (1 << 3), + DYNTOPO_ERROR_MULTIRES = (1 << 4) }; +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 +421,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,43 +432,46 @@ 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. */ struct AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss); -struct AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob); +struct AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, Object *ob); void SCULPT_automasking_cache_free(struct AutomaskingCache *automasking); -bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, - const Brush *br, - const eAutomasking_flag mode); -bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br); +bool SCULPT_is_automasking_mode_enabled(Sculpt *sd, const Brush *br, const eAutomasking_flag mode); +bool SCULPT_is_automasking_enabled(Sculpt *sd, const SculptSession *ss, const Brush *br); 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); + const float limit_radius, + SculptVertRef *r_closest_verts, + float (*vertco_override)[3]); 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 +609,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,37 +639,62 @@ 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); - -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); +/* Topology rake */ +void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, + float avg[3], + float direction[3], + struct BMVert *v, + float projection, + bool check_fsets, + int cd_temp, + int cd_dyn_vert, + bool do_origco); + +/* Smoothing api */ +void SCULPT_neighbor_coords_average( + SculptSession *ss, float result[3], SculptVertRef index, float projection, bool check_fsets); +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, + SculptCustomLayer *bound_scl, + bool do_origco); + +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, + bool do_origco); + +void SCULPT_do_smooth_brush( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection); /* Surface Smooth Brush. */ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], - float (*laplacian_disp)[3], - const int v_index, + struct SculptCustomLayer *scl, + const SculptVertRef v_index, const float origco[3], - const float alpha); + const float alpha, + const float projection, + bool check_fsets); + void SCULPT_surface_smooth_displace_step(SculptSession *ss, float *co, - float (*laplacian_disp)[3], - const int v_index, + struct SculptCustomLayer *scl, + 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 +753,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 +791,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,10 +909,10 @@ 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; + int face_set, face_set2; int filter_undo_type; int mask_init_mode; @@ -797,6 +920,14 @@ typedef struct SculptThreadedTaskData { ThreadMutex mutex; + // Layer brush + int cd_pers_co, cd_pers_no, cd_pers_disp; + int cd_layer_disp, cd_temp, cd_dyn_vert; + + float smooth_projection; + float rake_projection; + SculptCustomLayer *scl, *scl2; + bool do_origco; } SculptThreadedTaskData; /*************** Brush testing declarations ****************/ @@ -830,6 +961,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 +999,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 +1030,16 @@ typedef struct AutomaskingSettings { /* Flags from eAutomasking_flag. */ int flags; int initial_face_set; + int current_face_set; // used by faceset draw tool + 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 { @@ -977,6 +1113,7 @@ typedef struct StrokeCache { /* Symmetry index between 0 and 7 bit combo 0 is Brush only; * 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ int symmetry; + int boundary_symmetry; // controls splitting face sets by mirror axis int mirror_symmetry_pass; /* The symmetry pass we are currently on between 0 and 7. */ float true_view_normal[3]; float view_normal[3]; @@ -1066,6 +1203,18 @@ 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; + struct bContext *C; } StrokeCache; /* Sculpt Filters */ @@ -1141,7 +1290,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 +1451,19 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, const float angle); void SCULPT_cache_free(StrokeCache *cache); +void SCULPT_vertex_check_origdata(SculptSession *ss, SculptVertRef vertex); + +void SCULPT_orig_vert_data_init(SculptOrigVertData *data, + Object *ob, + PBVHNode *node, + SculptUndoType type); +void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, SculptVertRef vertex); +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 +1530,186 @@ 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_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; + int idx = (int)vertex.i; + + if (scl->from_bmesh) { + BMVert *v = (BMVert *)vertex.i; + idx = v->head.index; + } + + return p + scl->elemsize * (int)vertex.i; + } + else { + BMVert *v = (BMVert *)vertex.i; + return BM_ELEM_CD_GET_VOID_P(v, scl->cd_offset); + } + + return NULL; +} + +/* +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, + Sculpt *sd, + const Brush *br, + Object *ob, + 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); + +// returns true if edge disk list around vertex was sorted +// be careful of this function. +bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, SculptVertRef vertex); +void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss); + +// call SCULPT_cotangents_begin in the main thread before any calls to this function +void SCULPT_dyntopo_get_cotangents(SculptSession *ss, + SculptVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea); + +// call SCULPT_cotangents_begin in the main thread before any calls to this function +void SCULPT_get_cotangents(SculptSession *ss, + SculptVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea); + +// call this in the main thread before any calls to SCULPT_get_cotangents +void SCULPT_cotangents_begin(struct Object *ob, SculptSession *ss); +char SCULPT_mesh_fset_boundary_symmetry_get(struct Object *object); + +// exponent to make boundary_smooth_factor more user-friendly +#define BOUNDARY_SMOOTH_EXP 2.0 + +// edges + +SculptBoundaryType SCULPT_edge_is_boundary(const SculptSession *ss, + const SculptEdgeRef edge, + SculptBoundaryType typemask); +void SCULPT_edge_get_verts(const SculptSession *ss, + const SculptEdgeRef edge, + SculptVertRef *r_v1, + SculptVertRef *r_v2); +SculptVertRef SCULPT_edge_other_vertex(const SculptSession *ss, + const SculptEdgeRef edge, + const SculptVertRef vertex); + +#define SCULPT_REPLAY +#ifdef SCULPT_REPLAY +struct SculptReplayLog; +struct SculptBrushSample; + +# ifdef WIN32 +# define REPLAY_EXPORT __declspec(dllexport) +# else +# define REPLAY_EXPORT +# endif + +void SCULPT_replay_log_free(struct SculptReplayLog *log); +struct SculptReplayLog *SCULPT_replay_log_create(); +void SCULPT_replay_log_end(); +void SCULPT_replay_log_start(); +char *SCULPT_replay_serialize(); +void SCULPT_replay_log_append(struct Sculpt *sd, struct SculptSession *ss, struct Object *ob); +void SCULPT_replay_test(void); + +#endif + +struct BMesh *SCULPT_dyntopo_empty_bmesh(); + +#define SCULPT_stroke_needs_original(brush) \ + ELEM(brush->sculpt_tool, \ + SCULPT_TOOL_DRAW_SHARP, \ + SCULPT_TOOL_GRAB, \ + SCULPT_TOOL_ROTATE, \ + SCULPT_TOOL_THUMB, \ + SCULPT_TOOL_ELASTIC_DEFORM, \ + SCULPT_TOOL_BOUNDARY, \ + SCULPT_TOOL_POSE) + +void SCULPT_undo_ensure_bmlog(struct Object *ob); 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 936ebb7e8f7..e86c5b9ecb9 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]); @@ -139,7 +140,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, IMB_colormanagement_srgb_to_scene_linear_v3(brush_color); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); bool affect_vertex = false; float distance_to_stroke_location = 0.0f; @@ -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..09517820485 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -172,10 +172,10 @@ 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); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); float total_disp[3]; zero_v3(total_disp); @@ -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_replay.c b/source/blender/editors/sculpt_paint/sculpt_replay.c new file mode 100644 index 00000000000..5ec37901369 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_replay.c @@ -0,0 +1,1228 @@ +#include "MEM_guardedalloc.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" +#include "ED_view3d.h" + +#include "BLI_array.h" +#include "BLI_buffer.h" +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_dynstr.h" +#include "BLI_ghash.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_rand.h" +#include "BLI_smallhash.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "PIL_time.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" + +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "bmesh.h" +#include <string.h> + +typedef struct SculptBrushSample { + Sculpt sd; // copy of sd settings + + float active_vertex_co[3]; + float active_face_co[3]; + + bool have_active_vertex; + bool have_active_face; + + StrokeCache cache; + UnifiedPaintSettings ups; + PaintStroke stroke; + + double time; +} SculptBrushSample; + +typedef struct SculptReplayLog { + SculptBrushSample *samples; + int totsample, samples_size; + Tex **textures; + int tot_textures, textures_size; + MemArena *arena; + SmallHash texmap; + + bool is_playing; +} SculptReplayLog; + +static SculptReplayLog *current_log = NULL; + +void SCULPT_replay_log_free(SculptReplayLog *log) +{ + MEM_SAFE_FREE(log->samples); + MEM_SAFE_FREE(log->textures); + + BLI_smallhash_release(&log->texmap); + BLI_memarena_free(log->arena); + MEM_freeN(log); +} + +SculptReplayLog *SCULPT_replay_log_create() +{ + SculptReplayLog *log = MEM_callocN(sizeof(*log), "SculptReplayLog"); + + log->arena = BLI_memarena_new(1024, __func__); + BLI_smallhash_init(&log->texmap); + + return log; +} + +void SCULPT_replay_log_end() +{ + if (!current_log) { + printf("could not find log!"); + return; + } + + SCULPT_replay_log_free(current_log); + current_log = NULL; +} +void SCULPT_replay_log_start() +{ + if (current_log) { + printf("%s: recording has already started. . .\n", __func__); + return; + } + + current_log = MEM_callocN(sizeof(*current_log), "sculpt replay log"); + current_log->arena = BLI_memarena_new(8192, "sculpt replay log"); +} + +#if 0 +# define WRITE(key, fmt, ...) \ + { \ + char _buf[256], _prefix[64]; \ + if (shead >= 0) { \ + sprintf(_prefix, "%s%s%s", stack[shead].prefix, stack[shead].op, key); \ + } \ + else { \ + sprintf(_prefix, "%s", key); \ + } \ + sprintf(_buf, "%s " fmt "\n", _prefix, __VA_ARGS__); \ + BLI_dynstr_append(out, _buf); \ + } \ + ((void *)0) + +# define STACK_PUSH(key, memberop) \ + shead++; \ + sprintf(stack[shead].prefix, "%s", key); \ + stack[shead].op = memberop + +# define STACK_POP() shead-- +#endif + +enum { + REPLAY_FLOAT, + REPLAY_INT, + REPLAY_VEC2, + REPLAY_VEC3, + REPLAY_VEC4, + REPLAY_STRUCT, + REPLAY_STRUCT_PTR, + REPLAY_BOOL, + REPLAY_BYTE, + REPLAY_SHORT, +}; + +struct ReplaySerialStruct; +typedef struct ReplaySerialDef { + char name[32]; + int type; //-1 is used for sentinal ending member list + int struct_offset; + struct ReplaySerialStruct *sdef; +} ReplaySerialDef; + +typedef struct ReplaySerialStruct { + char name[32]; + ReplaySerialDef *members; +} ReplaySerialStruct; + +#ifdef DEF +# undef DEF +#endif + +/* clang-format off */ +#define DEF(key, type, structtype, ...) {#key, type, offsetof(structtype, key), __VA_ARGS__} + +static ReplaySerialDef dyntopo_def[] = { + {"detail_range", REPLAY_FLOAT, offsetof(DynTopoSettings, detail_range)}, + {"detail_percent", REPLAY_FLOAT, offsetof(DynTopoSettings, detail_percent)}, + {"detail_size", REPLAY_FLOAT, offsetof(DynTopoSettings, detail_size)}, + {"constant_detail", REPLAY_FLOAT, offsetof(DynTopoSettings, constant_detail)}, + {"flag", REPLAY_SHORT, offsetof(DynTopoSettings, flag)}, + {"mode", REPLAY_SHORT, offsetof(DynTopoSettings, mode)}, + {"inherit", REPLAY_INT, offsetof(DynTopoSettings, inherit)}, + {"spacing", REPLAY_INT, offsetof(DynTopoSettings, spacing)}, + {"", -1, -1}}; +static ReplaySerialStruct DynTopoSettingsDef = {"DynTopoSettings", dyntopo_def}; + +static ReplaySerialDef paint_stroke_def[] = { + DEF(last_mouse_position, REPLAY_VEC2, PaintStroke), + DEF(last_world_space_position, REPLAY_VEC3, PaintStroke), + DEF(stroke_over_mesh, REPLAY_BOOL, PaintStroke), + DEF(stroke_distance, REPLAY_FLOAT, PaintStroke), + DEF(stroke_distance_t, REPLAY_FLOAT, PaintStroke), + DEF(stroke_started, REPLAY_BOOL, PaintStroke), + DEF(rake_started, REPLAY_BOOL, PaintStroke), + DEF(event_type, REPLAY_INT, PaintStroke), + DEF(stroke_init, REPLAY_BOOL, PaintStroke), + DEF(brush_init, REPLAY_BOOL, PaintStroke), + DEF(initial_mouse, REPLAY_VEC2, PaintStroke), + DEF(cached_size_pressure, REPLAY_FLOAT, PaintStroke), + DEF(last_pressure, REPLAY_FLOAT, PaintStroke), + DEF(stroke_mode, REPLAY_INT, PaintStroke), + DEF(last_tablet_event_pressure, REPLAY_FLOAT, PaintStroke), + DEF(pen_flip, REPLAY_INT, PaintStroke), + DEF(x_tilt, REPLAY_FLOAT, PaintStroke), + DEF(y_tilt, REPLAY_FLOAT, PaintStroke), + DEF(spacing, REPLAY_FLOAT, PaintStroke), + DEF(constrain_line, REPLAY_BOOL, PaintStroke), + DEF(constrained_pos, REPLAY_VEC2, PaintStroke), + {"", -1, -1} +}; + +static ReplaySerialStruct PaintStrokeDef = {"PaintStroke", paint_stroke_def}; + +static ReplaySerialDef brush_def[] = { + DEF(weight, REPLAY_FLOAT, Brush), + DEF(size, REPLAY_INT, Brush), + DEF(dyntopo, REPLAY_STRUCT, Brush, &DynTopoSettingsDef), + DEF(flag, REPLAY_INT, Brush), + DEF(flag2, REPLAY_INT, Brush), + DEF(automasking_flags, REPLAY_INT, Brush), + DEF(normal_radius_factor, REPLAY_FLOAT, Brush), + DEF(area_radius_factor, REPLAY_FLOAT, Brush), + DEF(wet_paint_radius_factor, REPLAY_FLOAT, Brush), + DEF(plane_trim, REPLAY_FLOAT, Brush), + DEF(height, REPLAY_FLOAT, Brush), + DEF(vcol_boundary_factor, REPLAY_FLOAT, Brush), + DEF(vcol_boundary_exponent, REPLAY_FLOAT, Brush), + DEF(topology_rake_factor, REPLAY_FLOAT, Brush), + DEF(topology_rake_radius_factor, REPLAY_FLOAT, Brush), + DEF(topology_rake_projection, REPLAY_FLOAT, Brush), + DEF(topology_rake_spacing, REPLAY_FLOAT, Brush), + DEF(tilt_strength_factor, REPLAY_FLOAT, Brush), + DEF(autosmooth_factor, REPLAY_FLOAT, Brush), + DEF(tilt_strength_factor, REPLAY_FLOAT, Brush), + DEF(autosmooth_radius_factor, REPLAY_FLOAT, Brush), + DEF(autosmooth_projection, REPLAY_FLOAT, Brush), + DEF(autosmooth_spacing, REPLAY_FLOAT, Brush), + DEF(boundary_smooth_factor, REPLAY_FLOAT, Brush), + DEF(autosmooth_fset_slide, REPLAY_FLOAT, Brush), + DEF(sculpt_tool, REPLAY_BYTE, Brush), + DEF(falloff_shape, REPLAY_BYTE, Brush), + DEF(falloff_angle, REPLAY_FLOAT, Brush), + DEF(paint_flags, REPLAY_INT, Brush), + DEF(density, REPLAY_FLOAT, Brush), + DEF(wet_persistence, REPLAY_FLOAT, Brush), + DEF(wet_mix, REPLAY_FLOAT, Brush), + DEF(flow, REPLAY_FLOAT, Brush), + DEF(hardness, REPLAY_FLOAT, Brush), + DEF(alpha, REPLAY_FLOAT, Brush), + DEF(rgb, REPLAY_VEC3, Brush), + DEF(rate, REPLAY_FLOAT, Brush), + DEF(smooth_stroke_factor, REPLAY_FLOAT, Brush), + DEF(smooth_stroke_radius, REPLAY_INT, Brush), + DEF(spacing, REPLAY_INT, Brush), + DEF(overlay_flags, REPLAY_INT, Brush), + DEF(mask_pressure, REPLAY_INT, Brush), + DEF(jitter, REPLAY_FLOAT, Brush), + DEF(overlay_flags, REPLAY_INT, Brush), + DEF(sampling_flag, REPLAY_INT, Brush), + DEF(normal_weight, REPLAY_FLOAT, Brush), + DEF(blend, REPLAY_SHORT, Brush), + DEF(concave_mask_factor, REPLAY_FLOAT, Brush), + {"", -1, -1}}; + +static ReplaySerialStruct BrushDef = {"Brush", brush_def}; + +static ReplaySerialDef stroke_cache_def[] = { + DEF(bstrength, REPLAY_FLOAT, StrokeCache), + DEF(radius, REPLAY_FLOAT, StrokeCache), + DEF(pressure, REPLAY_FLOAT, StrokeCache), + DEF(brush, REPLAY_STRUCT_PTR, StrokeCache, &BrushDef), + DEF(location, REPLAY_VEC3, StrokeCache), + DEF(view_normal, REPLAY_VEC3, StrokeCache), + DEF(true_location, REPLAY_VEC3, StrokeCache), + DEF(location, REPLAY_VEC3, StrokeCache), + DEF(initial_radius, REPLAY_FLOAT, StrokeCache), + DEF(dyntopo_pixel_radius, REPLAY_FLOAT, StrokeCache), + DEF(radius_squared, REPLAY_FLOAT, StrokeCache), + DEF(iteration_count, REPLAY_INT, StrokeCache), + DEF(special_rotation, REPLAY_FLOAT, StrokeCache), + DEF(grab_delta, REPLAY_VEC3, StrokeCache), + DEF(grab_delta_symmetry, REPLAY_VEC3, StrokeCache), + DEF(old_grab_location, REPLAY_VEC3, StrokeCache), + DEF(orig_grab_location, REPLAY_VEC3, StrokeCache), + DEF(rake_rotation, REPLAY_VEC4, StrokeCache), + DEF(rake_rotation_symmetry, REPLAY_VEC4, StrokeCache), + DEF(is_rake_rotation_valid, REPLAY_BOOL, StrokeCache), + DEF(paint_face_set, REPLAY_INT, StrokeCache), + DEF(symmetry, REPLAY_INT, StrokeCache), + DEF(boundary_symmetry, REPLAY_INT, StrokeCache), + DEF(mirror_symmetry_pass, REPLAY_INT, StrokeCache), + DEF(true_view_normal, REPLAY_VEC3, StrokeCache), + DEF(view_normal, REPLAY_VEC3, StrokeCache), + DEF(sculpt_normal, REPLAY_VEC3, StrokeCache), + DEF(sculpt_normal_symm, REPLAY_VEC3, StrokeCache), + DEF(plane_offset, REPLAY_VEC3, StrokeCache), + DEF(radial_symmetry_pass, REPLAY_INT, StrokeCache), + DEF(last_center, REPLAY_VEC3, StrokeCache), + DEF(original, REPLAY_BOOL, StrokeCache), + DEF(initial_location, REPLAY_VEC3, StrokeCache), + DEF(true_initial_location, REPLAY_VEC3, StrokeCache), + DEF(initial_normal, REPLAY_VEC3, StrokeCache), + DEF(true_initial_normal, REPLAY_VEC3, StrokeCache), + DEF(vertex_rotation, REPLAY_FLOAT, StrokeCache), + DEF(plane_trim_squared, REPLAY_FLOAT, StrokeCache), + DEF(saved_smooth_size, REPLAY_FLOAT, StrokeCache), + DEF(alt_smooth, REPLAY_BOOL, StrokeCache), + DEF(density_seed, REPLAY_FLOAT, StrokeCache), + DEF(stroke_distance, REPLAY_FLOAT, StrokeCache), + DEF(stroke_distance_t, REPLAY_FLOAT, StrokeCache), + DEF(last_dyntopo_t, REPLAY_FLOAT, StrokeCache), + DEF(scale, REPLAY_VEC3, StrokeCache), + {"", -1, -1} +}; + +static ReplaySerialStruct StrokeCacheDef = {"StrokeCache", stroke_cache_def}; + +static ReplaySerialDef paint_def[] = { + DEF(symmetry_flags, REPLAY_INT, Paint), + {"", -1, -1} +}; +static ReplaySerialStruct PaintDef = {"Paint", paint_def}; + +static ReplaySerialDef sculpt_def[] = { + DEF(paint, REPLAY_STRUCT, Sculpt, &PaintDef), + DEF(detail_size, REPLAY_FLOAT, Sculpt), + DEF(detail_range , REPLAY_FLOAT, Sculpt), + DEF(constant_detail , REPLAY_FLOAT, Sculpt), + DEF(detail_percent , REPLAY_FLOAT, Sculpt), + DEF(dyntopo_spacing , REPLAY_INT, Sculpt), + DEF(automasking_flags, REPLAY_INT, Sculpt), + DEF(flags, REPLAY_INT, Sculpt), + {"", -1, -1} +}; + +static ReplaySerialStruct SculptDef = {"Sculpt", sculpt_def}; + +static ReplaySerialDef ups_def[] = { + DEF(size, REPLAY_INT, UnifiedPaintSettings), + DEF(unprojected_radius, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(alpha, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(weight, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(rgb, REPLAY_VEC3, UnifiedPaintSettings), + DEF(secondary_rgb, REPLAY_VEC3, UnifiedPaintSettings), + DEF(flag, REPLAY_INT, UnifiedPaintSettings), + DEF(last_rake, REPLAY_VEC2, UnifiedPaintSettings), + DEF(last_rake_angle, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(last_stroke_valid, REPLAY_INT, UnifiedPaintSettings), + DEF(average_stroke_accum, REPLAY_VEC3, UnifiedPaintSettings), + DEF(unprojected_radius, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(average_stroke_counter, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(brush_rotation, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(brush_rotation_sec, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(anchored_size, REPLAY_INT, UnifiedPaintSettings), + DEF(overlap_factor, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(draw_inverted, REPLAY_BYTE, UnifiedPaintSettings), + DEF(stroke_active, REPLAY_BYTE, UnifiedPaintSettings), + DEF(draw_anchored, REPLAY_BYTE, UnifiedPaintSettings), + DEF(last_location, REPLAY_VEC3, UnifiedPaintSettings), + DEF(last_hit, REPLAY_INT, UnifiedPaintSettings), + DEF(anchored_initial_mouse, REPLAY_VEC2, UnifiedPaintSettings), + DEF(pixel_radius, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(initial_pixel_radius, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(size_pressure_value, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(tex_mouse, REPLAY_VEC2, UnifiedPaintSettings), + DEF(mask_tex_mouse, REPLAY_VEC2, UnifiedPaintSettings), + {"", -1, -1} +}; +static ReplaySerialStruct UnifiedPaintSettingsDef = { + "UnifiedPaintSettings", ups_def +}; + +static ReplaySerialDef sample_def[] = { + {"active_vertex_co", REPLAY_VEC3, offsetof(SculptBrushSample, active_vertex_co)}, + {"active_face_co", REPLAY_VEC3, offsetof(SculptBrushSample, active_face_co)}, + {"have_active_vertex", REPLAY_BOOL, offsetof(SculptBrushSample, have_active_vertex)}, + {"have_active_face", REPLAY_BOOL, offsetof(SculptBrushSample, have_active_face)}, + {"cache", REPLAY_STRUCT, offsetof(SculptBrushSample, cache), &StrokeCacheDef}, + // {"brush", REPLAY_STRUCT, offsetof(SculptBrushSample, brush), &BrushDef}, + {"sd", REPLAY_STRUCT, offsetof(SculptBrushSample, sd), &SculptDef}, + DEF(ups, REPLAY_STRUCT, SculptBrushSample, &UnifiedPaintSettingsDef), + DEF(stroke, REPLAY_STRUCT, SculptBrushSample, &PaintStrokeDef), + {"", -1, -1}}; + +static ReplaySerialStruct SculptBrushSampleDef = {"SculptBrushSample", sample_def}; + +/* clang-format on */ + +typedef struct ReplaySerializer { + struct { + char prefix[256], op[32]; + } stack[16]; + int stack_head; + DynStr *out; +} ReplaySerializer; + +static void replay_samples_ensure_size(SculptReplayLog *log); + +void replay_write_path(ReplaySerializer *state, char *key) +{ + char buf[512]; + + if (state->stack_head >= 0) { + sprintf(buf, + "%s%s%s", + state->stack[state->stack_head].prefix, + state->stack[state->stack_head].op, + key); + } + else { + sprintf(buf, "%s", key); + } + + BLI_dynstr_append(state->out, buf); +} + +void replay_push_stack(ReplaySerializer *state, char *key, char *op) +{ + state->stack_head++; + + if (state->stack_head > 0) { + sprintf(state->stack[state->stack_head].prefix, + "%s%s%s", + state->stack[state->stack_head - 1].prefix, + state->stack[state->stack_head - 1].op, + key); + } + else { + sprintf(state->stack[state->stack_head].prefix, "%s", key); + } + + sprintf(state->stack[state->stack_head].op, "%s", op); +} + +void replay_pop_stack(ReplaySerializer *state) +{ + state->stack_head--; +} + +#define SKIP_WS \ + while (i < len && ELEM(buf[i], ' ', '\t', '\r')) \ + i++ + +#define SKIP_ALL_WS \ + while (i < len && ELEM(buf[i], ' ', '\t', '\r', '\n')) \ + i++ + +#include <stdarg.h> + +static int parse_replay_member(const char *buf, int len, ReplaySerialStruct *st, void *data) +{ + char *ptr = (char *)data; + int i = 0; + int n = 0; + + SKIP_WS; + ReplaySerialDef *mdef = NULL; + + while (buf[i] != ':') { + int a = strcspn(buf + i, ".-:"); + + if (a < 0 || i + a >= len) { + break; + } + + char *name = alloca(a + 1); + memcpy(name, buf + i, a); + name[a] = 0; + + i += a; + + while (ELEM(buf[i], '-', '>', '.')) { + i++; + } + + SKIP_WS; + + ReplaySerialDef *mdef2 = st->members; + while (mdef2->type != -1) { + if (STREQ(mdef2->name, name)) { + break; + } + mdef2++; + } + + if (mdef2->type == -1) { + printf("Failed to find memer \"%s!\n", name); + return len; + } + + SKIP_WS; + + ptr += mdef2->struct_offset; + + if (mdef2->type == REPLAY_STRUCT_PTR) { + void **vptr = (void **)ptr; + + if (!*vptr) { + char *line = alloca(len + 1); + memcpy(line, buf, len); + line[len] = 0; + + printf("error; missing memory for %s\n", line); + return len; + } + + ptr = (char *)*vptr; + st = mdef2->sdef; + } + else if (mdef2->type == REPLAY_STRUCT) { + st = mdef2->sdef; + } + + mdef = mdef2; + } + + if (!mdef) { + printf("replay parse error\n"); + return len; + } + + i++; + SKIP_WS; + + switch (mdef->type) { + case REPLAY_FLOAT: { + float f = 0.0; + + sscanf(buf + i, "%f%n", &f, &n); + i += n; + *(float *)ptr = f; + break; + } + case REPLAY_INT: { + int f = 0; + + sscanf(buf + i, "%d%n", &f, &n); + i += n; + *(int *)ptr = f; + break; + } + case REPLAY_BOOL: + case REPLAY_BYTE: { + int f = 0; + + sscanf(buf + i, "%d%n", &f, &n); + i += n; + *(unsigned char *)ptr = (unsigned char)f; + break; + } + case REPLAY_VEC2: { + float f[2]; + + sscanf(buf + i, "[%f,%f]%n", &f[0], &f[1], &n); + i += n; + + copy_v2_v2((float *)ptr, f); + break; + } + case REPLAY_VEC3: { + float f[3]; + + sscanf(buf + i, "[%f,%f,%f]%n", &f[0], &f[1], &f[2], &n); + i += n; + + copy_v3_v3((float *)ptr, f); + break; + } + case REPLAY_VEC4: { + float f[4]; + + sscanf(buf + i, "[%f,%f,%f,%f]%n", &f[0], &f[1], &f[2], &f[3], &n); + i += n; + + copy_v4_v4((float *)ptr, f); + break; + } + case REPLAY_SHORT: { + int f = 0; + + sscanf(buf + i, "%d%n", &f, &n); + i += n; + *(short *)ptr = (short)f; + break; + } + default: + printf("replay parse error: invalid type %d\n", mdef->type); + break; + } + return i; +} + +// data1 is dest, data2 is source +static void replay_load(ReplaySerialStruct *st, void *data1, void *data2) +{ + ReplaySerialDef *mdef = st->members; + + while (mdef->type != -1) { + char *ptr1 = ((char *)data1) + mdef->struct_offset; + char *ptr2 = ((char *)data2) + mdef->struct_offset; + + switch (mdef->type) { + case REPLAY_STRUCT_PTR: { + void **vptr1 = (void **)ptr1; + void **vptr2 = (void **)ptr2; + + if (!*vptr1 || !*vptr2) { + printf("failed to load pointers %p %p\n", *vptr1, *vptr2); + mdef++; + continue; + } + + ptr1 = *vptr1; + ptr2 = *vptr2; + } + case REPLAY_STRUCT: + replay_load(mdef->sdef, ptr1, ptr2); + break; + case REPLAY_INT: + case REPLAY_FLOAT: + memcpy(ptr1, ptr2, sizeof(int)); + break; + case REPLAY_BYTE: + case REPLAY_BOOL: + *ptr1 = *ptr2; + break; + case REPLAY_VEC2: + memcpy(ptr1, ptr2, sizeof(float) * 2); + break; + case REPLAY_VEC3: + memcpy(ptr1, ptr2, sizeof(float) * 3); + break; + case REPLAY_VEC4: + memcpy(ptr1, ptr2, sizeof(float) * 4); + break; + case REPLAY_SHORT: + memcpy(ptr1, ptr2, 2); + break; + } + mdef++; + } +} + +void do_brush_action(struct Sculpt *sd, + struct Object *ob, + struct Brush *brush, + struct UnifiedPaintSettings *ups); +void sculpt_combine_proxies(Sculpt *sd, Object *ob); +bool sculpt_tool_is_proxy_used(const char sculpt_tool); +void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr); + +static void *hashco(float fx, float fy, float fz, float fdimen) +{ + double x = (double)fx; + double y = (double)fy; + double z = (double)fz; + double dimen = (double)fdimen; + + return (void *)((intptr_t)(z * dimen * dimen * dimen + y * dimen * dimen + x * dimen)); +} + +void SCULPT_replay_make_cube(struct bContext *C, int steps) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + if (!ss || !ss->bm) { + return; + } + + GHash *vhash = BLI_ghash_ptr_new("vhash"); + + float df = 2.0f / (float)(steps - 1); + + int hashdimen = steps * 8; + + BMVert **grid = MEM_malloc_arrayN(steps * steps * 2, sizeof(*grid), "bmvert grid"); + BMesh *bm = ss->bm; + + BM_mesh_clear(bm); + + for (int side = 0; side < 6; side++) { + int axis = side >= 3 ? side - 3 : side; + float sign = side >= 3 ? -1.0f : 1.0f; + + printf("AXIS: %d\n", axis); + + float u = -1.0f; + + for (int i = 0; i < steps; i++, u += df) { + float v = -1.0f; + + for (int j = 0; j < steps; j++, v += df) { + float co[3]; + + co[axis] = u; + co[(axis + 1) % 3] = v; + co[(axis + 2) % 3] = sign; + + // turn into sphere + normalize_v3(co); + // mul_v3_fl(co, 2.0f); + + void *key = hashco(co[0], co[1], co[2], hashdimen); + +#if 0 + printf("%.3f %.3f %.3f, key: %p i: %d j: %d df: %f, u: %f v: %f\n", + co[0], + co[1], + co[2], + key, + i, + j, + df, + u, + v); +#endif + + void **val = NULL; + + if (!BLI_ghash_ensure_p(vhash, key, &val)) { + BMVert *v2 = BM_vert_create(bm, co, NULL, BM_CREATE_NOP); + + *val = (void *)v2; + } + + BMVert *v2 = (BMVert *)*val; + int idx = j * steps + i; + + grid[idx] = v2; + } + } + + for (int i = 0; i < steps - 1; i++) { + for (int j = 0; j < steps - 1; j++) { + int idx1 = j * steps + i; + int idx2 = (j + 1) * steps + i; + int idx3 = (j + 1) * steps + i + 1; + int idx4 = j * steps + i + 1; + + BMVert *v1 = grid[idx1]; + BMVert *v2 = grid[idx2]; + BMVert *v3 = grid[idx3]; + BMVert *v4 = grid[idx4]; + + if (v1 == v2 || v1 == v3 || v1 == v4 || v2 == v3 || v2 == v4 || v3 == v4) { + printf("ERROR!\n"); + continue; + } + + if (sign >= 0) { + BMVert *vs[4] = {v4, v3, v2, v1}; + BM_face_create_verts(bm, vs, 4, NULL, BM_CREATE_NOP, true); + } + else { + BMVert *vs[4] = {v1, v2, v3, v4}; + BM_face_create_verts(bm, vs, 4, NULL, BM_CREATE_NOP, true); + } + } + } + } + + MEM_SAFE_FREE(grid); + BLI_ghash_free(vhash, NULL, NULL); + +#if 1 + // randomize + uint *rands[4]; + uint tots[4] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + + RNG *rng = BLI_rng_new(0); + + for (uint i = 0; i < 4; i++) { + rands[i] = MEM_malloc_arrayN(tots[i], sizeof(uint), "rands[i]"); + + for (uint j = 0; j < tots[i]; j++) { + rands[i][j] = j; + } + + for (uint j = 0; j < tots[i] >> 1; j++) { + int j2 = BLI_rng_get_int(rng) % tots[i]; + SWAP(uint, rands[i][j], rands[i][j2]); + } + } + + BM_mesh_remap(bm, rands[0], rands[1], rands[3], rands[2]); + + for (int i = 0; i < 4; i++) { + MEM_SAFE_FREE(rands[i]); + } + + BLI_rng_free(rng); +#endif + + BKE_pbvh_free(ss->pbvh); + ss->pbvh = NULL; + + SCULPT_dyntopo_node_layers_update_offsets(ss); + + /* Redraw. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, ND_DATA | NC_OBJECT | ND_DRAW, ob); +} + +void SCULPT_replay(struct bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if (!ob) { + printf("no object\n"); + return; + } + + Scene *scene = CTX_data_scene(C); + + if (!scene) { + printf("no scene\n"); + return; + } + + Sculpt *sd = scene->toolsettings->sculpt; + + if (!sd) { + printf("no sculpt settings\n"); + return; + } + + SculptSession *ss = ob->sculpt; + + if (!ss) { + printf("object must be in sculpt mode\n"); + return; + } + + if (!current_log) { + printf("%s: no reply data\n", __func__); + return; + } + + SculptReplayLog *log = current_log; + SculptBrushSample *samp = log->samples; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + bool have_cache = ss->cache; + ViewContext vc; + + log->is_playing = true; + float last_dyntopo_t = 0.0f; + + SCULPT_undo_push_begin(ob, "Replay"); + + if (!have_cache) { + ED_view3d_viewcontext_init(C, &vc, depsgraph); + } + + for (int i = 0; i < log->totsample; i++, samp++) { + if (!have_cache) { + ss->cache = &samp->cache; + ss->cache->vc = &vc; + } + else { + replay_load(&StrokeCacheDef, &samp->cache, ss->cache); + } + + replay_load(&SculptDef, &samp->sd, sd); + replay_load( + &UnifiedPaintSettingsDef, &samp->ups, &scene->toolsettings->unified_paint_settings); + + ss->cache->first_time = i == 0; + samp->ups.last_stroke_valid = i > 0; + + Brush _brush = *ss->cache->brush; + Brush *brush = &_brush; + + samp->stroke.brush = brush; + samp->stroke.ups = &samp->ups; + samp->stroke.vc = vc; + samp->sd.paint.brush = brush; + + ss->cache->stroke = &samp->stroke; + + ss->cache->last_dyntopo_t = last_dyntopo_t; + sculpt_stroke_update_step(C, ss->cache->stroke, NULL); + last_dyntopo_t = ss->cache->last_dyntopo_t; + continue; + do_brush_action(sd, ob, brush, &scene->toolsettings->unified_paint_settings); + sculpt_combine_proxies(sd, ob); + + /* Hack to fix noise texture tearing mesh. */ + // sculpt_fix_noise_tear(sd, ob); + + /* TODO(sergey): This is not really needed for the solid shading, + * which does use pBVH drawing anyway, but texture and wireframe + * requires this. + * + * Could be optimized later, but currently don't think it's so + * much common scenario. + * + * Same applies to the DEG_id_tag_update() invoked from + * sculpt_flush_update_step(). + */ + if (ss->deform_modifiers_active) { + SCULPT_flush_stroke_deform(sd, ob, sculpt_tool_is_proxy_used(brush->sculpt_tool)); + } + else if (ss->shapekey_active) { + // sculpt_update_keyblock(ob); + } + + ss->cache->first_time = false; + copy_v3_v3(ss->cache->true_last_location, ss->cache->true_location); + + /* Cleanup. */ + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); + } + else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) { + SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR); + } + else { + SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); + } + + int update = SCULPT_UPDATE_COORDS | SCULPT_UPDATE_COLOR | SCULPT_UPDATE_VISIBILITY | + SCULPT_UPDATE_MASK; + SCULPT_flush_update_done(C, ob, update); + } + + if (!have_cache) { + ss->cache = NULL; + } + + SCULPT_undo_push_end(); + log->is_playing = false; +} + +void SCULPT_replay_parse(const char *buf) +{ + if (current_log) { + SCULPT_replay_log_end(); + } + + SculptReplayLog *log = current_log = SCULPT_replay_log_create(); + + int i = 0; + int n = 0; + int len = strlen(buf); + + SKIP_ALL_WS; + + int version = 0; + + sscanf(buf + i, "version:%d\n%n", &version, &n); + i += n; + + SKIP_ALL_WS; + + while (i < len) { + // find newline + + SKIP_WS; + + int end = strcspn(buf + i, "\n"); + if (end < 0) { + end = len - 1; // last line? + } + + if (end == 0) { + // empty line + i++; + continue; + } + + int nr = 0; + if (sscanf(buf + i, "samp:%d.%n", &nr, &n) == 0) { + i += end; + SKIP_ALL_WS; + continue; + } + i += n; + + log->totsample = MAX2(log->totsample, nr + 1); + replay_samples_ensure_size(log); + + SculptBrushSample *samp = log->samples + nr; + + if (!samp->cache.brush) { + samp->cache.brush = BLI_memarena_calloc(log->arena, sizeof(Brush)); + } + + i += parse_replay_member(buf + i, end, &SculptBrushSampleDef, samp); + + SKIP_ALL_WS; + } + + return; +} + +void replay_serialize_struct(ReplaySerializer *state, ReplaySerialStruct *def, void *struct_data) +{ + DynStr *out = state->out; + + ReplaySerialDef *mdef = def->members; + char buf[256]; + + while (mdef->type >= 0) { + char *ptr = (char *)struct_data; + ptr += mdef->struct_offset; + + if (!ELEM(mdef->type, REPLAY_STRUCT, REPLAY_STRUCT_PTR)) { + replay_write_path(state, mdef->name); + } + + switch (mdef->type) { + case REPLAY_STRUCT: + case REPLAY_STRUCT_PTR: + replay_push_stack(state, mdef->name, mdef->type == REPLAY_STRUCT ? "." : "->"); + // BLI_dynstr_append(state->out, " {\n"); + if (mdef->type == REPLAY_STRUCT_PTR) { + replay_serialize_struct(state, mdef->sdef, *(void **)ptr); + } + else { + replay_serialize_struct(state, mdef->sdef, ptr); + } + replay_pop_stack(state); + // BLI_dynstr_append(state->out, "}\n"); + break; + case REPLAY_INT: + sprintf(buf, ": %d\n", *((int *)ptr)); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_FLOAT: + sprintf(buf, ": %f\n", *((float *)ptr)); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_VEC2: + sprintf(buf, ": [%f,%f]\n", ((float *)ptr)[0], ((float *)ptr)[1]); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_VEC3: + sprintf(buf, ": [%f,%f,%f]\n", ((float *)ptr)[0], ((float *)ptr)[1], ((float *)ptr)[2]); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_VEC4: + sprintf(buf, + ": [%f,%f,%f,%f]\n", + ((float *)ptr)[0], + ((float *)ptr)[1], + ((float *)ptr)[2], + ((float *)ptr)[3]); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_BOOL: + sprintf(buf, ": %s\n", *ptr ? "1" : "0"); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_BYTE: + sprintf(buf, ": %d\n", (int)*ptr); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_SHORT: + sprintf(buf, ": %d\n", (int)*((short *)ptr)); + BLI_dynstr_append(state->out, buf); + break; + } + + mdef++; + } +} + +void replay_state_init(ReplaySerializer *state) +{ + memset(state, 0, sizeof(*state)); + state->stack_head = -1; +} + +char *SCULPT_replay_serialize() +{ + if (!current_log) { + return ""; + } + + SculptReplayLog *log = current_log; + DynStr *out = BLI_dynstr_new(); + + ReplaySerializer state; + + BLI_dynstr_append(out, "version:1\n"); + + replay_state_init(&state); + state.out = out; + + for (int i = 0; i < log->totsample; i++) { + char buf[32]; + + sprintf(buf, "samp:%d", i); + replay_push_stack(&state, buf, "."); + + replay_serialize_struct(&state, &SculptBrushSampleDef, log->samples + i); + + replay_pop_stack(&state); + } + + char *ret = BLI_dynstr_get_cstring(out); + BLI_dynstr_free(out); + + return ret; +} + +static void SCULPT_replay_deserialize(SculptReplayLog *log) +{ +} + +static void replay_samples_ensure_size(SculptReplayLog *log) +{ + if (log->totsample >= log->samples_size) { + int size = (2 + log->samples_size); + size += size >> 1; + + if (!log->samples) { + log->samples = MEM_calloc_arrayN(size, sizeof(*log->samples), "log->samples"); + } + else { + log->samples = MEM_recallocN(log->samples, sizeof(*log->samples) * size); + } + + log->samples_size = size; + } +} + +static bool replay_ensure_tex(SculptReplayLog *log, MTex *tex) +{ + if (!tex->tex) { + return true; + } + + for (int i = 0; i < log->tot_textures; i++) { + if (STREQ(log->textures[i]->id.name, tex->tex->id.name)) { + return true; + } + } + + Tex *texcpy = (Tex *)BLI_memarena_alloc(log->arena, sizeof(Tex)); + *texcpy = *tex->tex; + + tex->tex = texcpy; + + if (texcpy->ima) { + Image *ima = BLI_memarena_alloc(log->arena, sizeof(*ima)); + *ima = *texcpy->ima; + texcpy->ima = ima; + } + // if (texcpy->ima && texcpy->ima->id); + + return false; +} + +void SCULPT_replay_test() +{ + SculptSession ss = {0}; + Sculpt sd = {0}; + Object ob = {0}; + StrokeCache cache = {0}; + Brush brush = {0}; + + brush.size = 1.5f; + brush.weight = 2.0f; + brush.autosmooth_factor = 2.0f; + + ss.cache = &cache; + cache.bstrength = 1.0f; + cache.radius = 1.5f; + cache.brush = &brush; + + ss.active_vertex_index.i = -1LL; + ss.active_face_index.i = -1LL; + + SCULPT_replay_log_start(); + SCULPT_replay_log_append(&sd, &ss, &ob); + char *buf = SCULPT_replay_serialize(); + + if (buf) { + printf("=========result=======\n%s\n", buf); + } + + MEM_SAFE_FREE(buf); + SCULPT_replay_log_end(); +} + +void SCULPT_replay_log_append(Sculpt *sd, SculptSession *ss, Object *ob) +{ + SculptReplayLog *log = current_log; + + if (!log || log->is_playing) { + return; + } + + log->totsample++; + replay_samples_ensure_size(log); + + SculptBrushSample *samp = log->samples + log->totsample - 1; + + if (!ss->cache) { + printf("Error!!"); + return; + } + + samp->time = PIL_check_seconds_timer(); + samp->stroke = *ss->cache->stroke; + + samp->sd = *sd; + samp->cache = *ss->cache; + + // replay_ensure_tex(log, &samp->cache->brush.mtex); + + if (ss->active_vertex_index.i != -1LL) { + samp->have_active_vertex = true; + // copy_v3_v3(samp->active_vertex_co, SCULPT_vertex_co_get(ss, ss->active_vertex_index)); + } + else { + zero_v3(samp->active_vertex_co); + samp->have_active_vertex = false; + } + + // TODO: active face + samp->have_active_face = false; +} diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 38165b7622f..870c45339aa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -1,3 +1,4 @@ + /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -23,15 +24,20 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" #include "BLI_blenlib.h" +#include "BLI_compiler_attrs.h" #include "BLI_hash.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_task.h" +#include "BLI_threads.h" #include "DNA_brush_types.h" #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" @@ -57,79 +63,607 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "atomic_ops.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, + SculptCustomLayer *bound_scl, + bool do_origco) +{ + float avg[3] = {0.0f, 0.0f, 0.0f}; + + MDynTopoVert *mv = SCULPT_vertex_get_mdyntopo(ss, vertex); + + if (do_origco) { + SCULPT_vertex_check_origdata(ss, vertex); + } + + float total = 0.0f; + int neighbor_count = 0; + bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS; + + int bflag = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP; + float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP); + float slide_fset = BKE_brush_fset_slide_get(ss->scene, ss->cache->brush); + + slide_fset = MAX2(slide_fset, bound_smooth); + + if (check_fsets) { + bflag |= SCULPT_BOUNDARY_FACE_SET; + } + + const SculptBoundaryType is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bflag); + + const float *co = do_origco ? mv->origco : SCULPT_vertex_co_get(ss, vertex); + float no[3]; + + if (true || projection > 0.0f) { + if (do_origco) { + copy_v3_v3(no, mv->origno); + } + else { + SCULPT_vertex_normal_get(ss, vertex, no); + } + } + + const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) && !is_boundary; + float *areas = NULL; + + SculptCornerType ctype = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP; + if (check_fsets) { + ctype |= SCULPT_CORNER_FACE_SET; + } + + bool have_bmesh = ss->bm; + + if (weighted || bound_scl) { + int val = SCULPT_vertex_valence_get(ss, vertex); + areas = BLI_array_alloca(areas, val); + + BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val); + } + + float *b1 = NULL, btot = 0.0f, b1_orig; + + if (bound_scl) { + b1 = SCULPT_temp_cdata_get(vertex, bound_scl); + b1_orig = *b1; + *b1 = 0.0f; + } + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + MDynTopoVert *mv2 = SCULPT_vertex_get_mdyntopo(ss, ni.vertex); + const float *co2; + + if (!do_origco || mv2->stroke_id != ss->stroke_id) { + co2 = SCULPT_vertex_co_get(ss, ni.vertex); + } + else { + co2 = mv2->origco; + } + + neighbor_count++; + + float tmp[3], w; + bool ok = false; + + if (weighted) { + w = areas[ni.i]; + } + else { + w = 1.0f; + } + + bool do_diffuse = false; + + /*use the new edge api if edges are available, if not estimate boundary + from verts*/ + + SculptBoundaryType final_boundary = 0; + + if (ni.has_edge) { + final_boundary = SCULPT_edge_is_boundary(ss, ni.edge, bflag); + +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + if (ss->bm) { + BMEdge *e = (BMEdge *)ni.edge.i; + if (!(e->head.hflag & BM_ELEM_DRAW)) { + neighbor_count--; + continue; + } + } +#endif + } + else { + final_boundary = is_boundary & SCULPT_vertex_is_boundary(ss, ni.vertex, bflag); + } + + do_diffuse = bound_scl != NULL; + + if (is_boundary) { + /* Boundary vertices use only other boundary vertices. + + This if statement needs to be refactored a bit, it's confusing. + + */ + + bool slide = (slide_fset > 0.0f && is_boundary == SCULPT_BOUNDARY_FACE_SET) || + bound_smooth > 0.0f; + slide = slide && !final_boundary; + + if (slide) { + // project non-boundary offset onto boundary normal + float t[3]; + + w *= slide_fset; + + sub_v3_v3v3(t, co2, co); + madd_v3_v3v3fl(tmp, co, no, dot_v3v3(t, no)); + ok = true; + } + else if (final_boundary) { + copy_v3_v3(tmp, co2); + ok = true; + do_diffuse = false; + } + else { + ok = false; + } + } + else { + copy_v3_v3(tmp, co2); + ok = true; + } + + if (do_diffuse && bound_scl && !is_boundary) { + /* + simple boundary inflator using an ad-hoc diffusion-based pseudo-geodesic field + + makes more rounded edges. + */ + copy_v3_v3(tmp, co2); + ok = true; + + float len = len_v3v3(co, tmp); + float w2 = 1.0f; + + float *b2 = SCULPT_temp_cdata_get(ni.vertex, bound_scl); + float b2_val = *b2 + len; + + if (SCULPT_vertex_is_boundary(ss, ni.vertex, bflag)) { + w2 = 1000.0f; + b2_val = len; + } + + *b1 += b2_val * w2; + btot += w2; + + float no2[3]; + + if (!do_origco || mv2->stroke_id != ss->stroke_id) { + SCULPT_vertex_normal_get(ss, ni.vertex, no2); + } + else { + copy_v3_v3(no2, mv2->origno); + } + + float radius = ss->cache->radius * 10.0f; + + float th = radius - b1_orig; + th = MAX2(th, 0.0f); + th /= radius; + +#if 0 + float *color = (float *)SCULPT_vertex_color_get(ss, ni.vertex); + color[0] = color[1] = color[2] = th; + color[3] = 1.0f; +#endif + + float fac = ss->cache->brush->boundary_smooth_factor; + fac = MIN2(fac * 4.0f, 1.0f); + fac = powf(fac, 0.2); + th *= fac; + + sub_v3_v3(tmp, co); + madd_v3_v3fl(tmp, no2, th * dot_v3v3(no2, tmp)); + add_v3_v3(tmp, co); + } + + 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); + madd_v3_v3fl(avg, tmp, w); + } + else { + madd_v3_v3fl(avg, tmp, w); + } + + total += w; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (btot != 0.0f) { + *b1 /= btot; + //*b1 += (b1_orig - *b1) * 0.95f; + } + else if (b1) { + *b1 = b1_orig; + } + + /* Do not modify corner vertices. */ + if (neighbor_count <= 2 && is_boundary) { + copy_v3_v3(result, co); + return; + } + + /* Avoid division by 0 when there are no neighbors. */ + if (total == 0.0f) { + copy_v3_v3(result, co); + return; + } + + mul_v3_v3fl(result, avg, 1.0f / total); + + if (projection > 0.0f) { + add_v3_v3(result, co); + } + + SculptCornerType c = SCULPT_vertex_is_corner(ss, vertex, ctype); + float corner_smooth; + + if (c == 0) { + return; + } + + if (c & SCULPT_CORNER_FACE_SET) { + corner_smooth = MAX2(slide_fset, bound_smooth); + } + else { + corner_smooth = bound_smooth; + } + + interp_v3_v3v3(result, result, co, 1.0f - corner_smooth); +} + +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, index); + bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS; + int bflag = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP; + + if (check_fsets) { + bflag |= SCULPT_BOUNDARY_FACE_SET; + } + + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bflag); + 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, index, 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.index)) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); - total++; + if (SCULPT_vertex_is_boundary(ss, ni.vertex, bflag)) { + 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 && is_boundary) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + 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, 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); + } +} + +int closest_vec_to_perp(float dir[3], float r_dir2[3], float no[3], float *buckets, float w) +{ + int bits = 0; + + if (dot_v3v3(r_dir2, dir) < 0.0f) { + negate_v3(r_dir2); + bits |= 1; + } + + float dir4[3]; + cross_v3_v3v3(dir4, r_dir2, no); + normalize_v3(dir4); + + if (dot_v3v3(dir4, dir) < 0.0f) { + negate_v3(dir4); + bits |= 2; + } + + if (dot_v3v3(dir4, dir) > dot_v3v3(r_dir2, dir)) { + copy_v3_v3(r_dir2, dir4); + bits |= 4; + } + + buckets[bits] += w; + + return bits; +} + +void vec_transform(float r_dir2[3], float no[3], int bits) +{ + if (bits & 4) { + float dir4[3]; + + copy_v3_v3(dir4, r_dir2); + + if (bits & 2) { + negate_v3(dir4); + } + + float dir5[3]; + + cross_v3_v3v3(dir5, no, dir4); + normalize_v3(dir5); + + copy_v3_v3(r_dir2, dir5); + } + + if (bits & 1) { + negate_v3(r_dir2); + } +} + +volatile int blehrand = 0; +static int blehrand_get() +{ + int i = blehrand; + i = (i * 124325 + 231423322) & 524287; + + blehrand = i; + return i; } /* 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(SculptSession *ss, + float avg[3], + float direction[3], + BMVert *v, + float projection, + bool check_fsets, + int cd_temp, + int cd_dyn_vert, + bool do_origco) { - float avg_co[3] = {0.0f, 0.0f, 0.0f}; float tot_co = 0.0f; + float buckets[8] = {0}; + + // zero_v3(direction); + + MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v); + + float *col = BM_ELEM_CD_GET_VOID_P(v, cd_temp); + float dir[3]; + float dir3[3] = {0.0f, 0.0f, 0.0f}; + + const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT); + float *areas; + + SCULPT_vertex_check_origdata(ss, (SculptVertRef){.i = (intptr_t)v}); + + if (do_origco) { + // SCULPT_vertex_check_origdata(ss, (SculptVertRef){.i = (intptr_t)v}); + madd_v3_v3fl(direction, mv->origno, -dot_v3v3(mv->origno, direction)); + normalize_v3(direction); + } + + float *co1 = do_origco ? mv->origco : v->co; + float *no1 = do_origco ? mv->origno : v->no; + + if (weighted) { + SculptVertRef vertex = {.i = (intptr_t)v}; + + int val = SCULPT_vertex_valence_get(ss, vertex); + areas = BLI_array_alloca(areas, val * 2); + + BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val); + } + + copy_v3_v3(dir, col); + + if (dot_v3v3(dir, dir) == 0.0f) { + copy_v3_v3(dir, direction); + } + else { + closest_vec_to_perp(dir, direction, no1, buckets, 1.0f); // col[3]); + } + + float totdir3 = 0.0f; + + const float selfw = (float)mv->valence * 0.0025f; + madd_v3_v3fl(dir3, direction, selfw); + totdir3 += selfw; + BMIter eiter; BMEdge *e; + bool had_bound = false; + int area_i = 0; - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (BM_edge_is_boundary(e)) { - copy_v3_v3(avg, v->co); - return; - } + BM_ITER_ELEM_INDEX (e, &eiter, v, BM_EDGES_OF_VERT, area_i) { BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; + + float dir2[3]; + float *col2 = BM_ELEM_CD_GET_VOID_P(v_other, cd_temp); + + float bucketw = 1.0f; // col2[3] < col[3] ? 2.0f : 1.0f; + // bucketw /= 0.00001f + len_v3v3(e->v1->co, e->v2->co); + // if (weighted) { + // bucketw = 1.0 / (0.000001 + areas[area_i]); + //} + // if (e == v->e) { + // bucketw *= 2.0; + //} + + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v_other); + float *co2; + float *no2; + + if (!do_origco || mv2->stroke_id != ss->stroke_id) { + co2 = v_other->co; + no2 = v_other->no; + } + else { + co2 = mv2->origco; + no2 = mv2->origno; + } + + // bool bound = (mv2->flag & + // (DYNVERT_BOUNDARY)); // | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY)); + // bool bound2 = (mv2->flag & + // (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY)); + + SculptBoundaryType bflag = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH | + SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM; + + int bound = SCULPT_edge_is_boundary(ss, (SculptEdgeRef){.i = (intptr_t)e}, bflag); + float dirw = 1.0f; + + if (bound) { + had_bound = true; + + sub_v3_v3v3(dir2, co2, co1); + madd_v3_v3fl(dir2, no1, -dot_v3v3(no1, dir2)); + normalize_v3(dir2); + dirw = 100000.0f; + } + else { + dirw = col2[3]; + + copy_v3_v3(dir2, col2); + if (dot_v3v3(dir2, dir2) == 0.0f) { + copy_v3_v3(dir2, dir); + } + } + + closest_vec_to_perp(dir, dir2, no1, buckets, bucketw); // col2[3]); + + madd_v3_v3fl(dir3, dir2, dirw); + totdir3 += dirw; + + if (had_bound) { + tot_co = 0.0f; + continue; + } + float vec[3]; - sub_v3_v3v3(vec, v_other->co, v->co); - madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no)); + sub_v3_v3v3(vec, co2, co1); + + madd_v3_v3fl(vec, no1, -dot_v3v3(vec, no1) * projection); normalize_v3(vec); /* fac is a measure of how orthogonal or parallel the edge is * relative to the direction. */ - float fac = dot_v3v3(vec, direction); + float fac = dot_v3v3(vec, dir); +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + float th = fabsf(saacos(fac)) / M_PI + 0.5f; + th -= floorf(th); + + const float limit = 0.045; + + if (fabsf(th - 0.25) < limit || fabsf(th - 0.75) < limit) { + BMEdge enew = *e, eold = *e; + + enew.head.hflag &= ~BM_ELEM_DRAW; + // enew.head.hflag |= BM_ELEM_SEAM; // XXX debug + + atomic_cas_int64((intptr_t *)(&e->head.index), + *(intptr_t *)(&eold.head.index), + *(intptr_t *)(&enew.head.index)); + } +#endif + fac = fac * fac - 0.5f; fac *= fac; - madd_v3_v3fl(avg_co, v_other->co, fac); + + if (weighted) { + fac *= areas[area_i]; + } + + madd_v3_v3fl(avg_co, co2, fac); tot_co += fac; } @@ -139,47 +673,201 @@ 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)); + sub_v3_v3(avg, co1); + mul_v3_v3fl(vec, no1, dot_v3v3(avg, no1) * projection); sub_v3_v3(avg, vec); - add_v3_v3(avg, v->co); + add_v3_v3(avg, co1); } else { - zero_v3(avg); + // zero_v3(avg); + copy_v3_v3(avg, co1); + } + + // do not update in do_origco + if (do_origco) { + return; + } + + if (totdir3 > 0.0f) { + float outdir = totdir3 / (float)mv->valence; + + // mul_v3_fl(dir3, 1.0 / totdir3); + normalize_v3(dir3); + if (had_bound) { + copy_v3_v3(col, dir3); + col[3] = 1000.0f; + } + else { + + mul_v3_fl(col, col[3]); + madd_v3_v3fl(col, dir3, outdir); + + col[3] = (col[3] + outdir) * 0.4; + normalize_v3(col); + } + + float maxb = 0.0f; + int bi = 0; + for (int i = 0; i < 8; i++) { + if (buckets[i] > maxb) { + maxb = buckets[i]; + bi = i; + } + } + + // negate_v3(col); + vec_transform(col, no1, bi); + // negate_v3(col); } } +static void sculpt_neighbor_coords_average_fset(SculptSession *ss, + float result[3], + SculptVertRef vertex, + float projection) +{ + float avg[3] = {0.0f, 0.0f, 0.0f}; + float *co, no[3]; + float total = 0.0f; + + bool boundary = !SCULPT_vertex_has_unique_face_set(ss, vertex); + + if (projection > 0.0f) { + co = (float *)SCULPT_vertex_co_get(ss, vertex); + SCULPT_vertex_normal_get(ss, vertex, no); + } + + const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) && !boundary; + float *areas; + + if (weighted) { + int val = SCULPT_vertex_valence_get(ss, vertex); + areas = BLI_array_alloca(areas, val); + + BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val); + } + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); + float w; + + if (weighted) { + w = areas[ni.i]; + } + else { + w = 1.0f; + } + + if (boundary && SCULPT_vertex_has_unique_face_set(ss, ni.vertex)) { + continue; + } + + 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); + + madd_v3_v3fl(avg, tmp, w); + } + else { + madd_v3_v3fl(avg, co2, w); + } + total += w; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (total > (boundary ? 1.0f : 0.0f)) { + 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, vertex)); + } +} /* 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, bool check_fsets) { + if (check_fsets) { + sculpt_neighbor_coords_average_fset(ss, result, vertex, projection); + return; + } + float avg[3] = {0.0f, 0.0f, 0.0f}; - int total = 0; + float *co, no[3]; + float total = 0.0f; + + if (projection > 0.0f) { + co = (float *)SCULPT_vertex_co_get(ss, vertex); + SCULPT_vertex_normal_get(ss, vertex, no); + } + + const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + float *areas; + + if (weighted) { + int val = SCULPT_vertex_valence_get(ss, vertex); + areas = BLI_array_alloca(areas, val); + + BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val); + } SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); - total++; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); + float w; + + if (weighted) { + w = areas[ni.i]; + } + else { + w = 1.0f; + } + + 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); + + madd_v3_v3fl(avg, tmp, w); + } + else { + madd_v3_v3fl(avg, co2, w); + } + total += w; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (total > 0) { + if (total > 0.0f) { 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 +878,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,11 +929,13 @@ 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]; - madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade); + float *dir = SCULPT_temp_cdata_get(vd.vertex, data->scl); + + madd_v3_v3v3fl(disp, vd.co, dir, fade); SCULPT_clip(sd, ss, vd.co, disp); if (vd.mvert) { @@ -263,33 +953,40 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + SculptCustomLayer scl; + + SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_detail_dir"); + SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_detail_dir", &scl); + + if (SCULPT_stroke_is_first_brush_step(ss->cache) && + (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) { + BKE_pbvh_update_all_tri_areas(ss->pbvh); + } + SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); if (SCULPT_stroke_is_first_brush_step(ss->cache)) { const int totvert = SCULPT_vertex_count_get(ss); - ss->cache->detail_directions = MEM_malloc_arrayN( - totvert, 3 * sizeof(float), "details directions"); 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); + float *dir = SCULPT_temp_cdata_get(vertex, &scl); + + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, false); + sub_v3_v3v3(dir, avg, SCULPT_vertex_co_get(ss, vertex)); } } - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; + SculptThreadedTaskData data = {.sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .scl = &scl}; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); 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) @@ -311,6 +1008,106 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, 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) +{ + 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; + float projection = data->smooth_projection; + + 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); + const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + const bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS; + + SculptCornerType ctype = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP; + if (check_fsets) { + ctype |= SCULPT_CORNER_FACE_SET; + } + + if (weighted || ss->cache->brush->boundary_smooth_factor > 0.0f) { + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + } + + bool modified = false; + const float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP); + const float slide_fset = BKE_brush_fset_slide_get(ss->scene, ss->cache->brush); + + SculptCustomLayer *bound_scl = data->scl2; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -323,21 +1120,96 @@ 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); - sub_v3_v3v3(val, avg, vd.co); - madd_v3_v3v3fl(val, vd.co, val, fade); - SCULPT_clip(sd, ss, vd.co, val); + + // if (SCULPT_vertex_is_corner(ss, vd.vertex, ctype) & ~SCULPT_CORNER_FACE_SET) { + // continue; + //} + + int steps = data->do_origco ? 2 : 1; + for (int step = 0; step < steps; step++) { + float *co = step ? (float *)SCULPT_vertex_origco_get(ss, vd.vertex) : vd.co; + + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, bound_scl, step); + + sub_v3_v3v3(val, avg, co); + madd_v3_v3v3fl(val, co, val, fade); + SCULPT_clip(sd, ss, co, val); + } + } + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + + modified = true; + } + BKE_pbvh_vertex_iter_end; + + if (modified && weighted) { + BKE_pbvh_node_mark_update_tri_area(data->nodes[n]); + } +} +#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; } @@ -350,7 +1222,9 @@ void SCULPT_smooth(Sculpt *sd, PBVHNode **nodes, const int totnode, float bstrength, - const bool smooth_mask) + const bool smooth_mask, + float projection, + bool do_origco) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -361,11 +1235,29 @@ void SCULPT_smooth(Sculpt *sd, int iteration, count; float last; + if (SCULPT_stroke_is_first_brush_step(ss->cache) && + ((ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) || + ss->cache->brush->boundary_smooth_factor > 0.0f)) { + BKE_pbvh_update_all_tri_areas(ss->pbvh); + } + CLAMP(bstrength, 0.0f, 1.0f); 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,6 +1266,23 @@ void SCULPT_smooth(Sculpt *sd, SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); + SculptCustomLayer _scl, *bound_scl = NULL; + + /* create temp layer for psuedo-geodesic field */ + if (ss->cache->brush->boundary_smooth_factor > 0.0f) { + float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP); + + bound_scl = &_scl; + SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist"); + SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist", bound_scl); + } + +#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; @@ -384,24 +1293,45 @@ void SCULPT_smooth(Sculpt *sd, .nodes = nodes, .smooth_mask = smooth_mask, .strength = strength, + .smooth_projection = projection, + .scl = have_scl ? &scl : NULL, + .scl2 = bound_scl, + .do_origco = SCULPT_stroke_needs_original(ss->cache->brush), }; 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 (0) { // 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 (SCULPT_stroke_is_first_brush_step(ss->cache) && + ((ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) || + ss->cache->brush->boundary_smooth_factor > 0.0f)) { + BKE_pbvh_update_all_tri_areas(ss->pbvh); + } + if (ss->cache->bstrength <= 0.0f) { /* Invert mode, intensify details. */ SCULPT_enhance_details_brush(sd, ob, nodes, totnode); } 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, false); } } @@ -411,42 +1341,49 @@ void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], - float (*laplacian_disp)[3], - const int v_index, + SculptCustomLayer *scl, + const SculptVertRef v_index, const float origco[3], - const float alpha) + const float alpha, + const float projection, + bool check_fsets) { 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, check_fsets); + + 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((float *)SCULPT_temp_cdata_get(v_index, scl), laplacian_smooth_co, d); sub_v3_v3v3(disp, laplacian_smooth_co, co); } void SCULPT_surface_smooth_displace_step(SculptSession *ss, float *co, - float (*laplacian_disp)[3], - const int v_index, + SculptCustomLayer *scl, + 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]); + add_v3_v3(b_avg, (float *)SCULPT_temp_cdata_get(ni.vertex, scl)); 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, (float *)SCULPT_temp_cdata_get(v_index, scl), beta); mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f)); sub_v3_v3(co, b_current_vertex); } @@ -469,10 +1406,20 @@ 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]); + const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + + if (weighted) { + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + } + + bool modified = false; + + bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS; + 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); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -483,18 +1430,31 @@ 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, + data->scl, + vd.vertex, + orig_data.co, + alpha, + data->smooth_projection, + check_fsets); madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; } + + modified = true; } BKE_pbvh_vertex_iter_end; + + if (modified && weighted) { + BKE_pbvh_node_mark_update_tri_area(data->nodes[n]); + } } static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( @@ -524,10 +1484,9 @@ 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); + SCULPT_surface_smooth_displace_step(ss, vd.co, data->scl, vd.vertex, beta, fade); } BKE_pbvh_vertex_iter_end; } @@ -537,19 +1496,29 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; + SculptCustomLayer scl; + + SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_lapsmooth"); + SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_lapsmooth", &scl); + + if (SCULPT_stroke_is_first_brush_step(ss->cache) && + (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) { + BKE_pbvh_update_all_tri_areas(ss->pbvh); + } + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL); - ss->cache->surface_smooth_laplacian_disp = MEM_callocN( - sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b"); + // BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL); + // ss->cache->surface_smooth_laplacian_disp = MEM_callocN( + // sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b"); } /* 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, + .scl = &scl}; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -560,3 +1529,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..61ea43efd66 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -159,13 +159,13 @@ 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; SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); float transformed_co[3], orig_co[3], disp[3]; float *start_co; float fade = vd.mask ? *vd.mask : 0.0f; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 501a1e53276..12f4f90a6e4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -23,6 +23,7 @@ */ #include <stddef.h> +#include <string.h> #include "MEM_guardedalloc.h" @@ -70,6 +71,8 @@ #include "bmesh.h" #include "sculpt_intern.h" +#define WHEN_GLOBAL_UNDO_WORKS + /* Implementation of undo system for objects in sculpt mode. * * Each undo step in sculpt mode consists of list of nodes, each node contains: @@ -113,10 +116,21 @@ typedef struct UndoSculpt { ListBase nodes; size_t undo_size; + BMLog *bm_restore; } UndoSculpt; -static UndoSculpt *sculpt_undo_get_nodes(void); +typedef struct SculptUndoStep { + UndoStep step; + /* NOTE: will split out into list for multi-object-sculpt-mode. */ + UndoSculpt data; + int id; +} SculptUndoStep; +static void update_unode_bmesh_memsize(SculptUndoNode *unode); +static UndoSculpt *sculpt_undo_get_nodes(void); +void sculpt_undo_print_nodes(void *active); +static bool check_first_undo_entry_dyntopo(Object *ob); +void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check); static void update_cb(PBVHNode *node, void *rebuild) { BKE_pbvh_node_mark_update(node); @@ -133,6 +147,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 +186,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 +205,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 +239,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 +267,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 +321,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 +348,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 +368,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 +378,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,44 +415,291 @@ 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; } -static void sculpt_undo_bmesh_restore_generic_task_cb( - void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls)) +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; + int cd_dyn_vert; + bool regen_all_unique_verts; + bool is_redo; +} BmeshUndoData; + +static void bmesh_undo_on_vert_kill(BMVert *v, void *userdata) { - PBVHNode **nodes = 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; + } + + 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; + + data->balance_pbvh = true; + + // let face add vert + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); + + MDynTopoVert *mv = BKE_PBVH_DYNVERT(data->cd_dyn_vert, v); + mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE | DYNVERT_NEED_TRIANGULATE | + DYNVERT_NEED_BOUNDARY; +} + +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); + + BKE_pbvh_bmesh_remove_face(data->pbvh, f, false); + + if (ni >= 0) { + PBVHNode *node = BKE_pbvh_get_node(data->pbvh, ni); + BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node); + } + + // 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); + + int ni = BM_ELEM_CD_GET_INT(f, data->cd_face_node_offset); + PBVHNode *node = BKE_pbvh_get_node(data->pbvh, ni); + + BMLoop *l = f->l_first; + do { + MDynTopoVert *mv = BKE_PBVH_DYNVERT(data->cd_dyn_vert, l->v); + mv->flag |= DYNVERT_NEED_DISK_SORT; + + int ni_l = BM_ELEM_CD_GET_INT(l->v, data->cd_vert_node_offset); + + if (ni_l < 0 && ni >= 0) { + BM_ELEM_CD_SET_INT(l->v, ni_l, ni); + TableGSet *bm_unique_verts = BKE_pbvh_bmesh_node_unique_verts(node); + + BLI_table_gset_add(bm_unique_verts, l->v); + } + } while ((l = l->next) != f->l_first); + + data->balance_pbvh = true; +} +static void bmesh_undo_full_mesh(void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + if (data->pbvh) { + BKE_pbvh_bmesh_update_all_valence(data->pbvh); + } + + data->do_full_recalc = true; +} + +static void bmesh_undo_on_edge_change(BMEdge *v, void *userdata, void *old_customdata) +{ +} + +static void bmesh_undo_on_edge_kill(BMEdge *e, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(data->cd_dyn_vert, e->v1); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(data->cd_dyn_vert, e->v2); + + mv1->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_TRIANGULATE | DYNVERT_NEED_DISK_SORT | + DYNVERT_NEED_VALENCE; + mv2->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_TRIANGULATE | DYNVERT_NEED_DISK_SORT | + DYNVERT_NEED_VALENCE; +} + +static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(data->cd_dyn_vert, e->v1); + MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(data->cd_dyn_vert, e->v2); - BKE_pbvh_node_mark_redraw(nodes[n]); + mv1->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_TRIANGULATE | DYNVERT_NEED_DISK_SORT | + DYNVERT_NEED_VALENCE; + mv2->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_TRIANGULATE | DYNVERT_NEED_DISK_SORT | + DYNVERT_NEED_VALENCE; +} + +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); + data->regen_all_unique_verts = true; + return; + } + + BMElem h; + h.head.data = old_customdata; + + int ni = BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset); + + int ni2 = BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset); + + // attempt to find old node + PBVHNode *node = BKE_pbvh_get_node_leaf_safe(data->pbvh, ni); + if (node) { + // BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node); + BKE_pbvh_node_mark_update(node); + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, ni); + } + else { + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); + data->regen_all_unique_verts = true; + } + + return; + // preserve pbvh node references + + 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; + + if (!old_customdata) { + data->do_full_recalc = true; // can't recover? + return; + } + + BMElem h; + h.head.data = old_customdata; + + int ni = BM_ELEM_CD_GET_INT(&h, data->cd_face_node_offset); + + // attempt to find old node in old_customdata + PBVHNode *node = BKE_pbvh_get_node_leaf_safe(data->pbvh, ni); + if (node) { + BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, ni); + BKE_pbvh_node_mark_update(node); + } + else { + printf("pbvh face undo error\n"); + data->do_full_recalc = true; + BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, -1); + } +} + +static void update_unode_bmesh_memsize(SculptUndoNode *unode) +{ + // update memory size + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + + // subtract old size + if (usculpt->undo_size >= unode->undo_size) { + usculpt->undo_size -= unode->undo_size; + } + + unode->undo_size = BM_log_entry_size(unode->bm_entry); + + // add new size + usculpt->undo_size += unode->undo_size; } 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, + ss->cd_dyn_vert, + false, + !unode->applied}; + + BMLogCallbacks callbacks = {bmesh_undo_on_vert_add, + bmesh_undo_on_vert_kill, + bmesh_undo_on_vert_change, + bmesh_undo_on_edge_add, + bmesh_undo_on_edge_kill, + bmesh_undo_on_edge_change, + bmesh_undo_on_face_add, + bmesh_undo_on_face_kill, + bmesh_undo_on_face_change, + bmesh_undo_full_mesh, + NULL, + (void *)&data}; + + SCULPT_dyntopo_node_layers_update_offsets(ss); + + pbvh_bmesh_check_nodes(ss->pbvh); + 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) { + update_unode_bmesh_memsize(unode); + + if (!data.do_full_recalc) { int totnode; PBVHNode **nodes; BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range( - 0, totnode, nodes, sculpt_undo_bmesh_restore_generic_task_cb, &settings); + if (data.regen_all_unique_verts) { + for (int i = 0; i < totnode; i++) { + BKE_pbvh_bmesh_mark_node_regen(ss->pbvh, nodes[i]); + } + } + + pbvh_bmesh_check_nodes(ss->pbvh); + BKE_pbvh_bmesh_regen_node_verts(ss->pbvh); + pbvh_bmesh_check_nodes(ss->pbvh); + + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); if (nodes) { MEM_freeN(nodes); } + + if (data.balance_pbvh) { + BKE_pbvh_bmesh_after_stroke(ss->pbvh); + } + + pbvh_bmesh_check_nodes(ss->pbvh); } else { SCULPT_pbvh_clear(ob); @@ -442,63 +707,154 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, } /* Create empty sculpt BMesh and enable logging. */ -static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) +static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_redo) { SculptSession *ss = ob->sculpt; 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 = SCULPT_dyntopo_empty_bmesh(); +#if 0 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, + .create_unique_ids = true, + .id_elem_mask = BM_VERT | BM_EDGE | BM_FACE, + .id_map = true, + .temporary_ids = false, + .no_reuse_ids = false})); +#endif + + BM_mesh_bm_from_me(NULL, + ss->bm, + me, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + .use_shapekey = true, + .active_shapekey = ob->shapenr, + })); + SCULPT_dyntopo_node_layers_add(ss); - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + SCULPT_dyntopo_node_layers_update_offsets(ss); + + if (ss->pbvh && ss->bm) { + SCULT_dyntopo_flag_all_disk_sort(ss); + } - /* Restore the BMLog using saved entries. */ - ss->bm_log = BM_log_from_existing_entries_create(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); + BMLogEntry *entry = is_redo ? BM_log_entry_prev(unode->bm_entry) : unode->bm_entry; + + BM_log_set_current_entry(ss->bm_log, entry); + } + + BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert); } -static void sculpt_undo_bmesh_restore_begin(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static void sculpt_undo_bmesh_restore_begin( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { if (unode->applied) { + if (ss->bm && ss->bm_log) { + /*note that we can't log ids here. + not entirely sure why, and in thoery it shouldn't be necassary. + ids end up corrupted. + */ + +#if 1 + // BM_log_all_ids(ss->bm, ss->bm_log, unode->bm_entry); + + // need to run bmlog undo on empty log, + // getting a refcount error in the log + // ref counting system otherwise + + if (dir == -1) { + BM_log_undo_skip(ss->bm, ss->bm_log); + } + else { + BM_log_redo_skip(ss->bm, ss->bm_log); + } +#endif + } + SCULPT_dynamic_topology_disable(C, unode); unode->applied = false; } else { - sculpt_undo_bmesh_enable(ob, unode); + /*load bmesh from mesh data*/ + sculpt_undo_bmesh_enable(ob, unode, true); - /* Restore the mesh from the first log entry. */ - BM_log_redo(ss->bm, ss->bm_log); +#if 1 + // need to run bmlog undo on empty log, + // getting a refcount error in the log + // ref counting system otherwise + + if (dir == 1) { + BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + } + else { + BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + } +#endif unode->applied = true; } + + if (ss->bm) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_FACE); + } } -static void sculpt_undo_bmesh_restore_end(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static void sculpt_undo_bmesh_restore_end( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { + if (unode->applied) { - sculpt_undo_bmesh_enable(ob, unode); + /*load bmesh from mesh data*/ + sculpt_undo_bmesh_enable(ob, unode, false); + +#if 1 + // need to run bmlog undo on empty log, + // getting a refcount error in the log + // ref counting system otherwise - /* Restore the mesh from the last log entry. */ - BM_log_undo(ss->bm, ss->bm_log); + if (dir == -1) { + BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + } + else { + BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + } +#endif unode->applied = false; } else { +#if 1 + if (ss->bm && ss->bm_log) { + // need to run bmlog undo on empty log, + // getting a refcount error in the log + // ref counting system otherwise + + if (dir == -1) { + BM_log_undo_skip(ss->bm, ss->bm_log); + } + else { + BM_log_redo_skip(ss->bm, ss->bm_log); + } + } +#endif + /* Disable dynamic topology sculpting. */ SCULPT_dynamic_topology_disable(C, NULL); unode->applied = true; } + + if (ss->bm) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_FACE); + } } static void sculpt_undo_geometry_store_data(SculptUndoNodeGeometry *geometry, Object *object) @@ -585,28 +941,109 @@ static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object) * * Returns true if this was a dynamic-topology undo step, otherwise * returns false to indicate the non-dyntopo code should run. */ -static int sculpt_undo_bmesh_restore(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static int sculpt_undo_bmesh_restore( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { + // handle transition from another undo type + +#ifdef WHEN_GLOBAL_UNDO_WORKS + if (!ss->bm_log && ss->bm && unode->bm_entry) { // && BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry); + } +#endif + + 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 0 + 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; + } +#endif + ss->active_face_index.i = ss->active_vertex_index.i = 0; + } + else { + ss->active_face_index.i = ss->active_vertex_index.i = -1; + } + + bool ret = false; + bool set_active_vertex = true; + switch (unode->type) { case SCULPT_UNDO_DYNTOPO_BEGIN: - sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); - return true; + sculpt_undo_bmesh_restore_begin(C, unode, ob, ss, dir); + SCULPT_vertex_random_access_ensure(ss); + ss->active_face_index.i = ss->active_vertex_index.i = 0; + set_active_vertex = false; + + 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; + set_active_vertex = false; + + sculpt_undo_bmesh_restore_end(C, unode, ob, ss, dir); + 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 (set_active_vertex && 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 @@ -635,7 +1072,7 @@ static void sculpt_undo_refine_subdiv(Depsgraph *depsgraph, MEM_freeN(deformed_verts); } -static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase *lb) +static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase *lb, int dir) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -647,8 +1084,38 @@ 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 0 + 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); + } +#endif + /* Restore pivot. */ copy_v3_v3(ss->pivot_pos, unode->pivot_pos); copy_v3_v3(ss->pivot_rot, unode->pivot_rot); @@ -664,7 +1131,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); @@ -697,11 +1166,12 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * Undo steps like geometry does not need object to be updated before they run and will * ensure object is updated after the node is handled. */ const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first; - if (first_unode->type != SCULPT_UNDO_GEOMETRY) { + if (first_unode->type != SCULPT_UNDO_GEOMETRY && + first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) { BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false); } - if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) { + if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss, dir)) { return; } } @@ -719,6 +1189,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 +1330,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 +1362,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 +1402,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 +1431,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 +1612,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 +1625,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.vertex); + + 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 +1672,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); } @@ -1209,6 +1726,64 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ return unode; } +void SCULPT_undo_ensure_bmlog(Object *ob) +{ + if (!ob->sculpt) { + return; + } + + UndoStack *ustack = ED_undo_stack_get(); + + if (!ustack) { + return; + } + + UndoStep *us = BKE_undosys_stack_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT); + + if (!us) { + // check next step + if (ustack->step_active && ustack->step_active->next && + ustack->step_active->next->type == BKE_UNDOSYS_TYPE_SCULPT) { + us = ustack->step_active->next; + } + } + + if (!us) { + return; + } + + UndoSculpt *usculpt = sculpt_undosys_step_get_nodes(us); + + SculptSession *ss = ob->sculpt; + Mesh *me = BKE_object_get_original_mesh(ob); + + if (!ss->bm && !(me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY)) { + return; + } + + if (!usculpt) { + // happens during file load + return; + } + + SculptUndoNode *unode = usculpt->nodes.first; + + // this can happen in certain cases when going to/from other undo types + // I think. + if (!ss->bm_log) { + if (unode && unode->bm_entry) { + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry); + } + else { + ss->bm_log = BM_log_create(ss->bm, ss->cd_dyn_vert); + } + + if (ss->pbvh) { + BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); + } + } +} + static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); @@ -1217,76 +1792,217 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt SculptUndoNode *unode = usculpt->nodes.first; + SCULPT_undo_ensure_bmlog(ob); + + if (!ss->bm_log) { + ss->bm_log = BM_log_create(ss->bm, ss->cd_dyn_vert); + } + + 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)); unode->type = type; unode->applied = true; + /* note that every undo type must push a bm_entry for + so we can recreate the BMLog from chained entries + when going to/from other undo system steps */ + 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_all_ids(ss->bm, ss->bm_log, NULL); + 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 - * polys. A full copy like this is needed because entering - * dynamic-topology immediately does topological edits - * (converting polys to triangles) that the BMLog can't - * fully restore from. */ - SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; - sculpt_undo_geometry_store_data(geometry, ob); + // unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL); + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); - unode->bm_entry = BM_log_entry_add(ss->bm_log); - BM_log_all_added(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; +} + +static bool check_first_undo_entry_dyntopo(Object *ob) +{ + UndoStack *ustack = ED_undo_stack_get(); + if (!ustack || !ob->sculpt || !ob->sculpt->bm) { + return false; + } + + UndoStep *us = ustack->step_init ? ustack->step_init : ustack->step_active; + bool bad = false; + + if (!us) { + bad = true; + } + else if (us->type) { + if (!STREQ(us->type->name, "Sculpt")) { + bad = true; + } + else { + SculptUndoStep *step = (SculptUndoStep *)us; + SculptUndoNode *unode = step->data.nodes.first; + + if (!unode) { + bad = true; + } + else { + UndoStep *act = ustack->step_active; + + if (!act->type || !STREQ(act->type->name, "Sculpt")) { + bad = unode->type != SCULPT_UNDO_DYNTOPO_BEGIN; + } + } + } + } + else { + bad = true; + } + + if (bad) { + sculpt_undo_push_begin_ex(ob, "Dyntopo Begin", true); + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + SCULPT_undo_push_end(); + } + + return bad; +} + SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) { SculptSession *ss = ob->sculpt; @@ -1301,20 +2017,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 +2056,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,16 +2097,24 @@ 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; } -void SCULPT_undo_push_begin(Object *ob, const char *name) +void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check) { + SCULPT_undo_ensure_bmlog(ob); + UndoStack *ustack = ED_undo_stack_get(); if (ob != NULL) { + if (!no_first_entry_check && ob->sculpt && ob->sculpt->bm) { + check_first_undo_entry_dyntopo(ob); + } + /* If possible, we need to tag the object and its geometry data as 'changed in the future' in * the previous undo step if it's a memfile one. */ ED_undosys_stack_memfile_id_changed_tag(ustack, &ob->id); @@ -1395,6 +2127,11 @@ void SCULPT_undo_push_begin(Object *ob, const char *name) BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); } +void SCULPT_undo_push_begin(Object *ob, const char *name) +{ + sculpt_undo_push_begin_ex(ob, name, false); +} + void SCULPT_undo_push_end(void) { SCULPT_undo_push_end_ex(false); @@ -1407,6 +2144,10 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo) /* We don't need normals in the undo stack. */ for (unode = usculpt->nodes.first; unode; unode = unode->next) { + if (unode->bm_entry) { + update_unode_bmesh_memsize(unode); + } + if (unode->no) { usculpt->undo_size -= MEM_allocN_len(unode->no); MEM_freeN(unode->no); @@ -1430,12 +2171,6 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo) /** \name Implements ED Undo System * \{ */ -typedef struct SculptUndoStep { - UndoStep step; - /* NOTE: will split out into list for multi-object-sculpt-mode. */ - UndoSculpt data; -} SculptUndoStep; - static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) { SculptUndoStep *us = (SculptUndoStep *)us_p; @@ -1470,8 +2205,10 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == true); - sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes, -1); us->step.is_applied = false; + + sculpt_undo_print_nodes(us); } static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, @@ -1479,8 +2216,10 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == false); - sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes, 1); us->step.is_applied = true; + + sculpt_undo_print_nodes(us); } static void sculpt_undosys_step_decode_undo(struct bContext *C, @@ -1554,10 +2293,13 @@ static void sculpt_undosys_step_decode( BKE_scene_graph_evaluated_ensure(depsgraph, bmain); Mesh *me = ob->data; + +#ifndef WHEN_GLOBAL_UNDO_WORKS /* Don't add sculpt topology undo steps when reading back undo state. * The undo steps must enter/exit for us. */ me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, NULL); +#endif + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, NULL, false); } if (ob->sculpt) { @@ -1628,10 +2370,19 @@ static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p) static UndoSculpt *sculpt_undo_get_nodes(void) { UndoStack *ustack = ED_undo_stack_get(); + + if (!ustack) { // happens during file load + return NULL; + } + UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT); return sculpt_undosys_step_get_nodes(us); } +void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss) +{ +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1677,7 +2428,8 @@ static void sculpt_undo_push_all_grids(Object *object) * to the current operation without making any stroke in between. * * Skip pushing nodes based on the following logic: on redo SCULPT_UNDO_COORDS will ensure - * PBVH for the new base geometry, which will have same coordinates as if we create PBVH here. */ + * PBVH for the new base geometry, which will have same coordinates as if we create PBVH here. + */ if (ss->pbvh == NULL) { return; } @@ -1726,3 +2478,186 @@ 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*/); + if (node->bm_entry) { + BM_log_print_entry(NULL, node->bm_entry); + } +} + +static void print_sculpt_undo_step(UndoStep *us, UndoStep *active, int i) +{ + SculptUndoNode *node; + + if (us->type != BKE_UNDOSYS_TYPE_SCULPT) { + printf("%d %s (non-sculpt): '%s', type:%s, use_memfile_step:%s\n", + i, + us == active ? "->" : " ", + us->name, + us->type->name, + us->use_memfile_step ? "true" : "false"); + return; + } + + int id = -1; + + SculptUndoStep *su = (SculptUndoStep *)us; + if (!su->id) { + su->id = nodeidgen++; + } + + id = su->id; + + printf("id=%d %s %d %s (use_memfile_step=%s)\n", + id, + us == active ? "->" : " ", + i, + us->name, + us->use_memfile_step ? "true" : "false"); + + 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 + + printf("=================== sculpt undo steps ==============\n"); + + 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, act_i = -1; + for (; us; us = us->next, i++) { + if (active == us) { + act_i = i; + } + + print_sculpt_undo_step(us, active, i); + } + + if (ustack->step_active) { + printf("\n\n==active step:==\n"); + print_sculpt_undo_step(ustack->step_active, active, act_i); + } + +#endif +} + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +void BM_log_undo_single(BMesh *bm, + BMLog *log, + BMLogCallbacks *callbacks, + const char *node_layer_id); + +void SCULPT_substep_undo(bContext *C, int dir) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + if (!scene || !ob || !ob->sculpt) { + printf("not in sculpt mode\n"); + return; + } + + SculptSession *ss = ob->sculpt; + + if (!ss->bm) { + printf("not in dyntopo mode\n"); + return; + } + + BmeshUndoData data = {ss->pbvh, + ss->bm, + false, + false, + ss->cd_face_node_offset, + ss->cd_vert_node_offset, + ss->cd_dyn_vert, + false, + false}; + + BMLogCallbacks callbacks = {bmesh_undo_on_vert_add, + bmesh_undo_on_vert_kill, + bmesh_undo_on_vert_change, + bmesh_undo_on_edge_add, + bmesh_undo_on_edge_kill, + bmesh_undo_on_edge_change, + bmesh_undo_on_face_add, + bmesh_undo_on_face_kill, + bmesh_undo_on_face_change, + bmesh_undo_full_mesh, + NULL, + (void *)&data}; + + BM_log_undo_single(ss->bm, ss->bm_log, &callbacks, dyntopop_node_idx_layer_id); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); +} diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index e749e1a7947..ebc192c56b8 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/util/ed_util.c b/source/blender/editors/util/ed_util.c index 73f328f85d7..92d56ccf521 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -135,7 +135,7 @@ void ED_editors_init(bContext *C) else if (mode & OB_MODE_ALL_SCULPT) { if (obact == ob) { if (mode == OB_MODE_SCULPT) { - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, reports); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, reports, true); } else if (mode == OB_MODE_VERTEX_PAINT) { ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob); 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) { |