Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt4
-rw-r--r--source/blender/editors/include/ED_object.h3
-rw-r--r--source/blender/editors/include/UI_icons.h3
-rw-r--r--source/blender/editors/interface/interface.c36
-rw-r--r--source/blender/editors/interface/interface_handlers.c4
-rw-r--r--source/blender/editors/interface/interface_intern.h10
-rw-r--r--source/blender/editors/mesh/editmesh_mask_extract.c59
-rw-r--r--source/blender/editors/mesh/editmesh_polybuild.c4
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c122
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c4
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c1
-rw-r--r--source/blender/editors/mesh/mesh_intern.h2
-rw-r--r--source/blender/editors/mesh/mesh_ops.c3
-rw-r--r--source/blender/editors/object/object_modifier.c22
-rw-r--r--source/blender/editors/object/object_remesh.cc8
-rw-r--r--source/blender/editors/object/object_vgroup.c3
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt9
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_hide.c20
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c15
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h91
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c88
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c165
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c3690
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.cc20
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.hh474
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.c259
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_boundary.c1305
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_brush_machine.c0
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c76
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_curvature.c261
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c104
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_displacement.c1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_displacement.h1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_dyntopo.c1632
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_expand.c521
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c782
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c14
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c63
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_geodesic.c781
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h519
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c26
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_init.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c29
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c125
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_replay.c1228
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c1328
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c1149
-rw-r--r--source/blender/editors/space_info/info_stats.c16
-rw-r--r--source/blender/editors/util/ed_util.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c5
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,
&params, 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) {