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/UI_icons.h3
-rw-r--r--source/blender/editors/mesh/editmesh_mask_extract.c59
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c120
-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.c15
-rw-r--r--source/blender/editors/object/object_remesh.c8
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-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.c19
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h91
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c85
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c165
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c2462
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.c233
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_boundary.c425
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c76
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_curvature.c239
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c51
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_dyntopo.c1232
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_expand.c512
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c548
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c14
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c41
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_geodesic.c262
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h347
-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.c27
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c123
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c640
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c634
-rw-r--r--source/blender/editors/space_info/info_stats.c16
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c5
41 files changed, 7151 insertions, 1385 deletions
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index beb22d43930..49e6ab32f64 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -743,7 +743,6 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
brush.sculpt.cloth
brush.sculpt.crease
brush.sculpt.displacement_eraser
- brush.sculpt.displacement_smear
brush.sculpt.draw
brush.sculpt.draw_face_sets
brush.sculpt.draw_sharp
@@ -756,15 +755,18 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
brush.sculpt.mask
brush.sculpt.multiplane_scrape
brush.sculpt.nudge
+ brush.sculpt.paint
brush.sculpt.pinch
brush.sculpt.pose
brush.sculpt.rotate
brush.sculpt.scrape
brush.sculpt.simplify
+ brush.sculpt.smear
brush.sculpt.smooth
brush.sculpt.snake_hook
brush.sculpt.thumb
brush.sculpt.topology
+ brush.sculpt.vcol_boundary
brush.uv_sculpt.grab
brush.uv_sculpt.pinch
brush.uv_sculpt.relax
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index 7ccdc49d291..467dfb66186 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -919,6 +919,9 @@ DEF_ICON_COLOR(BRUSH_TEXFILL)
DEF_ICON_COLOR(BRUSH_TEXMASK)
DEF_ICON_COLOR(BRUSH_THUMB)
DEF_ICON_COLOR(BRUSH_ROTATE)
+DEF_ICON_COLOR(BRUSH_VCOL_BOUNDARY)
+DEF_ICON_COLOR(BRUSH_PAINT)
+DEF_ICON_COLOR(BRUSH_SCULPT_SMEAR)
/* grease pencil sculpt */
DEF_ICON_COLOR(GPBRUSH_SMOOTH)
diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c
index cccfc7e934c..38e6bb80c0f 100644
--- a/source/blender/editors/mesh/editmesh_mask_extract.c
+++ b/source/blender/editors/mesh/editmesh_mask_extract.c
@@ -61,14 +61,16 @@
#include "mesh_intern.h" /* own include */
+#include "../sculpt_paint/sculpt_intern.h"
+
static bool geometry_extract_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
if (ob != NULL && ob->mode == OB_MODE_SCULPT) {
- if (ob->sculpt->bm) {
- CTX_wm_operator_poll_msg_set(C, "The geometry can not be extracted with dyntopo activated");
- return false;
- }
+ // if (ob->sculpt->bm) {
+ // CTX_wm_operator_poll_msg_set(C, "The geometry can not be extracted with dyntopo activated");
+ // return false;
+ //}
return ED_operator_object_active_editable_mesh(C);
}
return false;
@@ -120,7 +122,8 @@ static int geometry_extract_apply(bContext *C,
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
new_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -373,6 +376,7 @@ static int face_set_extract_invoke(bContext *C, wmOperator *op, const wmEvent *U
ED_workspace_status_text(C, TIP_("Click on the mesh to select a Face Set"));
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER);
WM_event_add_modal_handler(C, op);
+
return OPERATOR_RUNNING_MODAL;
}
@@ -513,7 +517,8 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
new_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -544,7 +549,8 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
new_ob_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -580,15 +586,42 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
if (ob->mode == OB_MODE_SCULPT) {
SculptSession *ss = ob->sculpt;
- ss->face_sets = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS);
- if (ss->face_sets) {
- /* Assign a new Face Set ID to the new faces created by the slice operation. */
- const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data);
- ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id);
+
+ /* Assign a new Face Set ID to the new faces created by the slice operation. */
+
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_GRIDS:
+ case PBVH_FACES:
+ ss->face_sets = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS);
+
+ if (ss->face_sets) {
+ const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data);
+ ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id);
+ }
+ break;
+ case PBVH_BMESH: {
+ if (ss->bm && CustomData_has_layer(&ss->bm->pdata, CD_SCULPT_FACE_SETS)) {
+ const int cd_fset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS);
+ BMFace *f;
+ BMIter iter;
+
+ const int next_face_set_id = SCULPT_face_set_next_available_get(ss);
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = BM_ELEM_CD_GET_INT(f, cd_fset);
+
+ if (fset == SCULPT_FACE_SET_NONE) {
+ BM_ELEM_CD_SET_INT(f, cd_fset, next_face_set_id);
+ }
+ }
+ }
+ break;
+ }
}
- ED_sculpt_undo_geometry_end(ob);
}
+ ED_sculpt_undo_geometry_end(ob);
+
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 41a9f426798..4278d3906ff 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -4394,7 +4394,8 @@ static Base *mesh_separate_tagged(
BM_mesh_normals_update(bm_new);
- BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0}));
+ BM_mesh_bm_to_me(
+ bmain, base_new->object, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0}));
BM_mesh_free(bm_new);
((Mesh *)base_new->object->data)->edit_mesh = NULL;
@@ -4460,7 +4461,8 @@ static Base *mesh_separate_arrays(Main *bmain,
BM_vert_kill(bm_old, verts[i]);
}
- BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0}));
+ BM_mesh_bm_to_me(
+ bmain, base_new->object, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0}));
BM_mesh_free(bm_new);
((Mesh *)base_new->object->data)->edit_mesh = NULL;
@@ -4643,6 +4645,7 @@ static bool mesh_separate_loose(
if (clear_object_data) {
BM_mesh_bm_to_me(NULL,
+ base_old->object,
bm_old,
me_old,
(&(struct BMeshToMeshParams){
@@ -4734,7 +4737,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm_old, me, (&(struct BMeshFromMeshParams){0}));
+ BM_mesh_bm_from_me(NULL, bm_old, me, (&(struct BMeshFromMeshParams){0}));
switch (type) {
case MESH_SEPARATE_MATERIAL:
@@ -4750,6 +4753,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
if (retval_iter) {
BM_mesh_bm_to_me(bmain,
+ ob,
bm_old,
me,
(&(struct BMeshToMeshParams){
@@ -5936,6 +5940,116 @@ static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot)
"Split off face corners instead of merging faces");
}
+static int edbm_mres_test_exec(bContext *C, wmOperator *op)
+{
+ const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
+ const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear");
+
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
+ view_layer, CTX_wm_view3d(C), &objects_len);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+
+ if (em->bm->totvertsel == 0) {
+ continue;
+ }
+
+ BM_custom_loop_normals_to_vector_layer(em->bm);
+
+ if (!EDBM_op_callf(em, op, "test_mres_smooth")) {
+ continue;
+ }
+
+ BM_custom_loop_normals_from_vector_layer(em->bm, false);
+
+ EDBM_update(obedit->data,
+ &(const struct EDBMUpdate_Params){
+ .calc_looptri = true,
+ .calc_normals = false,
+ .is_destructive = true,
+ });
+ // EDBM_update_generic(obedit->data, true, true);
+ }
+
+ MEM_freeN(objects);
+ return OPERATOR_FINISHED;
+}
+
+extern Object *multires_dump_grids_bmesh(Object *bmob, BMesh *bm);
+
+static int edbm_dump_mres_grids_exec(bContext *C, wmOperator *op)
+{
+ const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
+ const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear");
+
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
+ view_layer, CTX_wm_view3d(C), &objects_len);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+
+ multires_dump_grids_bmesh(obedit, em->bm);
+ }
+
+ WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C));
+
+ MEM_freeN(objects);
+ return OPERATOR_FINISHED;
+}
+
+static bool mres_test_poll(bContext *C)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ if (obedit && obedit->type == OB_MESH) {
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+
+ if (!em || !CustomData_has_layer(&em->bm->ldata, CD_MDISPS)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void MESH_OT_dump_mres_grids(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Dump Multires Grids";
+ ot->description = "Dump Multires Grids";
+ ot->idname = "MESH_OT_dump_mres_grids";
+
+ /* api callbacks */
+ ot->exec = edbm_dump_mres_grids_exec;
+ ot->poll = mres_test_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+void MESH_OT_mres_test(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Test Multires Boundary Smooth";
+ ot->description = "Test multires boundary smooth";
+ ot->idname = "MESH_OT_mres_test";
+
+ /* api callbacks */
+ ot->exec = edbm_mres_test_exec;
+ ot->poll = mres_test_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op)
{
const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index fc9e1aa8b1a..8dc7a1d7675 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -607,7 +607,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo
/* BM_mesh_validate(em->bm); */ /* for troubleshooting */
BM_mesh_bm_to_me(
- NULL,
+ NULL, NULL,
em->bm,
&um->me,
(&(struct BMeshToMeshParams){
@@ -679,7 +679,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL, bm,
&um->me,
(&(struct BMeshFromMeshParams){
/* Handled with tessellation. */
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 85c646d689c..d765f6b50af 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -337,6 +337,7 @@ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data)
}
BM_mesh_bm_to_me(bmain,
+ ob,
bm,
me,
(&(struct BMeshToMeshParams){
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index f25317e8e85..721486c149c 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -259,6 +259,7 @@ void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot);
void MESH_OT_average_normals(struct wmOperatorType *ot);
void MESH_OT_smooth_normals(struct wmOperatorType *ot);
void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot);
+void MESH_OT_mres_test(struct wmOperatorType *ot);
/* *** editmesh_mask_extract.c *** */
void MESH_OT_paint_mask_extract(struct wmOperatorType *ot);
@@ -285,3 +286,4 @@ void MESH_OT_customdata_skin_add(struct wmOperatorType *ot);
void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot);
void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot);
void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot);
+void MESH_OT_dump_mres_grids(struct wmOperatorType *ot);
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index 34e9a3f45a5..7962e687d7e 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -205,6 +205,9 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_average_normals);
WM_operatortype_append(MESH_OT_smooth_normals);
WM_operatortype_append(MESH_OT_mod_weighted_strength);
+ WM_operatortype_append(MESH_OT_mres_test);
+ WM_operatortype_append(MESH_OT_dump_mres_grids);
+
}
#if 0 /* UNUSED, remove? */
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 7bbca7ea9e6..aff7ec8ef6c 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -859,7 +859,17 @@ bool ED_object_modifier_apply(Main *bmain,
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in edit mode");
return false;
}
- if (mode != MODIFIER_APPLY_SHAPE && ID_REAL_USERS(ob->data) > 1) {
+
+ bool allow_multi_user = mode == MODIFIER_APPLY_SHAPE;
+ if (md) {
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+
+ allow_multi_user |= ELEM(
+ mti->type, eModifierTypeType_NonGeometrical, eModifierTypeType_OnlyDeform);
+ }
+
+ // bool allow_multi_user = md && md->type == eModifierType_DataTransfer || md->flag & ;
+ if (!allow_multi_user && ID_REAL_USERS(ob->data) > 1) {
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
return false;
}
@@ -1396,6 +1406,9 @@ static bool modifier_apply_poll_ex(bContext *C, bool allow_shared)
Object *ob = (ptr.owner_id != NULL) ? (Object *)ptr.owner_id : ED_object_active_context(C);
ModifierData *md = ptr.data; /* May be NULL. */
+ allow_shared = true;
+ // allow_shared = allow_shared || (md && md->type == eModifierType_DataTransfer);
+
if (ID_IS_OVERRIDE_LIBRARY(ob) || ((ob->data != NULL) && ID_IS_OVERRIDE_LIBRARY(ob->data))) {
CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data");
return false;
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
index 05f9980e0ab..d3162aa62f0 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -761,7 +761,7 @@ static void quadriflow_update_job(void *customdata, float progress, int *cancel)
*(qj->progress) = progress;
}
-static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes)
+static Mesh *remesh_symmetry_bisect(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes)
{
MirrorModifierData mmd = {{0}};
mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE;
@@ -782,8 +782,10 @@ static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes)
zero_v3(plane_no);
plane_no[axis] = -1.0f;
mesh_bisect_temp = mesh_bisect;
- mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(
+
+ mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(ob,
&mmd, mesh_bisect, axis, plane_co, plane_no);
+
if (mesh_bisect_temp != mesh_bisect) {
BKE_id_free(NULL, mesh_bisect_temp);
}
@@ -851,7 +853,7 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
bisect_mesh = BKE_mesh_copy_for_eval(mesh, false);
/* Bisect the input mesh using the paint symmetry settings */
- bisect_mesh = remesh_symmetry_bisect(bisect_mesh, qj->symmetry_axes);
+ bisect_mesh = remesh_symmetry_bisect(ob, bisect_mesh, qj->symmetry_axes);
new_mesh = BKE_mesh_remesh_quadriflow_to_mesh_nomain(
bisect_mesh,
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 3b668a1bd4c..509caed9c2f 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -56,6 +56,7 @@ set(SRC
paint_vertex_weight_ops.c
paint_vertex_weight_utils.c
sculpt.c
+ sculpt_curvature.c
sculpt_automasking.c
sculpt_boundary.c
sculpt_cloth.c
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index ab2b2f4b16b..2c918033ad5 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -1223,7 +1223,7 @@ typedef struct PaintCursorContext {
/* Sculpt related data. */
Sculpt *sd;
SculptSession *ss;
- int prev_active_vertex_index;
+ SculptVertRef prev_active_vertex_index;
bool is_stroke_active;
bool is_cursor_over_mesh;
bool is_multires;
@@ -1589,8 +1589,8 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
paint_cursor_update_object_space_radius(pcontext);
- const bool update_previews = pcontext->prev_active_vertex_index !=
- SCULPT_active_vertex_get(pcontext->ss);
+ const bool update_previews = pcontext->prev_active_vertex_index.i !=
+ SCULPT_active_vertex_get(pcontext->ss).i;
/* Setup drawing. */
wmViewport(&pcontext->region->winrct);
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c
index da627c6b7db..4e3e074c9bf 100644
--- a/source/blender/editors/sculpt_paint/paint_hide.c
+++ b/source/blender/editors/sculpt_paint/paint_hide.c
@@ -209,17 +209,15 @@ static void partialvis_update_grids(Depsgraph *depsgraph,
}
static void partialvis_update_bmesh_verts(BMesh *bm,
- GSet *verts,
+ TableGSet *verts,
PartialVisAction action,
PartialVisArea area,
float planes[4][4],
bool *any_changed,
bool *any_visible)
{
- GSetIterator gs_iter;
-
- GSET_ITER (gs_iter, verts) {
- BMVert *v = BLI_gsetIterator_getKey(&gs_iter);
+ BMVert *v;
+ TGSET_ITER (v, verts) {
float *vmask = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_PAINT_MASK);
/* Hide vertex if in the hide volume. */
@@ -237,15 +235,14 @@ static void partialvis_update_bmesh_verts(BMesh *bm,
(*any_visible) = true;
}
}
+ TGSET_ITER_END
}
-static void partialvis_update_bmesh_faces(GSet *faces)
+static void partialvis_update_bmesh_faces(TableGSet *faces)
{
- GSetIterator gs_iter;
-
- GSET_ITER (gs_iter, faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
+ BMFace *f;
+ TGSET_ITER (f, faces) {
if (paint_is_bmesh_face_hidden(f)) {
BM_elem_flag_enable(f, BM_ELEM_HIDDEN);
}
@@ -253,6 +250,7 @@ static void partialvis_update_bmesh_faces(GSet *faces)
BM_elem_flag_disable(f, BM_ELEM_HIDDEN);
}
}
+ TGSET_ITER_END
}
static void partialvis_update_bmesh(Object *ob,
@@ -263,7 +261,7 @@ static void partialvis_update_bmesh(Object *ob,
float planes[4][4])
{
BMesh *bm;
- GSet *unique, *other, *faces;
+ TableGSet *unique, *other, *faces;
bool any_changed = false, any_visible = false;
bm = BKE_pbvh_get_bmesh(pbvh);
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index bd05d309421..2ca19f76672 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -1081,10 +1081,15 @@ static bool pixel_bounds_uv(const float uv_quad[4][2],
#endif
static bool pixel_bounds_array(
- float (*uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot)
+ float (*in_uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot)
{
/* UV bounds */
float min_uv[2], max_uv[2];
+ float(*uv)[2] = in_uv;
+
+ if (tot < 0) {
+ printf("eek!\n");
+ }
if (tot == 0) {
return false;
@@ -1092,9 +1097,8 @@ static bool pixel_bounds_array(
INIT_MINMAX2(min_uv, max_uv);
- while (tot--) {
+ for (int i = 0; i < tot; i++, uv++) {
minmax_v2v2_v2(min_uv, max_uv, (*uv));
- uv++;
}
bounds_px->xmin = (int)(ibuf_x * min_uv[0]);
@@ -1103,6 +1107,15 @@ static bool pixel_bounds_array(
bounds_px->xmax = (int)(ibuf_x * max_uv[0]) + 1;
bounds_px->ymax = (int)(ibuf_y * max_uv[1]) + 1;
+ const int d = -1000;
+ if (bounds_px->xmin < d || bounds_px->ymin < d || bounds_px->xmax < d || bounds_px->ymax < d) {
+ printf("error! xmin %d xmax %d ymin %d ymax %d\n",
+ bounds_px->xmin,
+ bounds_px->xmax,
+ bounds_px->ymin,
+ bounds_px->ymax);
+ }
+
// printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]);
/* face uses no UV area when quantized to pixels? */
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 7341d984c91..4f5187a71d0 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -51,6 +51,10 @@ typedef struct CoNo {
float no[3];
} CoNo;
+#include "DNA_listBase.h"
+#include "DNA_scene_types.h"
+#include "ED_view3d.h"
+
/* paint_stroke.c */
typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]);
typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]);
@@ -60,6 +64,80 @@ typedef void (*StrokeUpdateStep)(struct bContext *C,
typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final);
typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke);
+typedef struct PaintSample {
+ float mouse[2];
+ float pressure;
+} PaintSample;
+
+typedef struct PaintStroke {
+ void *mode_data;
+ void *stroke_cursor;
+ struct wmTimer *timer;
+ struct RNG *rng;
+
+ /* Cached values */
+ struct ViewContext vc;
+ struct Brush *brush;
+ struct UnifiedPaintSettings *ups;
+
+ /* used for lines and curves */
+ ListBase line;
+
+ /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
+ * to smooth the stroke */
+ PaintSample samples[PAINT_MAX_INPUT_SAMPLES];
+ int num_samples;
+ int cur_sample;
+ int tot_samples;
+
+ float last_mouse_position[2];
+ float last_world_space_position[3];
+ bool stroke_over_mesh;
+ /* space distance covered so far */
+ float stroke_distance;
+ float stroke_distance_t; // divided by brush radius
+
+ /* Set whether any stroke step has yet occurred
+ * e.g. in sculpt mode, stroke doesn't start until cursor
+ * passes over the mesh */
+ bool stroke_started;
+ /* Set when enough motion was found for rake rotation */
+ bool rake_started;
+ /* event that started stroke, for modal() return */
+ int event_type;
+ /* check if stroke variables have been initialized */
+ bool stroke_init;
+ /* check if various brush mapping variables have been initialized */
+ bool brush_init;
+ float initial_mouse[2];
+ /* cached_pressure stores initial pressure for size pressure influence mainly */
+ float cached_size_pressure;
+ /* last pressure will store last pressure value for use in interpolation for space strokes */
+ float last_pressure;
+ int stroke_mode;
+
+ float last_tablet_event_pressure;
+
+ float zoom_2d;
+ int pen_flip;
+
+ /* Tilt, as read from the event. */
+ float x_tilt;
+ float y_tilt;
+
+ /* line constraint */
+ bool constrain_line;
+ float constrained_pos[2];
+
+ StrokeGetLocation get_location;
+ StrokeTestStart test_start;
+ StrokeUpdateStep update_step;
+ StrokeRedraw redraw;
+ StrokeDone done;
+
+ float spacing;
+} PaintStroke;
+
struct PaintStroke *paint_stroke_new(struct bContext *C,
struct wmOperator *op,
StrokeGetLocation get_location,
@@ -91,6 +169,19 @@ bool PAINT_brush_tool_poll(struct bContext *C);
void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C));
void paint_cursor_delete_textures(void);
+/**
+* used by various actions that have their own spacing that
+* is coarser then the brush spacing. e.g. sculpt dyntopo.
+*
+* \param state: pointer to a float used for internal state, should be initialized to zero at start of stroke
+* \return false if the action should be skipped.
+*
+*/
+bool paint_stroke_apply_subspacing(struct PaintStroke *stroke,
+ const float spacing,
+ const enum ePaintMode mode,
+ float *state);
+
/* paint_vertex.c */
bool weight_paint_poll(struct bContext *C);
bool weight_paint_poll_ignore_tool(bContext *C);
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index d968b6cc319..b3fcb9155f3 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -679,7 +679,7 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co
static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd)
{
float vertex_normal[3];
- SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal);
+ SCULPT_vertex_normal_get(sgcontext->ss, vd->vertex, vertex_normal);
float dot = dot_v3v3(sgcontext->view_normal, vertex_normal);
const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f);
@@ -758,7 +758,7 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) {
- SCULPT_vertex_face_set_set(sgcontext->ss, vd.index, face_set_operation->new_face_set_id);
+ SCULPT_vertex_face_set_set(sgcontext->ss, vd.vertex, face_set_operation->new_face_set_id);
any_updated = true;
}
}
@@ -974,7 +974,8 @@ static void sculpt_gesture_trim_normals_update(SculptGestureContext *sgcontext)
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
trim_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -1040,7 +1041,7 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext)
trim_operation->depth_back = -FLT_MAX;
for (int i = 0; i < totvert; i++) {
- const float *vco = SCULPT_vertex_co_get(ss, i);
+ const float *vco = SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
/* Convert the coordinates to world space to calculate the depth. When generating the trimming
* mesh, coordinates are first calculated in world space, then converted to object space to
* store them. */
@@ -1210,7 +1211,7 @@ static void sculpt_gesture_trim_geometry_free(SculptGestureContext *sgcontext)
static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
{
- return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
+ return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 0 : 1;
}
static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
@@ -1220,20 +1221,30 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
Mesh *trim_mesh = trim_operation->mesh;
BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
- BM_mesh_bm_from_me(bm,
- trim_mesh,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ if (sgcontext->ss && sgcontext->ss->bm) {
+ bm = sgcontext->ss->bm;
+ BM_mesh_normals_update(bm);
+ }
+
+ else {
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh);
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = false,
+ }));
+
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ sculpt_mesh,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ }
- BM_mesh_bm_from_me(bm,
- sculpt_mesh,
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ trim_mesh,
&((struct BMeshFromMeshParams){
.calc_face_normal = true,
}));
@@ -1294,15 +1305,29 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
MEM_freeN(looptris);
- Mesh *result = BKE_mesh_from_bmesh_nomain(bm,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }),
- sculpt_mesh);
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- BKE_mesh_nomain_to_mesh(
- result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true);
+ if (sgcontext->ss && sgcontext->ss->bm) { //rebuild pbvh
+ BKE_pbvh_free(sgcontext->ss->pbvh);
+ sgcontext->ss->pbvh = BKE_pbvh_new();
+
+ BKE_pbvh_build_bmesh(sgcontext->ss->pbvh,
+ sgcontext->ss->bm,
+ false,
+ sgcontext->ss->bm_log,
+ sgcontext->ss->cd_vert_node_offset,
+ sgcontext->ss->cd_face_node_offset,
+ sgcontext->ss->cd_dyn_vert);
+ }
+ else { //save result to mesh
+ Mesh *result = BKE_mesh_from_bmesh_nomain(bm,
+ (&(struct BMeshToMeshParams){
+ .calc_object_remap = false,
+ }),
+ sculpt_mesh);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ BKE_mesh_nomain_to_mesh(
+ result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true);
+ }
}
static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgcontext)
@@ -1339,6 +1364,10 @@ static void sculpt_gesture_trim_end(bContext *UNUSED(C), SculptGestureContext *s
sculpt_gesture_trim_geometry_free(sgcontext);
+ if (sgcontext->ss && sgcontext->ss->bm) {
+ SCULPT_dynamic_topology_triangulate(sgcontext->ss, sgcontext->ss->bm);
+ }
+
SCULPT_undo_push_node(sgcontext->vc.obact, NULL, SCULPT_UNDO_GEOMETRY);
BKE_mesh_batch_cache_dirty_tag(sgcontext->vc.obact->data, BKE_MESH_BATCH_DIRTY_ALL);
DEG_id_tag_update(&sgcontext->vc.obact->id, ID_RECALC_GEOMETRY);
@@ -1548,8 +1577,8 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op)
{
Object *object = CTX_data_active_object(C);
SculptSession *ss = object->sculpt;
- if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
- /* Not supported in Multires and Dyntopo. */
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ /* Not supported in Multires. */
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index de01bc3a474..a8cb05c6d2c 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -67,77 +67,6 @@
# include "PIL_time_utildefines.h"
#endif
-typedef struct PaintSample {
- float mouse[2];
- float pressure;
-} PaintSample;
-
-typedef struct PaintStroke {
- void *mode_data;
- void *stroke_cursor;
- wmTimer *timer;
- struct RNG *rng;
-
- /* Cached values */
- ViewContext vc;
- Brush *brush;
- UnifiedPaintSettings *ups;
-
- /* used for lines and curves */
- ListBase line;
-
- /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
- * to smooth the stroke */
- PaintSample samples[PAINT_MAX_INPUT_SAMPLES];
- int num_samples;
- int cur_sample;
- int tot_samples;
-
- float last_mouse_position[2];
- float last_world_space_position[3];
- bool stroke_over_mesh;
- /* space distance covered so far */
- float stroke_distance;
-
- /* Set whether any stroke step has yet occurred
- * e.g. in sculpt mode, stroke doesn't start until cursor
- * passes over the mesh */
- bool stroke_started;
- /* Set when enough motion was found for rake rotation */
- bool rake_started;
- /* event that started stroke, for modal() return */
- int event_type;
- /* check if stroke variables have been initialized */
- bool stroke_init;
- /* check if various brush mapping variables have been initialized */
- bool brush_init;
- float initial_mouse[2];
- /* cached_pressure stores initial pressure for size pressure influence mainly */
- float cached_size_pressure;
- /* last pressure will store last pressure value for use in interpolation for space strokes */
- float last_pressure;
- int stroke_mode;
-
- float last_tablet_event_pressure;
-
- float zoom_2d;
- int pen_flip;
-
- /* Tilt, as read from the event. */
- float x_tilt;
- float y_tilt;
-
- /* line constraint */
- bool constrain_line;
- float constrained_pos[2];
-
- StrokeGetLocation get_location;
- StrokeTestStart test_start;
- StrokeUpdateStep update_step;
- StrokeRedraw redraw;
- StrokeDone done;
-} PaintStroke;
-
/*** Cursors ***/
static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata)
{
@@ -288,6 +217,26 @@ static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode m
return true;
}
+bool paint_stroke_apply_subspacing(struct PaintStroke *stroke,
+ const float spacing,
+ const enum ePaintMode mode,
+ float *state)
+{
+ if (!paint_space_stroke_enabled(stroke->brush, mode)) {
+ return true;
+ }
+
+ const float t = stroke->stroke_distance_t;
+
+ if (t != 0.0f && t < *state + spacing) {
+ return false;
+ }
+ else {
+ *state = t;
+ return true;
+ }
+}
+
/* Initialize the stroke cache variants from operator properties */
static bool paint_brush_update(bContext *C,
Brush *brush,
@@ -430,11 +379,13 @@ static bool paint_brush_update(bContext *C,
ups->anchored_size /= 2.0f;
ups->pixel_radius /= 2.0f;
stroke->stroke_distance = ups->pixel_radius;
+ stroke->stroke_distance_t = 1.0f;
}
else {
copy_v2_v2(ups->anchored_initial_mouse, stroke->initial_mouse);
copy_v2_v2(mouse, stroke->initial_mouse);
stroke->stroke_distance = ups->pixel_radius;
+ stroke->stroke_distance_t = 1.0f;
}
ups->pixel_radius /= stroke->zoom_2d;
ups->draw_anchored = true;
@@ -767,6 +718,58 @@ static float paint_stroke_integrate_overlap(Brush *br, float factor)
return 1.0f / max;
}
+static float paint_space_get_final_size_intern(
+ bContext *C, const Scene *scene, PaintStroke *stroke, float pressure, float dpressure)
+{
+ ePaintMode mode = BKE_paintmode_get_active_from_context(C);
+ float size = BKE_brush_size_get(scene, stroke->brush) * pressure;
+
+ if (paint_stroke_use_scene_spacing(stroke->brush, mode)) {
+ if (!BKE_brush_use_locked_size(scene, stroke->brush)) {
+ float last_object_space_position[3];
+ mul_v3_m4v3(
+ last_object_space_position, stroke->vc.obact->imat, stroke->last_world_space_position);
+ size = paint_calc_object_space_radius(&stroke->vc, last_object_space_position, size);
+ }
+ else {
+ size = BKE_brush_unprojected_radius_get(scene, stroke->brush) * pressure;
+ }
+ }
+
+ return size;
+}
+
+static float paint_space_get_final_size(bContext *C,
+ const Scene *scene,
+ PaintStroke *stroke,
+ float pressure,
+ float dpressure,
+ float length)
+{
+ if (BKE_brush_use_size_pressure(stroke->brush)) {
+ /* use pressure to modify size. set spacing so that at 100%, the circles
+ * are aligned nicely with no overlap. for this the spacing needs to be
+ * the average of the previous and next size. */
+ float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
+ float q = s * dpressure / (2.0f * length);
+ float pressure_fac = (1.0f + q) / (1.0f - q);
+
+ float last_size_pressure = stroke->last_pressure;
+ float new_size_pressure = stroke->last_pressure * pressure_fac;
+
+ /* average spacing */
+ float last_size = paint_space_get_final_size_intern(
+ C, scene, stroke, last_size_pressure, pressure);
+ float new_size = paint_space_get_final_size_intern(
+ C, scene, stroke, new_size_pressure, pressure);
+
+ return 0.5f * (last_size + new_size);
+ }
+ else {
+ return paint_space_get_final_size_intern(C, scene, stroke, 1.0, 0.0);
+ }
+}
+
static float paint_space_stroke_spacing_variable(bContext *C,
const Scene *scene,
PaintStroke *stroke,
@@ -797,6 +800,8 @@ static float paint_space_stroke_spacing_variable(bContext *C,
return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
}
+#include "BLI_compiler_attrs.h"
+
/* For brushes with stroke spacing enabled, moves mouse in steps
* towards the final mouse location. */
static int paint_space_stroke(bContext *C,
@@ -866,8 +871,17 @@ static int paint_space_stroke(bContext *C,
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush,
spacing / no_pressure_spacing);
+ if (use_scene_spacing) {
+ float size = paint_space_get_final_size(C, scene, stroke, pressure, dpressure, length);
+
+ stroke->stroke_distance += stroke->ups->pixel_radius * spacing / size;
+ stroke->stroke_distance_t += spacing / size;
+ }
+ else {
+ stroke->stroke_distance += spacing / stroke->zoom_2d;
+ stroke->stroke_distance_t += (spacing / stroke->zoom_2d) / stroke->ups->pixel_radius;
+ }
- stroke->stroke_distance += spacing / stroke->zoom_2d;
paint_brush_stroke_add_step(C, op, mouse, pressure);
length -= spacing;
@@ -1233,6 +1247,8 @@ static void paint_line_strokes_spacing(bContext *C,
length += *length_residue;
*length_residue = 0.0;
+ stroke->spacing = spacing;
+
if (length >= spacing) {
if (use_scene_spacing) {
float final_world_space_position[3];
@@ -1250,6 +1266,8 @@ static void paint_line_strokes_spacing(bContext *C,
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0);
stroke->stroke_distance += spacing / stroke->zoom_2d;
+ stroke->stroke_distance_t += (spacing / stroke->zoom_2d) / stroke->ups->pixel_radius;
+
paint_brush_stroke_add_step(C, op, mouse, 1.0);
length -= spacing;
@@ -1472,6 +1490,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* one time stroke initialization */
if (!stroke->stroke_started) {
stroke->last_pressure = sample_average.pressure;
+
copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
if (paint_stroke_use_scene_spacing(br, mode)) {
stroke->stroke_over_mesh = SCULPT_stroke_get_location(
@@ -1558,6 +1577,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
float dmouse[2];
sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position);
stroke->stroke_distance += len_v2(dmouse);
+ stroke->stroke_distance_t += len_v2(dmouse) / stroke->ups->pixel_radius;
+
paint_brush_stroke_add_step(C, op, mouse, pressure);
redraw = true;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 83388c1aef2..f7785df0d3c 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -46,6 +46,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "BKE_attribute.h"
#include "BKE_brush.h"
#include "BKE_ccg.h"
#include "BKE_colortools.h"
@@ -111,12 +112,34 @@
void SCULPT_vertex_random_access_ensure(SculptSession *ss)
{
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ if (ss->bm) {
+ ss->totfaces = ss->totpoly = ss->bm->totface;
+ ss->totvert = ss->bm->totvert;
+
BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
BM_mesh_elem_table_ensure(ss->bm, BM_VERT);
}
}
+/* Sculpt PBVH abstraction API
+ *
+ * This is read-only, for writing use PBVH vertex iterators. There vd.index matches
+ * the indices used here.
+ *
+ * For multi-resolution, the same vertex in multiple grids is counted multiple times, with
+ * different index for each grid. */
+
+void SCULPT_face_random_access_ensure(SculptSession *ss)
+{
+ if (ss->bm) {
+ ss->totfaces = ss->totpoly = ss->bm->totface;
+ ss->totvert = ss->bm->totvert;
+
+ BM_mesh_elem_index_ensure(ss->bm, BM_FACE);
+ BM_mesh_elem_table_ensure(ss->bm, BM_FACE);
+ }
+}
+
int SCULPT_vertex_count_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
@@ -131,22 +154,39 @@ int SCULPT_vertex_count_get(SculptSession *ss)
return 0;
}
-const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex)
{
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BMVert *v = (BMVert *)vertex.i;
+ return BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v)->origco;
+ }
+
+ // XXX implement me
+ return SCULPT_vertex_co_get(ss, vertex);
+}
+
+const float *SCULPT_vertex_co_get(SculptSession *ss, SculptVertRef index)
+{
+ if (ss->bm) {
+ return ((BMVert *)index.i)->co;
+ }
+
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
if (ss->shapekey_active || ss->deform_modifiers_active) {
const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
- return mverts[index].co;
+ return mverts[index.i].co;
}
- return ss->mvert[index].co;
+ return ss->mvert[index.i].co;
+ }
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)index.i;
+ return v->co;
}
- case PBVH_BMESH:
- return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co;
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index));
}
@@ -154,41 +194,53 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
return NULL;
}
-const float *SCULPT_vertex_color_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_color_get(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
if (ss->vcol) {
- return ss->vcol[index].color;
+ return ss->vcol[index.i].color;
}
break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)index.i;
+
+ if (ss->cd_vcol_offset >= 0) {
+ MPropCol *col = BM_ELEM_CD_GET_VOID_P(v, ss->cd_vcol_offset);
+ return col->color;
+ }
+
+ break;
+ }
case PBVH_GRIDS:
break;
}
return NULL;
}
-void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
+void SCULPT_vertex_normal_get(SculptSession *ss, SculptVertRef index, float no[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
if (ss->shapekey_active || ss->deform_modifiers_active) {
const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
- normal_short_to_float_v3(no, mverts[index].no);
+ normal_short_to_float_v3(no, mverts[index.i].no);
}
else {
- normal_short_to_float_v3(no, ss->mvert[index].no);
+ normal_short_to_float_v3(no, ss->mvert[index.i].no);
}
break;
}
- case PBVH_BMESH:
- copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no);
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)index.i;
+
+ copy_v3_v3(no, v->no);
break;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
copy_v3_v3(no, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index)));
break;
@@ -196,32 +248,48 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
}
}
-const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_persistent_co_get(SculptSession *ss,
+ SculptVertRef index,
+ int cd_pers_co)
{
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ if (cd_pers_co >= 0) {
+ BMVert *v = (BMVert *)index.i;
+ float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co);
+
+ return co;
+ }
+
+ return SCULPT_vertex_co_get(ss, index);
+ }
+
if (ss->persistent_base) {
- return ss->persistent_base[index].co;
+ int i = BKE_pbvh_vertex_index_to_table(ss->pbvh, index);
+
+ return ss->persistent_base[i].co;
}
+
return SCULPT_vertex_co_get(ss, index);
}
-const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, SculptVertRef vertex)
{
/* Always grab active shape key if the sculpt happens on shapekey. */
if (ss->shapekey_active) {
const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
- return mverts[index].co;
+ return mverts[BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)].co;
}
/* Sculpting on the base mesh. */
if (ss->mvert) {
- return ss->mvert[index].co;
+ return ss->mvert[BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)].co;
}
/* Everything else, such as sculpting on multires. */
- return SCULPT_vertex_co_get(ss, index);
+ return SCULPT_vertex_co_get(ss, vertex);
}
-void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3])
+void SCULPT_vertex_limit_surface_get(SculptSession *ss, SculptVertRef index, float r_co[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -230,8 +298,8 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]
break;
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
@@ -242,30 +310,172 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]
}
}
-void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3])
+void SCULPT_vertex_persistent_normal_get(SculptSession *ss,
+ SculptVertRef index,
+ float no[3],
+ int cd_pers_no)
{
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ if (cd_pers_no >= 0) {
+ BMVert *v = (BMVert *)index.i;
+ float(*no2)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no);
+
+ copy_v3_v3(no, (float *)no2);
+ return;
+ }
+
+ SCULPT_vertex_normal_get(ss, index, no);
+ return;
+ }
+
if (ss->persistent_base) {
- copy_v3_v3(no, ss->persistent_base[index].no);
+ copy_v3_v3(no, ss->persistent_base[BKE_pbvh_vertex_index_to_table(ss->pbvh, index)].no);
return;
}
SCULPT_vertex_normal_get(ss, index, no);
}
-float SCULPT_vertex_mask_get(SculptSession *ss, int index)
+static bool sculpt_temp_customlayer_get(SculptSession *ss,
+ AttributeDomain domain,
+ int proptype,
+ char *name,
+ SculptCustomLayer *out,
+ bool autocreate)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH: {
+ CustomData *cdata = NULL;
+
+ if (!ss->bm) {
+ return false;
+ }
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ cdata = &ss->bm->vdata;
+ break;
+ case ATTR_DOMAIN_FACE:
+ cdata = &ss->bm->pdata;
+ break;
+ default:
+ return false;
+ }
+
+ int idx = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (idx < 0) {
+ if (!autocreate) {
+ return false;
+ }
+
+ BM_data_layer_add_named(ss->bm, cdata, proptype, name);
+ idx = CustomData_get_named_layer_index(cdata, proptype, name);
+ cdata->layers[idx].flag |= CD_FLAG_TEMPORARY;
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ }
+
+ out->data = NULL;
+ out->is_cdlayer = true;
+ out->layer = cdata->layers + idx;
+ out->cd_offset = out->layer->offset;
+ out->elemsize = CustomData_get_elem_size(out->layer);
+
+ break;
+ }
+ case PBVH_FACES: {
+ CustomData *cdata = NULL;
+ int totelem = 0;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ totelem = ss->totvert;
+ cdata = ss->vdata;
+ break;
+ case ATTR_DOMAIN_FACE:
+ totelem = ss->totfaces;
+ cdata = ss->pdata;
+ break;
+ default:
+ return false;
+ }
+
+ int idx = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (idx < 0) {
+ if (!autocreate) {
+ return false;
+ }
+
+ CustomData_add_layer_named(cdata, proptype, CD_CALLOC, NULL, totelem, name);
+ idx = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ cdata->layers[idx].flag |= CD_FLAG_TEMPORARY;
+ }
+
+ out->data = NULL;
+ out->is_cdlayer = true;
+ out->layer = cdata->layers + idx;
+ out->cd_offset = -1;
+ out->data = out->layer->data;
+ out->elemsize = CustomData_get_elem_size(out->layer);
+ break;
+ }
+ case PBVH_GRIDS: {
+ CustomData *cdata = NULL;
+ int totelem = 0;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ totelem = BKE_pbvh_get_grid_num_vertices(ss->pbvh);
+ cdata = &ss->temp_vdata;
+ break;
+ case ATTR_DOMAIN_FACE:
+ // not supported
+ return false;
+ default:
+ return false;
+ }
+
+ int idx = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (idx < 0) {
+ if (!autocreate) {
+ return false;
+ }
+
+ CustomData_add_layer_named(cdata, proptype, CD_CALLOC, NULL, totelem, name);
+ idx = CustomData_get_named_layer_index(cdata, proptype, name);
+ }
+
+ out->data = NULL;
+ out->is_cdlayer = true;
+ out->layer = cdata->layers + idx;
+ out->cd_offset = -1;
+ out->data = out->layer->data;
+ out->elemsize = CustomData_get_elem_size(out->layer);
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+float SCULPT_vertex_mask_get(SculptSession *ss, SculptVertRef index)
{
BMVert *v;
float *mask;
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- return ss->vmask[index];
+ return ss->vmask[index.i];
case PBVH_BMESH:
- v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index);
+ v = (BMVert *)index.i;
mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK));
return *mask;
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
return *CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index));
}
@@ -274,12 +484,27 @@ float SCULPT_vertex_mask_get(SculptSession *ss, int index)
return 0.0f;
}
-int SCULPT_active_vertex_get(SculptSession *ss)
+bool SCULPT_temp_customlayer_ensure(SculptSession *ss,
+ AttributeDomain domain,
+ int proptype,
+ char *name)
+{
+ SculptCustomLayer scl;
+ return sculpt_temp_customlayer_get(ss, domain, proptype, name, &scl, true);
+}
+
+bool SCULPT_temp_customlayer_get(
+ SculptSession *ss, AttributeDomain domain, int proptype, char *name, SculptCustomLayer *scl)
+{
+ return sculpt_temp_customlayer_get(ss, domain, proptype, name, scl, true);
+}
+
+SculptVertRef SCULPT_active_vertex_get(SculptSession *ss)
{
if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) {
return ss->active_vertex_index;
}
- return 0;
+ return BKE_pbvh_make_vref(0);
}
const float *SCULPT_active_vertex_co_get(SculptSession *ss)
@@ -332,44 +557,49 @@ int SCULPT_active_face_set_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- return ss->face_sets[ss->active_face_index];
+ return ss->face_sets[ss->active_face_index.i];
case PBVH_GRIDS: {
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg,
ss->active_grid_index);
return ss->face_sets[face_index];
}
case PBVH_BMESH:
+ if (ss->cd_faceset_offset && ss->active_face_index.i) {
+ BMFace *f = (BMFace *)ss->active_face_index.i;
+ return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+ }
+
return SCULPT_FACE_SET_NONE;
}
return SCULPT_FACE_SET_NONE;
}
-void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible)
+void SCULPT_vertex_visible_set(SculptSession *ss, SculptVertRef index, bool visible)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- SET_FLAG_FROM_TEST(ss->mvert[index].flag, !visible, ME_HIDE);
- ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE;
+ SET_FLAG_FROM_TEST(ss->mvert[index.i].flag, !visible, ME_HIDE);
+ ss->mvert[index.i].flag |= ME_VERT_PBVH_UPDATE;
break;
case PBVH_BMESH:
- BM_elem_flag_set(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN, !visible);
+ BM_elem_flag_set((BMVert *)index.i, BM_ELEM_HIDDEN, !visible);
break;
case PBVH_GRIDS:
break;
}
}
-bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_visible_get(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- return !(ss->mvert[index].flag & ME_HIDE);
+ return !(ss->mvert[index.i].flag & ME_HIDE);
case PBVH_BMESH:
- return !BM_elem_flag_test(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN);
+ return !BM_elem_flag_test(((BMVert *)index.i), BM_ELEM_HIDDEN);
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
BLI_bitmap **grid_hidden = BKE_pbvh_get_grid_visibility(ss->pbvh);
if (grid_hidden && grid_hidden[grid_index]) {
return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index);
@@ -396,8 +626,28 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl
}
}
break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ if (abs(fset) != face_set) {
+ continue;
+ }
+
+ if (visible) {
+ fset = abs(fset);
+ }
+ else {
+ fset = -abs(fset);
+ }
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ }
break;
+ }
}
}
@@ -410,8 +660,19 @@ void SCULPT_face_sets_visibility_invert(SculptSession *ss)
ss->face_sets[i] *= -1;
}
break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ fset = -fset;
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ }
break;
+ }
}
}
@@ -437,48 +698,108 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
}
}
break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+
+ if (!ss->bm) {
+ return;
+ }
+
+ // paranoia check of cd_faceset_offset
+ if (ss->cd_faceset_offset < 0) {
+ ss->cd_faceset_offset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS);
+ }
+ if (ss->cd_faceset_offset < 0) {
+ return;
+ }
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ /* This can run on geometry without a face set assigned, so its ID sign can't be changed to
+ * modify the visibility. Force that geometry to the ID 1 to enable changing the visibility
+ * here. */
+
+ if (fset == SCULPT_FACE_SET_NONE) {
+ fset = 1;
+ }
+
+ if (visible) {
+ fset = abs(fset);
+ }
+ else {
+ fset = -abs(fset);
+ }
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ }
break;
+ }
}
}
-bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
+ MeshElemMap *vert_map = &ss->pmap[index.i];
+ for (int j = 0; j < ss->pmap[index.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] > 0) {
return true;
}
}
return false;
}
- case PBVH_BMESH:
- return true;
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset);
+ if (fset >= 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
case PBVH_GRIDS:
return true;
}
return true;
}
-bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index)
+bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
+ MeshElemMap *vert_map = &ss->pmap[index.i];
+ for (int j = 0; j < ss->pmap[index.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] < 0) {
return false;
}
}
return true;
}
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset);
+ if (fset < 0) {
+ return false;
+ }
+ }
+
return true;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = index.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index] > 0;
}
@@ -486,22 +807,34 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index)
return true;
}
-void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
+void SCULPT_vertex_face_set_set(SculptSession *ss, SculptVertRef index, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
+ MeshElemMap *vert_map = &ss->pmap[index.i];
+ for (int j = 0; j < ss->pmap[index.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] > 0) {
ss->face_sets[vert_map->indices[j]] = abs(face_set);
}
}
} break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset);
+ if (fset >= 0) {
+ BM_ELEM_CD_SET_INT(l->f, ss->cd_faceset_offset, abs(face_set));
+ }
+ }
+
break;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = index.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
if (ss->face_sets[face_index] > 0) {
ss->face_sets[face_index] = abs(face_set);
@@ -511,24 +844,39 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
}
}
-int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
+int SCULPT_vertex_face_set_get(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
+ MeshElemMap *vert_map = &ss->pmap[index.i];
int face_set = 0;
- for (int i = 0; i < ss->pmap[index].count; i++) {
+ for (int i = 0; i < ss->pmap[index.i].count; i++) {
if (ss->face_sets[vert_map->indices[i]] > face_set) {
face_set = abs(ss->face_sets[vert_map->indices[i]]);
}
}
return face_set;
}
- case PBVH_BMESH:
- return 0;
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+ int ret = -1;
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset);
+ fset = abs(fset);
+
+ if (fset > ret) {
+ ret = fset;
+ }
+ }
+
+ return ret;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = index.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index];
}
@@ -536,23 +884,40 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
return 0;
}
-bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
+bool SCULPT_vertex_has_face_set(SculptSession *ss, SculptVertRef index, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int i = 0; i < ss->pmap[index].count; i++) {
+ MeshElemMap *vert_map = &ss->pmap[index.i];
+ for (int i = 0; i < ss->pmap[index.i].count; i++) {
if (ss->face_sets[vert_map->indices[i]] == face_set) {
return true;
}
}
return false;
}
- case PBVH_BMESH:
- return true;
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ if (ss->cd_faceset_offset == -1) {
+ return false;
+ }
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ BMFace *f = l->f;
+
+ if (abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)) == abs(face_set)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = index.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index] == face_set;
}
@@ -560,6 +925,54 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
return true;
}
+/*
+calcs visibility state based on face sets.
+todo: also calc a face set boundary flag.
+*/
+void sculpt_vertex_faceset_update_bmesh(SculptSession *ss, SculptVertRef vert)
+{
+ if (!ss->bm) {
+ return;
+ }
+
+ BMVert *v = (BMVert *)vert.i;
+ BMEdge *e = v->e;
+ bool ok = false;
+ const int cd_faceset_offset = ss->cd_faceset_offset;
+
+ if (!e) {
+ return;
+ }
+
+ do {
+ BMLoop *l = e->l;
+ if (l) {
+ do {
+ if (BM_ELEM_CD_GET_INT(l->f, cd_faceset_offset) > 0) {
+ ok = true;
+ break;
+ }
+
+ l = l->radial_next;
+ } while (l != e->l);
+
+ if (ok) {
+ break;
+ }
+ }
+ e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next;
+ } while (e != v->e);
+
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, ss->cd_dyn_vert);
+
+ if (ok) {
+ mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN;
+ }
+ else {
+ mv->flag |= DYNVERT_VERT_FSET_HIDDEN;
+ }
+}
+
void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
{
SculptSession *ss = ob->sculpt;
@@ -574,16 +987,58 @@ void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
BKE_sculpt_sync_face_sets_visibility_to_grids(mesh, ss->subdiv_ccg);
break;
}
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+ BMVert *v;
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ if (fset < 0) {
+ BM_elem_flag_enable(f, BM_ELEM_HIDDEN);
+ }
+ else {
+ BM_elem_flag_disable(f, BM_ELEM_HIDDEN);
+ }
+ }
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, ss->cd_dyn_vert);
+
+ BMIter iter2;
+ BMLoop *l;
+
+ int visible = false;
+
+ BM_ITER_ELEM (l, &iter2, v, BM_LOOPS_OF_VERT) {
+ if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
+ visible = true;
+ break;
+ }
+ }
+
+ if (!visible) {
+ mv->flag |= DYNVERT_VERT_FSET_HIDDEN;
+ BM_elem_flag_enable(v, BM_ELEM_HIDDEN);
+ }
+ else {
+ mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN;
+ BM_elem_flag_disable(v, BM_ELEM_HIDDEN);
+ }
+ }
break;
+ }
}
}
static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSession *ss,
- int index)
+ SculptVertRef vertex)
{
+ int index = (int)vertex.i;
MeshElemMap *vert_map = &ss->pmap[index];
- const bool visible = SCULPT_vertex_visible_get(ss, index);
+ const bool visible = SCULPT_vertex_visible_get(ss, vertex);
+
for (int i = 0; i < ss->pmap[index].count; i++) {
if (visible) {
ss->face_sets[vert_map->indices[i]] = abs(ss->face_sets[vert_map->indices[i]]);
@@ -597,28 +1052,68 @@ static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSe
void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
{
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
- for (int i = 0; i < ss->totfaces; i++) {
- MPoly *poly = &ss->mpoly[i];
- bool poly_visible = true;
- for (int l = 0; l < poly->totloop; l++) {
- MLoop *loop = &ss->mloop[poly->loopstart + l];
- if (!SCULPT_vertex_visible_get(ss, (int)loop->v)) {
- poly_visible = false;
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES: {
+ for (int i = 0; i < ss->totfaces; i++) {
+ MPoly *poly = &ss->mpoly[i];
+ bool poly_visible = true;
+ for (int l = 0; l < poly->totloop; l++) {
+ MLoop *loop = &ss->mloop[poly->loopstart + l];
+ if (!SCULPT_vertex_visible_get(ss, BKE_pbvh_make_vref(loop->v))) {
+ poly_visible = false;
+ }
+ }
+ if (poly_visible) {
+ ss->face_sets[i] = abs(ss->face_sets[i]);
+ }
+ else {
+ ss->face_sets[i] = -abs(ss->face_sets[i]);
}
}
- if (poly_visible) {
- ss->face_sets[i] = abs(ss->face_sets[i]);
+ break;
+ }
+ case PBVH_GRIDS:
+ break;
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+
+ if (!ss->bm) {
+ return;
}
- else {
- ss->face_sets[i] = -abs(ss->face_sets[i]);
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ BMLoop *l = f->l_first;
+ bool visible = true;
+
+ do {
+ if (BM_elem_flag_test(l->v, BM_ELEM_HIDDEN)) {
+ visible = false;
+ break;
+ }
+ l = l->next;
+ } while (l != f->l_first);
+
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+ if (visible) {
+ fset = abs(fset);
+ }
+ else {
+ fset = -abs(fset);
+ }
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
}
+
+ break;
}
}
}
-static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index)
+static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, SculptVertRef vertex)
{
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
MeshElemMap *vert_map = &ss->pmap[index];
int face_set = -1;
for (int i = 0; i < ss->pmap[index].count; i++) {
@@ -666,18 +1161,41 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss
return true;
}
-bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
+bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
return sculpt_check_unique_face_set_in_base_mesh(ss, index);
}
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+ int face_set = 0;
+ bool first = true;
+
+ if (ss->cd_faceset_offset == -1) {
+ return false;
+ }
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ BMFace *f = l->f;
+ int face_set2 = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ if (!first && abs(face_set2) != abs(face_set)) {
+ return false;
+ }
+
+ first = false;
+ face_set = face_set2;
+ }
+
return true;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
const SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
.y = vertex_index / key->grid_size};
@@ -686,7 +1204,7 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2);
switch (adjacency) {
case SUBDIV_CCG_ADJACENT_VERTEX:
- return sculpt_check_unique_face_set_in_base_mesh(ss, v1);
+ return sculpt_check_unique_face_set_in_base_mesh(ss, BKE_pbvh_make_vref(v1));
case SUBDIV_CCG_ADJACENT_EDGE:
return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2);
case SUBDIV_CCG_ADJACENT_NONE:
@@ -711,8 +1229,24 @@ int SCULPT_face_set_next_available_get(SculptSession *ss)
next_face_set++;
return next_face_set;
}
- case PBVH_BMESH:
- return 0;
+ case PBVH_BMESH: {
+ int next_face_set = 0;
+ BMIter iter;
+ BMFace *f;
+ if (!ss->cd_faceset_offset) {
+ return 0;
+ }
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset));
+ if (fset > next_face_set) {
+ next_face_set = fset;
+ }
+ }
+
+ next_face_set++;
+ return next_face_set;
+ }
}
return 0;
}
@@ -721,10 +1255,12 @@ int SCULPT_face_set_next_available_get(SculptSession *ss)
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
-static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index)
+static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter,
+ SculptVertRef neighbor,
+ int neighbor_index)
{
for (int i = 0; i < iter->size; i++) {
- if (iter->neighbors[i] == neighbor_index) {
+ if (iter->neighbors[i].i == neighbor.i) {
return;
}
}
@@ -733,51 +1269,134 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neigh
iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
if (iter->neighbors == iter->neighbors_fixed) {
- iter->neighbors = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
- memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(int) * iter->size);
+ iter->neighbors = MEM_mallocN(iter->capacity * sizeof(SculptVertRef), "neighbor array");
+ iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
+
+ memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(SculptVertRef) * iter->size);
+ memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size);
}
else {
iter->neighbors = MEM_reallocN_id(
- iter->neighbors, iter->capacity * sizeof(int), "neighbor array");
+ iter->neighbors, iter->capacity * sizeof(SculptVertRef), "neighbor array");
+ iter->neighbor_indices = MEM_reallocN_id(
+ iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array");
}
}
- iter->neighbors[iter->size] = neighbor_index;
+ iter->neighbors[iter->size] = neighbor;
+ iter->neighbor_indices[iter->size] = neighbor_index;
+ iter->size++;
+}
+
+static void sculpt_vertex_neighbor_add_nocheck(SculptVertexNeighborIter *iter,
+ SculptVertRef neighbor,
+ int neighbor_index)
+{
+ if (iter->size >= iter->capacity) {
+ iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
+
+ if (iter->neighbors == iter->neighbors_fixed) {
+ iter->neighbors = MEM_mallocN(iter->capacity * sizeof(SculptVertRef), "neighbor array");
+ iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
+
+ memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(SculptVertRef) * iter->size);
+ memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size);
+ }
+ else {
+ iter->neighbors = MEM_reallocN_id(
+ iter->neighbors, iter->capacity * sizeof(SculptVertRef), "neighbor array");
+ iter->neighbor_indices = MEM_reallocN_id(
+ iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array");
+ }
+ }
+
+ iter->neighbors[iter->size] = neighbor;
+ iter->neighbor_indices[iter->size] = neighbor_index;
iter->size++;
}
static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss,
- int index,
+ SculptVertRef index,
SculptVertexNeighborIter *iter)
{
- BMVert *v = BM_vert_at_index(ss->bm, index);
+ BMVert *v = (BMVert *)index.i;
BMIter liter;
BMLoop *l;
+
+ iter->is_duplicate = false;
iter->size = 0;
iter->num_duplicates = 0;
iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
iter->neighbors = iter->neighbors_fixed;
+ iter->neighbor_indices = iter->neighbor_indices_fixed;
+ iter->i = 0;
- BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
- const BMVert *adj_v[2] = {l->prev->v, l->next->v};
- for (int i = 0; i < ARRAY_SIZE(adj_v); i++) {
- const BMVert *v_other = adj_v[i];
- if (BM_elem_index_get(v_other) != (int)index) {
- sculpt_vertex_neighbor_add(iter, BM_elem_index_get(v_other));
- }
- }
+ // cache profiling revealed a hotspot here, don't use BM_ITER
+ BMEdge *e = v->e;
+
+ if (!v->e) {
+ return;
}
+
+ const bool have_facesets = ss->cd_faceset_offset >= 0;
+ const int cd_faceset_offset = ss->cd_faceset_offset;
+
+ do {
+ BMVert *v2;
+ BMEdge *e2;
+
+ if (v == e->v1) {
+ v2 = e->v2;
+ e2 = e->v1_disk_link.next;
+ }
+ else {
+ v2 = e->v1;
+ e2 = e->v2_disk_link.next;
+ }
+
+ // TODO: cache this in MDynTopoVert to avoid excessive DRAM fetches
+ BMLoop *l = e->l;
+ bool ok = false;
+
+#if 0
+ if (l && have_facesets) {
+ do {
+ if (BM_ELEM_CD_GET_INT(l->f, cd_faceset_offset) > 0) {
+ ok = true;
+ break;
+ }
+
+ l = l->radial_next;
+ } while (l != e->l);
+ }
+ else {
+ ok = true;
+ }
+#else
+ ok = true;
+#endif
+
+ e = e2;
+ if (ok) {
+ sculpt_vertex_neighbor_add_nocheck(
+ iter, BKE_pbvh_make_vref((intptr_t)v2), BM_elem_index_get(v2));
+ }
+ } while (e != v->e);
}
static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
- int index,
+ SculptVertRef vertex,
SculptVertexNeighborIter *iter)
{
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
MeshElemMap *vert_map = &ss->pmap[index];
iter->size = 0;
iter->num_duplicates = 0;
iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
iter->neighbors = iter->neighbors_fixed;
+ iter->neighbor_indices = iter->neighbor_indices_fixed;
+ iter->is_duplicate = false;
for (int i = 0; i < ss->pmap[index].count; i++) {
if (ss->face_sets[vert_map->indices[i]] < 0) {
@@ -789,7 +1408,7 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
if (f_adj_v[j] != index) {
- sculpt_vertex_neighbor_add(iter, f_adj_v[j]);
+ sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]);
}
}
}
@@ -797,17 +1416,21 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
if (ss->fake_neighbors.use_fake_neighbors) {
BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
- if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
- sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) {
+ sculpt_vertex_neighbor_add(iter,
+ ss->fake_neighbors.fake_neighbor_index[index],
+ ss->fake_neighbors.fake_neighbor_index[index].i);
}
}
}
static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
- const int index,
+ const SculptVertRef vertex,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
{
+ int index = (int)vertex.i;
+
/* TODO: optimize this. We could fill #SculptVertexNeighborIter directly,
* maybe provide coordinate and mask pointers directly rather than converting
* back and forth between #CCGElem and global index. */
@@ -822,21 +1445,27 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
SubdivCCGNeighbors neighbors;
BKE_subdiv_ccg_neighbor_coords_get(ss->subdiv_ccg, &coord, include_duplicates, &neighbors);
+ iter->is_duplicate = include_duplicates;
+
iter->size = 0;
iter->num_duplicates = neighbors.num_duplicates;
iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
iter->neighbors = iter->neighbors_fixed;
+ iter->neighbor_indices = iter->neighbor_indices_fixed;
for (int i = 0; i < neighbors.size; i++) {
- sculpt_vertex_neighbor_add(iter,
- neighbors.coords[i].grid_index * key->grid_area +
- neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x);
+ int idx = neighbors.coords[i].grid_index * key->grid_area +
+ neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x;
+
+ sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(idx), idx);
}
if (ss->fake_neighbors.use_fake_neighbors) {
BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
- if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
- sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) {
+ sculpt_vertex_neighbor_add(iter,
+ ss->fake_neighbors.fake_neighbor_index[index],
+ ss->fake_neighbors.fake_neighbor_index[index].i);
}
}
@@ -846,44 +1475,47 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
}
void SCULPT_vertex_neighbors_get(SculptSession *ss,
- const int index,
+ const SculptVertRef vertex,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- sculpt_vertex_neighbors_get_faces(ss, index, iter);
+ sculpt_vertex_neighbors_get_faces(ss, vertex, iter);
return;
case PBVH_BMESH:
- sculpt_vertex_neighbors_get_bmesh(ss, index, iter);
+ sculpt_vertex_neighbors_get_bmesh(ss, vertex, iter);
return;
case PBVH_GRIDS:
- sculpt_vertex_neighbors_get_grids(ss, index, include_duplicates, iter);
+ sculpt_vertex_neighbors_get_grids(ss, vertex, include_duplicates, iter);
return;
}
}
-static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, const int index)
+static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss,
+ const SculptVertRef index)
{
BLI_assert(ss->vertex_info.boundary);
- return BLI_BITMAP_TEST(ss->vertex_info.boundary, index);
+ return BLI_BITMAP_TEST(ss->vertex_info.boundary,
+ BKE_pbvh_vertex_index_to_table(ss->pbvh, index));
}
-bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index)
+bool SCULPT_vertex_is_boundary(const SculptSession *ss, const SculptVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH: {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, ((BMVert *)(vertex.i)));
+ return mv->flag & DYNVERT_BOUNDARY;
+ }
case PBVH_FACES: {
- if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) {
+ if (!SCULPT_vertex_all_face_sets_visible_get(ss, vertex)) {
return true;
}
- return sculpt_check_boundary_vertex_in_base_mesh(ss, index);
- }
- case PBVH_BMESH: {
- BMVert *v = BM_vert_at_index(ss->bm, index);
- return BM_vert_is_boundary(v);
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, vertex);
}
case PBVH_GRIDS: {
+ int index = (int)vertex.i;
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = index / key->grid_area;
const int vertex_index = index - grid_index * key->grid_area;
@@ -895,10 +1527,10 @@ bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index)
ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2);
switch (adjacency) {
case SUBDIV_CCG_ADJACENT_VERTEX:
- return sculpt_check_boundary_vertex_in_base_mesh(ss, v1);
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v1));
case SUBDIV_CCG_ADJACENT_EDGE:
- return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) &&
- sculpt_check_boundary_vertex_in_base_mesh(ss, v2);
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v1)) &&
+ sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v2));
case SUBDIV_CCG_ADJACENT_NONE:
return false;
}
@@ -962,6 +1594,7 @@ bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3],
typedef struct NearestVertexTLSData {
int nearest_vertex_index;
+ SculptVertRef nearest_vertex;
float nearest_vertex_distance_squared;
} NearestVertexTLSData;
@@ -979,6 +1612,7 @@ static void do_nearest_vertex_get_task_cb(void *__restrict userdata,
if (distance_squared < nvtd->nearest_vertex_distance_squared &&
distance_squared < data->max_distance_squared) {
nvtd->nearest_vertex_index = vd.index;
+ nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared;
}
}
@@ -993,15 +1627,17 @@ static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata),
NearestVertexTLSData *nvtd = chunk;
if (join->nearest_vertex_index == -1) {
join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
-int SCULPT_nearest_vertex_get(
+SculptVertRef SCULPT_nearest_vertex_get(
Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original)
{
SculptSession *ss = ob->sculpt;
@@ -1016,7 +1652,7 @@ int SCULPT_nearest_vertex_get(
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
if (totnode == 0) {
- return -1;
+ return BKE_pbvh_make_vref(-1);
}
SculptThreadedTaskData task_data = {
@@ -1029,6 +1665,7 @@ int SCULPT_nearest_vertex_get(
copy_v3_v3(task_data.nearest_vertex_search_co, co);
NearestVertexTLSData nvtd;
nvtd.nearest_vertex_index = -1;
+ nvtd.nearest_vertex.i = -1;
nvtd.nearest_vertex_distance_squared = FLT_MAX;
TaskParallelSettings settings;
@@ -1040,7 +1677,7 @@ int SCULPT_nearest_vertex_get(
MEM_SAFE_FREE(nodes);
- return nvtd.nearest_vertex_index;
+ return nvtd.nearest_vertex;
}
bool SCULPT_is_symmetry_iteration_valid(char i, char symm)
@@ -1091,23 +1728,29 @@ void SCULPT_floodfill_init(SculptSession *ss, SculptFloodFill *flood)
int vertex_count = SCULPT_vertex_count_get(ss);
SCULPT_vertex_random_access_ensure(ss);
- flood->queue = BLI_gsqueue_new(sizeof(int));
+ flood->queue = BLI_gsqueue_new(sizeof(SculptVertRef));
flood->visited_vertices = BLI_BITMAP_NEW(vertex_count, "visited vertices");
}
-void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index)
+void SCULPT_floodfill_add_initial(SculptFloodFill *flood, SculptVertRef vertex)
{
- BLI_gsqueue_push(flood->queue, &index);
+ BLI_gsqueue_push(flood->queue, &vertex);
}
-void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index)
+void SCULPT_floodfill_add_and_skip_initial(SculptSession *ss,
+ SculptFloodFill *flood,
+ SculptVertRef vertex)
{
- BLI_gsqueue_push(flood->queue, &index);
- BLI_BITMAP_ENABLE(flood->visited_vertices, index);
+ BLI_gsqueue_push(flood->queue, &vertex);
+ BLI_BITMAP_ENABLE(flood->visited_vertices, BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex));
}
-void SCULPT_floodfill_add_initial_with_symmetry(
- Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius)
+void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd,
+ Object *ob,
+ SculptSession *ss,
+ SculptFloodFill *flood,
+ SculptVertRef vertex,
+ float radius)
{
/* Add active vertex and symmetric vertices to the queue. */
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
@@ -1115,18 +1758,18 @@ void SCULPT_floodfill_add_initial_with_symmetry(
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
- int v = -1;
+ SculptVertRef v = {-1};
if (i == 0) {
- v = index;
+ v = vertex;
}
else if (radius > 0.0f) {
float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius;
float location[3];
- flip_v3_v3(location, SCULPT_vertex_co_get(ss, index), i);
+ flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i);
v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false);
}
- if (v != -1) {
+ if (v.i != -1) {
SCULPT_floodfill_add_initial(flood, v);
}
}
@@ -1141,7 +1784,9 @@ void SCULPT_floodfill_add_active(
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
- int v = -1;
+
+ SculptVertRef v = {-1};
+
if (i == 0) {
v = SCULPT_active_vertex_get(ss);
}
@@ -1151,26 +1796,31 @@ void SCULPT_floodfill_add_active(
v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false);
}
- if (v != -1) {
+ if (v.i != -1) {
SCULPT_floodfill_add_initial(flood, v);
}
}
}
-void SCULPT_floodfill_execute(
- SculptSession *ss,
- SculptFloodFill *flood,
- bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata),
- void *userdata)
+void SCULPT_floodfill_execute(SculptSession *ss,
+ SculptFloodFill *flood,
+ bool (*func)(SculptSession *ss,
+ SculptVertRef from_v,
+ SculptVertRef to_v,
+ bool is_duplicate,
+ void *userdata),
+ void *userdata)
{
while (!BLI_gsqueue_is_empty(flood->queue)) {
- int from_v;
+ SculptVertRef from_v;
BLI_gsqueue_pop(flood->queue, &from_v);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
- const int to_v = ni.index;
+ const SculptVertRef to_v = ni.vertex;
+
+ const int to_index = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
- if (BLI_BITMAP_TEST(flood->visited_vertices, to_v)) {
+ if (BLI_BITMAP_TEST(flood->visited_vertices, to_index)) {
continue;
}
@@ -1178,7 +1828,7 @@ void SCULPT_floodfill_execute(
continue;
}
- BLI_BITMAP_ENABLE(flood->visited_vertices, to_v);
+ BLI_BITMAP_ENABLE(flood->visited_vertices, to_index);
if (func(ss, from_v, to_v, ni.is_duplicate, userdata)) {
BLI_gsqueue_push(flood->queue, &to_v);
@@ -1213,6 +1863,8 @@ static bool sculpt_tool_needs_original(const char sculpt_tool)
SCULPT_TOOL_DRAW_SHARP,
SCULPT_TOOL_ELASTIC_DEFORM,
SCULPT_TOOL_SMOOTH,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_VCOL_BOUNDARY,
SCULPT_TOOL_BOUNDARY,
SCULPT_TOOL_POSE);
}
@@ -1285,10 +1937,14 @@ void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, Scul
memset(data, 0, sizeof(*data));
data->unode = unode;
+ data->pbvh = ss->pbvh;
+ data->ss = ss;
+
if (bm) {
data->bm_log = ss->bm_log;
}
else {
+ data->datatype = data->unode->type;
data->coords = data->unode->co;
data->normals = data->unode->no;
data->vmasks = data->unode->mask;
@@ -1300,11 +1956,21 @@ void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, Scul
* Initialize a #SculptOrigVertData for accessing original vertex data;
* handles #BMesh, #Mesh, and multi-resolution.
*/
-void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node)
+void SCULPT_orig_vert_data_init(SculptOrigVertData *data,
+ Object *ob,
+ PBVHNode *node,
+ SculptUndoType type)
{
- SculptUndoNode *unode;
- unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
+ SculptUndoNode *unode = NULL;
+ data->ss = ob->sculpt;
+
+ /*do not allocate an undo node for bmesh pbvh*/
+ if (!ob->sculpt->bm) {
+ unode = SCULPT_undo_push_node(ob, node, type);
+ }
+
SCULPT_orig_vert_data_unode_init(data, ob, unode);
+ data->datatype = type;
}
/**
@@ -1312,21 +1978,57 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *
*/
void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter)
{
- if (orig_data->unode->type == SCULPT_UNDO_COORDS) {
+ // check if we need to update original data for current stroke
+ if (orig_data->bm_log) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert);
+ if (mv->stroke_id != orig_data->ss->stroke_id) {
+ mv->stroke_id = orig_data->ss->stroke_id;
+
+ copy_v3_v3(mv->origco, iter->bm_vert->co);
+ copy_v3_v3(mv->origno, iter->bm_vert->no);
+
+ const int cd_vcol = iter->cd_vcol_offset;
+ const int cd_mask = iter->cd_vert_mask_offset;
+
+ if (cd_vcol >= 0) {
+ MPropCol *col = BM_ELEM_CD_GET_VOID_P(iter->bm_vert, cd_vcol);
+ copy_v4_v4(mv->origcolor, col->color);
+ }
+
+ if (cd_mask >= 0) {
+ mv->origmask = BM_ELEM_CD_GET_FLOAT(iter->bm_vert, cd_mask);
+ }
+ }
+ }
+
+ if (orig_data->datatype == SCULPT_UNDO_COORDS) {
if (orig_data->bm_log) {
- BM_log_original_vert_data(orig_data->bm_log, iter->bm_vert, &orig_data->co, &orig_data->no);
+ orig_data->co = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origco;
+
+ float *no = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origno;
+ normal_float_to_short_v3(orig_data->_no, no);
+ orig_data->no = orig_data->_no;
+
+ orig_data->col = iter->cd_vcol_offset >= 0 ?
+ BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origcolor :
+ NULL;
}
else {
orig_data->co = orig_data->coords[iter->i];
orig_data->no = orig_data->normals[iter->i];
}
}
- else if (orig_data->unode->type == SCULPT_UNDO_COLOR) {
- orig_data->col = orig_data->colors[iter->i];
+ else if (orig_data->datatype == SCULPT_UNDO_COLOR) {
+ if (orig_data->bm_log) {
+ orig_data->col = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origcolor;
+ }
+ else {
+ orig_data->col = orig_data->colors[iter->i];
+ }
}
- else if (orig_data->unode->type == SCULPT_UNDO_MASK) {
+ else if (orig_data->datatype == SCULPT_UNDO_MASK) {
if (orig_data->bm_log) {
- orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert);
+ orig_data->mask = BKE_PBVH_DYNVERT(iter->cd_dyn_vert, iter->bm_vert)->origmask;
}
else {
orig_data->mask = orig_data->vmasks[iter->i];
@@ -1450,15 +2152,17 @@ static void sculpt_project_v3(const SculptProjectVector *spvc, const float vec[3
* Same goes for alt-key smoothing. */
bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush)
{
- return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) &&
+ return (
+ (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) &&
- (!ss->cache || (!ss->cache->alt_smooth)) &&
+ (!ss->cache || (!ss->cache->alt_smooth)) &&
- /* Requires mesh restore, which doesn't work with
- * dynamic-topology. */
- !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) &&
-
- SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool));
+ /* Requires mesh restore, which doesn't work with
+ * dynamic-topology. */
+ !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) &&
+ (brush->cached_dyntopo.flag & (DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP)) &&
+ !(brush->cached_dyntopo.flag & DYNTOPO_DISABLED) &&
+ SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool));
}
/*** paint mesh ***/
@@ -1478,7 +2182,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type);
}
else {
- unode = SCULPT_undo_get_node(data->nodes[n]);
+ unode = SCULPT_undo_get_node(data->nodes[n], type);
}
if (!unode) {
@@ -2001,13 +2705,18 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
int(*orco_tris)[3];
int orco_tris_num;
- BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords);
+ PBVHTriBuf *tribuf = BKE_pbvh_bmesh_get_tris(ss->pbvh, data->nodes[n]);
+
+ for (int i = 0; i < tribuf->tottri; i++) {
+ PBVHTri *tri = tribuf->tris + i;
+ SculptVertRef v1 = tribuf->verts[tri->v[0]];
+ SculptVertRef v2 = tribuf->verts[tri->v[1]];
+ SculptVertRef v3 = tribuf->verts[tri->v[2]];
- for (int i = 0; i < orco_tris_num; i++) {
- const float *co_tri[3] = {
- orco_coords[orco_tris[i][0]],
- orco_coords[orco_tris[i][1]],
- orco_coords[orco_tris[i][2]],
+ const float(*co_tri[3]) = {
+ SCULPT_vertex_origco_get(ss, v1),
+ SCULPT_vertex_origco_get(ss, v2),
+ SCULPT_vertex_origco_get(ss, v3),
};
float co[3];
@@ -2059,11 +2768,11 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata,
if (use_original) {
if (unode->bm_entry) {
- const float *temp_co;
- const short *temp_no_s;
- BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &temp_co, &temp_no_s);
- copy_v3_v3(co, temp_co);
- copy_v3_v3_short(no_s, temp_no_s);
+ BMVert *v = vd.bm_vert;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(vd.cd_dyn_vert, v);
+
+ normal_float_to_short_v3(no_s, mv->origno);
+ copy_v3_v3(co, mv->origco);
}
else {
copy_v3_v3(co, unode->co[vd.i]);
@@ -2427,7 +3136,10 @@ static float brush_strength(const Sculpt *sd,
case SCULPT_TOOL_SMOOTH:
return flip * alpha * pressure * feather;
-
+ case SCULPT_TOOL_VCOL_BOUNDARY:
+ return flip * alpha * pressure * feather;
+ case SCULPT_TOOL_UV_SMOOTH:
+ return flip * alpha * pressure * feather;
case SCULPT_TOOL_PINCH:
if (flip > 0.0f) {
return alpha * flip * pressure * overlap * feather;
@@ -2470,7 +3182,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
const short vno[3],
const float fno[3],
const float mask,
- const int vertex_index,
+ const SculptVertRef vertex_index,
const int thread_id)
{
StrokeCache *cache = ss->cache;
@@ -2874,10 +3586,10 @@ typedef struct {
float depth;
bool original;
- int active_vertex_index;
+ SculptVertRef active_vertex_index;
float *face_normal;
- int active_face_grid_index;
+ SculptFaceRef active_face_grid_index;
struct IsectRayPrecalc isect_precalc;
} SculptRaycastData;
@@ -2899,6 +3611,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
+ PBVHNode *node = data->nodes[n];
float direction[3];
copy_v3_v3(direction, ss->cache->grab_delta_symmetry);
@@ -2921,20 +3634,37 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
+ const bool use_curvature = ss->cache->brush->flag2 & BRUSH_CURVATURE_RAKE;
+ // const bool update_curvature = node->flag & PBVH_UpdateCurvatureDir;
+ const bool update_curvature = BKE_pbvh_curvature_update_get(node);
+
+ if (use_curvature) {
+ SCULPT_curvature_begin(ss, node, false);
+ }
+
PBVHVertexIter vd;
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
+
+ float direction2[3];
const float fade =
bstrength *
SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) *
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) *
ss->cache->pressure;
float avg[3], val[3];
- SCULPT_bmesh_four_neighbor_average(avg, direction, vd.bm_vert);
+ if (use_curvature) {
+ SCULPT_curvature_dir_get(ss, vd.vertex, direction2, false);
+ }
+ else {
+ copy_v3_v3(direction2, direction);
+ }
+
+ SCULPT_bmesh_four_neighbor_average(avg, direction2, vd.bm_vert, data->rake_projection);
sub_v3_v3v3(val, avg, vd.co);
@@ -2955,8 +3685,20 @@ static void bmesh_topology_rake(
Brush *brush = BKE_paint_brush(&sd->paint);
const float strength = clamp_f(bstrength, 0.0f, 1.0f);
- /* Interactions increase both strength and quality. */
- const int iterations = 3;
+ Brush local_brush;
+
+ if (brush->flag2 & BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF) {
+ local_brush = *brush;
+ brush = &local_brush;
+
+ brush->curve_preset = BRUSH_CURVE_SMOOTH;
+
+ /*note that brush hardness is calculated from ss->cache->paint_brush,
+ we can't override it by changing the brush here.
+ this seems desirably though?*/
+ }
+ /* Iterations increase both strength and quality. */
+ const int iterations = 3 + ((int)bstrength) * 2;
int iteration;
const int count = iterations * strength + 1;
@@ -2964,13 +3706,12 @@ static void bmesh_topology_rake(
for (iteration = 0; iteration <= count; iteration++) {
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .strength = factor,
- };
+ SculptThreadedTaskData data = {.sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .strength = factor,
+ .rake_projection = brush->topology_rake_projection};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
@@ -3000,7 +3741,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
}
const float fade = SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id);
if (bstrength > 0.0f) {
(*vd.mask) += fade * bstrength * (1.0f - *vd.mask);
@@ -3044,7 +3785,7 @@ static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
do_mask_brush_draw(sd, ob, nodes, totnode);
break;
case BRUSH_MASK_SMOOTH:
- SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true);
+ SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true, 0.0f);
break;
}
}
@@ -3081,12 +3822,12 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float limit_co[3];
float disp[3];
- SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co);
+ SCULPT_vertex_limit_surface_get(ss, vd.vertex, limit_co);
sub_v3_v3v3(disp, limit_co, vd.co);
mul_v3_v3fl(proxy[vd.i], disp, fade);
@@ -3146,7 +3887,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
@@ -3173,11 +3914,11 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
float weights_accum = 1.0f;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
float neighbor_limit_co[3];
- SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co);
+ SCULPT_vertex_limit_surface_get(ss, ni.vertex, neighbor_limit_co);
sub_v3_v3v3(vertex_disp,
ss->cache->limit_surface_co[ni.index],
ss->cache->limit_surface_co[vd.index]);
@@ -3217,7 +3958,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex(
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
sub_v3_v3v3(ss->cache->prev_displacement[vd.index],
- SCULPT_vertex_co_get(ss, vd.index),
+ SCULPT_vertex_co_get(ss, vd.vertex),
ss->cache->limit_surface_co[vd.index]);
}
BKE_pbvh_vertex_iter_end;
@@ -3229,16 +3970,20 @@ static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes
SculptSession *ss = ob->sculpt;
BKE_curvemapping_init(brush->curve);
+ SCULPT_vertex_random_access_ensure(ss);
const int totvert = SCULPT_vertex_count_get(ss);
if (!ss->cache->prev_displacement) {
ss->cache->prev_displacement = MEM_malloc_arrayN(
totvert, sizeof(float[3]), "prev displacement");
ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co");
+
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]);
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_limit_surface_get(ss, vref, ss->cache->limit_surface_co[i]);
sub_v3_v3v3(ss->cache->prev_displacement[i],
- SCULPT_vertex_co_get(ss, i),
+ SCULPT_vertex_co_get(ss, vref),
ss->cache->limit_surface_co[i]);
}
}
@@ -3290,7 +4035,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -3347,7 +4092,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
float(*proxy)[3];
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -3358,6 +4103,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
+
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
@@ -3369,7 +4115,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -3429,7 +4175,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
float(*proxy)[3];
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -3440,6 +4186,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
+
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
@@ -3450,7 +4197,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
float current_disp_norm[3];
@@ -3472,10 +4219,10 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
- sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co);
normalize_v3_v3(vertex_disp_norm, vertex_disp);
if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) {
madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp));
@@ -3505,31 +4252,31 @@ void SCULPT_relax_vertex(SculptSession *ss,
int neighbor_count = 0;
zero_v3(smooth_pos);
zero_v3(boundary_normal);
- const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index);
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) {
neighbor_count++;
if (!filter_boundary_face_sets ||
- (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) {
+ (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.vertex))) {
/* When the vertex to relax is boundary, use only connected boundary vertices for the average
* position. */
if (is_boundary) {
- if (!SCULPT_vertex_is_boundary(ss, ni.index)) {
+ if (!SCULPT_vertex_is_boundary(ss, ni.vertex)) {
continue;
}
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex));
avg_count++;
/* Calculate a normal for the constraint plane using the edges of the boundary. */
float to_neighbor[3];
- sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co);
+ sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.vertex), vd->co);
normalize_v3(to_neighbor);
add_v3_v3(boundary_normal, to_neighbor);
}
else {
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex));
avg_count++;
}
}
@@ -3558,7 +4305,7 @@ void SCULPT_relax_vertex(SculptSession *ss,
normalize_v3_v3(vno, boundary_normal);
}
else {
- SCULPT_vertex_normal_get(ss, vd->index, vno);
+ SCULPT_vertex_normal_get(ss, vd->vertex, vno);
}
if (is_zero_v3(vno)) {
@@ -3586,7 +4333,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]);
@@ -3597,6 +4344,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
+
if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) {
continue;
}
@@ -3607,7 +4355,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co);
@@ -3765,6 +4513,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
+
/* Offset vertex. */
const float fade = SCULPT_brush_strength_factor(ss,
brush,
@@ -3773,7 +4522,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float val1[3];
float val2[3];
@@ -3819,7 +4568,7 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
/* We divide out the squared alpha and multiply by the squared crease
* to give us the pinch strength. */
- crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor;
+ crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor * 2.0;
brush_alpha = BKE_brush_alpha_get(scene, brush);
if (brush_alpha > 0.0f) {
crease_correction /= brush_alpha * brush_alpha;
@@ -3889,7 +4638,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp_center[3];
float x_disp[3];
@@ -3981,7 +4730,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
float(*proxy)[3];
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -4005,7 +4754,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (grab_silhouette) {
@@ -4069,7 +4818,8 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ bool update = false;
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -4125,13 +4875,15 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
mul_v3_fl(final_disp, 1.0f - *vd.mask);
}
- mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
-
- copy_v3_v3(proxy[vd.i], final_disp);
+ mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex));
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ if (dot_v3v3(final_disp, final_disp) > 0.0000001) {
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
}
+
+ copy_v3_v3(proxy[vd.i], final_disp);
}
BKE_pbvh_vertex_iter_end;
}
@@ -4157,6 +4909,9 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
};
TaskParallelSettings settings;
+
+ SCULPT_vertex_random_access_ensure(ss);
+
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings);
}
@@ -4334,7 +5089,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -4419,7 +5174,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
}
@@ -4467,7 +5222,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
if (vd.mask) {
mul_v3_fl(disp, 1.0f - *vd.mask);
}
- mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
+ mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex));
copy_v3_v3(proxy[vd.i], disp);
}
@@ -4530,7 +5285,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
float(*proxy)[3];
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -4552,7 +5307,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -4603,7 +5358,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
float(*proxy)[3];
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -4626,7 +5381,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
sub_v3_v3v3(vec, orig_data.co, ss->cache->location);
@@ -4672,18 +5427,49 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
- const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT;
+ bool use_persistent_base = brush->flag & BRUSH_PERSISTENT;
+ const bool is_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH;
+
+ if (is_bmesh) {
+ use_persistent_base = use_persistent_base && data->cd_pers_co >= 0;
+
+ // check if we need to zero displacement factor
+ // in first run of brush stroke
+ if (!use_persistent_base) {
+ int nidx = BKE_pbvh_get_node_index(ss->pbvh, data->nodes[n]);
+
+ bool reset_disp = !BLI_BITMAP_TEST(ss->cache->layer_disp_map, nidx);
+ if (reset_disp) {
+ PBVHVertexIter vd;
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ BMVert *v = (BMVert *)vd.vertex.i;
+ float *disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_layer_disp);
+
+ *disp_factor = 0.0f;
+
+ BLI_BITMAP_SET(ss->cache->layer_disp_map, nidx, true);
+ }
+ BKE_pbvh_vertex_iter_end;
+ }
+ }
+ }
+ else {
+ use_persistent_base = use_persistent_base && ss->persistent_base;
+ }
PBVHVertexIter vd;
SculptOrigVertData orig_data;
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
+ bool bmeshpbvh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH;
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
@@ -4697,13 +5483,23 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
const int vi = vd.index;
float *disp_factor;
if (use_persistent_base) {
- disp_factor = &ss->persistent_base[vi].disp;
+ if (is_bmesh) {
+ BMVert *v = (BMVert *)vd.vertex.i;
+ disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_pers_disp);
+ }
+ else {
+ disp_factor = &ss->persistent_base[vi].disp;
+ }
+ }
+ else if (is_bmesh) {
+ BMVert *v = (BMVert *)vd.vertex.i;
+ disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_layer_disp);
}
else {
disp_factor = &ss->cache->layer_displacement_factor[vi];
@@ -4733,9 +5529,12 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
float normal[3];
if (use_persistent_base) {
- SCULPT_vertex_persistent_normal_get(ss, vi, normal);
+ SCULPT_vertex_persistent_normal_get(ss, vd.vertex, normal, data->cd_pers_no);
mul_v3_fl(normal, brush->height);
- madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor);
+ madd_v3_v3v3fl(final_co,
+ SCULPT_vertex_persistent_co_get(ss, vd.vertex, data->cd_pers_co),
+ normal,
+ *disp_factor);
}
else {
normal_short_to_float_v3(normal, orig_data.no);
@@ -4761,17 +5560,58 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
+ int cd_pers_co = -1, cd_pers_no = -1, cd_pers_disp = -1, cd_layer_disp = -1;
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ if (ss->cache->layer_displacement_factor) {
+ MEM_SAFE_FREE(ss->cache->layer_displacement_factor);
+ ss->cache->layer_displacement_factor = NULL;
+ }
+
+ // note that we don't allow dyntopo to split the PBVH during
+ // the stroke (see DYNTOPO_HAS_DYNAMIC_SPLIT)
+ // so we don't have to worry about resizing ss->cache->layer_disp_map
+ if (!ss->cache->layer_disp_map) {
+ int totnode2 = BKE_pbvh_get_totnodes(ss->pbvh);
+
+ ss->cache->layer_disp_map = BLI_BITMAP_NEW(totnode2, "ss->cache->layer_disp_map");
+ ss->cache->layer_disp_map_size = totnode2;
+ }
+
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP);
+
+ cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
- if (ss->cache->layer_displacement_factor == NULL) {
+ cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP);
+
+ // should never happen
+ if (cd_pers_co < 0 || cd_pers_no < 0 || cd_pers_disp < 0 || cd_layer_disp < 0) {
+ printf("error!! $d $d $d $d\n", cd_pers_co, cd_pers_no, cd_pers_disp, cd_layer_disp);
+ return;
+ }
+ }
+ else if (ss->cache->layer_displacement_factor == NULL) {
ss->cache->layer_displacement_factor = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss),
"layer displacement factor");
}
+ SCULPT_vertex_random_access_ensure(ss);
+
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
+ .cd_pers_co = cd_pers_co,
+ .cd_pers_no = cd_pers_no,
+ .cd_pers_disp = cd_pers_disp,
+ .cd_layer_disp = cd_layer_disp,
};
TaskParallelSettings settings;
@@ -4809,7 +5649,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float val[3];
@@ -4922,9 +5762,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
-
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
@@ -5077,7 +5916,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -5203,9 +6042,8 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
-
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
@@ -5356,7 +6194,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -5454,7 +6292,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -5570,7 +6408,7 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -5709,7 +6547,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -5791,7 +6629,91 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3])
BKE_keyblock_update_from_vertcos(ob, kb, vertCos);
}
-/* NOTE: we do the topology update before any brush actions to avoid
+static void topology_undopush_cb(PBVHNode *node, void *data)
+{
+ SculptSearchSphereData *sdata = (SculptSearchSphereData *)data;
+
+ SCULPT_ensure_dyntopo_node_undo(
+ sdata->ob,
+ node,
+ sdata->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS,
+ 0);
+
+ BKE_pbvh_node_mark_update(node);
+}
+
+int SCULPT_get_symmetry_pass(const SculptSession *ss)
+{
+ int symidx = ss->cache->mirror_symmetry_pass + (ss->cache->radial_symmetry_pass * 8);
+
+ if (symidx >= SCULPT_MAX_SYMMETRY_PASSES) {
+ symidx = SCULPT_MAX_SYMMETRY_PASSES - 1;
+ }
+
+ return symidx;
+}
+
+typedef struct DynTopoAutomaskState {
+ AutomaskingCache *cache;
+ SculptSession *ss;
+} DynTopoAutomaskState;
+
+static float sculpt_topology_automasking_cb(SculptVertRef vertex, void *vdata)
+{
+ DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata;
+ float mask = SCULPT_automasking_factor_get(state->cache, state->ss, vertex);
+ float mask2 = 1.0f - SCULPT_vertex_mask_get(state->ss, vertex);
+
+ return mask * mask2;
+}
+
+static float sculpt_topology_automasking_mask_cb(SculptVertRef vertex, void *vdata)
+{
+ DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata;
+ return 1.0f - SCULPT_vertex_mask_get(state->ss, vertex);
+}
+
+bool SCULPT_dyntopo_automasking_init(const SculptSession *ss,
+ const Brush *br,
+ const Sculpt *sd,
+ DyntopoMaskCB *r_mask_cb,
+ void **r_mask_cb_data)
+{
+ if (!SCULPT_is_automasking_enabled(sd, ss, br)) {
+ if (CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) {
+ DynTopoAutomaskState *state = MEM_callocN(sizeof(DynTopoAutomaskState),
+ "DynTopoAutomaskState");
+ state->cache = ss->cache->automasking;
+ state->ss = (SculptSession *)ss;
+
+ *r_mask_cb_data = (void *)state;
+ *r_mask_cb = sculpt_topology_automasking_mask_cb;
+
+ return true;
+ }
+ else {
+ *r_mask_cb = NULL;
+ *r_mask_cb_data = NULL;
+ return false;
+ }
+ }
+
+ DynTopoAutomaskState *state = MEM_callocN(sizeof(DynTopoAutomaskState), "DynTopoAutomaskState");
+ state->cache = ss->cache->automasking;
+ state->ss = (SculptSession *)ss;
+
+ *r_mask_cb_data = (void *)state;
+ *r_mask_cb = sculpt_topology_automasking_cb;
+
+ return true;
+}
+
+void SCULPT_dyntopo_automasking_end(void *mask_data)
+{
+ MEM_SAFE_FREE(mask_data);
+}
+
+/* Note: we do the topology update before any brush actions to avoid
* issues with the proxies. The size of the proxy can't change, so
* topology must be updated first. */
static void sculpt_topology_update(Sculpt *sd,
@@ -5801,18 +6723,21 @@ static void sculpt_topology_update(Sculpt *sd,
{
SculptSession *ss = ob->sculpt;
+ // build brush radius scale
+ float radius_scale = 1.0f;
+
+ if (brush->autosmooth_factor > 0.0f) {
+ radius_scale = MAX2(radius_scale, brush->autosmooth_radius_factor);
+ }
+
+ if (brush->topology_rake_factor > 0.0f) {
+ radius_scale = MAX2(radius_scale, brush->topology_rake_factor);
+ }
+
int n, totnode;
/* Build a list of all nodes that are potentially within the brush's area of influence. */
const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
ss->cache->original;
- const float radius_scale = 1.25f;
- PBVHNode **nodes = sculpt_pbvh_gather_generic(
- ob, sd, brush, use_original, radius_scale, &totnode);
-
- /* Only act if some verts are inside the brush area. */
- if (totnode == 0) {
- return;
- }
/* Free index based vertex info as it will become invalid after modifying the topology during the
* stroke. */
@@ -5822,44 +6747,64 @@ static void sculpt_topology_update(Sculpt *sd,
PBVHTopologyUpdateMode mode = 0;
float location[3];
- if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) {
- if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) {
+ if (brush->cached_dyntopo.mode != DYNTOPO_DETAIL_MANUAL) {
+ if (brush->cached_dyntopo.flag & DYNTOPO_SUBDIVIDE) {
mode |= PBVH_Subdivide;
}
- if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) {
+ if (brush->cached_dyntopo.flag & DYNTOPO_COLLAPSE) {
mode |= PBVH_Collapse;
}
}
- for (n = 0; n < totnode; n++) {
- SCULPT_undo_push_node(ob,
- nodes[n],
- brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK :
- SCULPT_UNDO_COORDS);
- BKE_pbvh_node_mark_update(nodes[n]);
-
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- BKE_pbvh_node_mark_topology_update(nodes[n]);
- BKE_pbvh_bmesh_node_save_orig(ss->bm, nodes[n]);
- }
+ if (brush->cached_dyntopo.flag & DYNTOPO_CLEANUP) {
+ mode |= PBVH_Cleanup;
}
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- BKE_pbvh_bmesh_update_topology(ss->pbvh,
- mode,
- ss->cache->location,
- ss->cache->view_normal,
- ss->cache->radius,
- (brush->flag & BRUSH_FRONTFACE) != 0,
- (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE));
- }
-
- MEM_SAFE_FREE(nodes);
+ PBVHNode **nodes = NULL;
+ SculptSearchSphereData sdata = {
+ .ss = ss,
+ .sd = sd,
+ .ob = ob,
+ .radius_squared = square_f(ss->cache->radius * radius_scale * 1.25f),
+ .original = use_original,
+ .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK,
+ .center = NULL,
+ .brush = brush};
+
+ int symidx = SCULPT_get_symmetry_pass(ss);
+
+ bool modified;
+ void *mask_cb_data;
+ DyntopoMaskCB mask_cb;
+
+ SCULPT_dyntopo_automasking_init(ss, brush, sd, &mask_cb, &mask_cb_data);
+
+ /* do nodes under the brush cursor */
+ modified = BKE_pbvh_bmesh_update_topology_nodes(
+ ss->pbvh,
+ SCULPT_search_sphere_cb,
+ topology_undopush_cb,
+ &sdata,
+ mode,
+ ss->cache->location,
+ ss->cache->view_normal,
+ ss->cache->radius * radius_scale,
+ (brush->flag & BRUSH_FRONTFACE) != 0,
+ (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE),
+ symidx,
+ DYNTOPO_HAS_DYNAMIC_SPLIT(brush->sculpt_tool),
+ mask_cb,
+ mask_cb_data);
+
+ SCULPT_dyntopo_automasking_end(mask_cb_data);
/* Update average stroke position. */
copy_v3_v3(location, ss->cache->true_location);
mul_m4_v3(ob->obmat, location);
+
+ ss->totfaces = ss->totpoly = ss->bm->totface;
+ ss->totvert = ss->bm->totvert;
}
static void do_brush_action_task_cb(void *__restrict userdata,
@@ -5883,11 +6828,20 @@ static void do_brush_action_task_cb(void *__restrict userdata,
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
}
else if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
- SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+ if (!ss->bm) {
+ if (data->brush->vcol_boundary_factor > 0.0f) {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ }
+
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+ }
+
BKE_pbvh_node_mark_update_color(data->nodes[n]);
}
else {
- SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ if (!ss->bm) {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ }
BKE_pbvh_node_mark_update(data->nodes[n]);
}
}
@@ -5898,17 +6852,27 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
int totnode;
PBVHNode **nodes;
- /* Check for unsupported features. */
- PBVHType type = BKE_pbvh_type(ss->pbvh);
- if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) {
- return;
+ float radius_scale = 1.0f;
+
+ if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
+ brush->autosmooth_factor > 0 && brush->autosmooth_radius_factor != 1.0f) {
+ radius_scale = MAX2(radius_scale, brush->autosmooth_radius_factor);
}
- if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) {
+ if (sculpt_brush_use_topology_rake(ss, brush)) {
+ radius_scale = MAX2(radius_scale, brush->topology_rake_radius_factor);
+ }
+
+ /* Check for unsupported features. */
+ PBVHType type = BKE_pbvh_type(ss->pbvh);
+ if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) &&
+ !ELEM(type, PBVH_BMESH, PBVH_FACES)) {
return;
}
/* Build a list of all nodes that are potentially within the brush's area of influence */
+ const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
+ ss->cache->original;
if (SCULPT_tool_needs_all_pbvh_nodes(brush)) {
/* These brushes need to update all nodes as they are not constrained by the brush radius */
@@ -5918,13 +6882,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode);
}
else {
- const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
- ss->cache->original;
- float radius_scale = 1.0f;
/* With these options enabled not all required nodes are inside the original brush radius, so
* the brush can produce artifacts in some situations. */
if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) {
- radius_scale = 2.0f;
+ radius_scale = MAX2(radius_scale, 2.0f);
}
nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode);
}
@@ -5936,9 +6897,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS &&
SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) {
- /* Dynamic-topology does not support Face Sets data, so it can't store/restore it from undo. */
- /* TODO(pablodp606): This check should be done in the undo code and not here, but the rest of
- * the sculpt code is not checking for unsupported undo types that may return a null node. */
+ // faceset undo node is created below for pbvh_bmesh
if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS);
}
@@ -5970,16 +6929,48 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
}
float location[3];
- SculptThreadedTaskData task_data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ // dyntopo can't push undo nodes inside a thread
+ if (ss->bm) {
+ if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+ for (int i = 0; i < totnode; i++) {
+ int other = brush->vcol_boundary_factor > 0.0f ? SCULPT_UNDO_COORDS : -1;
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings);
+ SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COLOR, other);
+ BKE_pbvh_node_mark_update_color(nodes[i]);
+ }
+ }
+ else if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
+ for (int i = 0; i < totnode; i++) {
+ if (ss->cache->alt_smooth) {
+ SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, SCULPT_UNDO_COORDS);
+ }
+ else {
+ SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, -1);
+ }
+
+ BKE_pbvh_node_mark_update(nodes[i]);
+ }
+ }
+ else {
+ for (int i = 0; i < totnode; i++) {
+ SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COORDS, -1);
+
+ BKE_pbvh_node_mark_update(nodes[i]);
+ }
+ }
+ }
+ else {
+ SculptThreadedTaskData task_data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings);
+ }
if (sculpt_brush_needs_normal(ss, brush)) {
update_sculpt_normal(sd, ob, nodes, totnode);
@@ -6013,7 +7004,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
break;
case SCULPT_TOOL_SMOOTH:
if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_LAPLACIAN) {
- SCULPT_do_smooth_brush(sd, ob, nodes, totnode);
+ SCULPT_do_smooth_brush(sd, ob, nodes, totnode, brush->autosmooth_projection);
}
else if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE) {
SCULPT_do_surface_smooth_brush(sd, ob, nodes, totnode);
@@ -6116,21 +7107,89 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
case SCULPT_TOOL_SMEAR:
SCULPT_do_smear_brush(sd, ob, nodes, totnode);
break;
+ case SCULPT_TOOL_VCOL_BOUNDARY:
+ SCULPT_smooth_vcol_boundary(sd, ob, nodes, totnode, ss->cache->bstrength);
+ break;
+ case SCULPT_TOOL_UV_SMOOTH:
+ SCULPT_uv_brush(sd, ob, nodes, totnode);
+ break;
}
- if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
- brush->autosmooth_factor > 0) {
+ bool apply_autosmooth = !ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
+ brush->autosmooth_factor > 0;
+
+ if (brush->flag2 & BRUSH_CUSTOM_AUTOSMOOTH_SPACING) {
+ float spacing = (float)brush->autosmooth_spacing / 100.0f;
+
+ apply_autosmooth = apply_autosmooth &&
+ paint_stroke_apply_subspacing(
+ ss->cache->stroke,
+ spacing,
+ PAINT_MODE_SCULPT,
+ &ss->cache->last_smooth_t[SCULPT_get_symmetry_pass(ss)]);
+ }
+
+ if (apply_autosmooth) {
+ float start_radius = ss->cache->radius;
+
+ if (brush->autosmooth_radius_factor != 1.0f) {
+ // note that we expanded the pbvh node search radius earlier in this function
+ // so we just have to adjust the brush radius that's inside ss->cache
+
+ ss->cache->radius *= brush->autosmooth_radius_factor;
+ ss->cache->radius_squared = ss->cache->radius * ss->cache->radius;
+ }
+
if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) {
- SCULPT_smooth(
- sd, ob, nodes, totnode, brush->autosmooth_factor * (1.0f - ss->cache->pressure), false);
+ SCULPT_smooth(sd,
+ ob,
+ nodes,
+ totnode,
+ brush->autosmooth_factor * (1.0f - ss->cache->pressure),
+ false,
+ brush->autosmooth_projection);
}
else {
- SCULPT_smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, false);
+ SCULPT_smooth(
+ sd, ob, nodes, totnode, brush->autosmooth_factor, false, brush->autosmooth_projection);
+ }
+
+ if (brush->autosmooth_radius_factor != 1.0f) {
+ ss->cache->radius = start_radius;
+ ss->cache->radius_squared = ss->cache->radius * ss->cache->radius;
}
}
- if (sculpt_brush_use_topology_rake(ss, brush)) {
+ bool use_topology_rake = sculpt_brush_use_topology_rake(ss, brush);
+
+ if (brush->flag2 & BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING) {
+ float spacing = (float)brush->topology_rake_spacing / 100.0f;
+
+ use_topology_rake = use_topology_rake &&
+ paint_stroke_apply_subspacing(
+ ss->cache->stroke,
+ spacing,
+ PAINT_MODE_SCULPT,
+ &ss->cache->last_rake_t[SCULPT_get_symmetry_pass(ss)]);
+ }
+
+ if (use_topology_rake) {
+ float start_radius = ss->cache->radius;
+
+ if (brush->topology_rake_radius_factor != 1.0f) {
+ // note that we expanded the pbvh node search radius earlier in this function
+ // so we just have to adjust the brush radius that's inside ss->cache
+
+ ss->cache->radius *= brush->topology_rake_radius_factor;
+ ss->cache->radius_squared = ss->cache->radius * ss->cache->radius;
+ }
+
bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor);
+
+ if (brush->topology_rake_radius_factor != 1.0f) {
+ ss->cache->radius = start_radius;
+ ss->cache->radius_squared = ss->cache->radius * ss->cache->radius;
+ }
}
/* The cloth brush adds the gravity as a regular force and it is processed in the solver. */
@@ -6214,7 +7273,9 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata,
if (use_orco) {
if (ss->bm) {
- copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert));
+ float *co = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, vd.bm_vert)->origco;
+ copy_v3_v3(val, co);
+ // copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert));
}
else {
copy_v3_v3(val, orco[vd.i]);
@@ -6304,6 +7365,10 @@ static void SCULPT_flush_stroke_deform_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
+ }
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
sculpt_flush_pbvhvert_deform(ob, &vd);
@@ -6590,6 +7655,24 @@ bool SCULPT_vertex_colors_poll(bContext *C)
if (!U.experimental.use_sculpt_vertex_colors) {
return false;
}
+
+ Object *ob = CTX_data_active_object(C);
+
+ return SCULPT_mode_poll(C);
+}
+
+bool SCULPT_vertex_colors_poll_no_bmesh(bContext *C)
+{
+ if (!U.experimental.use_sculpt_vertex_colors) {
+ return false;
+ }
+
+ Object *ob = CTX_data_active_object(C);
+
+ if (ob && ob->sculpt && ob->sculpt->bm) {
+ return false;
+ }
+
return SCULPT_mode_poll(C);
}
@@ -6677,6 +7760,10 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "Paint Brush";
case SCULPT_TOOL_SMEAR:
return "Smear Brush";
+ case SCULPT_TOOL_VCOL_BOUNDARY:
+ return "Color Boundary";
+ case SCULPT_TOOL_UV_SMOOTH:
+ return "UV Smooth";
}
return "Sculpting";
@@ -6696,6 +7783,10 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->prev_displacement);
MEM_SAFE_FREE(cache->limit_surface_co);
+ MEM_SAFE_FREE(cache->layer_disp_map);
+ cache->layer_disp_map = NULL;
+ cache->layer_disp_map_size = 0;
+
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
}
@@ -6703,6 +7794,7 @@ void SCULPT_cache_free(StrokeCache *cache)
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
if (cache->boundaries[i]) {
SCULPT_boundary_data_free(cache->boundaries[i]);
+ cache->boundaries[i] = NULL;
}
}
@@ -6935,8 +8027,8 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
}
}
-/* In these brushes the grab delta is calculated always from the initial stroke location, which is
- * generally used to create grab deformations. */
+/* In these brushes the grab delta is calculated always from the initial stroke location, which
+ * is generally used to create grab deformations. */
static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
{
if (ELEM(brush->sculpt_tool,
@@ -7149,9 +8241,9 @@ static void sculpt_update_cache_paint_variants(StrokeCache *cache, const Brush *
1.0f - cache->pressure :
cache->pressure;
- /* This makes wet mix more sensible in higher values, which allows to create brushes that have
- * a wider pressure range were they only blend colors without applying too much of the brush
- * color. */
+ /* This makes wet mix more sensible in higher values, which allows to create brushes that
+ * have a wider pressure range were they only blend colors without applying too much of the
+ * brush color. */
cache->paint_brush.wet_mix = 1.0f - pow2f(1.0f - cache->paint_brush.wet_mix);
}
@@ -7289,6 +8381,9 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd,
(brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || (brush->autosmooth_factor > 0) ||
((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) ||
(brush->sculpt_tool == SCULPT_TOOL_POSE) ||
+ (brush->sculpt_tool == SCULPT_TOOL_VCOL_BOUNDARY) ||
+ (brush->sculpt_tool == SCULPT_TOOL_UV_SMOOTH) ||
+ (brush->sculpt_tool == SCULPT_TOOL_PAINT && brush->vcol_boundary_factor > 0.0f) ||
(brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) ||
(brush->sculpt_tool == SCULPT_TOOL_SLIDE_RELAX) ||
(brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) ||
@@ -7325,7 +8420,7 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
}
else {
/* Intersect with coordinates from before we started stroke. */
- SculptUndoNode *unode = SCULPT_undo_get_node(node);
+ SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS);
origco = (unode) ? unode->co : NULL;
use_origco = origco ? true : false;
}
@@ -7341,7 +8436,8 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
&srd->depth,
&srd->active_vertex_index,
&srd->active_face_grid_index,
- srd->face_normal)) {
+ srd->face_normal,
+ srd->ss->stroke_id)) {
srd->hit = true;
*tmin = srd->depth;
}
@@ -7362,7 +8458,7 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t
}
else {
/* Intersect with coordinates from before we started stroke. */
- SculptUndoNode *unode = SCULPT_undo_get_node(node);
+ SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS);
origco = (unode) ? unode->co : NULL;
use_origco = origco ? true : false;
}
@@ -7375,7 +8471,8 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t
srd->ray_start,
srd->ray_normal,
&srd->depth,
- &srd->dist_sq_to_ray)) {
+ &srd->dist_sq_to_ray,
+ srd->ss->stroke_id)) {
srd->hit = true;
*tmin = srd->dist_sq_to_ray;
}
@@ -7418,8 +8515,9 @@ float SCULPT_raycast_init(ViewContext *vc,
return dist;
}
-/* Gets the normal, location and active vertex location of the geometry under the cursor. This also
- * updates the active vertex and cursor related data of the SculptSession using the mouse position
+/* Gets the normal, location and active vertex location of the geometry under the cursor. This
+ * also updates the active vertex and cursor related data of the SculptSession using the mouse
+ * position
*/
bool SCULPT_cursor_geometry_info_update(bContext *C,
SculptCursorGeometryInfo *out,
@@ -7465,7 +8563,8 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
.face_normal = face_normal,
};
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
- BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original);
+ BKE_pbvh_raycast(
+ ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original, srd.ss->stroke_id);
/* Cursor is not over the mesh, return default values. */
if (!srd.hit) {
@@ -7477,6 +8576,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
/* Update the active vertex of the SculptSession. */
ss->active_vertex_index = srd.active_vertex_index;
+
SCULPT_vertex_random_access_ensure(ss);
copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss));
@@ -7486,11 +8586,11 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
ss->active_grid_index = 0;
break;
case PBVH_GRIDS:
- ss->active_face_index = 0;
- ss->active_grid_index = srd.active_face_grid_index;
+ ss->active_face_index.i = 0;
+ ss->active_grid_index = srd.active_face_grid_index.i;
break;
case PBVH_BMESH:
- ss->active_face_index = 0;
+ ss->active_face_index = srd.active_face_grid_index;
ss->active_grid_index = 0;
break;
}
@@ -7577,11 +8677,6 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2])
depth = SCULPT_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original);
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- BM_mesh_elem_table_ensure(ss->bm, BM_VERT);
- BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
- }
-
bool hit = false;
{
SculptRaycastData srd;
@@ -7594,7 +8689,8 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2])
srd.face_normal = face_normal;
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
- BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original);
+ BKE_pbvh_raycast(
+ ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original, srd.ss->stroke_id);
if (srd.hit) {
hit = true;
copy_v3_v3(out, ray_normal);
@@ -7788,6 +8884,13 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags)
}
}
+bool all_nodes_callback(PBVHNode *node, void *data)
+{
+ return true;
+}
+
+void sculpt_undo_print_nodes(void *active);
+
void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags)
{
/* After we are done drawing the stroke, check if we need to do a more
@@ -7839,12 +8942,31 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
}
- if (update_flags & SCULPT_UPDATE_COLOR) {
- BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
- }
-
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BKE_pbvh_bmesh_after_stroke(ss->pbvh);
+#if 0
+ if (update_flags & SCULPT_UPDATE_COLOR) {
+ PBVHNode **nodes;
+ int totnode = 0;
+
+ // BKE_pbvh_get_nodes(ss->pbvh, PBVH_UpdateColor, &nodes, &totnode);
+ BKE_pbvh_search_gather(ss->pbvh, all_nodes_callback, NULL, &nodes, &totnode);
+
+ for (int i = 0; i < totnode; i++) {
+ SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COLOR);
+ }
+
+ if (nodes) {
+ MEM_freeN(nodes);
+ }
+ }
+#endif
+
+ sculpt_undo_print_nodes(NULL);
+ }
+
+ if (update_flags & SCULPT_UPDATE_COLOR) {
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
}
/* Optimization: if there is locked key and active modifiers present in */
@@ -7885,6 +9007,8 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C));
+ // increment stroke_id to flag origdata update
+ ss->stroke_id++;
sculpt_update_cache_invariants(C, sd, ss, op, mouse);
SCULPT_undo_push_begin(ob, sculpt_tool_name(sd));
@@ -7894,35 +9018,57 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
return false;
}
-static void sculpt_stroke_update_step(bContext *C,
- struct PaintStroke *UNUSED(stroke),
- PointerRNA *itemptr)
+static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
{
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
- const Brush *brush = BKE_paint_brush(&sd->paint);
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ ss->cache->stroke_distance = stroke->stroke_distance;
+ ss->cache->stroke_distance_t = stroke->stroke_distance_t;
+ ss->cache->stroke = stroke;
+
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ ss->cache->last_dyntopo_t = 0.0f;
+ memset((void *)ss->cache->last_smooth_t, 0, sizeof(ss->cache->last_smooth_t));
+ memset((void *)ss->cache->last_rake_t, 0, sizeof(ss->cache->last_rake_t));
+ }
+
+ BKE_brush_get_dyntopo(brush, sd, &brush->cached_dyntopo);
SCULPT_stroke_modifiers_check(C, ob, brush);
sculpt_update_cache_variants(C, sd, ob, itemptr);
sculpt_restore_mesh(sd, ob);
- if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) {
- float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat));
- BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail);
+ if (brush->cached_dyntopo.mode == DYNTOPO_DETAIL_CONSTANT ||
+ brush->cached_dyntopo.mode == DYNTOPO_DETAIL_MANUAL) {
+ float object_space_constant_detail = 1.0f / (brush->cached_dyntopo.constant_detail *
+ mat4_to_scale(ob->obmat));
+ BKE_pbvh_bmesh_detail_size_set(
+ ss->pbvh, object_space_constant_detail, brush->cached_dyntopo.detail_range);
}
- else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) {
- BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f);
+ else if (brush->cached_dyntopo.mode == DYNTOPO_DETAIL_BRUSH) {
+ BKE_pbvh_bmesh_detail_size_set(ss->pbvh,
+ ss->cache->radius * brush->cached_dyntopo.detail_percent /
+ 100.0f,
+ brush->cached_dyntopo.detail_range);
}
else {
BKE_pbvh_bmesh_detail_size_set(ss->pbvh,
(ss->cache->radius / ss->cache->dyntopo_pixel_radius) *
- (sd->detail_size * U.pixelsize) / 0.4f);
+ (brush->cached_dyntopo.detail_size * U.pixelsize) / 0.4f,
+ brush->cached_dyntopo.detail_range);
}
if (SCULPT_stroke_is_dynamic_topology(ss, brush)) {
- do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups);
+ float spacing = (float)brush->cached_dyntopo.spacing / 100.0f;
+
+ if (paint_stroke_apply_subspacing(
+ ss->cache->stroke, spacing, PAINT_MODE_SCULPT, &ss->cache->last_dyntopo_t)) {
+ do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups);
+ }
}
do_symmetrical_brush_actions(sd, ob, do_brush_action, ups);
@@ -8161,13 +9307,43 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
MEM_SAFE_FREE(ss->persistent_base);
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ ss->persistent_base = NULL;
+
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ int cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ int cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ int cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ BMVert *v = (BMVert *)vertex.i;
+
+ float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co);
+ float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no);
+ float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp);
+
+ copy_v3_v3(co, SCULPT_vertex_co_get(ss, vertex));
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ *disp = 0.0f;
+ }
+
+ return OPERATOR_FINISHED;
+ }
+
const int totvert = SCULPT_vertex_count_get(ss);
ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert,
"layer persistent base");
for (int i = 0; i < totvert; i++) {
- copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i));
- SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, vertex));
+ SCULPT_vertex_normal_get(ss, vertex, ss->persistent_base[i].no);
ss->persistent_base[i].disp = 0.0f;
}
@@ -8252,7 +9428,6 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
* parts that symmetrize modifies). */
SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize");
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE);
- BM_log_before_all_removed(ss->bm, ss->bm_log);
BM_mesh_toolflags_set(ss->bm, true);
@@ -8263,15 +9438,21 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
sd->symmetrize_direction,
dist,
true);
- SCULPT_dynamic_topology_triangulate(ss->bm);
+ SCULPT_dynamic_topology_triangulate(ss, ss->bm);
/* Bisect operator flags edges (keep tags clean for edge queue). */
BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false);
BM_mesh_toolflags_set(ss->bm, false);
+ BKE_pbvh_recalc_bmesh_boundary(ss->pbvh);
+
+ // symmetrize is messing up ids, regenerate them from scratch
+ BM_reassign_ids(ss->bm);
+
+ BM_log_full_mesh(ss->bm, ss->bm_log);
+
/* Finish undo. */
- BM_log_all_added(ss->bm, ss->bm_log);
SCULPT_undo_push_end();
break;
@@ -8344,10 +9525,10 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene,
/* Here we can detect geometry that was just added to Sculpt Mode as it has the
* SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
/* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not
- * initialized, which is used is some operators that modify the mesh topology to perform certain
- * actions in the new polys. After these operations are finished, all polys should have a valid
- * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility
- * correctly. */
+ * initialized, which is used is some operators that modify the mesh topology to perform
+ * certain actions in the new polys. After these operations are finished, all polys should have
+ * a valid face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their
+ * visibility correctly. */
/* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new
* objects, like moving the transform pivot position to the new area or masking existing
* geometry. */
@@ -8427,10 +9608,12 @@ void ED_object_sculptmode_enter_ex(Main *bmain,
/* Needed because we may be entering this mode before the undo system loads. */
wmWindowManager *wm = bmain->wm.first;
bool has_undo = wm->undo_stack != NULL;
+
/* Undo push is needed to prevent memory leak. */
if (has_undo) {
SCULPT_undo_push_begin(ob, "Dynamic topology enable");
}
+
SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob);
if (has_undo) {
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN);
@@ -8610,29 +9793,38 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2;
if (ss->preview_vert_index_list == NULL) {
- ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines");
+ ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(SculptVertRef),
+ "preview lines");
}
- GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int));
- int active_v = SCULPT_active_vertex_get(ss);
+ GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(SculptVertRef));
+ SculptVertRef active_v = SCULPT_active_vertex_get(ss);
BLI_gsqueue_push(not_visited_vertices, &active_v);
while (!BLI_gsqueue_is_empty(not_visited_vertices)) {
- int from_v;
+ SculptVertRef from_v;
+
BLI_gsqueue_pop(not_visited_vertices, &from_v);
+
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
if (totpoints + (ni.size * 2) < max_preview_vertices) {
- int to_v = ni.index;
+ SculptVertRef to_v = ni.vertex;
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+
ss->preview_vert_index_list[totpoints] = from_v;
totpoints++;
ss->preview_vert_index_list[totpoints] = to_v;
totpoints++;
- if (BLI_BITMAP_TEST(visited_vertices, to_v)) {
+
+ if (BLI_BITMAP_TEST(visited_vertices, to_v_i)) {
continue;
}
- BLI_BITMAP_ENABLE(visited_vertices, to_v);
+
+ BLI_BITMAP_ENABLE(visited_vertices, to_v_i);
+
const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v);
+
if (len_squared_v3v3(brush_co, co) < radius * radius) {
BLI_gsqueue_push(not_visited_vertices, &to_v);
}
@@ -8705,7 +9897,7 @@ static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
ot->idname = "SCULPT_OT_vertex_to_loop_colors";
/* api callbacks */
- ot->poll = SCULPT_vertex_colors_poll;
+ ot->poll = SCULPT_vertex_colors_poll_no_bmesh;
ot->exec = vertex_to_loop_colors_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -8768,7 +9960,7 @@ static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
ot->idname = "SCULPT_OT_loop_to_vertex_colors";
/* api callbacks */
- ot->poll = SCULPT_vertex_colors_poll;
+ ot->poll = SCULPT_vertex_colors_poll_no_bmesh;
ot->exec = loop_to_vertex_colors_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -8783,7 +9975,7 @@ static int sculpt_sample_color_invoke(bContext *C,
Object *ob = CTX_data_active_object(C);
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
- int active_vertex = SCULPT_active_vertex_get(ss);
+ SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex);
if (!active_vertex_color) {
return OPERATOR_CANCELLED;
@@ -8840,10 +10032,14 @@ enum {
SCULPT_TOPOLOGY_ID_DEFAULT,
};
-static int SCULPT_vertex_get_connected_component(SculptSession *ss, int index)
+static int SCULPT_vertex_get_connected_component(SculptSession *ss, SculptVertRef vertex)
{
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ vertex.i = BM_elem_index_get((BMVert *)vertex.i);
+ }
+
if (ss->vertex_info.connected_component) {
- return ss->vertex_info.connected_component[index];
+ return ss->vertex_info.connected_component[vertex.i];
}
return SCULPT_TOPOLOGY_ID_DEFAULT;
}
@@ -8852,19 +10048,28 @@ static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist)
{
const int totvert = SCULPT_vertex_count_get(ss);
ss->fake_neighbors.fake_neighbor_index = MEM_malloc_arrayN(
- totvert, sizeof(int), "fake neighbor");
+ totvert, sizeof(SculptVertRef), "fake neighbor");
for (int i = 0; i < totvert; i++) {
- ss->fake_neighbors.fake_neighbor_index[i] = FAKE_NEIGHBOR_NONE;
+ ss->fake_neighbors.fake_neighbor_index[i].i = FAKE_NEIGHBOR_NONE;
}
ss->fake_neighbors.current_max_distance = max_dist;
}
-static void SCULPT_fake_neighbor_add(SculptSession *ss, int v_index_a, int v_index_b)
+static void SCULPT_fake_neighbor_add(SculptSession *ss,
+ SculptVertRef v_index_a,
+ SculptVertRef v_index_b)
{
- if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) {
- ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b;
- ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a;
+ int tablea = (int)v_index_a.i, tableb = (int)v_index_b.i;
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ tablea = BM_elem_index_get((BMVert *)v_index_a.i);
+ tableb = BM_elem_index_get((BMVert *)v_index_b.i);
+ }
+
+ if (ss->fake_neighbors.fake_neighbor_index[tablea].i == FAKE_NEIGHBOR_NONE) {
+ ss->fake_neighbors.fake_neighbor_index[tablea] = v_index_b;
+ ss->fake_neighbors.fake_neighbor_index[tableb] = v_index_a;
}
}
@@ -8875,6 +10080,7 @@ static void sculpt_pose_fake_neighbors_free(SculptSession *ss)
typedef struct NearestVertexFakeNeighborTLSData {
int nearest_vertex_index;
+ SculptVertRef nearest_vertex;
float nearest_vertex_distance_squared;
int current_topology_id;
} NearestVertexFakeNeighborTLSData;
@@ -8888,14 +10094,17 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata,
NearestVertexFakeNeighborTLSData *nvtd = tls->userdata_chunk;
PBVHVertexIter vd;
+ SCULPT_vertex_random_access_ensure(ss);
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index);
+ int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.vertex);
if (vd_topology_id != nvtd->current_topology_id &&
- ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) {
+ ss->fake_neighbors.fake_neighbor_index[vd.index].i == FAKE_NEIGHBOR_NONE) {
float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co);
if (distance_squared < nvtd->nearest_vertex_distance_squared &&
distance_squared < data->max_distance_squared) {
nvtd->nearest_vertex_index = vd.index;
+ nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared;
}
}
@@ -8909,17 +10118,23 @@ static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata),
{
NearestVertexFakeNeighborTLSData *join = chunk_join;
NearestVertexFakeNeighborTLSData *nvtd = chunk;
+
if (join->nearest_vertex_index == -1) {
join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
-static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, float max_distance)
+static SculptVertRef SCULPT_fake_neighbor_search(Sculpt *sd,
+ Object *ob,
+ const SculptVertRef index,
+ float max_distance)
{
SculptSession *ss = ob->sculpt;
PBVHNode **nodes = NULL;
@@ -8934,7 +10149,7 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
if (totnode == 0) {
- return -1;
+ return BKE_pbvh_make_vref(-1);
}
SculptThreadedTaskData task_data = {
@@ -8948,6 +10163,7 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
NearestVertexFakeNeighborTLSData nvtd;
nvtd.nearest_vertex_index = -1;
+ nvtd.nearest_vertex.i = -1;
nvtd.nearest_vertex_distance_squared = FLT_MAX;
nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, index);
@@ -8960,19 +10176,24 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
MEM_SAFE_FREE(nodes);
- return nvtd.nearest_vertex_index;
+ return nvtd.nearest_vertex;
}
typedef struct SculptTopologyIDFloodFillData {
int next_id;
} SculptTopologyIDFloodFillData;
-static bool SCULPT_connected_components_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool SCULPT_connected_components_floodfill_cb(SculptSession *ss,
+ SculptVertRef from_v,
+ SculptVertRef to_v,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
SculptTopologyIDFloodFillData *data = userdata;
- ss->vertex_info.connected_component[from_v] = data->next_id;
- ss->vertex_info.connected_component[to_v] = data->next_id;
+ ss->vertex_info.connected_component[BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v)] =
+ data->next_id;
+ ss->vertex_info.connected_component[BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v)] =
+ data->next_id;
return true;
}
@@ -8980,7 +10201,10 @@ void SCULPT_connected_components_ensure(Object *ob)
{
SculptSession *ss = ob->sculpt;
- /* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild.
+ SCULPT_vertex_random_access_ensure(ss);
+
+ /* Topology IDs already initialized. They only need to be recalculated when the PBVH is
+ * rebuild.
*/
if (ss->vertex_info.connected_component) {
return;
@@ -8995,10 +10219,16 @@ void SCULPT_connected_components_ensure(Object *ob)
int next_id = 0;
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_visible_get(ss, vertex)) {
+ continue;
+ }
+
if (ss->vertex_info.connected_component[i] == SCULPT_TOPOLOGY_ID_NONE) {
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
- SCULPT_floodfill_add_initial(&flood, i);
+ SCULPT_floodfill_add_initial(&flood, vertex);
SculptTopologyIDFloodFillData data;
data.next_id = next_id;
SCULPT_floodfill_execute(ss, &flood, SCULPT_connected_components_floodfill_cb, &data);
@@ -9011,32 +10241,39 @@ void SCULPT_connected_components_ensure(Object *ob)
void SCULPT_boundary_info_ensure(Object *object)
{
SculptSession *ss = object->sculpt;
- if (ss->vertex_info.boundary) {
+
+ // PBVH_BMESH now handles itself
+ if (ss->bm) {
return;
}
+ else {
+ if (ss->vertex_info.boundary) {
+ return;
+ }
- Mesh *base_mesh = BKE_mesh_from_object(object);
- ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info");
- int *adjacent_faces_edge_count = MEM_calloc_arrayN(
- base_mesh->totedge, sizeof(int), "Adjacent face edge count");
+ Mesh *base_mesh = BKE_mesh_from_object(object);
+ ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info");
+ int *adjacent_faces_edge_count = MEM_calloc_arrayN(
+ base_mesh->totedge, sizeof(int), "Adjacent face edge count");
- for (int p = 0; p < base_mesh->totpoly; p++) {
- MPoly *poly = &base_mesh->mpoly[p];
- for (int l = 0; l < poly->totloop; l++) {
- MLoop *loop = &base_mesh->mloop[l + poly->loopstart];
- adjacent_faces_edge_count[loop->e]++;
+ for (int p = 0; p < base_mesh->totpoly; p++) {
+ MPoly *poly = &base_mesh->mpoly[p];
+ for (int l = 0; l < poly->totloop; l++) {
+ MLoop *loop = &base_mesh->mloop[l + poly->loopstart];
+ adjacent_faces_edge_count[loop->e]++;
+ }
}
- }
- for (int e = 0; e < base_mesh->totedge; e++) {
- if (adjacent_faces_edge_count[e] < 2) {
- MEdge *edge = &base_mesh->medge[e];
- BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true);
- BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true);
+ for (int e = 0; e < base_mesh->totedge; e++) {
+ if (adjacent_faces_edge_count[e] < 2) {
+ MEdge *edge = &base_mesh->medge[e];
+ BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true);
+ BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true);
+ }
}
- }
- MEM_freeN(adjacent_faces_edge_count);
+ MEM_freeN(adjacent_faces_edge_count);
+ }
}
void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
@@ -9044,7 +10281,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
- /* Fake neighbors were already initialized with the same distance, so no need to be recalculated.
+ /* Fake neighbors were already initialized with the same distance, so no need to be
+ * recalculated.
*/
if (ss->fake_neighbors.fake_neighbor_index &&
ss->fake_neighbors.current_max_distance == max_dist) {
@@ -9055,12 +10293,13 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
SCULPT_fake_neighbor_init(ss, max_dist);
for (int i = 0; i < totvert; i++) {
- const int from_v = i;
+ const SculptVertRef from_v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
- /* This vertex does not have a fake neighbor yet, search one for it. */
- if (ss->fake_neighbors.fake_neighbor_index[from_v] == FAKE_NEIGHBOR_NONE) {
- const int to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist);
- if (to_v != -1) {
+ /* This vertex does not have a fake neighbor yet, seach one for it. */
+ if (ss->fake_neighbors.fake_neighbor_index[i].i == FAKE_NEIGHBOR_NONE) {
+ const SculptVertRef to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist);
+
+ if (to_v.i != -1) {
/* Add the fake neighbor if available. */
SCULPT_fake_neighbor_add(ss, from_v, to_v);
}
@@ -9177,16 +10416,19 @@ static void do_mask_by_color_contiguous_update_nodes_cb(
}
static bool sculpt_mask_by_color_contiguous_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata)
{
MaskByColorContiguousFloodFillData *data = userdata;
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+ int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
+
const float *current_color = SCULPT_vertex_color_get(ss, to_v);
float new_vertex_mask = sculpt_mask_by_color_delta_get(
current_color, data->initial_color, data->threshold, data->invert);
- data->new_mask[to_v] = new_vertex_mask;
+ data->new_mask[to_v_i] = new_vertex_mask;
if (is_duplicate) {
- data->new_mask[to_v] = data->new_mask[from_v];
+ data->new_mask[to_v_i] = data->new_mask[from_v_i];
}
float len = len_v3v3(current_color, data->initial_color);
@@ -9195,7 +10437,7 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb(
}
static void sculpt_mask_by_color_contiguous(Object *object,
- const int vertex,
+ const SculptVertRef vertex,
const float threshold,
const bool invert,
const bool preserve_mask)
@@ -9284,7 +10526,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata,
}
static void sculpt_mask_by_color_full_mesh(Object *object,
- const int vertex,
+ const SculptVertRef vertex,
const float threshold,
const bool invert,
const bool preserve_mask)
@@ -9330,8 +10572,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
SCULPT_vertex_random_access_ensure(ss);
- /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
- * so it needs to be updated here. */
+ /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse
+ * move, so it needs to be updated here. */
SculptCursorGeometryInfo sgi;
float mouse[2];
mouse[0] = event->mval[0];
@@ -9340,7 +10582,7 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
SCULPT_undo_push_begin(ob, "Mask by color");
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
const float threshold = RNA_float_get(op->ptr, "threshold");
const bool invert = RNA_boolean_get(op->ptr, "invert");
const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
@@ -9395,6 +10637,338 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
1.0f);
}
+#if 0
+/* -------------------------------------------------------------------- */
+/** \name Dyntopo Detail Size Edit Operator
+ * \{ */
+
+/* Defines how much the mouse movement will modify the detail size value. */
+# define DETAIL_SIZE_DELTA_SPEED 0.08f
+# define DETAIL_SIZE_DELTA_ACCURATE_SPEED 0.004f
+
+typedef struct DyntopoDetailSizeEditCustomData {
+ void *draw_handle;
+ Object *active_object;
+
+ float init_mval[2];
+ float accurate_mval[2];
+
+ float outline_col[4];
+
+ bool accurate_mode;
+ bool sample_mode;
+
+ float init_detail_size;
+ float accurate_detail_size;
+ float detail_size;
+ float radius;
+
+ float preview_tri[3][3];
+ float gizmo_mat[4][4];
+} DyntopoDetailSizeEditCustomData;
+
+static void dyntopo_detail_size_parallel_lines_draw(uint pos3d,
+ DyntopoDetailSizeEditCustomData *cd,
+ const float start_co[3],
+ const float end_co[3],
+ bool flip,
+ const float angle)
+{
+ float object_space_constant_detail = 1.0f /
+ (cd->detail_size * mat4_to_scale(cd->active_object->obmat));
+
+ /* The constant detail represents the maximum edge length allowed before subdividing it. If the
+ * triangle grid preview is created with this value it will represent an ideal mesh density where
+ * all edges have the exact maximum length, which never happens in practice. As the minimum edge
+ * length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the average
+ * between max and min edge length so the preview is more accurate. */
+ object_space_constant_detail *= 0.7f;
+
+ const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]);
+ const int tot_lines = (int)(total_len / object_space_constant_detail) + 1;
+ const float tot_lines_fl = total_len / object_space_constant_detail;
+ float spacing_disp[3];
+ sub_v3_v3v3(spacing_disp, end_co, start_co);
+ normalize_v3(spacing_disp);
+
+ float line_disp[3];
+ rotate_v2_v2fl(line_disp, spacing_disp, DEG2RAD(angle));
+ mul_v3_fl(spacing_disp, total_len / tot_lines_fl);
+
+ immBegin(GPU_PRIM_LINES, (uint)tot_lines * 2);
+ for (int i = 0; i < tot_lines; i++) {
+ float line_length;
+ if (flip) {
+ line_length = total_len * ((float)i / (float)tot_lines_fl);
+ }
+ else {
+ line_length = total_len * (1.0f - ((float)i / (float)tot_lines_fl));
+ }
+ float line_start[3];
+ copy_v3_v3(line_start, start_co);
+ madd_v3_v3v3fl(line_start, line_start, spacing_disp, i);
+ float line_end[3];
+ madd_v3_v3v3fl(line_end, line_start, line_disp, line_length);
+ immVertex3fv(pos3d, line_start);
+ immVertex3fv(pos3d, line_end);
+ }
+ immEnd();
+}
+
+static void dyntopo_detail_size_edit_draw(const bContext *UNUSED(C),
+ ARegion *UNUSED(ar),
+ void *arg)
+{
+ DyntopoDetailSizeEditCustomData *cd = arg;
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_line_smooth(true);
+
+ uint pos3d = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ GPU_matrix_push();
+ GPU_matrix_mul(cd->gizmo_mat);
+
+ /* Draw Cursor */
+ immUniformColor4fv(cd->outline_col);
+ GPU_line_width(3.0f);
+
+ imm_draw_circle_wire_3d(pos3d, 0, 0, cd->radius, 80);
+
+ /* Draw Triangle. */
+ immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f);
+ immBegin(GPU_PRIM_LINES, 6);
+ immVertex3fv(pos3d, cd->preview_tri[0]);
+ immVertex3fv(pos3d, cd->preview_tri[1]);
+
+ immVertex3fv(pos3d, cd->preview_tri[1]);
+ immVertex3fv(pos3d, cd->preview_tri[2]);
+
+ immVertex3fv(pos3d, cd->preview_tri[2]);
+ immVertex3fv(pos3d, cd->preview_tri[0]);
+ immEnd();
+
+ /* Draw Grid */
+ GPU_line_width(1.0f);
+ dyntopo_detail_size_parallel_lines_draw(
+ pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], false, 60.0f);
+ dyntopo_detail_size_parallel_lines_draw(
+ pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], true, 120.0f);
+ dyntopo_detail_size_parallel_lines_draw(
+ pos3d, cd, cd->preview_tri[0], cd->preview_tri[2], false, -60.0f);
+
+ immUnbindProgram();
+ GPU_matrix_pop();
+ GPU_blend(GPU_BLEND_NONE);
+ GPU_line_smooth(false);
+}
+
+static void dyntopo_detail_size_edit_cancel(bContext *C, wmOperator *op)
+{
+ Object *active_object = CTX_data_active_object(C);
+ SculptSession *ss = active_object->sculpt;
+ ARegion *region = CTX_wm_region(C);
+ DyntopoDetailSizeEditCustomData *cd = op->customdata;
+ ED_region_draw_cb_exit(region->type, cd->draw_handle);
+ ss->draw_faded_cursor = false;
+ MEM_freeN(op->customdata);
+ ED_workspace_status_text(C, NULL);
+}
+
+static void dyntopo_detail_size_sample_from_surface(Object *ob,
+ DyntopoDetailSizeEditCustomData *cd)
+{
+ SculptSession *ss = ob->sculpt;
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
+
+ float len_accum = 0;
+ int num_neighbors = 0;
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
+ len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex),
+ SCULPT_vertex_co_get(ss, ni.vertex));
+ num_neighbors++;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (num_neighbors > 0) {
+ const float avg_edge_len = len_accum / num_neighbors;
+ /* Use 0.7 as the average of min and max dyntopo edge length. */
+ const float detail_size = 0.7f / (avg_edge_len * mat4_to_scale(cd->active_object->obmat));
+ cd->detail_size = clamp_f(detail_size, 1.0f, 500.0f);
+ }
+}
+
+static void dyntopo_detail_size_update_from_mouse_delta(DyntopoDetailSizeEditCustomData *cd,
+ const wmEvent *event)
+{
+ const float mval[2] = {event->mval[0], event->mval[1]};
+
+ float detail_size_delta;
+ if (cd->accurate_mode) {
+ detail_size_delta = mval[0] - cd->accurate_mval[0];
+ cd->detail_size = cd->accurate_detail_size +
+ detail_size_delta * DETAIL_SIZE_DELTA_ACCURATE_SPEED;
+ }
+ else {
+ detail_size_delta = mval[0] - cd->init_mval[0];
+ cd->detail_size = cd->init_detail_size + detail_size_delta * DETAIL_SIZE_DELTA_SPEED;
+ }
+
+ if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) {
+ cd->accurate_mode = true;
+ copy_v2_v2(cd->accurate_mval, mval);
+ cd->accurate_detail_size = cd->detail_size;
+ }
+ if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) {
+ cd->accurate_mode = false;
+ cd->accurate_detail_size = 0.0f;
+ }
+
+ cd->detail_size = clamp_f(cd->detail_size, 1.0f, 500.0f);
+}
+
+static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *active_object = CTX_data_active_object(C);
+ SculptSession *ss = active_object->sculpt;
+ ARegion *region = CTX_wm_region(C);
+ DyntopoDetailSizeEditCustomData *cd = op->customdata;
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+
+ /* Cancel modal operator */
+ if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) ||
+ (event->type == RIGHTMOUSE && event->val == KM_PRESS)) {
+ dyntopo_detail_size_edit_cancel(C, op);
+ ED_region_tag_redraw(region);
+ return OPERATOR_FINISHED;
+ }
+
+ /* Finish modal operator */
+ if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
+ (event->type == EVT_RETKEY && event->val == KM_PRESS) ||
+ (event->type == EVT_PADENTER && event->val == KM_PRESS)) {
+ ED_region_draw_cb_exit(region->type, cd->draw_handle);
+ sd->constant_detail = cd->detail_size;
+ ss->draw_faded_cursor = false;
+ MEM_freeN(op->customdata);
+ ED_region_tag_redraw(region);
+ ED_workspace_status_text(C, NULL);
+ return OPERATOR_FINISHED;
+ }
+
+ ED_region_tag_redraw(region);
+
+ if (event->type == EVT_LEFTCTRLKEY && event->val == KM_PRESS) {
+ cd->sample_mode = true;
+ }
+ if (event->type == EVT_LEFTCTRLKEY && event->val == KM_RELEASE) {
+ cd->sample_mode = false;
+ }
+
+ /* Sample mode sets the detail size sampling the average edge length under the surface. */
+ if (cd->sample_mode) {
+ dyntopo_detail_size_sample_from_surface(active_object, cd);
+ return OPERATOR_RUNNING_MODAL;
+ }
+ /* Regular mode, changes the detail size by moving the cursor. */
+ dyntopo_detail_size_update_from_mouse_delta(cd, event);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+ Object *active_object = CTX_data_active_object(C);
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ DyntopoDetailSizeEditCustomData *cd = MEM_callocN(sizeof(DyntopoDetailSizeEditCustomData),
+ "Dyntopo Detail Size Edit OP Custom Data");
+
+ /* Initial operator Custom Data setup. */
+ cd->draw_handle = ED_region_draw_cb_activate(
+ region->type, dyntopo_detail_size_edit_draw, cd, REGION_DRAW_POST_VIEW);
+ cd->active_object = active_object;
+ cd->init_mval[0] = event->mval[0];
+ cd->init_mval[1] = event->mval[1];
+ cd->detail_size = sd->constant_detail;
+ cd->init_detail_size = sd->constant_detail;
+ copy_v4_v4(cd->outline_col, brush->add_col);
+ op->customdata = cd;
+
+ SculptSession *ss = active_object->sculpt;
+ cd->radius = ss->cursor_radius;
+
+ /* Generates the matrix to position the gizmo in the surface of the mesh using the same location
+ * and orientation as the brush cursor. */
+ float cursor_trans[4][4], cursor_rot[4][4];
+ const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f};
+ float quat[4];
+ copy_m4_m4(cursor_trans, active_object->obmat);
+ translate_m4(
+ cursor_trans, ss->cursor_location[0], ss->cursor_location[1], ss->cursor_location[2]);
+
+ float cursor_normal[3];
+ if (!is_zero_v3(ss->cursor_sampled_normal)) {
+ copy_v3_v3(cursor_normal, ss->cursor_sampled_normal);
+ }
+ else {
+ copy_v3_v3(cursor_normal, ss->cursor_normal);
+ }
+
+ rotation_between_vecs_to_quat(quat, z_axis, cursor_normal);
+ quat_to_mat4(cursor_rot, quat);
+ copy_m4_m4(cd->gizmo_mat, cursor_trans);
+ mul_m4_m4_post(cd->gizmo_mat, cursor_rot);
+
+ /* Initialize the position of the triangle vertices. */
+ const float y_axis[3] = {0.0f, cd->radius, 0.0f};
+ for (int i = 0; i < 3; i++) {
+ zero_v3(cd->preview_tri[i]);
+ rotate_v2_v2fl(cd->preview_tri[i], y_axis, DEG2RAD(120.0f * i));
+ }
+
+ SCULPT_vertex_random_access_ensure(ss);
+
+ WM_event_add_modal_handler(C, op);
+ ED_region_tag_redraw(region);
+
+ ss->draw_faded_cursor = true;
+
+ const char *status_str = TIP_(
+ "Move the mouse to change the dyntopo detail size. LMB: confirm size, ESC/RMB: cancel");
+ ED_workspace_status_text(C, status_str);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static bool dyntopo_detail_size_edit_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+
+ return SCULPT_mode_poll(C) && ob->sculpt->bm && (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT);
+}
+
+static void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Edit Dyntopo Detail Size";
+ ot->description = "Modify the constant detail size of dyntopo interactively";
+ ot->idname = "SCULPT_OT_dyntopo_detail_size_edit";
+
+ /* api callbacks */
+ ot->poll = dyntopo_detail_size_edit_poll;
+ ot->invoke = dyntopo_detail_size_edit_invoke;
+ ot->modal = dyntopo_detail_size_edit_modal;
+ ot->cancel = dyntopo_detail_size_edit_cancel;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+#endif
+
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c
index 35f48400fe2..135d9af1af2 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.c
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c
@@ -85,9 +85,11 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
{
+ /*
if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
return false;
- }
+ }*/
+
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {
return true;
}
@@ -100,6 +102,13 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
return true;
}
+ if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_CONCAVITY)) {
+ if (br && br->concave_mask_factor == 0.0f) {
+ return false;
+ }
+ return true;
+ }
+
return false;
}
@@ -111,6 +120,18 @@ static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Br
return sculpt->automasking_flags;
}
+static float sculpt_concavity_factor(AutomaskingCache *automasking, float fac)
+{
+ if (automasking->settings.flags & BRUSH_AUTOMASKING_INVERT_CONCAVITY) {
+ fac = 1.0 - fac;
+ }
+
+ fac = pow(fac * 1.5f, (0.5f + automasking->settings.concave_factor) * 8.0);
+ CLAMP(fac, 0.0f, 1.0f);
+
+ return fac;
+}
+
static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush)
{
@@ -127,16 +148,37 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush
return false;
}
-float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert)
+float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
+ SculptSession *ss,
+ SculptVertRef vert)
{
+ float mask = 1.0f;
+ bool do_concave;
+
if (!automasking) {
- return 1.0f;
+ return mask;
}
+
+ do_concave = ss->cache && ss->cache->brush && ss->cache->brush->concave_mask_factor > 0.0f &&
+ (automasking->settings.flags & BRUSH_AUTOMASKING_CONCAVITY);
+
/* If the cache is initialized with valid info, use the cache. This is used when the
* automasking information can't be computed in real time per vertex and needs to be
* initialized for the whole mesh when the stroke starts. */
- if (automasking->factor) {
- return automasking->factor[vert];
+ if (automasking->factorlayer) {
+ mask = *(float *)SCULPT_temp_cdata_get(vert, automasking->factorlayer);
+ }
+
+ // don't used cached automasking factors for facesets or concave in
+ // dyntopo
+ if (automasking->factorlayer && !ss->bm) {
+ return mask;
+ }
+
+ if (do_concave) {
+ float fac = SCULPT_calc_concavity(ss, vert);
+
+ mask *= sculpt_concavity_factor(automasking, fac);
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
@@ -157,7 +199,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession
}
}
- return 1.0f;
+ return mask;
}
void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
@@ -166,7 +208,10 @@ void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
return;
}
- MEM_SAFE_FREE(automasking->factor);
+ if (automasking->factorlayer) {
+ MEM_SAFE_FREE(automasking->factorlayer);
+ }
+
MEM_SAFE_FREE(automasking);
}
@@ -184,26 +229,32 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
}
typedef struct AutomaskFloodFillData {
- float *automask_factor;
+ SculptCustomLayer *factorlayer;
float radius;
bool use_radius;
float location[3];
char symm;
} AutomaskFloodFillData;
-static bool automask_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool automask_floodfill_cb(SculptSession *ss,
+ SculptVertRef from_vref,
+ SculptVertRef to_vref,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
AutomaskFloodFillData *data = userdata;
- data->automask_factor[to_v] = 1.0f;
- data->automask_factor[from_v] = 1.0f;
+ *(float *)SCULPT_temp_cdata_get(to_vref, data->factorlayer) = 1.0f;
+ *(float *)SCULPT_temp_cdata_get(from_vref, data->factorlayer) = 1.0f;
+
return (!data->use_radius ||
SCULPT_is_vertex_inside_brush_radius_symm(
- SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm));
+ SCULPT_vertex_co_get(ss, to_vref), data->location, data->radius, data->symm));
}
-static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+static void SCULPT_topology_automasking_init(Sculpt *sd,
+ Object *ob,
+ SculptCustomLayer *factorlayer)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -215,7 +266,10 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
const int totvert = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totvert; i++) {
- automask_factor[i] = 0.0f;
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ float *fac = SCULPT_temp_cdata_get(vertex, factorlayer);
+ *fac = 0.0f;
}
/* Flood fill automask to connected vertices. Limited to vertices inside
@@ -226,7 +280,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
AutomaskFloodFillData fdata = {
- .automask_factor = automask_factor,
+ .factorlayer = factorlayer,
.radius = radius,
.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush),
.symm = SCULPT_mesh_symmetry_xyz_get(ob),
@@ -234,17 +288,17 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
-
- return automask_factor;
}
-static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+static void sculpt_face_sets_automasking_init(Sculpt *sd,
+ Object *ob,
+ SculptCustomLayer *factorlayer)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
- return NULL;
+ return;
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
@@ -255,20 +309,22 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a
int tot_vert = SCULPT_vertex_count_get(ss);
int active_face_set = SCULPT_active_face_set_get(ss);
for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
- automask_factor[i] *= 0.0f;
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) {
+ *(float *)SCULPT_temp_cdata_get(vertex, factorlayer) = 0.0f;
}
}
- return automask_factor;
+ return;
}
#define EDGE_DISTANCE_INF -1
-float *SCULPT_boundary_automasking_init(Object *ob,
- eBoundaryAutomaskMode mode,
- int propagation_steps,
- float *automask_factor)
+void SCULPT_boundary_automasking_init(Object *ob,
+ eBoundaryAutomaskMode mode,
+ int propagation_steps,
+ SculptCustomLayer *factorlayer)
{
SculptSession *ss = ob->sculpt;
@@ -281,15 +337,17 @@ float *SCULPT_boundary_automasking_init(Object *ob,
int *edge_distance = MEM_callocN(sizeof(int) * totvert, "automask_factor");
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
edge_distance[i] = EDGE_DISTANCE_INF;
switch (mode) {
case AUTOMASK_INIT_BOUNDARY_EDGES:
- if (SCULPT_vertex_is_boundary(ss, i)) {
+ if (SCULPT_vertex_is_boundary(ss, vertex)) {
edge_distance[i] = 0;
}
break;
case AUTOMASK_INIT_BOUNDARY_FACE_SETS:
- if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
+ if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) {
edge_distance[i] = 0;
}
break;
@@ -298,11 +356,13 @@ float *SCULPT_boundary_automasking_init(Object *ob,
for (int propagation_it = 0; propagation_it < propagation_steps; propagation_it++) {
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (edge_distance[i] != EDGE_DISTANCE_INF) {
continue;
}
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) {
if (edge_distance[ni.index] == propagation_it) {
edge_distance[i] = propagation_it + 1;
}
@@ -312,16 +372,18 @@ float *SCULPT_boundary_automasking_init(Object *ob,
}
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (edge_distance[i] == EDGE_DISTANCE_INF) {
continue;
}
const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps);
const float edge_boundary_automask = pow2f(p);
- automask_factor[i] *= (1.0f - edge_boundary_automask);
+
+ *(float *)SCULPT_temp_cdata_get(vertex, factorlayer) *= (1.0f - edge_boundary_automask);
}
MEM_SAFE_FREE(edge_distance);
- return automask_factor;
}
static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
@@ -330,7 +392,65 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski
Brush *brush)
{
automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
+
automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss);
+ automasking->settings.concave_factor = brush ? brush->concave_mask_factor : 0.0f;
+}
+
+float SCULPT_calc_concavity(SculptSession *ss, SculptVertRef vref)
+{
+ SculptVertexNeighborIter ni;
+ float co[3], tot = 0.0, elen = 0.0;
+ const float *vco = SCULPT_vertex_co_get(ss, vref);
+
+ zero_v3(co);
+
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) {
+ const float *vco2 = SCULPT_vertex_co_get(ss, ni.vertex);
+
+ elen += len_v3v3(vco, vco2);
+ add_v3_v3(co, vco2);
+ tot += 1.0f;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (!tot) {
+ return 0.5f;
+ }
+
+ elen /= tot;
+ mul_v3_fl(co, 1.0 / tot);
+ sub_v3_v3(co, vco);
+ mul_v3_fl(co, -1.0 / elen);
+
+ float no[3];
+ SCULPT_vertex_normal_get(ss, vref, no);
+
+ float f = dot_v3v3(co, no) * 0.5 + 0.5;
+ return 1.0 - f;
+}
+
+static void SCULPT_concavity_automasking_init(Object *ob,
+ Brush *brush,
+ AutomaskingCache *automasking,
+ SculptCustomLayer *factorlayer)
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss) {
+ return;
+ }
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ for (int i = 0; i < totvert; i++) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ float f = SCULPT_calc_concavity(ss, vref);
+ f = sculpt_concavity_factor(automasking, f);
+
+ *(float *)SCULPT_temp_cdata_get(vref, factorlayer) *= f;
+ }
+ // BKE_pbvh_vertex_iter_begin
}
AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob)
@@ -350,9 +470,29 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
return automasking;
}
- automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_face_random_access_ensure(ss);
+
+ automasking->factorlayer = MEM_callocN(sizeof(*automasking->factorlayer),
+ "automasking->factorlayer");
+
+ if (!SCULPT_temp_customlayer_get(ss,
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT,
+ "__sculpt_mask_factor",
+ automasking->factorlayer)) {
+ // failed
+ MEM_freeN(automasking->factorlayer);
+ return automasking;
+ }
+
+ // automasking->factorlayer = SCULPT_temp_customlayer_ensure()
+ // automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
for (int i = 0; i < totvert; i++) {
- automasking->factor[i] = 1.0f;
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ float *f = SCULPT_temp_cdata_get(vertex, automasking->factorlayer);
+
+ *f = 1.0f;
}
const int boundary_propagation_steps = brush ?
@@ -361,22 +501,35 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
SCULPT_vertex_random_access_ensure(ss);
- SCULPT_topology_automasking_init(sd, ob, automasking->factor);
+ SCULPT_topology_automasking_init(sd, ob, automasking->factorlayer);
+ }
+
+ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_boundary_automasking_init(ob,
+ AUTOMASK_INIT_BOUNDARY_FACE_SETS,
+ boundary_propagation_steps,
+ automasking->factorlayer);
}
+
+ // for dyntopo, only topology and fset boundary area initialized here
+ if (ss->bm) {
+ return automasking;
+ }
+
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
- sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
+ sculpt_face_sets_automasking_init(sd, ob, automasking->factorlayer);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(
- ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
+ ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factorlayer);
}
- if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
+ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CONCAVITY)) {
SCULPT_vertex_random_access_ensure(ss);
- SCULPT_boundary_automasking_init(
- ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
+ SCULPT_concavity_automasking_init(ob, brush, automasking, automasking->factorlayer);
}
return automasking;
diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c
index 37678ec276a..8262eb4a486 100644
--- a/source/blender/editors/sculpt_paint/sculpt_boundary.c
+++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c
@@ -58,23 +58,47 @@
#include <math.h>
#include <stdlib.h>
+#if 1
+# ifdef NDEBUG
+# define NDEBUG_UNDEFD
+# undef NDEBUG
+# endif
+
+# include "BLI_assert.h"
+
+# ifdef NDEBUG_UNDEFD
+# define NDEBUG 1
+# endif
+#endif
+
#define BOUNDARY_VERTEX_NONE -1
#define BOUNDARY_STEPS_NONE -1
+#define TSTN 4
+
typedef struct BoundaryInitialVertexFloodFillData {
- int initial_vertex;
+ SculptVertRef initial_vertex;
+ int initial_vertex_index;
int boundary_initial_vertex_steps;
- int boundary_initial_vertex;
+
+ SculptVertRef boundary_initial_vertex;
+
int *floodfill_steps;
float radius_sq;
} BoundaryInitialVertexFloodFillData;
-static bool boundary_initial_vertex_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+static bool boundary_initial_vertex_floodfill_cb(SculptSession *ss,
+ SculptVertRef from_vref,
+ SculptVertRef to_vref,
+ bool is_duplicate,
+ void *userdata)
{
BoundaryInitialVertexFloodFillData *data = userdata;
- if (!SCULPT_vertex_visible_get(ss, to_v)) {
+ int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref);
+ int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref);
+
+ if (!SCULPT_vertex_visible_get(ss, to_vref)) {
return false;
}
@@ -85,25 +109,26 @@ static bool boundary_initial_vertex_floodfill_cb(
data->floodfill_steps[to_v] = data->floodfill_steps[from_v];
}
- if (SCULPT_vertex_is_boundary(ss, to_v)) {
+ if (SCULPT_vertex_is_boundary(ss, to_vref)) {
if (data->floodfill_steps[to_v] < data->boundary_initial_vertex_steps) {
data->boundary_initial_vertex_steps = data->floodfill_steps[to_v];
- data->boundary_initial_vertex = to_v;
+ data->boundary_initial_vertex = to_vref;
}
}
const float len_sq = len_squared_v3v3(SCULPT_vertex_co_get(ss, data->initial_vertex),
- SCULPT_vertex_co_get(ss, to_v));
+ SCULPT_vertex_co_get(ss, to_vref));
return len_sq < data->radius_sq;
}
/* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside
* the given radius, if it exists. */
-static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
- const int initial_vertex,
- const float radius)
+static SculptVertRef sculpt_boundary_get_closest_boundary_vertex(
+ SculptSession *ss,
+ const SculptVertRef initial_vertex,
+ const int initial_vertex_index,
+ const float radius)
{
-
if (SCULPT_vertex_is_boundary(ss, initial_vertex)) {
return initial_vertex;
}
@@ -114,13 +139,14 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
BoundaryInitialVertexFloodFillData fdata = {
.initial_vertex = initial_vertex,
- .boundary_initial_vertex = BOUNDARY_VERTEX_NONE,
+ .initial_vertex_index = initial_vertex_index,
+ .boundary_initial_vertex = {BOUNDARY_VERTEX_NONE},
.boundary_initial_vertex_steps = INT_MAX,
.radius_sq = radius * radius,
};
fdata.floodfill_steps = MEM_calloc_arrayN(
- SCULPT_vertex_count_get(ss), sizeof(int), "floodfill steps");
+ SCULPT_vertex_count_get(ss), sizeof(int) * TSTN, "floodfill steps");
SCULPT_floodfill_execute(ss, &flood, boundary_initial_vertex_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
@@ -134,28 +160,39 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
* deformations usually need in the boundary. */
static int BOUNDARY_INDICES_BLOCK_SIZE = 300;
-static void sculpt_boundary_index_add(SculptBoundary *boundary,
- const int new_index,
+static void sculpt_boundary_index_add(SculptSession *ss,
+ SculptBoundary *boundary,
+ const SculptVertRef new_index,
const float distance,
GSet *included_vertices)
{
boundary->vertices[boundary->num_vertices] = new_index;
+ boundary->vertex_indices[boundary->num_vertices] = BKE_pbvh_vertex_index_to_table(ss->pbvh,
+ new_index);
+
if (boundary->distance) {
- boundary->distance[new_index] = distance;
+ boundary->distance[BKE_pbvh_vertex_index_to_table(ss->pbvh, new_index)] = distance;
}
if (included_vertices) {
- BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index));
+ BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index.i));
}
boundary->num_vertices++;
if (boundary->num_vertices >= boundary->vertices_capacity) {
boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
- boundary->vertices = MEM_reallocN_id(
- boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices");
+ boundary->vertices = MEM_reallocN_id(boundary->vertices,
+ boundary->vertices_capacity * sizeof(SculptVertRef) *
+ TSTN,
+ "boundary vertrefs");
+ boundary->vertex_indices = MEM_reallocN_id(boundary->vertex_indices,
+ boundary->vertices_capacity * sizeof(int) * TSTN,
+ "boundary indices");
}
};
-static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2)
+static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary,
+ const SculptVertRef v1,
+ const SculptVertRef v2)
{
boundary->edges[boundary->num_edges].v1 = v1;
@@ -165,7 +202,8 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int
if (boundary->num_edges >= boundary->edges_capacity) {
boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
boundary->edges = MEM_reallocN_id(boundary->edges,
- boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge),
+ boundary->edges_capacity *
+ sizeof(SculptBoundaryPreviewEdge) * TSTN,
"boundary edges");
}
};
@@ -175,7 +213,7 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int
* as well as to check if the initial vertex is valid.
*/
static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
- const int initial_vertex)
+ const SculptVertRef initial_vertex)
{
if (!SCULPT_vertex_visible_get(ss, initial_vertex)) {
@@ -186,9 +224,9 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
int boundary_vertex_count = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vertex, ni) {
- if (SCULPT_vertex_visible_get(ss, ni.index)) {
+ if (SCULPT_vertex_visible_get(ss, ni.vertex)) {
neighbor_count++;
- if (SCULPT_vertex_is_boundary(ss, ni.index)) {
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
boundary_vertex_count++;
}
}
@@ -218,44 +256,51 @@ typedef struct BoundaryFloodFillData {
GSet *included_vertices;
EdgeSet *preview_edges;
- int last_visited_vertex;
+ SculptVertRef last_visited_vertex;
} BoundaryFloodFillData;
static bool boundary_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata)
{
BoundaryFloodFillData *data = userdata;
SculptBoundary *boundary = data->boundary;
+ int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+
if (!SCULPT_vertex_is_boundary(ss, to_v)) {
return false;
}
const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v),
SCULPT_vertex_co_get(ss, to_v));
const float distance_boundary_to_dst = boundary->distance ?
- boundary->distance[from_v] + edge_len :
+ boundary->distance[from_v_i] + edge_len :
0.0f;
- sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices);
- if (!is_duplicate) {
- sculpt_boundary_preview_edge_add(boundary, from_v, to_v);
- }
+ sculpt_boundary_index_add(ss, boundary, to_v, distance_boundary_to_dst, data->included_vertices);
+ // if (!is_duplicate) {
+ sculpt_boundary_preview_edge_add(boundary, from_v, to_v);
+ //}
+
return sculpt_boundary_is_vertex_in_editable_boundary(ss, to_v);
}
static void sculpt_boundary_indices_init(SculptSession *ss,
SculptBoundary *boundary,
const bool init_boundary_distances,
- const int initial_boundary_index)
+ const SculptVertRef initial_boundary_index)
{
const int totvert = SCULPT_vertex_count_get(ss);
boundary->vertices = MEM_malloc_arrayN(
- BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices");
+ BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptVertRef) * TSTN, "boundary vrefs");
+ boundary->vertex_indices = MEM_malloc_arrayN(
+ BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int) * TSTN, "boundary indices");
+
if (init_boundary_distances) {
- boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances");
+ boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float) * TSTN, "boundary distances");
}
boundary->edges = MEM_malloc_arrayN(
- BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges");
+ BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge) * TSTN, "boundary edges");
GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE);
SculptFloodFill flood;
@@ -264,13 +309,13 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
boundary->initial_vertex = initial_boundary_index;
copy_v3_v3(boundary->initial_vertex_position,
SCULPT_vertex_co_get(ss, boundary->initial_vertex));
- sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices);
+ sculpt_boundary_index_add(ss, boundary, initial_boundary_index, 0.0f, included_vertices);
SCULPT_floodfill_add_initial(&flood, initial_boundary_index);
BoundaryFloodFillData fdata = {
.boundary = boundary,
.included_vertices = included_vertices,
- .last_visited_vertex = BOUNDARY_VERTEX_NONE,
+ .last_visited_vertex = {BOUNDARY_VERTEX_NONE},
};
@@ -278,13 +323,13 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
SCULPT_floodfill_free(&flood);
/* Check if the boundary loops into itself and add the extra preview edge to close the loop. */
- if (fdata.last_visited_vertex != BOUNDARY_VERTEX_NONE &&
+ if (fdata.last_visited_vertex.i != BOUNDARY_VERTEX_NONE &&
sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) {
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) {
- if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) &&
- sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) {
- sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index);
+ if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.vertex.i)) &&
+ sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.vertex)) {
+ sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.vertex);
boundary->forms_loop = true;
}
}
@@ -302,7 +347,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
*/
static void sculpt_boundary_edit_data_init(SculptSession *ss,
SculptBoundary *boundary,
- const int initial_vertex,
+ const SculptVertRef initial_vertex,
const float radius)
{
const int totvert = SCULPT_vertex_count_get(ss);
@@ -310,22 +355,25 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
const bool has_duplicates = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS;
boundary->edit_info = MEM_malloc_arrayN(
- totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info");
+ totvert, sizeof(SculptBoundaryEditInfo) * TSTN, "Boundary edit info");
for (int i = 0; i < totvert; i++) {
- boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE;
+ boundary->edit_info[i].original_vertex.i = BOUNDARY_VERTEX_NONE;
+ boundary->edit_info[i].original_vertex_i = BOUNDARY_VERTEX_NONE;
boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE;
}
- GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int));
- GSQueue *next_iteration = BLI_gsqueue_new(sizeof(int));
+ GSQueue *current_iteration = BLI_gsqueue_new(sizeof(SculptVertRef));
+ GSQueue *next_iteration = BLI_gsqueue_new(sizeof(SculptVertRef));
/* Initialized the first iteration with the vertices already in the boundary. This is propagation
* step 0. */
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
for (int i = 0; i < boundary->num_vertices; i++) {
- boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i];
- boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0;
+ boundary->edit_info[boundary->vertex_indices[i]].original_vertex = boundary->vertices[i];
+ boundary->edit_info[boundary->vertex_indices[i]].original_vertex_i =
+ boundary->vertex_indices[i];
+ boundary->edit_info[boundary->vertex_indices[i]].num_propagation_steps = 0;
/* This ensures that all duplicate vertices in the boundary have the same original_vertex
* index, so the deformation for them will be the same. */
@@ -333,7 +381,10 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
SculptVertexNeighborIter ni_duplis;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) {
if (ni_duplis.is_duplicate) {
- boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i];
+ int index = ni_duplis.index;
+
+ boundary->edit_info[index].original_vertex = boundary->vertices[i];
+ boundary->edit_info[index].original_vertex_i = boundary->vertex_indices[i];
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
@@ -354,31 +405,36 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
}
while (!BLI_gsqueue_is_empty(current_iteration)) {
- int from_v;
+ SculptVertRef from_v;
BLI_gsqueue_pop(current_iteration, &from_v);
+ const int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
- const bool is_visible = SCULPT_vertex_visible_get(ss, ni.index);
+ const bool is_visible = SCULPT_vertex_visible_get(ss, ni.vertex);
+
if (!is_visible ||
boundary->edit_info[ni.index].num_propagation_steps != BOUNDARY_STEPS_NONE) {
continue;
}
boundary->edit_info[ni.index].original_vertex =
- boundary->edit_info[from_v].original_vertex;
+ boundary->edit_info[from_v_i].original_vertex;
+
+ boundary->edit_info[ni.index].original_vertex_i =
+ boundary->edit_info[from_v_i].original_vertex_i;
BLI_BITMAP_ENABLE(visited_vertices, ni.index);
if (ni.is_duplicate) {
/* Grids duplicates handling. */
boundary->edit_info[ni.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps;
+ boundary->edit_info[from_v_i].num_propagation_steps;
}
else {
boundary->edit_info[ni.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps + 1;
+ boundary->edit_info[from_v_i].num_propagation_steps + 1;
- BLI_gsqueue_push(next_iteration, &ni.index);
+ BLI_gsqueue_push(next_iteration, &ni.vertex);
/* When copying the data to the neighbor for the next iteration, it has to be copied to
* all its duplicates too. This is because it is not possible to know if the updated
@@ -386,12 +442,14 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
* copy the data in the from_v neighbor iterator. */
if (has_duplicates) {
SculptVertexNeighborIter ni_duplis;
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) {
+ SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni_duplis) {
if (ni_duplis.is_duplicate) {
boundary->edit_info[ni_duplis.index].original_vertex =
- boundary->edit_info[from_v].original_vertex;
+ boundary->edit_info[from_v_i].original_vertex;
+ boundary->edit_info[ni_duplis.index].original_vertex_i =
+ boundary->edit_info[from_v_i].original_vertex_i;
boundary->edit_info[ni_duplis.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps + 1;
+ boundary->edit_info[from_v_i].num_propagation_steps + 1;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
@@ -399,11 +457,11 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* Check the distance using the vertex that was propagated from the initial vertex that
* was used to initialize the boundary. */
- if (boundary->edit_info[from_v].original_vertex == initial_vertex) {
- boundary->pivot_vertex = ni.index;
- copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index));
+ if (boundary->edit_info[from_v_i].original_vertex.i == initial_vertex.i) {
+ boundary->pivot_vertex = ni.vertex;
+ copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.vertex));
accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v),
- SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_vertex_co_get(ss, ni.vertex));
}
}
}
@@ -412,7 +470,7 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* Copy the new vertices to the queue to be processed in the next iteration. */
while (!BLI_gsqueue_is_empty(next_iteration)) {
- int next_v;
+ SculptVertRef next_v;
BLI_gsqueue_pop(next_iteration, &next_v);
BLI_gsqueue_push(current_iteration, &next_v);
}
@@ -443,7 +501,7 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps);
}
- if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) {
+ if (boundary->edit_info[i].original_vertex.i == boundary->initial_vertex.i) {
/* All vertices that are propagated from the original vertex won't be affected by the
* boundary falloff, so there is no need to calculate anything else. */
continue;
@@ -455,7 +513,8 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
continue;
}
- const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex];
+ const float boundary_distance = boundary->distance[BKE_pbvh_vertex_index_to_table(
+ ss->pbvh, boundary->edit_info[i].original_vertex)];
float falloff_distance = 0.0f;
float direction = 1.0f;
@@ -491,22 +550,27 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
* return NULL if there is no boundary from the given vertex using the given radius. */
SculptBoundary *SCULPT_boundary_data_init(Object *object,
Brush *brush,
- const int initial_vertex,
+ const SculptVertRef initial_vertex,
const float radius)
{
SculptSession *ss = object->sculpt;
- if (initial_vertex == BOUNDARY_VERTEX_NONE) {
+ if (initial_vertex.i == BOUNDARY_VERTEX_NONE) {
return NULL;
}
+ // XXX force update of BMVert->head.index
+ if (ss->bm) {
+ ss->bm->elem_index_dirty |= BM_VERT;
+ }
+
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(object);
- const int boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex(
- ss, initial_vertex, radius);
+ const SculptVertRef boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex(
+ ss, initial_vertex, BKE_pbvh_vertex_index_to_table(ss->pbvh, initial_vertex), radius);
- if (boundary_initial_vertex == BOUNDARY_VERTEX_NONE) {
+ if (boundary_initial_vertex.i == BOUNDARY_VERTEX_NONE) {
return NULL;
}
@@ -516,7 +580,7 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object,
return NULL;
}
- SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data");
+ SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary) * TSTN, "Boundary edit data");
const bool init_boundary_distances = brush ? brush->boundary_falloff_type !=
BRUSH_BOUNDARY_FALLOFF_CONSTANT :
@@ -532,9 +596,12 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object,
void SCULPT_boundary_data_free(SculptBoundary *boundary)
{
+ printf(" ======================= boundary free!\n\n");
+
MEM_SAFE_FREE(boundary->vertices);
MEM_SAFE_FREE(boundary->edges);
MEM_SAFE_FREE(boundary->distance);
+
MEM_SAFE_FREE(boundary->edit_info);
MEM_SAFE_FREE(boundary->bend.pivot_positions);
MEM_SAFE_FREE(boundary->bend.pivot_rotation_axis);
@@ -550,51 +617,187 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo
{
const int totvert = SCULPT_vertex_count_get(ss);
boundary->bend.pivot_rotation_axis = MEM_calloc_arrayN(
- totvert, 3 * sizeof(float), "pivot rotation axis");
+ totvert, 3 * sizeof(float) * TSTN, "pivot rotation axis");
boundary->bend.pivot_positions = MEM_calloc_arrayN(
- totvert, 3 * sizeof(float), "pivot positions");
+ totvert, 4 * sizeof(float) * TSTN, "pivot positions");
- for (int i = 0; i < totvert; i++) {
- if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
- continue;
+ for (int step = 0; step < 1; step++) {
+ bool ok = true;
+
+ for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ bool bad = boundary->edit_info[i].num_propagation_steps !=
+ boundary->max_propagation_steps - step;
+ if (step == 2) {
+ bad = false;
+ }
+
+ bad = bad || boundary->edit_info[i].original_vertex_i == BOUNDARY_VERTEX_NONE;
+ bad = bad ||
+ boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] != 0.0f;
+
+ if (bad && (step != 3 || boundary->edit_info[i].original_vertex_i == i)) {
+ continue;
+ }
+
+ float dir[3];
+ float normal[3];
+ SCULPT_vertex_normal_get(ss, vertex, normal);
+ sub_v3_v3v3(dir,
+ SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
+ SCULPT_vertex_co_get(ss, vertex));
+
+ cross_v3_v3v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i],
+ dir,
+ normal);
+ normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]);
+ copy_v3_v3(boundary->bend.pivot_positions[i], SCULPT_vertex_co_get(ss, vertex));
+
+ add_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i],
+ SCULPT_vertex_co_get(ss, vertex));
+ boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] += 1.0f;
+ }
+
+ for (int i = 0; i < totvert; i++) {
+ if (boundary->edit_info[i].num_propagation_steps == 0 &&
+ boundary->bend.pivot_positions[i][3] == 0.0f) {
+
+ printf("eek!\n");
+ SculptVertexNeighborIter ni;
+ SculptVertexNeighborIter ni2;
+ const int maxstack = 32;
+ SculptVertRef stack[32];
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ const float *co1 = SCULPT_vertex_co_get(ss, vertex);
+ int si = 0;
+ stack[si++] = vertex;
+
+ float mindis = 1e17;
+ float pivota[3], pivotb[3];
+ float rota[3], rotb[3];
+ SculptVertRef v_a = {-1}, v_b = {-1};
+
+ copy_v3_v3(pivota, co1);
+ copy_v3_v3(pivotb, co1);
+ zero_v3(rota);
+ zero_v3(rotb);
+
+ float mindis2 = 1e17;
+
+ // do a short walk on mesh to find pivot
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni2) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.vertex, ni) {
+ SculptVertRef v2 = ni.vertex;
+
+ bool ok = ni.vertex.i != vertex.i;
+ ok = ok && boundary->edit_info[ni.index].num_propagation_steps == 0;
+ ok = ok && boundary->bend.pivot_positions[ni.index][3] > 0.0f;
+
+ if (ok) {
+ const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex);
+ float dis = len_squared_v3v3(co1, co2);
+
+ if (dis < mindis) {
+ copy_v3_v3(pivotb, pivota);
+ copy_v3_v3(rotb, rota);
+
+ v_b = v_a;
+ mindis2 = mindis;
+
+ copy_v3_v3(pivota, boundary->bend.pivot_positions[ni.index]);
+ copy_v3_v3(rota, boundary->bend.pivot_rotation_axis[ni.index]);
+
+ v_a = ni.vertex;
+ mindis = dis;
+ }
+ else if (dis < mindis2) {
+ v_b = ni.vertex;
+ copy_v3_v3(pivotb, boundary->bend.pivot_positions[ni.index]);
+ copy_v3_v3(rotb, boundary->bend.pivot_rotation_axis[ni.index]);
+ mindis2 = dis;
+ }
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni2);
+
+ float len = len_v3v3(pivota, pivotb);
+ if (len > 0.0) {
+ float vec1[3], vec2[3];
+
+ sub_v3_v3v3(vec1, pivotb, pivota);
+ normalize_v3(vec1);
+
+ sub_v3_v3v3(vec2, co1, pivota);
+
+ float t = dot_v3v3(vec2, vec1) / len;
+
+ interp_v3_v3v3(pivota, pivota, pivotb, t);
+ interp_v3_v3v3(rota, rota, rotb, t);
+
+ copy_v3_v3(boundary->bend.pivot_positions[i], pivota);
+ boundary->bend.pivot_positions[i][3] = 1.0f;
+ copy_v3_v3(boundary->bend.pivot_rotation_axis[i], rota);
+ }
+ }
+
+ if (boundary->bend.pivot_positions[i][3] > 0.0f) {
+ float mul = 1.0f / boundary->bend.pivot_positions[i][3];
+ boundary->bend.pivot_positions[i][0] *= mul;
+ boundary->bend.pivot_positions[i][1] *= mul;
+ boundary->bend.pivot_positions[i][2] *= mul;
+ boundary->bend.pivot_positions[i][3] = 1.0f;
+ }
+ else {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ const float *co = SCULPT_vertex_co_get(ss, vertex);
+
+ // boundary->bend.pivot_positions[i][0] = co[0];
+ // boundary->bend.pivot_positions[i][1] = co[1];
+ // boundary->bend.pivot_positions[i][2] = co[2];
+ // boundary->bend.pivot_positions[i][3] = 0.0f;
+ }
+ }
+
+ if (ok) {
+ break;
}
- float dir[3];
- float normal[3];
- SCULPT_vertex_normal_get(ss, i, normal);
- sub_v3_v3v3(dir,
- SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
- SCULPT_vertex_co_get(ss, i));
- cross_v3_v3v3(
- boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex], dir, normal);
- normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
- copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex],
- SCULPT_vertex_co_get(ss, i));
}
for (int i = 0; i < totvert; i++) {
if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) {
continue;
}
+
copy_v3_v3(boundary->bend.pivot_positions[i],
- boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]);
+ boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]);
+
+ boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] = 1.0f;
+
copy_v3_v3(boundary->bend.pivot_rotation_axis[i],
- boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
+ boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]);
}
}
static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary)
{
const int totvert = SCULPT_vertex_count_get(ss);
- boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions");
+ boundary->slide.directions = MEM_calloc_arrayN(
+ totvert, 3 * sizeof(float) * TSTN, "slide directions");
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
continue;
}
- sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex],
+
+ sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i],
SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
- SCULPT_vertex_co_get(ss, i));
- normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]);
+ SCULPT_vertex_co_get(ss, vertex));
+ normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i]);
}
for (int i = 0; i < totvert; i++) {
@@ -602,7 +805,7 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b
continue;
}
copy_v3_v3(boundary->slide.directions[i],
- boundary->slide.directions[boundary->edit_info[i].original_vertex]);
+ boundary->slide.directions[boundary->edit_info[i].original_vertex_i]);
}
}
@@ -610,7 +813,7 @@ static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *b
{
zero_v3(boundary->twist.pivot_position);
float(*poly_verts)[3] = MEM_malloc_arrayN(
- boundary->num_vertices, sizeof(float) * 3, "poly verts");
+ boundary->num_vertices, sizeof(float) * 3 * TSTN, "poly verts");
for (int i = 0; i < boundary->num_vertices; i++) {
add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i]));
copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i]));
@@ -657,7 +860,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
@@ -679,9 +882,10 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float t_orig_co[3];
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
+
sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]);
rotate_v3_v3v3fl(target_co,
t_orig_co,
@@ -711,7 +915,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
@@ -727,7 +931,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@@ -757,7 +961,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
@@ -773,7 +977,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float normal[3];
normal_short_to_float_v3(normal, orig_data.no);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
@@ -805,7 +1009,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
@@ -819,7 +1023,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@@ -848,7 +1052,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
@@ -870,7 +1074,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float t_orig_co[3];
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position);
@@ -902,7 +1106,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
@@ -919,9 +1123,9 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
int total_neighbors = 0;
const int current_propagation_steps = boundary->edit_info[vd.index].num_propagation_steps;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
if (current_propagation_steps == boundary->edit_info[ni.index].num_propagation_steps) {
- add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.index));
+ add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.vertex));
total_neighbors++;
}
}
@@ -955,7 +1159,8 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn
const int symm_area = ss->cache->mirror_symmetry_pass;
if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
- int initial_vertex;
+ SculptVertRef initial_vertex;
+
if (ss->cache->mirror_symmetry_pass == 0) {
initial_vertex = SCULPT_active_vertex_get(ss);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index a53a2126af4..a6d23131fdb 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -219,26 +219,32 @@ static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim)
static void cloth_brush_add_length_constraint(SculptSession *ss,
SculptClothSimulation *cloth_sim,
const int node_index,
- const int v1,
- const int v2,
+ const int v1i,
+ const int v2i,
const bool use_persistent)
{
SculptClothLengthConstraint *length_constraint =
&cloth_sim->length_constraints[cloth_sim->tot_length_constraints];
- length_constraint->elem_index_a = v1;
- length_constraint->elem_index_b = v2;
+ SculptVertRef v1, v2;
+
+ v1 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v1i);
+ v2 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v2i);
+
+ length_constraint->elem_index_a = v1i;
+ length_constraint->elem_index_b = v2i;
length_constraint->node = node_index;
- length_constraint->elem_position_a = cloth_sim->pos[v1];
- length_constraint->elem_position_b = cloth_sim->pos[v2];
+ length_constraint->elem_position_a = cloth_sim->pos[v1i];
+ length_constraint->elem_position_b = cloth_sim->pos[v2i];
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL;
if (use_persistent) {
- length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1),
- SCULPT_vertex_persistent_co_get(ss, v2));
+ length_constraint->length = len_v3v3(
+ SCULPT_vertex_persistent_co_get(ss, v1, cloth_sim->cd_pers_co),
+ SCULPT_vertex_persistent_co_get(ss, v2, cloth_sim->cd_pers_no));
}
else {
length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1),
@@ -252,7 +258,7 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
cloth_brush_reallocate_constraints(cloth_sim);
/* Add the constraint to the #GSet to avoid creating it again. */
- BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2);
+ BLI_edgeset_add(cloth_sim->created_length_constraints, v1i, v2i);
}
static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim,
@@ -386,7 +392,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
int tot_indices = 0;
build_indices[tot_indices] = vd.index;
tot_indices++;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
build_indices[tot_indices] = ni.index;
tot_indices++;
}
@@ -556,7 +562,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float brush_disp[3];
@@ -803,7 +809,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor);
const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) *
- SCULPT_automasking_factor_get(automasking, ss, vd.index);
+ SCULPT_automasking_factor_get(automasking, ss, vd.vertex);
madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v);
madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v);
@@ -849,6 +855,9 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
const int v1 = constraint->elem_index_a;
const int v2 = constraint->elem_index_b;
+ const SculptVertRef v1ref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v1);
+ const SculptVertRef v2ref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v2);
+
float v1_to_v2[3];
sub_v3_v3v3(v1_to_v2, constraint->elem_position_b, constraint->elem_position_a);
const float current_distance = len_v3(v1_to_v2);
@@ -871,10 +880,10 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f);
- const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) *
- SCULPT_automasking_factor_get(automasking, ss, v1);
- const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) *
- SCULPT_automasking_factor_get(automasking, ss, v2);
+ const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1ref)) *
+ SCULPT_automasking_factor_get(automasking, ss, v1ref);
+ const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2ref)) *
+ SCULPT_automasking_factor_get(automasking, ss, v2ref);
float sim_location[3];
cloth_brush_simulation_location_get(ss, brush, sim_location);
@@ -1065,6 +1074,13 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints");
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ cloth_sim->cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ cloth_sim->cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ cloth_sim->cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+ }
+
+ //cloth_sim->cd_pers_co = SCULPT_dyntopo_get_templayer
cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) *
CLOTH_LENGTH_CONSTRAINTS_BLOCK,
"cloth length constraints");
@@ -1147,16 +1163,20 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation
const int totverts = SCULPT_vertex_count_get(ss);
const bool has_deformation_pos = cloth_sim->deformation_pos != NULL;
const bool has_softbody_pos = cloth_sim->softbody_pos != NULL;
+ SCULPT_vertex_random_access_ensure(ss);
+
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, vertex));
+ copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, vertex));
+ copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, vertex));
if (has_deformation_pos) {
- copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, vertex));
cloth_sim->deformation_strength[i] = 1.0f;
}
if (has_softbody_pos) {
- copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, vertex));
}
}
}
@@ -1165,7 +1185,9 @@ void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSim
{
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -1448,13 +1470,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
float fade = vd.mask ? *vd.mask : 0.0f;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
fade = 1.0f - fade;
float force[3] = {0.0f, 0.0f, 0.0f};
float disp[3], temp[3], transform[3][3];
if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) {
- if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) {
+ if (!SCULPT_vertex_has_face_set(ss, vd.vertex, ss->filter_cache->active_face_set)) {
continue;
}
}
@@ -1473,7 +1495,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
break;
case CLOTH_FILTER_INFLATE: {
float normal[3];
- SCULPT_vertex_normal_get(ss, vd.index, normal);
+ SCULPT_vertex_normal_get(ss, vd.vertex, normal);
mul_v3_v3fl(force, normal, fade * data->filter_strength);
} break;
case CLOTH_FILTER_EXPAND:
@@ -1538,7 +1560,9 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex));
}
SculptThreadedTaskData data = {
diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.c b/source/blender/editors/sculpt_paint/sculpt_curvature.c
new file mode 100644
index 00000000000..a23afb93407
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_curvature.c
@@ -0,0 +1,239 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 by Joseph Eagar
+ * All rights reserved.
+ * Implements curvature analysis for sculpt tools
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_dial_2d.h"
+#include "BLI_ghash.h"
+#include "BLI_gsqueue.h"
+#include "BLI_hash.h"
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_math_solvers.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "PIL_time.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_ccg.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_image.h"
+#include "BKE_kelvinlet.h"
+#include "BKE_key.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_mesh_mirror.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_node.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_particle.h"
+#include "BKE_pbvh.h"
+#include "BKE_pointcache.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_subdiv_ccg.h"
+#include "BKE_subsurf.h"
+
+#include "DEG_depsgraph.h"
+
+#include "IMB_colormanagement.h"
+
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_matrix.h"
+#include "GPU_state.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "ED_space_api.h"
+#include "ED_view3d.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+If you're working with uniform triangle tesselations, the math for
+calculating principle curvatures reduces to doing an eigen decomposition
+of the smoothed normal covariance matrix.
+
+The normal covariance matrix is just:
+
+nx*nx nx*ny nx*nz
+ny*nx ny*ny ny*nz
+nz*nx nz*ny nz*nz
+
+To find principle curvatures, simply subtract neighboring covariance matrices.
+You can do this over any number of neighborhood rings to get more accurate result
+
+*/
+
+BLI_INLINE void normal_covariance(float mat[3][3], float no[3])
+{
+ mat[0][0] = no[0] * no[0];
+ mat[0][1] = no[0] * no[1];
+ mat[0][2] = no[0] * no[2];
+ mat[1][0] = no[1] * no[0];
+ mat[1][1] = no[1] * no[1];
+ mat[1][2] = no[1] * no[2];
+ mat[2][0] = no[2] * no[0];
+ mat[2][1] = no[2] * no[1];
+ mat[2][2] = no[2] * no[2];
+}
+
+bool SCULPT_calc_principle_curvatures(SculptSession *ss,
+ SculptVertRef vertex,
+ SculptCurvatureData *out,
+ bool useAccurateSolver)
+{
+ SculptVertexNeighborIter ni;
+ float nmat[3][3], nmat2[3][3];
+ float no[3], no2[3];
+
+ memset(out, 0, sizeof(SculptCurvatureData));
+
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ normal_covariance(nmat, no);
+
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ SCULPT_vertex_normal_get(ss, ni.vertex, no2);
+ sub_v3_v3(no2, no);
+
+ normal_covariance(nmat2, no2);
+
+ add_m3_m3m3(nmat, nmat, nmat2);
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (!useAccurateSolver || !BLI_eigen_solve_selfadjoint_m3(nmat, out->ks, out->principle)) {
+ // do simple power solve in one direction
+
+ float t[3];
+ float t2[3];
+
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ copy_v3_v3(t, no);
+
+ for (int i = 0; i < 15; i++) {
+ if (i > 0) {
+ normalize_v3(t);
+
+ if (i > 1 && len_squared_v3v3(t, t2) < 0.0001) {
+ break;
+ }
+
+ copy_v3_v3(t2, t);
+ }
+
+ mul_m3_v3(nmat, t);
+ }
+
+ out->ks[1] = normalize_v3(t);
+ copy_v3_v3(out->principle[1], t);
+
+ cross_v3_v3v3(out->principle[0], out->principle[1], no);
+ normalize_v3(out->principle[0]);
+ }
+
+ return true;
+}
+
+void SCULPT_curvature_dir_get(SculptSession *ss,
+ SculptVertRef v,
+ float dir[3],
+ bool useAccurateSolver)
+{
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
+ SculptCurvatureData curv;
+ SCULPT_calc_principle_curvatures(ss, v, &curv, useAccurateSolver);
+
+ copy_v3_v3(dir, curv.principle[0]);
+ return;
+ }
+
+ BMVert *bv = (BMVert *)v.i;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, bv);
+
+ copy_v3_v3(dir, mv->curvature_dir);
+}
+
+void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver)
+{
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
+ // caching only happens for bmesh for now
+ return;
+ }
+
+ if (BKE_pbvh_curvature_update_get(node)) {
+ PBVHVertexIter vi;
+
+ BKE_pbvh_curvature_update_set(node, false);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vi, PBVH_ITER_UNIQUE) {
+ BMVert *v = (BMVert *)vi.vertex.i;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ SculptCurvatureData curv;
+ SCULPT_calc_principle_curvatures(ss, vi.vertex, &curv, useAccurateSolver);
+
+ copy_v3_v3(mv->curvature_dir, curv.principle[0]);
+ }
+ BKE_pbvh_vertex_iter_end;
+ }
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index 188bb0a88eb..f94cdccae52 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -64,6 +64,7 @@ typedef struct {
float edge_length;
struct IsectRayPrecalc isect_precalc;
+ SculptSession *ss;
} SculptDetailRaycastData;
static bool sculpt_and_constant_or_manual_detail_poll(bContext *C)
@@ -110,18 +111,35 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op))
/* Update topology size. */
float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat));
- BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail);
+ BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, sd->detail_range);
SCULPT_undo_push_begin(ob, "Dynamic topology flood fill");
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS);
- while (BKE_pbvh_bmesh_update_topology(
- ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, size, false, false)) {
+ DyntopoMaskCB mask_cb;
+ void *mask_cb_data;
+
+ SCULPT_dyntopo_automasking_init(ss, NULL, sd, &mask_cb, &mask_cb_data);
+
+ while (BKE_pbvh_bmesh_update_topology(ss->pbvh,
+ PBVH_Collapse | PBVH_Subdivide,
+ center,
+ NULL,
+ size,
+ false,
+ false,
+ -1,
+ false,
+ mask_cb,
+ mask_cb_data)) {
+
for (int i = 0; i < totnodes; i++) {
BKE_pbvh_node_mark_topology_update(nodes[i]);
}
}
+ SCULPT_dyntopo_automasking_end(mask_cb_data);
+
MEM_SAFE_FREE(nodes);
SCULPT_undo_push_end();
@@ -174,13 +192,13 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my)
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
/* Average the edge length of the connected edges to the active vertex. */
- int active_vertex = SCULPT_active_vertex_get(ss);
+ SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
const float *active_vertex_co = SCULPT_active_vertex_co_get(ss);
float edge_length = 0.0f;
int tot = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
- edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.index));
+ edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.vertex));
tot += 1;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -193,8 +211,13 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin)
{
if (BKE_pbvh_node_get_tmin(node) < *tmin) {
SculptDetailRaycastData *srd = data_v;
- if (BKE_pbvh_bmesh_node_raycast_detail(
- node, srd->ray_start, &srd->isect_precalc, &srd->depth, &srd->edge_length)) {
+
+ if (BKE_pbvh_bmesh_node_raycast_detail(srd->ss->pbvh,
+ node,
+ srd->ray_start,
+ &srd->isect_precalc,
+ &srd->depth,
+ &srd->edge_length)) {
srd->hit = true;
*tmin = srd->depth;
}
@@ -215,12 +238,20 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region,
SculptDetailRaycastData srd;
srd.hit = 0;
+ srd.ss = ob->sculpt;
+
srd.ray_start = ray_start;
srd.depth = depth;
srd.edge_length = 0.0f;
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
- BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, ray_start, ray_normal, false);
+ BKE_pbvh_raycast(ob->sculpt->pbvh,
+ sculpt_raycast_detail_cb,
+ &srd,
+ ray_start,
+ ray_normal,
+ false,
+ srd.ss->stroke_id);
if (srd.hit && srd.edge_length > 0.0f) {
/* Convert edge length to world space detail resolution. */
@@ -569,14 +600,14 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob,
DyntopoDetailSizeEditCustomData *cd)
{
SculptSession *ss = ob->sculpt;
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
float len_accum = 0;
int num_neighbors = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex),
- SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_vertex_co_get(ss, ni.vertex));
num_neighbors++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
index 87e0ea7f6a9..5461d74cb47 100644
--- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
@@ -23,9 +23,15 @@
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
+#include "BLI_array.h"
#include "BLI_blenlib.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_hash.h"
+#include "BLI_linklist.h"
#include "BLI_math.h"
+#include "BLI_memarena.h"
+#include "BLI_polyfill_2d.h"
#include "BLI_task.h"
#include "BLT_translation.h"
@@ -35,6 +41,7 @@
#include "DNA_modifier_types.h"
#include "BKE_brush.h"
+#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
@@ -76,12 +83,126 @@
#include <math.h>
#include <stdlib.h>
-void SCULPT_dynamic_topology_triangulate(BMesh *bm)
+/*
+Copies the bmesh, but orders the elements
+according to PBVH node to improve memory locality
+*/
+void SCULPT_reorder_bmesh(SculptSession *ss)
{
- if (bm->totloop != bm->totface * 3) {
- BM_mesh_triangulate(
- bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL);
+#if 0
+ SCULPT_face_random_access_ensure(ss);
+ SCULPT_vertex_random_access_ensure(ss);
+
+ int actv = ss->active_vertex_index.i ?
+ BKE_pbvh_vertex_index_to_table(ss->pbvh, ss->active_vertex_index) :
+ -1;
+ int actf = ss->active_face_index.i ?
+ BKE_pbvh_face_index_to_table(ss->pbvh, ss->active_face_index) :
+ -1;
+
+ if (ss->bm_log) {
+ BM_log_full_mesh(ss->bm, ss->bm_log);
+ }
+
+ ss->bm = BKE_pbvh_reorder_bmesh(ss->pbvh);
+
+ SCULPT_face_random_access_ensure(ss);
+ SCULPT_vertex_random_access_ensure(ss);
+
+ if (actv >= 0) {
+ ss->active_vertex_index = BKE_pbvh_table_index_to_vertex(ss->pbvh, actv);
+ }
+ if (actf >= 0) {
+ ss->active_face_index = BKE_pbvh_table_index_to_face(ss->pbvh, actf);
+ }
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ if (ss->bm_log) {
+ BM_log_set_bm(ss->bm, ss->bm_log);
+ }
+#endif
+}
+
+void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm)
+{
+ if (bm->totloop == bm->totface * 3) {
+ ss->totfaces = ss->totpoly = ss->bm->totface;
+ ss->totvert = ss->bm->totvert;
+
+ return;
+ }
+
+ BMIter iter;
+ BMFace *f;
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BM_elem_flag_enable(f, BM_ELEM_TAG);
+ }
+
+ MemArena *pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__);
+ LinkNode *f_double = NULL;
+
+ BMFace **faces_array = NULL;
+ BLI_array_declare(faces_array);
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (f->len <= 3) {
+ continue;
+ }
+
+ bool sel = BM_elem_flag_test(f, BM_ELEM_SELECT);
+
+ int faces_array_tot = f->len;
+ BLI_array_clear(faces_array);
+ BLI_array_grow_items(faces_array, faces_array_tot);
+ // BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot);
+
+ BM_face_triangulate(bm,
+ f,
+ faces_array,
+ &faces_array_tot,
+ NULL,
+ NULL,
+ &f_double,
+ MOD_TRIANGULATE_QUAD_BEAUTY,
+ MOD_TRIANGULATE_NGON_EARCLIP,
+ true,
+ pf_arena,
+ NULL);
+
+ for (int i = 0; i < faces_array_tot; i++) {
+ BMFace *f2 = faces_array[i];
+
+ // forcibly copy selection state
+ if (sel) {
+ BM_face_select_set(bm, f2, true);
+
+ // restore original face selection state too, triangulate code unset it
+ BM_face_select_set(bm, f, true);
+ }
+
+ // paranoia check that tag flag wasn't copied over
+ BM_elem_flag_disable(f2, BM_ELEM_TAG);
+ }
}
+
+ while (f_double) {
+ LinkNode *next = f_double->next;
+ BM_face_kill(bm, f_double->link);
+ MEM_freeN(f_double);
+ f_double = next;
+ }
+
+ BLI_memarena_free(pf_arena);
+ MEM_SAFE_FREE(faces_array);
+
+ ss->totfaces = ss->totpoly = ss->bm->totface;
+ ss->totvert = ss->bm->totvert;
+
+ // BM_mesh_triangulate(
+ // bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL,
+ // NULL);
}
void SCULPT_pbvh_clear(Object *ob)
@@ -110,19 +231,99 @@ void SCULPT_pbvh_clear(Object *ob)
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
+void SCULPT_dyntopo_save_origverts(SculptSession *ss)
+{
+ BMIter iter;
+ BMVert *v;
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ copy_v3_v3(mv->origco, v->co);
+ copy_v3_v3(mv->origno, v->no);
+
+ if (ss->cd_vcol_offset >= 0) {
+ MPropCol *mp = (MPropCol *)BM_ELEM_CD_GET_VOID_P(v, ss->cd_vcol_offset);
+ copy_v4_v4(mv->origcolor, mp->color);
+ }
+ }
+}
+
+char dyntopop_node_idx_layer_id[] = "_dyntopo_node_id";
+
+void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss)
+{
+ SCULPT_dyntopo_node_layers_add(ss);
+ if (ss->pbvh) {
+ BKE_pbvh_update_offsets(
+ ss->pbvh, ss->cd_vert_node_offset, ss->cd_face_node_offset, ss->cd_dyn_vert);
+ }
+ if (ss->bm_log) {
+ BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
+ }
+}
+
+bool SCULPT_dyntopo_has_templayer(SculptSession *ss, int type, const char *name)
+{
+ return CustomData_get_named_layer_index(&ss->bm->vdata, type, name) >= 0;
+}
+
+void SCULPT_dyntopo_ensure_templayer(SculptSession *ss, int type, const char *name)
+{
+ int li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name);
+
+ if (li < 0) {
+ BM_data_layer_add_named(ss->bm, &ss->bm->vdata, type, name);
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name);
+ ss->bm->vdata.layers[li].flag |= CD_FLAG_TEMPORARY;
+ }
+}
+
+int SCULPT_dyntopo_get_templayer(SculptSession *ss, int type, const char *name)
+{
+ int li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name);
+
+ if (li < 0) {
+ return -1;
+ }
+
+ return CustomData_get_n_offset(
+ &ss->bm->vdata, type, li - CustomData_get_layer_index(&ss->bm->vdata, type));
+}
+
void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
{
- int cd_node_layer_index;
+ int cd_node_layer_index, cd_face_node_layer_index;
+
+ int cd_origco_index, cd_origno_index, cd_origvcol_index = -1;
+ bool have_vcol = CustomData_has_layer(&ss->bm->vdata, CD_PROP_COLOR);
- char layer_id[] = "_dyntopo_node_id";
+ BMCustomLayerReq vlayers[] = {{CD_PAINT_MASK, NULL, 0},
+ {CD_DYNTOPO_VERT, NULL, CD_FLAG_TEMPORARY},
+ {CD_PROP_INT32, dyntopop_node_idx_layer_id, CD_FLAG_TEMPORARY}};
- cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT32, layer_id);
- if (cd_node_layer_index == -1) {
- BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, layer_id);
- cd_node_layer_index = CustomData_get_named_layer_index(
- &ss->bm->vdata, CD_PROP_INT32, layer_id);
+ BM_data_layers_ensure(ss->bm, &ss->bm->vdata, vlayers, 3);
+
+ cd_face_node_layer_index = CustomData_get_named_layer_index(
+ &ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id);
+ if (cd_face_node_layer_index == -1) {
+ BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id);
}
+ // get indices again, as they might have changed after adding new layers
+ cd_node_layer_index = CustomData_get_named_layer_index(
+ &ss->bm->vdata, CD_PROP_INT32, dyntopop_node_idx_layer_id);
+ cd_face_node_layer_index = CustomData_get_named_layer_index(
+ &ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id);
+
+ ss->cd_origvcol_offset = -1;
+
+ ss->cd_dyn_vert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT);
+
+ ss->cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR);
+
ss->cd_vert_node_offset = CustomData_get_n_offset(
&ss->bm->vdata,
CD_PROP_INT32,
@@ -130,21 +331,143 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
- cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT32, layer_id);
- if (cd_node_layer_index == -1) {
- BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, layer_id);
- cd_node_layer_index = CustomData_get_named_layer_index(
- &ss->bm->pdata, CD_PROP_INT32, layer_id);
- }
-
ss->cd_face_node_offset = CustomData_get_n_offset(
&ss->bm->pdata,
CD_PROP_INT32,
- cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32));
+ cd_face_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32));
- ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
+ ss->bm->pdata.layers[cd_face_node_layer_index].flag |= CD_FLAG_TEMPORARY;
+ ss->cd_faceset_offset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS);
}
+/**
+ Syncs customdata layers with internal bmesh, but ignores deleted layers.
+*/
+void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me)
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss || !ss->bm) {
+ return;
+ }
+
+ bool modified = false;
+ BMesh *bm = ss->bm;
+
+ CustomData *cd1[4] = {&me->vdata, &me->edata, &me->ldata, &me->pdata};
+ CustomData *cd2[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata};
+ int types[4] = {BM_VERT, BM_EDGE, BM_LOOP, BM_FACE};
+ int badmask = CD_MASK_MLOOP | CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MPOLY | CD_MASK_ORIGINDEX |
+ CD_MASK_ORIGSPACE | CD_MASK_MFACE;
+
+ for (int i = 0; i < 4; i++) {
+ CustomDataLayer **newlayers = NULL;
+ BLI_array_declare(newlayers);
+
+ CustomData *data1 = cd1[i];
+ CustomData *data2 = cd2[i];
+
+ if (!data1->layers) {
+ modified |= data2->layers != NULL;
+ continue;
+ }
+
+ for (int j = 0; j < data1->totlayer; j++) {
+ CustomDataLayer *cl1 = data1->layers + j;
+
+ if ((1 << cl1->type) & badmask) {
+ continue;
+ }
+
+ int idx = CustomData_get_named_layer_index(data2, cl1->type, cl1->name);
+ if (idx < 0) {
+ BLI_array_append(newlayers, cl1);
+ }
+ }
+
+ for (int j = 0; j < BLI_array_len(newlayers); j++) {
+ BM_data_layer_add_named(bm, data2, newlayers[j]->type, newlayers[j]->name);
+ modified = true;
+ }
+
+ bool typemap[CD_NUMTYPES] = {0};
+
+ for (int j = 0; j < data1->totlayer; j++) {
+ CustomDataLayer *cl1 = data1->layers + j;
+
+ if ((1 << cl1->type) & badmask) {
+ continue;
+ }
+
+ if (typemap[cl1->type]) {
+ continue;
+ }
+
+ typemap[cl1->type] = true;
+
+ // find first layer
+ int baseidx = CustomData_get_layer_index(data2, cl1->type);
+
+ if (baseidx < 0) {
+ modified |= true;
+ continue;
+ }
+
+ CustomDataLayer *cl2 = data2->layers + baseidx;
+
+ int idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active].name);
+ if (idx >= 0) {
+ modified |= idx - baseidx != cl2->active;
+ cl2->active = idx - baseidx;
+ }
+
+ idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_rnd].name);
+ if (idx >= 0) {
+ modified |= idx - baseidx != cl2->active_rnd;
+ cl2->active_rnd = idx - baseidx;
+ }
+
+ idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_mask].name);
+ if (idx >= 0) {
+ modified |= idx - baseidx != cl2->active_mask;
+ cl2->active_mask = idx - baseidx;
+ }
+
+ idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_clone].name);
+ if (idx >= 0) {
+ modified |= idx - baseidx != cl2->active_clone;
+ cl2->active_clone = idx - baseidx;
+ }
+
+ for (int k = baseidx; k < data2->totlayer; k++) {
+ CustomDataLayer *cl3 = data2->layers + k;
+
+ if (cl3->type != cl2->type) {
+ break;
+ }
+
+ // based off of how CustomData_set_layer_XXXX_index works
+
+ cl3->active = (cl2->active + baseidx) - k;
+ cl3->active_rnd = (cl2->active_rnd + baseidx) - k;
+ cl3->active_mask = (cl2->active_mask + baseidx) - k;
+ cl3->active_clone = (cl2->active_clone + baseidx) - k;
+ }
+ }
+
+ BLI_array_free(newlayers);
+ }
+
+ if (modified) {
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ }
+}
+
+BMesh *BM_mesh_bm_from_me_threaded(BMesh *bm,
+ Object *ob,
+ const Mesh *me,
+ const struct BMeshFromMeshParams *params);
+
void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
{
SculptSession *ss = ob->sculpt;
@@ -160,21 +483,101 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
BKE_mesh_mselect_clear(me);
/* Create triangles-only BMesh. */
+#if 1
ss->bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
+ &((struct BMeshCreateParams){.use_toolflags = false,
+ .use_unique_ids = true,
+ .use_id_elem_mask = BM_VERT | BM_FACE,
+ .use_id_map = true}));
- BM_mesh_bm_from_me(ss->bm,
+ BM_mesh_bm_from_me(NULL,
+ ss->bm,
me,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
.use_shapekey = true,
.active_shapekey = ob->shapenr,
}));
- SCULPT_dynamic_topology_triangulate(ss->bm);
- BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK);
+#else
+ ss->bm = BM_mesh_bm_from_me_threaded(NULL,
+ NULL,
+ me,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ .use_shapekey = true,
+ .active_shapekey = ob->shapenr,
+ }));
+#endif
+ SCULPT_dynamic_topology_triangulate(ss, ss->bm);
+
SCULPT_dyntopo_node_layers_add(ss);
+ SCULPT_dyntopo_save_origverts(ss);
+
+ BMIter iter;
+ BMVert *v;
+ int cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR);
+
+ int cd_pers_co = -1, cd_pers_no = -1, cd_pers_disp = -1;
+ int cd_layer_disp = -1;
+
+ // convert layer brush data
+ if (ss->persistent_base) {
+ BMCustomLayerReq layers[] = {{CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO, CD_FLAG_TEMPORARY},
+ {CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO, CD_FLAG_TEMPORARY},
+ {CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP, CD_FLAG_TEMPORARY},
+ {CD_PROP_FLOAT, SCULPT_LAYER_DISP, CD_FLAG_TEMPORARY}};
+
+ BM_data_layers_ensure(ss->bm, &ss->bm->vdata, layers, 4);
+
+ cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+ cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP);
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR);
+ }
+ else {
+ cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+ }
+
+ int i = 0;
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ if (BM_vert_is_boundary(v)) {
+ mv->flag |= DYNVERT_BOUNDARY;
+ }
+
+ // persistent base
+ if (cd_pers_co >= 0) {
+ float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co);
+ float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no);
+ float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp);
+
+ copy_v3_v3(co, ss->persistent_base[i].co);
+ copy_v3_v3(no, ss->persistent_base[i].no);
+ *disp = ss->persistent_base[i].disp;
+ }
+
+ if (cd_layer_disp >= 0) {
+ float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_layer_disp);
+ *disp = 0.0f;
+ }
+
+ copy_v3_v3(mv->origco, v->co);
+ copy_v3_v3(mv->origno, v->no);
+
+ if (ss->cd_vcol_offset >= 0) {
+ MPropCol *color = (MPropCol *)BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset);
+ copy_v4_v4(mv->origcolor, color->color);
+ }
+
+ i++;
+ }
+
/* Make sure the data for existing faces are initialized. */
if (me->totpoly != ss->bm->totface) {
BM_mesh_normals_update(ss->bm);
@@ -184,14 +587,44 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
/* Enable logging for undo/redo. */
- ss->bm_log = BM_log_create(ss->bm);
+ ss->bm_log = BM_log_create(ss->bm, ss->cd_dyn_vert);
/* Update dependency graph, so modifiers that depend on dyntopo being enabled
* are re-evaluated and the PBVH is re-created. */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+
+ // TODO: this line here is being slow, do we need it? - joeedh
BKE_scene_graph_update_tagged(depsgraph, bmain);
}
+void SCULPT_dyntopo_save_persistent_base(SculptSession *ss)
+{
+ int cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ int cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ int cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ if (cd_pers_co >= 0) {
+ BMIter iter;
+
+ MEM_SAFE_FREE(ss->persistent_base);
+ ss->persistent_base = MEM_callocN(sizeof(*ss->persistent_base) * ss->bm->totvert,
+ "ss->persistent_base");
+ BMVert *v;
+ int i = 0;
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co);
+ float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no);
+ float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp);
+
+ copy_v3_v3(ss->persistent_base[i].co, co);
+ copy_v3_v3(ss->persistent_base[i].no, no);
+ ss->persistent_base[i].disp = *disp;
+
+ i++;
+ }
+ }
+}
/* Free the sculpt BMesh and BMLog
*
* If 'unode' is given, the BMesh's data is copied out to the unode
@@ -233,16 +666,6 @@ static void SCULPT_dynamic_topology_disable_ex(
else {
BKE_sculptsession_bm_to_me(ob, true);
- /* Reset Face Sets as they are no longer valid. */
- if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) {
- CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, me->totpoly);
- }
- ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
- for (int i = 0; i < me->totpoly; i++) {
- ss->face_sets[i] = 1;
- }
- me->face_sets_color_default = 1;
-
/* Sync the visibility to vertices manually as the pmap is still not initialized. */
for (int i = 0; i < me->totvert; i++) {
me->mvert[i].flag &= ~ME_HIDE;
@@ -253,15 +676,30 @@ static void SCULPT_dynamic_topology_disable_ex(
/* Clear data. */
me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY;
+ bool disp_saved = false;
+
+ if (ss->bm_log) {
+ if (ss->bm) {
+ disp_saved = true;
+
+ // rebuild ss->persistent_base if necassary
+ SCULPT_dyntopo_save_persistent_base(ss);
+ }
+
+ BM_log_free(ss->bm_log, true);
+ ss->bm_log = NULL;
+ }
+
/* Typically valid but with global-undo they can be NULL, see: T36234. */
if (ss->bm) {
+ if (!disp_saved) {
+ // rebuild ss->persistent_base if necassary
+ SCULPT_dyntopo_save_persistent_base(ss);
+ }
+
BM_mesh_free(ss->bm);
ss->bm = NULL;
}
- if (ss->bm_log) {
- BM_log_free(ss->bm_log);
- ss->bm_log = NULL;
- }
BKE_particlesystem_reset_all(ob);
BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED);
@@ -298,6 +736,8 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain,
if (use_undo) {
SCULPT_undo_push_end();
}
+
+ ss->active_vertex_index.i = ss->active_face_index.i = 0;
}
}
@@ -307,6 +747,7 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain,
Object *ob)
{
SculptSession *ss = ob->sculpt;
+
if (ss->bm == NULL) {
/* May be false in background mode. */
const bool use_undo = G.background ? (ED_undo_stack_get() != NULL) : true;
@@ -318,6 +759,8 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain,
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN);
SCULPT_undo_push_end();
}
+
+ ss->active_vertex_index.i = ss->active_face_index.i = 0;
}
}
@@ -384,6 +827,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob)
BLI_assert(ss->bm == NULL);
UNUSED_VARS_NDEBUG(ss);
+#ifndef DYNTOPO_CD_INTERP
for (int i = 0; i < CD_NUMTYPES; i++) {
if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) {
if (CustomData_has_layer(&me->vdata, i)) {
@@ -397,6 +841,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob)
}
}
}
+#endif
{
VirtualModifierData virtualModifierData;
@@ -453,3 +898,708 @@ void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
+#define MAXUVLOOPS 32
+#define MAXUVNEIGHBORS 32
+
+typedef struct UVSmoothVert {
+ double uv[2];
+ float co[3]; // world co
+ BMVert *v;
+ double w;
+ int totw;
+ bool pinned, boundary;
+ BMLoop *ls[MAXUVLOOPS];
+ struct UVSmoothVert *neighbors[MAXUVNEIGHBORS];
+ int totloop, totneighbor;
+} UVSmoothVert;
+
+typedef struct UVSmoothTri {
+ UVSmoothVert *vs[3];
+ float area2d, area3d;
+} UVSmoothTri;
+
+#define CON_MAX_VERTS 16
+typedef struct UVSmoothConstraint {
+ int type;
+ double k;
+ UVSmoothVert *vs[CON_MAX_VERTS];
+ UVSmoothTri *tri;
+ double gs[CON_MAX_VERTS][2];
+ int totvert;
+ double params[8];
+} UVSmoothConstraint;
+
+enum { CON_ANGLES = 0, CON_AREA = 1 };
+
+typedef struct UVSolver {
+ BLI_mempool *verts;
+ BLI_mempool *tris;
+ int totvert, tottri;
+ float snap_limit;
+ BLI_mempool *constraints;
+ GHash *vhash;
+ GHash *fhash;
+ int cd_uv;
+
+ double totarea3d;
+ double totarea2d;
+
+ double strength;
+} UVSolver;
+
+/*that that currently this tool is *not* threaded*/
+
+typedef struct SculptUVThreadData {
+ SculptThreadedTaskData data;
+ UVSolver *solver;
+} SculptUVThreadData;
+
+static UVSolver *uvsolver_new(int cd_uv)
+{
+ UVSolver *solver = MEM_callocN(sizeof(*solver), "solver");
+
+ solver->strength = 1.0;
+ solver->cd_uv = cd_uv;
+ solver->snap_limit = 0.0025;
+
+ solver->verts = BLI_mempool_create(sizeof(UVSmoothVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
+ solver->tris = BLI_mempool_create(sizeof(UVSmoothTri), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
+ solver->constraints = BLI_mempool_create(
+ sizeof(UVSmoothConstraint), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
+
+ solver->vhash = BLI_ghash_ptr_new("uvsolver");
+ solver->fhash = BLI_ghash_ptr_new("uvsolver");
+
+ return solver;
+}
+
+static void uvsolver_free(UVSolver *solver)
+{
+ BLI_mempool_destroy(solver->verts);
+ BLI_mempool_destroy(solver->tris);
+ BLI_mempool_destroy(solver->constraints);
+
+ BLI_ghash_free(solver->vhash, NULL, NULL);
+ BLI_ghash_free(solver->fhash, NULL, NULL);
+
+ MEM_freeN(solver);
+}
+
+void *uvsolver_calc_loop_key(UVSolver *solver, BMLoop *l)
+{
+ // return (void *)l->v;
+ MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv);
+
+ float u = floorf(uv->uv[0] / solver->snap_limit) * solver->snap_limit;
+ float v = floorf(uv->uv[1] / solver->snap_limit) * solver->snap_limit;
+
+ intptr_t x = (intptr_t)(uv->uv[0] * 16384.0);
+ intptr_t y = (intptr_t)(uv->uv[1] * 16384.0);
+ intptr_t key = y * 16384LL + x;
+
+ return POINTER_FROM_INT(key);
+}
+
+static UVSmoothVert *uvsolver_get_vert(UVSolver *solver, BMLoop *l)
+{
+ MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv);
+
+ void *pkey = uvsolver_calc_loop_key(solver, l);
+ void **entry = NULL;
+ UVSmoothVert *v;
+
+ if (!BLI_ghash_ensure_p(solver->vhash, pkey, &entry)) {
+ v = BLI_mempool_alloc(solver->verts);
+ memset(v, 0, sizeof(*v));
+
+ // copy_v2_v2(v->uv, uv->uv);
+ v->uv[0] = (double)uv->uv[0];
+ v->uv[1] = (double)uv->uv[1];
+
+ copy_v3_v3(v->co, l->v->co);
+ v->v = l->v;
+
+ *entry = (void *)v;
+ }
+
+ v = (UVSmoothVert *)*entry;
+
+ if (v->totloop < MAXUVLOOPS) {
+ v->ls[v->totloop++] = l;
+ }
+
+ return v;
+}
+
+MINLINE double area_tri_signed_v2_db(const double v1[2], const double v2[2], const double v3[2])
+{
+ return 0.5f * ((v1[0] - v2[0]) * (v2[1] - v3[1]) + (v1[1] - v2[1]) * (v3[0] - v2[0]));
+}
+
+MINLINE double area_tri_v2_db(const double v1[2], const double v2[2], const double v3[2])
+{
+ return fabsf(area_tri_signed_v2_db(v1, v2, v3));
+}
+
+void cross_tri_v3_db(double n[3], const double v1[3], const double v2[3], const double v3[3])
+{
+ double n1[3], n2[3];
+
+ n1[0] = v1[0] - v2[0];
+ n2[0] = v2[0] - v3[0];
+ n1[1] = v1[1] - v2[1];
+ n2[1] = v2[1] - v3[1];
+ n1[2] = v1[2] - v2[2];
+ n2[2] = v2[2] - v3[2];
+ n[0] = n1[1] * n2[2] - n1[2] * n2[1];
+ n[1] = n1[2] * n2[0] - n1[0] * n2[2];
+ n[2] = n1[0] * n2[1] - n1[1] * n2[0];
+}
+
+double area_tri_v3_db(const double v1[3], const double v2[3], const double v3[3])
+{
+ double n[3];
+ cross_tri_v3_db(n, v1, v2, v3);
+ return len_v3_db(n) * 0.5;
+}
+
+static UVSmoothTri *uvsolver_ensure_face(UVSolver *solver, BMFace *f)
+{
+ void **entry = NULL;
+
+ if (BLI_ghash_ensure_p(solver->fhash, (void *)f, &entry)) {
+ return (UVSmoothTri *)*entry;
+ }
+
+ UVSmoothTri *tri = BLI_mempool_alloc(solver->tris);
+ memset((void *)tri, 0, sizeof(*tri));
+ *entry = (void *)tri;
+
+ BMLoop *l = f->l_first;
+
+ bool nocon = false;
+ int i = 0;
+ do {
+ UVSmoothVert *sv = uvsolver_get_vert(solver, l);
+
+ if (BM_elem_flag_test(l->e, BM_ELEM_SEAM)) {
+ nocon = true;
+ }
+
+ tri->vs[i] = sv;
+
+ if (i > 3) {
+ // bad!
+ break;
+ }
+
+ i++;
+ } while ((l = l->next) != f->l_first);
+
+ double area3d = (double)area_tri_v3(tri->vs[0]->co, tri->vs[1]->co, tri->vs[2]->co);
+ double area2d = area_tri_v2_db(tri->vs[0]->uv, tri->vs[1]->uv, tri->vs[2]->uv);
+
+ if (area2d < 0.000001) {
+ tri->vs[0]->uv[0] -= 0.0001;
+ tri->vs[0]->uv[1] -= 0.0001;
+ tri->vs[1]->uv[0] += 0.0001;
+ tri->vs[2]->uv[1] += 0.0001;
+ }
+
+ solver->totarea2d += area2d;
+ solver->totarea3d += area3d;
+
+ tri->area2d = area2d;
+ tri->area3d = area3d;
+
+ for (int i = 0; !nocon && i < 3; i++) {
+
+ UVSmoothConstraint *con = BLI_mempool_alloc(solver->constraints);
+ memset((void *)con, 0, sizeof(*con));
+ con->type = CON_ANGLES;
+ con->k = 0.5;
+
+ UVSmoothVert *v0 = tri->vs[(i + 2) % 3];
+ UVSmoothVert *v1 = tri->vs[i];
+ UVSmoothVert *v2 = tri->vs[(i + 1) % 3];
+
+ con->vs[0] = v0;
+ con->vs[1] = v1;
+ con->vs[2] = v2;
+ con->totvert = 3;
+
+ float t1[3], t2[3];
+
+ sub_v3_v3v3(t1, v0->co, v1->co);
+ sub_v3_v3v3(t2, v2->co, v1->co);
+
+ normalize_v3(t1);
+ normalize_v3(t2);
+
+ float th3d = saacosf(dot_v3v3(t1, t2));
+
+ con->params[0] = (double)th3d;
+
+ // area constraint
+ con = BLI_mempool_alloc(solver->constraints);
+ memset((void *)con, 0, sizeof(*con));
+
+ con->vs[0] = v0;
+ con->vs[1] = v1;
+ con->vs[2] = v2;
+ con->totvert = 3;
+ con->tri = tri;
+ con->type = CON_AREA;
+ con->k = 1.0;
+ }
+
+#if 1
+ for (int i = 0; i < 3; i++) {
+ UVSmoothVert *v1 = tri->vs[i];
+ UVSmoothVert *v2 = tri->vs[(i + 1) % 3];
+
+ bool ok = true;
+
+ for (int j = 0; j < v1->totneighbor; j++) {
+ if (v1->neighbors[j] == v2) {
+ ok = false;
+ break;
+ }
+ }
+
+ ok = ok && v1->totneighbor < MAXUVNEIGHBORS && v2->totneighbor < MAXUVNEIGHBORS;
+
+ if (!ok) {
+ continue;
+ }
+
+ v1->neighbors[v1->totneighbor++] = v2;
+ v2->neighbors[v2->totneighbor++] = v1;
+ }
+#endif
+
+ return tri;
+}
+
+static double normalize_v2_db(double v[2])
+{
+ double len = v[0] * v[0] + v[1] * v[1];
+
+ if (len < 0.0000001) {
+ v[0] = v[1] = 0.0;
+ return 0.0;
+ }
+
+ len = sqrt(len);
+
+ double mul = 1.0 / len;
+
+ v[0] *= mul;
+ v[1] *= mul;
+
+ return len;
+}
+
+static double uvsolver_eval_constraint(UVSolver *solver, UVSmoothConstraint *con)
+{
+ switch (con->type) {
+ case CON_ANGLES: {
+ UVSmoothVert *v0 = con->vs[0];
+ UVSmoothVert *v1 = con->vs[1];
+ UVSmoothVert *v2 = con->vs[2];
+ double t1[2], t2[2];
+
+ sub_v2_v2v2_db(t1, v0->uv, v1->uv);
+ sub_v2_v2v2_db(t2, v2->uv, v1->uv);
+
+ normalize_v2_db(t1);
+ normalize_v2_db(t2);
+
+ double th = saacos(dot_v2v2_db(t1, t2));
+
+ double wind = t1[0] * t2[1] - t1[1] * t2[0];
+
+ if (wind >= 0.0) {
+ th = M_PI - th;
+ }
+
+ return th - con->params[0];
+ }
+ case CON_AREA: {
+ UVSmoothVert *v0 = con->vs[0];
+ UVSmoothVert *v1 = con->vs[1];
+ UVSmoothVert *v2 = con->vs[2];
+
+ if (con->tri->area3d == 0.0 || solver->totarea3d == 0.0) {
+ return 0.0;
+ }
+
+ double area2d = area_tri_signed_v2_db(v0->uv, v1->uv, v2->uv);
+ double goal = con->tri->area3d * solver->totarea2d / solver->totarea3d;
+
+ con->tri->area2d = area2d;
+ return (area2d - goal) * 1024.0;
+ }
+ default:
+ return 0.0f;
+ }
+}
+
+BLI_INLINE float uvsolver_vert_weight(UVSmoothVert *sv)
+{
+ double w = 1.0;
+
+ if (sv->pinned || sv->boundary) {
+ w = 100000.0;
+ }
+
+ return w;
+}
+
+static void uvsolver_solve_begin(UVSolver *solver)
+{
+ UVSmoothVert *sv;
+ BLI_mempool_iter iter;
+
+ BLI_mempool_iternew(solver->verts, &iter);
+ sv = BLI_mempool_iterstep(&iter);
+ BMIter liter;
+
+ for (; sv; sv = BLI_mempool_iterstep(&iter)) {
+ BMLoop *l;
+ sv->pinned = false;
+
+ BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
+ if (!BLI_ghash_haskey(solver->fhash, (void *)l->f)) {
+ sv->pinned = true;
+ }
+ }
+ }
+}
+
+static void uvsolver_simple_relax(UVSolver *solver, float strength)
+{
+ BLI_mempool_iter iter;
+
+ UVSmoothVert *sv1;
+ BLI_mempool_iternew(solver->verts, &iter);
+
+ sv1 = BLI_mempool_iterstep(&iter);
+ for (; sv1; sv1 = BLI_mempool_iterstep(&iter)) {
+ double uv[2] = {0.0, 0.0};
+ double tot = 0.0;
+
+ if (!sv1->totneighbor || sv1->pinned) {
+ continue;
+ }
+
+ for (int i = 0; i < sv1->totneighbor; i++) {
+ UVSmoothVert *sv2 = sv1->neighbors[i];
+
+ if (!sv2 || (sv1->boundary && !sv2->boundary)) {
+ continue;
+ }
+
+ uv[0] += sv2->uv[0];
+ uv[1] += sv2->uv[1];
+ tot += 1.0;
+ }
+
+ if (tot < 2.0) {
+ continue;
+ }
+
+ uv[0] /= tot;
+ uv[1] /= tot;
+
+ sv1->uv[0] += (uv[0] - sv1->uv[0]) * strength;
+ sv1->uv[1] += (uv[1] - sv1->uv[1]) * strength;
+ }
+
+ // update real uvs
+
+ const int cd_uv = solver->cd_uv;
+
+ BLI_mempool_iternew(solver->verts, &iter);
+ UVSmoothVert *sv = BLI_mempool_iterstep(&iter);
+ for (; sv; sv = BLI_mempool_iterstep(&iter)) {
+ for (int i = 0; i < sv->totloop; i++) {
+ BMLoop *l = sv->ls[i];
+ MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, cd_uv);
+
+ uv->uv[0] = (float)sv->uv[0];
+ uv->uv[1] = (float)sv->uv[1];
+ }
+ }
+}
+
+static float uvsolver_solve_step(UVSolver *solver)
+{
+ BLI_mempool_iter iter;
+
+ if (solver->strength < 0) {
+ uvsolver_simple_relax(solver, fabs(solver->strength));
+ return 0.0f;
+ }
+ else {
+ uvsolver_simple_relax(solver, solver->strength * 0.1f);
+ }
+
+ double error = 0.0;
+
+ const double eval_limit = 0.00001;
+ const double df = 0.0001;
+ int totcon = 0;
+
+ BLI_mempool_iternew(solver->constraints, &iter);
+ UVSmoothConstraint *con = BLI_mempool_iterstep(&iter);
+ for (; con; con = BLI_mempool_iterstep(&iter)) {
+ double r1 = uvsolver_eval_constraint(solver, con);
+
+ if (fabs(r1) < eval_limit) {
+ totcon++;
+ continue;
+ }
+
+ error += fabs(r1);
+ totcon++;
+
+ double totg = 0.0;
+ double totw = 0.0;
+
+ for (int i = 0; i < con->totvert; i++) {
+ UVSmoothVert *sv = con->vs[i];
+
+ for (int j = 0; j < 2; j++) {
+ double orig = sv->uv[j];
+ sv->uv[j] += df;
+
+ double r2 = uvsolver_eval_constraint(solver, con);
+ double g = (r2 - r1) / df;
+
+ con->gs[i][j] = g;
+ totg += g * g;
+
+ sv->uv[j] = orig;
+
+ totw += 1.0 / uvsolver_vert_weight(sv);
+ }
+ }
+
+ if (totg < eval_limit) {
+ continue;
+ }
+
+ r1 *= -solver->strength * 0.75 * con->k / totg;
+ // totw = 1.0 / totw;
+
+ for (int i = 0; i < con->totvert; i++) {
+ UVSmoothVert *sv = con->vs[i];
+ double w = 1.0 / (uvsolver_vert_weight(sv) * totw);
+
+ for (int j = 0; j < 2; j++) {
+ sv->uv[j] += r1 * con->gs[i][j] * w;
+ }
+ }
+ }
+
+ // update real uvs
+
+ const int cd_uv = solver->cd_uv;
+
+ BLI_mempool_iternew(solver->verts, &iter);
+ UVSmoothVert *sv = BLI_mempool_iterstep(&iter);
+ for (; sv; sv = BLI_mempool_iterstep(&iter)) {
+ for (int i = 0; i < sv->totloop; i++) {
+ BMLoop *l = sv->ls[i];
+ MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, cd_uv);
+
+ uv->uv[0] = (float)sv->uv[0];
+ uv->uv[1] = (float)sv->uv[1];
+ }
+ }
+
+ return (float)error / (float)totcon;
+}
+
+static void sculpt_uv_brush_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptUVThreadData *data1 = userdata;
+ SculptThreadedTaskData *data = &data1->data;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float *offset = data->offset;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ PBVHNode *node = data->nodes[n];
+ TableGSet *faces = BKE_pbvh_bmesh_node_faces(node);
+ BMFace *f;
+ const int cd_uv = CustomData_get_offset(&ss->bm->ldata, CD_MLOOPUV);
+
+ if (cd_uv < 0) {
+ return; // no uv layers
+ }
+
+ float bstrength = ss->cache->bstrength;
+ const int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK);
+
+ BKE_pbvh_node_mark_update_color(node);
+
+ TGSET_ITER (f, faces) {
+ BMLoop *l = f->l_first;
+ // float mask = 0.0f;
+ float cent[3] = {0};
+ int tot = 0;
+
+ // uvsolver_get_vert
+ do {
+ add_v3_v3(cent, l->v->co);
+ tot++;
+ } while ((l = l->next) != f->l_first);
+
+ mul_v3_fl(cent, 1.0f / (float)tot);
+
+ if (!sculpt_brush_test_sq_fn(&test, cent)) {
+ continue;
+ }
+
+ BM_log_face_modified(ss->bm_log, f);
+ uvsolver_ensure_face(data1->solver, f);
+
+ do {
+ BMIter iter;
+ BMLoop *l2;
+ int tot2 = 0;
+ float uv[2] = {0};
+ bool ok = true;
+ UVSmoothVert *lastv = NULL;
+
+ BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) {
+ if (l2->v != l->v) {
+ l2 = l2->prev->v == l->v ? l2->prev : l2->next;
+ }
+
+ UVSmoothVert *sv = uvsolver_get_vert(data1->solver, l2);
+
+ if (lastv && lastv != sv) {
+ ok = false;
+ lastv->boundary = true;
+ sv->boundary = true;
+ }
+
+ lastv = sv;
+
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l2, cd_uv);
+
+ add_v2_v2(uv, luv->uv);
+ tot2++;
+
+ if (BM_elem_flag_test(l2->e, BM_ELEM_SEAM)) {
+ ok = false;
+ sv->boundary = true;
+ }
+ }
+
+ ok = ok && tot2;
+
+ if (ok) {
+ mul_v2_fl(uv, 1.0f / (float)tot2);
+
+ BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) {
+ if (l2->v != l->v) {
+ l2 = l2->next;
+ }
+
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l2, cd_uv);
+
+ if (len_v2v2(luv->uv, uv) < 0.02) {
+ copy_v2_v2(luv->uv, uv);
+ }
+ }
+ }
+ } while ((l = l->next) != f->l_first);
+
+#if 0
+ do {
+ if (!sculpt_brush_test_sq_fn(&test, l->v->co)) {
+ continue;
+ }
+
+ if (cd_mask >= 0) {
+ mask = BM_ELEM_CD_GET_FLOAT(l->v, cd_mask);
+ }
+
+ SculptVertRef vertex = {(intptr_t)l->v};
+
+ float direction2[3];
+ const float fade =
+ bstrength *
+ SCULPT_brush_strength_factor(
+ ss, brush, vd.co, sqrtf(test.dist), NULL, l->v->no, mask, vertex, thread_id) *
+ ss->cache->pressure;
+
+ } while ((l = l->next) != f->l_first);
+#endif
+ }
+ TGSET_ITER_END;
+}
+
+void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ float offset[3];
+ const float bstrength = ss->cache->bstrength;
+
+ if (!ss->bm || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
+ // dyntopo only
+ return;
+ }
+
+ const int cd_uv = CustomData_get_offset(&ss->bm->ldata, CD_MLOOPUV);
+ if (cd_uv < 0) {
+ return; // no uv layer?
+ }
+
+ // add undo log subentry
+ BM_log_entry_add_ex(ss->bm, ss->bm_log, true);
+
+ BKE_curvemapping_init(brush->curve);
+
+ UVSolver *solver = uvsolver_new(cd_uv);
+ solver->strength = ss->cache->bstrength;
+
+ /* Threaded loop over nodes. */
+ SculptUVThreadData data = {.solver = solver,
+ .data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .offset = offset,
+ }};
+
+ TaskParallelSettings settings;
+
+ // for now, be single-threaded
+ BKE_pbvh_parallel_range_settings(&settings, false, totnode);
+ BLI_task_parallel_range(0, totnode, &data, sculpt_uv_brush_cb, &settings);
+
+ uvsolver_solve_begin(solver);
+
+ for (int i = 0; i < 5; i++) {
+ uvsolver_solve_step(solver);
+ }
+
+ // tear down solver
+ uvsolver_free(solver);
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c
index 40874375772..c7ad2bb20bd 100644
--- a/source/blender/editors/sculpt_paint/sculpt_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_expand.c
@@ -156,10 +156,12 @@ enum {
*/
static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const SculptVertRef v)
{
+ const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
+
for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
- if (ss->vertex_info.connected_component[v] == expand_cache->active_connected_components[i]) {
+ if (ss->vertex_info.connected_component[v_i] == expand_cache->active_connected_components[i]) {
return true;
}
}
@@ -171,10 +173,20 @@ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss,
*/
static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
ExpandCache *expand_cache,
- const int f)
+ const SculptFaceRef f)
{
- const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart];
- return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v);
+ if (ss->bm) {
+ BMFace *bf = (BMFace *)f.i;
+ BMLoop *l = bf->l_first;
+ SculptVertRef v = {(intptr_t)l->v};
+
+ return sculpt_expand_is_vert_in_active_component(ss, expand_cache, v);
+ }
+ else {
+ const MLoop *loop = &ss->mloop[ss->mpoly[f.i].loopstart];
+ return sculpt_expand_is_vert_in_active_component(
+ ss, expand_cache, BKE_pbvh_table_index_to_vertex(ss->pbvh, loop->v));
+ }
}
/**
@@ -183,14 +195,16 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
*/
static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const SculptVertRef v)
{
+ const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
+
if (expand_cache->texture_distortion_strength == 0.0f) {
- return expand_cache->vert_falloff[v];
+ return expand_cache->vert_falloff[v_i];
}
if (!expand_cache->brush->mtex.tex) {
- return expand_cache->vert_falloff[v];
+ return expand_cache->vert_falloff[v_i];
}
float rgba[4];
@@ -200,7 +214,7 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength *
expand_cache->max_vert_falloff;
- return expand_cache->vert_falloff[v] + distortion;
+ return expand_cache->vert_falloff[v_i] + distortion;
}
/**
@@ -225,7 +239,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache)
* Main function to get the state of a vertex for the current state and settings of a #ExpandCache.
* Returns true when the target data should be modified by expand.
*/
-static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v)
+static bool sculpt_expand_state_get(SculptSession *ss,
+ ExpandCache *expand_cache,
+ const SculptVertRef v)
{
if (!SCULPT_vertex_visible_get(ss, v)) {
return false;
@@ -271,9 +287,13 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
* Main function to get the state of a face for the current state and settings of a #ExpandCache.
* Returns true when the target data should be modified by expand.
*/
-static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f)
+static bool sculpt_expand_face_state_get(SculptSession *ss,
+ ExpandCache *expand_cache,
+ const SculptFaceRef f)
{
- if (expand_cache->original_face_sets[f] <= 0) {
+ const int f_i = BKE_pbvh_face_index_to_table(ss->pbvh, f);
+
+ if (expand_cache->original_face_sets[f_i] <= 0) {
return false;
}
@@ -288,7 +308,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
bool enabled = false;
if (expand_cache->snap_enabled_face_sets) {
- const int face_set = expand_cache->original_face_sets[f];
+ const int face_set = expand_cache->original_face_sets[f_i];
enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set));
}
else {
@@ -296,12 +316,12 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
SCULPT_EXPAND_LOOP_THRESHOLD;
const float active_factor = fmod(expand_cache->active_falloff, loop_len);
- const float falloff_factor = fmod(expand_cache->face_falloff[f], loop_len);
+ const float falloff_factor = fmod(expand_cache->face_falloff[f_i], loop_len);
enabled = falloff_factor < active_factor;
}
if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET) {
- if (ss->face_sets[f] == expand_cache->initial_active_face_set) {
+ if (SCULPT_face_set_get(ss, f) == expand_cache->initial_active_face_set) {
enabled = false;
}
}
@@ -319,7 +339,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
*/
static float sculpt_expand_gradient_value_get(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const SculptVertRef v)
{
if (!expand_cache->falloff_gradient) {
return 1.0f;
@@ -363,7 +383,8 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa
const int totvert = SCULPT_vertex_count_get(ss);
BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices");
for (int i = 0; i < totvert; i++) {
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, i);
+ const bool enabled = sculpt_expand_state_get(
+ ss, expand_cache, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
BLI_BITMAP_SET(enabled_vertices, i, enabled);
}
return enabled_vertices;
@@ -381,20 +402,22 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
const int totvert = SCULPT_vertex_count_get(ss);
BLI_bitmap *boundary_vertices = BLI_BITMAP_NEW(totvert, "boundary vertices");
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (!BLI_BITMAP_TEST(enabled_vertices, i)) {
continue;
}
bool is_expand_boundary = false;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
if (!BLI_BITMAP_TEST(enabled_vertices, ni.index)) {
is_expand_boundary = true;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, i)) {
+ if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, vertex)) {
is_expand_boundary = true;
}
@@ -410,12 +433,12 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
* Utility function to get the closet vertex after flipping an original vertex position based on
* an symmetry pass iteration index.
*/
-static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob,
- const char symm_it,
- const int original_vertex)
+static SculptVertRef sculpt_expand_get_vertex_index_for_symmetry_pass(
+ Object *ob, const char symm_it, const SculptVertRef original_vertex)
{
SculptSession *ss = ob->sculpt;
- int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
+ SculptVertRef symm_vertex = {SCULPT_EXPAND_VERTEX_NONE};
+
if (symm_it == 0) {
symm_vertex = original_vertex;
}
@@ -431,7 +454,7 @@ static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob,
* Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking
* symmetry into account.
*/
-static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v)
+static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const SculptVertRef v)
{
return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX);
}
@@ -448,20 +471,23 @@ typedef struct ExpandFloodFillData {
} ExpandFloodFillData;
static bool expand_topology_floodfill_cb(
- SculptSession *UNUSED(ss), int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata)
{
ExpandFloodFillData *data = userdata;
+ int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+
if (!is_duplicate) {
- const float to_it = data->dists[from_v] + 1.0f;
- data->dists[to_v] = to_it;
+ const float to_it = data->dists[from_v_i] + 1.0f;
+ data->dists[to_v_i] = to_it;
}
else {
- data->dists[to_v] = data->dists[from_v];
+ data->dists[to_v_i] = data->dists[from_v_i];
}
return true;
}
-static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int v)
+static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -486,23 +512,26 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons
* This creates falloff patterns that follow and snap to the hard edges of the object.
*/
static bool mask_expand_normal_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata)
{
ExpandFloodFillData *data = userdata;
+ int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+
if (!is_duplicate) {
float current_normal[3], prev_normal[3];
SCULPT_vertex_normal_get(ss, to_v, current_normal);
SCULPT_vertex_normal_get(ss, from_v, prev_normal);
- const float from_edge_factor = data->edge_factor[from_v];
- data->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * from_edge_factor;
- data->dists[to_v] = dot_v3v3(data->original_normal, current_normal) *
- powf(from_edge_factor, data->edge_sensitivity);
- CLAMP(data->dists[to_v], 0.0f, 1.0f);
+ const float from_edge_factor = data->edge_factor[from_v_i];
+ data->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) * from_edge_factor;
+ data->dists[to_v_i] = dot_v3v3(data->original_normal, current_normal) *
+ powf(from_edge_factor, data->edge_sensitivity);
+ CLAMP(data->dists[to_v_i], 0.0f, 1.0f);
}
else {
/* PBVH_GRIDS duplicate handling. */
- data->edge_factor[to_v] = data->edge_factor[from_v];
- data->dists[to_v] = data->dists[from_v];
+ data->edge_factor[to_v_i] = data->edge_factor[from_v_i];
+ data->dists[to_v_i] = data->dists[from_v_i];
}
return true;
@@ -510,7 +539,7 @@ static bool mask_expand_normal_floodfill_cb(
static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
Object *ob,
- const int v,
+ const SculptVertRef v,
const float edge_sensitivity)
{
SculptSession *ss = ob->sculpt;
@@ -537,8 +566,10 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
for (int repeat = 0; repeat < 2; repeat++) {
for (int i = 0; i < totvert; i++) {
float avg = 0.0f;
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) {
avg += dists[ni.index];
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -555,7 +586,7 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
* Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into
* account.
*/
-static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_spherical_falloff_create(Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -570,11 +601,14 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
- if (symm_vertex != -1) {
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
+ if (symm_vertex.i != -1) {
const float *co = SCULPT_vertex_co_get(ss, symm_vertex);
for (int i = 0; i < totvert; i++) {
- dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i)));
+ dists[i] = min_ff(
+ dists[i],
+ len_v3v3(co, SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i))));
}
}
}
@@ -587,13 +621,13 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
* boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it
* stays parallel to the boundary, increasing the falloff value by 1 on each step.
*/
-static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist");
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
- GSQueue *queue = BLI_gsqueue_new(sizeof(int));
+ GSQueue *queue = BLI_gsqueue_new(sizeof(SculptVertRef));
/* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
@@ -602,7 +636,8 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX);
if (!boundary) {
@@ -611,7 +646,8 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
for (int i = 0; i < boundary->num_vertices; i++) {
BLI_gsqueue_push(queue, &boundary->vertices[i]);
- BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]);
+ BLI_BITMAP_ENABLE(visited_vertices,
+ BKE_pbvh_vertex_index_to_table(ss->pbvh, boundary->vertices[i]));
}
SCULPT_boundary_data_free(boundary);
}
@@ -623,7 +659,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
/* Propagate the values from the boundaries to the rest of the mesh. */
while (!BLI_gsqueue_is_empty(queue)) {
- int v_next;
+ SculptVertRef v_next;
BLI_gsqueue_pop(queue, &v_next);
SculptVertexNeighborIter ni;
@@ -631,7 +667,10 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
if (BLI_BITMAP_TEST(visited_vertices, ni.index)) {
continue;
}
- dists[ni.index] = dists[v_next] + 1.0f;
+
+ const int v_next_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_next);
+
+ dists[ni.index] = dists[v_next_i] + 1.0f;
BLI_BITMAP_ENABLE(visited_vertices, ni.index);
BLI_gsqueue_push(queue, &ni.index);
}
@@ -648,32 +687,38 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
* the base mesh faces when checking a vertex neighbor. For this reason, this is not implement
* using the general flood-fill and sculpt neighbors accessors.
*/
-static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_diagonals_falloff_create(Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist");
/* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialized for
- * Multires. It also does not make sense to implement it for dyntopo as the result will be the
- * same as Topology falloff. */
- if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
+ * Multires. Also supports non-tri PBVH_BMESH, though untested until we implement that properly*/
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES ||
+ (ss->bm && ss->bm->totloop != ss->bm->totvert * 3)) {
return dists;
}
+ if (ss->bm) {
+ BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
+ BM_mesh_elem_table_ensure(ss->bm, BM_VERT);
+ }
+
/* Search and mask as visited the initial vertices using the enabled symmetry passes. */
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
- GSQueue *queue = BLI_gsqueue_new(sizeof(int));
+ GSQueue *queue = BLI_gsqueue_new(sizeof(SculptVertRef));
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char symm_it = 0; symm_it <= symm; symm_it++) {
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
BLI_gsqueue_push(queue, &symm_vertex);
- BLI_BITMAP_ENABLE(visited_vertices, symm_vertex);
+ BLI_BITMAP_ENABLE(visited_vertices, BKE_pbvh_vertex_index_to_table(ss->pbvh, symm_vertex));
}
if (BLI_gsqueue_is_empty(queue)) {
@@ -683,18 +728,50 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
/* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */
Mesh *mesh = ob->data;
while (!BLI_gsqueue_is_empty(queue)) {
- int v_next;
+ SculptVertRef v_next;
BLI_gsqueue_pop(queue, &v_next);
- for (int j = 0; j < ss->pmap[v_next].count; j++) {
- MPoly *p = &ss->mpoly[ss->pmap[v_next].indices[j]];
- for (int l = 0; l < p->totloop; l++) {
- const int neighbor_v = mesh->mloop[p->loopstart + l].v;
- if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) {
- continue;
+
+ int v_next_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_next);
+
+ if (ss->bm) {
+ BMIter iter;
+ BMFace *f;
+ BMVert *v = (BMVert *)v_next.i;
+
+ BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
+ BMLoop *l = f->l_first;
+
+ do {
+ BMVert *neighbor_v = l->next->v;
+ const int neighbor_v_i = BM_elem_index_get(neighbor_v);
+
+ if (BLI_BITMAP_TEST(visited_vertices, neighbor_v_i)) {
+ l = l->next;
+ continue;
+ }
+
+ dists[neighbor_v_i] = dists[v_next_i] + 1.0f;
+ BLI_BITMAP_ENABLE(visited_vertices, neighbor_v_i);
+ BLI_gsqueue_push(queue, &neighbor_v);
+
+ l = l->next;
+ } while (l != f->l_first);
+ }
+ }
+ else {
+ for (int j = 0; j < ss->pmap[v_next_i].count; j++) {
+ MPoly *p = &ss->mpoly[ss->pmap[v_next_i].indices[j]];
+ for (int l = 0; l < p->totloop; l++) {
+ const int neighbor_v = mesh->mloop[p->loopstart + l].v;
+
+ if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) {
+ continue;
+ }
+
+ dists[neighbor_v] = dists[v_next_i] + 1.0f;
+ BLI_BITMAP_ENABLE(visited_vertices, neighbor_v);
+ BLI_gsqueue_push(queue, &neighbor_v);
}
- dists[neighbor_v] = dists[v_next] + 1.0f;
- BLI_BITMAP_ENABLE(visited_vertices, neighbor_v);
- BLI_gsqueue_push(queue, &neighbor_v);
}
}
}
@@ -716,12 +793,15 @@ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss,
{
const int totvert = SCULPT_vertex_count_get(ss);
expand_cache->max_vert_falloff = -FLT_MAX;
+
for (int i = 0; i < totvert; i++) {
if (expand_cache->vert_falloff[i] == FLT_MAX) {
continue;
}
- if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
+ SculptVertRef v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) {
continue;
}
@@ -740,11 +820,13 @@ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss,
const int totface = ss->totfaces;
expand_cache->max_face_falloff = -FLT_MAX;
for (int i = 0; i < totface; i++) {
+ SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+
if (expand_cache->face_falloff[i] == FLT_MAX) {
continue;
}
- if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
+ if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, f)) {
continue;
}
@@ -792,6 +874,23 @@ static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expan
}
}
+static void sculpt_expand_vertex_to_faces_falloff_bmesh(BMesh *bm, ExpandCache *expand_cache)
+{
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l = f->l_first;
+
+ float accum = 0.0f;
+
+ do {
+ accum += expand_cache->vert_falloff[BM_elem_index_get(l->v)];
+ l = l->next;
+ } while (l != f->l_first);
+
+ expand_cache->face_falloff[BM_elem_index_get(f)] = accum / f->len;
+ }
+}
/**
* Main function to update the faces falloff from a already calculated vertex falloff.
*/
@@ -806,14 +905,16 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s
mesh->totpoly, sizeof(float), "face falloff factors");
}
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
- sculpt_expand_vertex_to_faces_falloff(mesh, expand_cache);
- }
- else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
- sculpt_expand_grids_to_faces_falloff(ss, mesh, expand_cache);
- }
- else {
- BLI_assert(false);
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES:
+ sculpt_expand_vertex_to_faces_falloff(mesh, expand_cache);
+ break;
+ case PBVH_GRIDS:
+ sculpt_expand_grids_to_faces_falloff(ss, mesh, expand_cache);
+ break;
+ case PBVH_BMESH:
+ sculpt_expand_vertex_to_faces_falloff_bmesh(ss->bm, expand_cache);
+ break;
}
}
@@ -829,7 +930,7 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob,
BLI_bitmap *enabled_vertices)
{
SculptSession *ss = ob->sculpt;
- BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES);
+ BLI_assert(ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH));
GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false);
@@ -872,7 +973,8 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob,
if (!BLI_BITMAP_TEST(boundary_vertices, i)) {
continue;
}
- SCULPT_floodfill_add_and_skip_initial(&flood, i);
+
+ SCULPT_floodfill_add_and_skip_initial(ss, &flood, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
}
MEM_freeN(boundary_vertices);
@@ -937,16 +1039,18 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices");
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_unique_face_set(ss, vref)) {
continue;
}
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
+ if (!SCULPT_vertex_has_face_set(ss, vref, active_face_set)) {
continue;
}
BLI_BITMAP_ENABLE(enabled_vertices, i);
}
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
+ if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH)) {
sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices);
}
else {
@@ -957,8 +1061,10 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
if (internal_falloff) {
for (int i = 0; i < totvert; i++) {
- if (!(SCULPT_vertex_has_face_set(ss, i, active_face_set) &&
- SCULPT_vertex_has_unique_face_set(ss, i))) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!(SCULPT_vertex_has_face_set(ss, vref, active_face_set) &&
+ SCULPT_vertex_has_unique_face_set(ss, vref))) {
continue;
}
expand_cache->vert_falloff[i] *= -1.0f;
@@ -976,7 +1082,9 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
}
else {
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vref, active_face_set)) {
continue;
}
expand_cache->vert_falloff[i] = 0.0f;
@@ -992,14 +1100,14 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
ExpandCache *expand_cache,
Sculpt *sd,
Object *ob,
- const int v,
+ const SculptVertRef v,
eSculptExpandFalloffType falloff_type)
{
MEM_SAFE_FREE(expand_cache->vert_falloff);
expand_cache->falloff_type = falloff_type;
SculptSession *ss = ob->sculpt;
- const bool has_topology_info = BKE_pbvh_type(ss->pbvh) == PBVH_FACES;
+ const bool has_topology_info = ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH);
switch (falloff_type) {
case SCULPT_EXPAND_FALLOFF_GEODESIC:
@@ -1131,7 +1239,9 @@ static void sculpt_expand_restore_face_set_data(SculptSession *ss, ExpandCache *
}
MEM_freeN(nodes);
for (int i = 0; i < ss->totfaces; i++) {
- ss->face_sets[i] = expand_cache->original_face_sets[i];
+ SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+
+ SCULPT_face_set_set(ss, f, expand_cache->original_face_sets[i]);
}
}
@@ -1231,12 +1341,12 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
const float initial_mask = *vd.mask;
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index);
+ const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex);
float new_mask;
if (enabled) {
- new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
+ new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex);
}
else {
new_mask = 0.0f;
@@ -1268,16 +1378,20 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expand_cache)
{
const int totface = ss->totfaces;
- for (int f = 0; f < totface; f++) {
+
+ for (int f_i = 0; f_i < totface; f_i++) {
+ SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, f_i);
+ int fset = SCULPT_face_set_get(ss, f);
+
const bool enabled = sculpt_expand_face_state_get(ss, expand_cache, f);
if (!enabled) {
continue;
}
if (expand_cache->preserve) {
- ss->face_sets[f] += expand_cache->next_face_set;
+ SCULPT_face_set_set(ss, f, fset + expand_cache->next_face_set);
}
else {
- ss->face_sets[f] = expand_cache->next_face_set;
+ SCULPT_face_set_set(ss, f, expand_cache->next_face_set);
}
}
@@ -1305,11 +1419,11 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata,
float initial_color[4];
copy_v4_v4(initial_color, vd.col);
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index);
+ const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex);
float fade;
if (enabled) {
- fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
+ fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex);
}
else {
fade = 0.0f;
@@ -1371,22 +1485,28 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c
/* Face Sets are always stored as they are needed for snapping. */
expand_cache->initial_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "initial face set");
expand_cache->original_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "original face set");
+
for (int i = 0; i < totface; i++) {
- expand_cache->initial_face_sets[i] = ss->face_sets[i];
- expand_cache->original_face_sets[i] = ss->face_sets[i];
+ const SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+ const int fset = SCULPT_face_set_get(ss, fref);
+
+ expand_cache->initial_face_sets[i] = fset;
+ expand_cache->original_face_sets[i] = fset;
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) {
expand_cache->original_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask");
for (int i = 0; i < totvert; i++) {
- expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, i);
+ expand_cache->original_mask[i] = SCULPT_vertex_mask_get(
+ ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
}
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) {
expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors");
for (int i = 0; i < totvert; i++) {
- copy_v4_v4(expand_cache->original_colors[i], SCULPT_vertex_color_get(ss, i));
+ copy_v4_v4(expand_cache->original_colors[i],
+ SCULPT_vertex_color_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)));
}
}
}
@@ -1397,26 +1517,32 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c
static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expand_cache)
{
const int totfaces = ss->totfaces;
+
for (int i = 0; i < totfaces; i++) {
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+
if (expand_cache->original_face_sets[i] <= 0) {
/* Do not modify hidden Face Sets, even when restoring the IDs state. */
continue;
}
- if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
+ if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, fref)) {
continue;
}
- ss->face_sets[i] = expand_cache->initial_face_sets[i];
+
+ SCULPT_face_set_set(ss, fref, expand_cache->initial_face_sets[i]);
}
}
-static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int vertex)
+static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const SculptVertRef vertex)
{
SculptSession *ss = ob->sculpt;
+ const int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ExpandCache *expand_cache = ss->expand_cache;
/* Update the active factor in the cache. */
- if (vertex == SCULPT_EXPAND_VERTEX_NONE) {
+ if (vertex.i == SCULPT_EXPAND_VERTEX_NONE) {
/* This means that the cursor is not over the mesh, so a valid active falloff can't be
* determined. In this situations, don't evaluate enabled states and default all vertices in
* connected components to enabled. */
@@ -1424,7 +1550,7 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
expand_cache->all_enabled = true;
}
else {
- expand_cache->active_falloff = expand_cache->vert_falloff[vertex];
+ expand_cache->active_falloff = expand_cache->vert_falloff[vertex_i];
expand_cache->all_enabled = false;
}
@@ -1465,16 +1591,18 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
* Updates the #SculptSession cursor data and gets the active vertex
* if the cursor is over the mesh.
*/
-static int sculpt_expand_target_vertex_update_and_get(bContext *C,
- Object *ob,
- const float mouse[2])
+static SculptVertRef sculpt_expand_target_vertex_update_and_get(bContext *C,
+ Object *ob,
+ const float mouse[2])
{
SculptSession *ss = ob->sculpt;
SculptCursorGeometryInfo sgi;
if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) {
return SCULPT_active_vertex_get(ss);
}
- return SCULPT_EXPAND_VERTEX_NONE;
+
+ SculptVertRef ret = {SCULPT_EXPAND_VERTEX_NONE};
+ return ret;
}
/**
@@ -1494,8 +1622,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
/* For boundary topology, position the pivot using only the boundary of the enabled vertices,
* without taking mesh boundary into account. This allows to create deformations like bending the
* mesh from the boundary of the mask that was just created. */
- const float use_mesh_boundary = expand_cache->falloff_type !=
- SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY;
+ const bool use_mesh_boundary = expand_cache->falloff_type !=
+ SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY;
BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(
ss, enabled_vertices, use_mesh_boundary);
@@ -1514,11 +1642,13 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
continue;
}
- if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
+ SculptVertRef v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) {
continue;
}
- const float *vertex_co = SCULPT_vertex_co_get(ss, i);
+ const float *vertex_co = SCULPT_vertex_co_get(ss, v);
if (!SCULPT_check_vertex_pivot_symmetry(vertex_co, expand_init_co, symm)) {
continue;
@@ -1573,9 +1703,8 @@ static void sculpt_expand_finish(bContext *C)
* Finds and stores in the #ExpandCache the sculpt connected component index for each symmetry pass
* needed for expand.
*/
-static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
- ExpandCache *expand_cache,
- const int initial_vertex)
+static void sculpt_expand_find_active_connected_components_from_vert(
+ Object *ob, ExpandCache *expand_cache, const SculptVertRef initial_vertex)
{
SculptSession *ss = ob->sculpt;
for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
@@ -1588,11 +1717,12 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
ob, symm_it, initial_vertex);
+ const int symm_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, symm_vertex);
expand_cache->active_connected_components[(int)symm_it] =
- ss->vertex_info.connected_component[symm_vertex];
+ ss->vertex_info.connected_component[symm_vertex_i];
}
}
@@ -1606,8 +1736,9 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C,
const float mouse[2])
{
SculptSession *ss = ob->sculpt;
- int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse);
- if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) {
+ SculptVertRef initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse);
+
+ if (initial_vertex.i == SCULPT_EXPAND_VERTEX_NONE) {
/* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active
* vertex in the sculpt session. */
initial_vertex = SCULPT_active_vertex_get(ss);
@@ -1680,17 +1811,15 @@ static void sculpt_expand_ensure_sculptsession_data(Object *ob)
static int sculpt_expand_active_face_set_id_get(SculptSession *ss, ExpandCache *expand_cache)
{
switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH:
case PBVH_FACES:
- return expand_cache->original_face_sets[ss->active_face_index];
+ return expand_cache
+ ->original_face_sets[BKE_pbvh_vertex_index_to_table(ss->pbvh, ss->active_face_index)];
case PBVH_GRIDS: {
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg,
ss->active_grid_index);
return expand_cache->original_face_sets[face_index];
}
- case PBVH_BMESH: {
- /* Dyntopo does not support Face Set functionality. */
- BLI_assert(false);
- }
}
return SCULPT_FACE_SET_NONE;
}
@@ -1713,7 +1842,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
/* Update and get the active vertex (and face) from the cursor. */
const float mouse[2] = {event->mval[0], event->mval[1]};
- const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse);
+ const SculptVertRef target_expand_vertex = sculpt_expand_target_vertex_update_and_get(
+ C, ob, mouse);
/* Handle the modal keymap state changes. */
ExpandCache *expand_cache = ss->expand_cache;
@@ -1899,12 +2029,133 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
* The faces that were using the `delete_id` Face Set are filled
* using the content from their neighbors.
*/
+static void sculpt_expand_delete_face_set_id_bmesh(int *r_face_sets,
+ SculptSession *ss,
+ ExpandCache *expand_cache,
+ const int delete_id)
+{
+ BMIter iter;
+ BMFace *f;
+ int i = 0;
+ const int totface = ss->totpoly;
+
+ /* Check that all the face sets IDs in the mesh are not equal to `delete_id`
+ * before attempting to delete it. */
+ bool all_same_id = true;
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ SculptFaceRef fref = {(intptr_t)f};
+ i++;
+
+ if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, fref)) {
+ continue;
+ }
+
+ if (r_face_sets[i] != delete_id) {
+ all_same_id = false;
+ break;
+ }
+ }
+
+ if (all_same_id) {
+ return;
+ }
+
+ BLI_LINKSTACK_DECLARE(queue, BMFace *);
+ BLI_LINKSTACK_DECLARE(queue_next, BMFace *);
+
+ BLI_LINKSTACK_INIT(queue);
+ BLI_LINKSTACK_INIT(queue_next);
+
+ for (int i = 0; i < totface; i++) {
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+
+ if (r_face_sets[i] == delete_id) {
+ BLI_LINKSTACK_PUSH(queue, (BMFace *)(fref.i));
+ }
+ }
+
+ while (BLI_LINKSTACK_SIZE(queue)) {
+ bool any_updated = false;
+
+ while (BLI_LINKSTACK_SIZE(queue)) {
+ const SculptFaceRef f = {(intptr_t)(BLI_LINKSTACK_POP(queue))};
+ BMFace *bf = (BMFace *)f.i;
+ const int f_index = BM_elem_index_get(bf);
+
+ int other_id = delete_id;
+ BMLoop *l = bf->l_first;
+ do {
+ BMLoop *l2 = l->radial_next;
+ do {
+ const int neighbor_face_index = BM_elem_index_get(l2->f);
+
+ if (expand_cache->original_face_sets[neighbor_face_index] <= 0) {
+ /* Skip picking IDs from hidden Face Sets. */
+ continue;
+ }
+
+ if (r_face_sets[neighbor_face_index] != delete_id) {
+ other_id = r_face_sets[neighbor_face_index];
+ }
+
+ l2 = l2->radial_next;
+ } while (l2 != l);
+
+ l = l->next;
+ } while (l != bf->l_first);
+
+ if (other_id != delete_id) {
+ any_updated = true;
+ r_face_sets[f_index] = other_id;
+ }
+ else {
+ BLI_LINKSTACK_PUSH(queue_next, bf);
+ }
+ }
+
+ if (!any_updated) {
+ /* No Face Sets where updated in this iteration, which means that no more content to keep
+ * filling the polys of the deleted Face Set was found. Break to avoid entering an infinite
+ * loop trying to search for those polys again. */
+ break;
+ }
+
+ BLI_LINKSTACK_SWAP(queue, queue_next);
+ }
+
+ BLI_LINKSTACK_FREE(queue);
+ BLI_LINKSTACK_FREE(queue_next);
+
+ /* Ensure that the visibility state of the modified Face Sets is the same as the original ones.
+ */
+ for (int i = 0; i < totface; i++) {
+ if (expand_cache->original_face_sets[i] >= 0) {
+ r_face_sets[i] = abs(r_face_sets[i]);
+ }
+ else {
+ r_face_sets[i] = -abs(r_face_sets[i]);
+ }
+ }
+}
+
+/**
+ * Deletes the `delete_id` Face Set ID from the mesh Face Sets
+ * and stores the result in `r_face_set`.
+ * The faces that were using the `delete_id` Face Set are filled
+ * using the content from their neighbors.
+ */
static void sculpt_expand_delete_face_set_id(int *r_face_sets,
SculptSession *ss,
ExpandCache *expand_cache,
Mesh *mesh,
const int delete_id)
{
+ if (ss->bm) {
+ sculpt_expand_delete_face_set_id_bmesh(r_face_sets, ss, expand_cache, delete_id);
+ return;
+ }
+
const int totface = ss->totfaces;
MeshElemMap *pmap = ss->pmap;
@@ -1912,7 +2163,8 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets,
* before attempting to delete it. */
bool all_same_id = true;
for (int i = 0; i < totface; i++) {
- if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
+ if (!sculpt_expand_is_face_in_active_component(
+ ss, expand_cache, BKE_pbvh_table_index_to_face(ss->pbvh, i))) {
continue;
}
if (r_face_sets[i] != delete_id) {
@@ -2060,6 +2312,9 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_face_random_access_ensure(ss);
+
/* Create and configure the Expand Cache. */
ss->expand_cache = MEM_callocN(sizeof(ExpandCache), "expand cache");
sculpt_expand_cache_initial_config_set(C, op, ss->expand_cache);
@@ -2083,13 +2338,6 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
return OPERATOR_CANCELLED;
}
- /* Face Set operations are not supported in dyntopo. */
- if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS &&
- BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- sculpt_expand_cache_free(ss);
- return OPERATOR_CANCELLED;
- }
-
sculpt_expand_ensure_sculptsession_data(ob);
/* Initialize undo. */
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index bdbdb75732a..f0f15fe3e3f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -23,7 +23,9 @@
#include "MEM_guardedalloc.h"
+#include "BLI_array.h"
#include "BLI_blenlib.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_task.h"
@@ -72,6 +74,78 @@
#include <math.h>
#include <stdlib.h>
+int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face)
+{
+ if (ss->bm) {
+ BMFace *f = (BMFace *)face.i;
+ return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+ }
+
+ return ss->face_sets[face.i];
+}
+
+// returns previous face set
+int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset)
+{
+ int ret = 0;
+
+ if (ss->bm) {
+ BMFace *f = (BMFace *)face.i;
+ ret = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ }
+ else {
+ ret = ss->face_sets[face.i];
+ ss->face_sets[face.i] = fset;
+ }
+
+ return ret;
+}
+
+int SCULPT_face_set_flag_get(SculptSession *ss, SculptFaceRef face, char flag)
+{
+ if (ss->bm) {
+ BMFace *f = (BMFace *)face.i;
+
+ flag = BM_face_flag_from_mflag(flag);
+ return f->head.hflag & flag;
+ }
+ else {
+ return ss->mpoly[face.i].flag & flag;
+ }
+}
+
+int SCULPT_face_set_flag_set(SculptSession *ss, SculptFaceRef face, char flag, bool state)
+{
+ int ret;
+
+ if (ss->bm) {
+ BMFace *f = (BMFace *)face.i;
+
+ flag = BM_face_flag_from_mflag(flag);
+ ret = f->head.hflag & flag;
+
+ if (state) {
+ f->head.hflag |= flag;
+ }
+ else {
+ f->head.hflag &= ~flag;
+ }
+ }
+ else {
+ ret = ss->mpoly[face.i].flag & flag;
+
+ if (state) {
+ ss->mpoly[face.i].flag |= flag;
+ }
+ else {
+ ss->mpoly[face.i].flag &= ~flag;
+ }
+ }
+
+ return ret;
+}
/* Utils. */
int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
{
@@ -118,6 +192,34 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo
return SCULPT_active_face_set_get(ss);
}
+static BMesh *sculpt_faceset_bm_begin(SculptSession *ss, Mesh *mesh)
+{
+ if (ss->bm) {
+ return ss->bm;
+ }
+
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
+ BMesh *bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ mesh,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ return bm;
+}
+
+static void sculpt_faceset_bm_end(SculptSession *ss, BMesh *bm)
+{
+ if (bm != ss->bm) {
+ BM_mesh_free(bm);
+ }
+}
+
/* Draw Face Sets Brush. */
static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
@@ -135,6 +237,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
+ const int active_fset = abs(ss->cache->paint_face_set);
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
@@ -157,7 +260,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) {
@@ -165,7 +268,34 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
}
}
}
+ else if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BMVert *v = vd.bm_vert;
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
+ float poly_center[3];
+ BM_face_calc_center_median(f, poly_center);
+
+ if (sculpt_brush_test_sq_fn(&test, poly_center)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.vertex,
+ thread_id);
+
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ if (fade > 0.05f && fset > 0) {
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, active_fset);
+ }
+ }
+ }
+ }
else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
{
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
@@ -178,11 +308,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (fade > 0.05f) {
- SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
+ SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set);
}
}
}
@@ -217,7 +347,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
- if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
+ if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) {
continue;
}
@@ -228,7 +358,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
@@ -316,13 +446,11 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
const int mode = RNA_enum_get(op->ptr, "mode");
- /* Dyntopo not supported. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- return OPERATOR_CANCELLED;
- }
-
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
+ SCULPT_face_random_access_ensure(ss);
+ SCULPT_vertex_random_access_ensure(ss);
+
const int tot_vert = SCULPT_vertex_count_get(ss);
float threshold = 0.5f;
@@ -342,8 +470,11 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
if (mode == SCULPT_FACE_SET_MASKED) {
for (int i = 0; i < tot_vert; i++) {
- if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (SCULPT_vertex_mask_get(ss, vertex) >= threshold &&
+ SCULPT_vertex_visible_get(ss, vertex)) {
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
}
@@ -355,7 +486,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
* sets and the performance hit of rendering the overlay. */
bool all_visible = true;
for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_visible_get(ss, i)) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_visible_get(ss, vertex)) {
all_visible = false;
break;
}
@@ -369,41 +502,40 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
}
for (int i = 0; i < tot_vert; i++) {
- if (SCULPT_vertex_visible_get(ss, i)) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (SCULPT_vertex_visible_get(ss, vertex)) {
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
}
if (mode == SCULPT_FACE_SET_ALL) {
for (int i = 0; i < tot_vert; i++) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
if (mode == SCULPT_FACE_SET_SELECTION) {
Mesh *mesh = ob->data;
BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
+ BMFace *f;
+ BMIter iter;
+ const int totface = ss->totfaces;
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ for (int i = 0; i < totface; i++) {
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
- BMIter iter;
- BMFace *f;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- ss->face_sets[BM_elem_index_get(f)] = next_face_set;
+ // XXX check hidden?
+ int ok = !SCULPT_face_set_flag_get(ss, fref, ME_HIDE);
+ ok = ok && SCULPT_face_set_flag_get(ss, fref, ME_FACE_SEL);
+
+ if (ok) {
+ SCULPT_face_set_set(ss, fref, next_face_set);
}
}
- BM_mesh_free(bm);
}
for (int i = 0; i < totnode; i++) {
@@ -579,25 +711,19 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
SculptSession *ss = ob->sculpt;
Mesh *mesh = ob->data;
BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_face_random_access_ensure(ss);
- BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces");
- const int totfaces = mesh->totpoly;
+ bm = sculpt_faceset_bm_begin(ss, mesh);
- int *face_sets = ss->face_sets;
+ BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces");
+ const int totfaces = ss->totfaces; // mesh->totpoly;
- BM_mesh_elem_table_init(bm, BM_FACE);
- BM_mesh_elem_table_ensure(bm, BM_FACE);
+ if (!ss->bm) {
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+ }
int next_face_set = 1;
@@ -608,7 +734,9 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
GSQueue *queue;
queue = BLI_gsqueue_new(sizeof(int));
- face_sets[i] = next_face_set;
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+ SCULPT_face_set_set(ss, fref, next_face_set);
+
BLI_BITMAP_ENABLE(visited_faces, i);
BLI_gsqueue_push(queue, &i);
@@ -635,7 +763,9 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
continue;
}
- face_sets[neighbor_face_index] = next_face_set;
+ SculptFaceRef fref2 = BKE_pbvh_table_index_to_face(ss->pbvh, neighbor_face_index);
+ SCULPT_face_set_set(ss, fref2, next_face_set);
+
BLI_BITMAP_ENABLE(visited_faces, neighbor_face_index);
BLI_gsqueue_push(queue, &neighbor_face_index);
}
@@ -649,44 +779,37 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
MEM_SAFE_FREE(visited_faces);
- BM_mesh_free(bm);
+ sculpt_faceset_bm_end(ss, bm);
}
static void sculpt_face_sets_init_loop(Object *ob, const int mode)
{
Mesh *mesh = ob->data;
SculptSession *ss = ob->sculpt;
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
+ BMesh *bm = sculpt_faceset_bm_begin(ss, mesh);
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
BMIter iter;
BMFace *f;
const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ SculptFaceRef fref = {(intptr_t)f};
+
if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
- ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1);
+ SCULPT_face_set_set(ss, fref, (int)(f->mat_nr + 1));
}
else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
if (cd_fmaps_offset != -1) {
- ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2;
+ SCULPT_face_set_set(ss, fref, BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2);
}
else {
- ss->face_sets[BM_elem_index_get(f)] = 1;
+ SCULPT_face_set_set(ss, fref, 1);
}
}
}
- BM_mesh_free(bm);
+
+ sculpt_faceset_bm_end(ss, bm);
}
static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
@@ -697,11 +820,6 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
const int mode = RNA_enum_get(op->ptr, "mode");
- /* Dyntopo not supported. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- return OPERATOR_CANCELLED;
- }
-
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;
@@ -851,11 +969,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- /* Dyntopo not supported. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- return OPERATOR_CANCELLED;
- }
-
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
@@ -884,17 +997,31 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
* be synced from face sets to non-manifold vertices. */
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_visible_get(ss, i)) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_visible_get(ss, vertex)) {
hidden_vertex = true;
break;
}
}
}
+ else if (ss->bm) {
+ BMIter iter;
+ BMFace *f;
- for (int i = 0; i < ss->totfaces; i++) {
- if (ss->face_sets[i] <= 0) {
- hidden_vertex = true;
- break;
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ if (BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset) <= 0) {
+ hidden_vertex = true;
+ break;
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < ss->totfaces; i++) {
+ if (ss->face_sets[i] <= 0) {
+ hidden_vertex = true;
+ break;
+ }
}
}
@@ -1001,22 +1128,21 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
- /* Dyntopo not supported. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- return OPERATOR_CANCELLED;
- }
-
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
int totnode;
Mesh *mesh = ob->data;
+ SCULPT_face_random_access_ensure(ss);
+
mesh->face_sets_color_seed += 1;
- if (ss->face_sets) {
+ if (ss->face_sets || (ss->bm && ss->cd_faceset_offset >= 0)) {
const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed),
0,
max_ii(0, ss->totfaces - 1));
- mesh->face_sets_color_default = ss->face_sets[random_index];
+
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, random_index);
+ mesh->face_sets_color_default = SCULPT_face_set_get(ss, fref);
}
BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
@@ -1095,12 +1221,60 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
{0, NULL, 0, NULL, NULL},
};
+static void sculpt_face_set_grow_bmesh(Object *ob,
+ SculptSession *ss,
+ const int *prev_face_sets,
+ const int active_face_set_id,
+ const bool modify_hidden)
+{
+ BMesh *bm = ss->bm;
+ BMIter iter;
+ BMFace *f;
+ BMFace **faces = NULL;
+ BLI_array_declare(faces);
+
+ if (ss->cd_faceset_offset < 0) {
+ return;
+ }
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) && !modify_hidden) {
+ continue;
+ }
+
+ int fset = abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset));
+
+ if (fset == active_face_set_id) {
+ BLI_array_append(faces, f);
+ }
+ }
+
+ for (int i = 0; i < BLI_array_len(faces); i++) {
+ BMFace *f = faces[i];
+ BMLoop *l = f->l_first;
+
+ do {
+ if (l->radial_next != l) {
+ BM_ELEM_CD_SET_INT(l->radial_next->f, ss->cd_faceset_offset, active_face_set_id);
+ }
+ l = l->next;
+ } while (l != f->l_first);
+ }
+
+ BLI_array_free(faces);
+}
+
static void sculpt_face_set_grow(Object *ob,
SculptSession *ss,
const int *prev_face_sets,
const int active_face_set_id,
const bool modify_hidden)
{
+ if (ss && ss->bm) {
+ sculpt_face_set_grow_bmesh(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
+ return;
+ }
+
Mesh *mesh = BKE_mesh_from_object(ob);
for (int p = 0; p < mesh->totpoly; p++) {
if (!modify_hidden && prev_face_sets[p] <= 0) {
@@ -1123,12 +1297,70 @@ static void sculpt_face_set_grow(Object *ob,
}
}
+static void sculpt_face_set_shrink_bmesh(Object *ob,
+ SculptSession *ss,
+ const int *prev_face_sets,
+ const int active_face_set_id,
+ const bool modify_hidden)
+{
+ BMesh *bm = ss->bm;
+ BMIter iter;
+ BMFace *f;
+ BMFace **faces = NULL;
+ BLI_array_declare(faces);
+
+ if (ss->cd_faceset_offset < 0) {
+ return;
+ }
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) && !modify_hidden) {
+ continue;
+ }
+
+ int fset = abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset));
+
+ if (fset == active_face_set_id) {
+ BLI_array_append(faces, f);
+ }
+ }
+
+ for (int i = 0; i < BLI_array_len(faces); i++) {
+ BMFace *f = faces[i];
+ BMLoop *l = f->l_first;
+
+ do {
+ if (!modify_hidden && BM_elem_flag_test(l->radial_next->f, BM_ELEM_HIDDEN)) {
+ l = l->next;
+ continue;
+ }
+
+ if (l->radial_next != l &&
+ abs(BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset)) !=
+ abs(active_face_set_id)) {
+ BM_ELEM_CD_SET_INT(f,
+ ss->cd_faceset_offset,
+ BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset));
+ break;
+ }
+ l = l->next;
+ } while (l != f->l_first);
+ }
+
+ BLI_array_free(faces);
+}
+
static void sculpt_face_set_shrink(Object *ob,
SculptSession *ss,
const int *prev_face_sets,
const int active_face_set_id,
const bool modify_hidden)
{
+ if (ss && ss->bm) {
+ sculpt_face_set_shrink_bmesh(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
+ return;
+ }
+
Mesh *mesh = BKE_mesh_from_object(ob);
for (int p = 0; p < mesh->totpoly; p++) {
if (!modify_hidden && prev_face_sets[p] <= 0) {
@@ -1153,20 +1385,28 @@ static void sculpt_face_set_shrink(Object *ob,
}
}
-static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only)
+static bool check_single_face_set(SculptSession *ss, const bool check_visible_only)
{
+ if (!ss->totfaces) {
+ return true;
+ }
int first_face_set = SCULPT_FACE_SET_NONE;
+
if (check_visible_only) {
for (int f = 0; f < ss->totfaces; f++) {
- if (face_sets[f] > 0) {
- first_face_set = face_sets[f];
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, f);
+ int fset = SCULPT_face_set_get(ss, fref);
+
+ if (fset > 0) {
+ first_face_set = fset;
break;
}
}
}
else {
- first_face_set = abs(face_sets[0]);
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, 0);
+ first_face_set = abs(SCULPT_face_set_get(ss, fref));
}
if (first_face_set == SCULPT_FACE_SET_NONE) {
@@ -1174,8 +1414,12 @@ static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool
}
for (int f = 0; f < ss->totfaces; f++) {
- const int face_set_id = check_visible_only ? face_sets[f] : abs(face_sets[f]);
- if (face_set_id != first_face_set) {
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, f);
+
+ int fset = SCULPT_face_set_get(ss, fref);
+ fset = check_visible_only ? abs(fset) : fset;
+
+ if (fset != first_face_set) {
return false;
}
}
@@ -1190,39 +1434,65 @@ static void sculpt_face_set_delete_geometry(Object *ob,
Mesh *mesh = ob->data;
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- BMesh *bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
+ if (ss->bm) {
+ BMFace **faces = NULL;
+ BLI_array_declare(faces);
+
+ BMIter iter;
+ BMFace *f;
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ const int face_set_id = modify_hidden ? abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)) :
+ BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+ if (face_set_id == active_face_set_id) {
+ BLI_array_append(faces, f);
+ }
+ }
+
+ for (int i = 0; i < BLI_array_len(faces); i++) {
+ BKE_pbvh_bmesh_remove_face(ss->pbvh, faces[i], true);
+ }
+
+ BLI_array_free(faces);
+ }
+ else {
+ BMesh *bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+
+ BM_mesh_bm_from_me(ob,
+ bm,
+ mesh,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+
+ BM_mesh_elem_table_init(bm, BM_FACE);
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+ BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ const int face_index = BM_elem_index_get(f);
+ const int face_set_id = modify_hidden ? abs(ss->face_sets[face_index]) :
+ ss->face_sets[face_index];
+ BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id);
+ }
+ BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES);
+ BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
+
+ BM_mesh_bm_to_me(NULL,
+ ob,
+ bm,
+ ob->data,
+ (&(struct BMeshToMeshParams){
+ .calc_object_remap = false,
}));
- BM_mesh_elem_table_init(bm, BM_FACE);
- BM_mesh_elem_table_ensure(bm, BM_FACE);
- BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
- BMIter iter;
- BMFace *f;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- const int face_index = BM_elem_index_get(f);
- const int face_set_id = modify_hidden ? abs(ss->face_sets[face_index]) :
- ss->face_sets[face_index];
- BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id);
- }
- BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES);
- BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
-
- BM_mesh_bm_to_me(NULL,
- bm,
- ob->data,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }));
-
- BM_mesh_free(bm);
+ BM_mesh_free(bm);
+ }
}
static void sculpt_face_set_edit_fair_face_set(Object *ob,
@@ -1230,21 +1500,31 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob,
const int fair_order)
{
SculptSession *ss = ob->sculpt;
+
const int totvert = SCULPT_vertex_count_get(ss);
Mesh *mesh = ob->data;
bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices");
+ SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < totvert; i++) {
- fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) &&
- SCULPT_vertex_has_face_set(ss, i, active_face_set_id) &&
- SCULPT_vertex_has_unique_face_set(ss, i);
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, vref) &&
+ SCULPT_vertex_has_face_set(ss, vref, active_face_set_id) &&
+ SCULPT_vertex_has_unique_face_set(ss, vref);
+ }
+
+ if (ss->bm) {
+ BKE_bmesh_prefair_and_fair_vertices(ss->bm, fair_vertices, fair_order);
+ }
+ else {
+ MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
+ BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order);
}
- MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
- BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order);
MEM_freeN(fair_vertices);
}
@@ -1257,13 +1537,13 @@ static void sculpt_face_set_apply_edit(Object *ob,
switch (mode) {
case SCULPT_FACE_SET_EDIT_GROW: {
- int *prev_face_sets = MEM_dupallocN(ss->face_sets);
+ int *prev_face_sets = ss->face_sets ? MEM_dupallocN(ss->face_sets) : NULL;
sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
MEM_SAFE_FREE(prev_face_sets);
break;
}
case SCULPT_FACE_SET_EDIT_SHRINK: {
- int *prev_face_sets = MEM_dupallocN(ss->face_sets);
+ int *prev_face_sets = ss->face_sets ? MEM_dupallocN(ss->face_sets) : NULL;
sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
MEM_SAFE_FREE(prev_face_sets);
break;
@@ -1284,10 +1564,8 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss,
const eSculptFaceSetEditMode mode,
const bool modify_hidden)
{
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- /* Dyntopo is not supported. */
- return false;
- }
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_face_random_access_ensure(ss);
if (mode == SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY) {
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
@@ -1297,7 +1575,7 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss,
* data remapping as what happens in the mesh edit mode. */
return false;
}
- if (check_single_face_set(ss, ss->face_sets, !modify_hidden)) {
+ if (check_single_face_set(ss, !modify_hidden)) {
/* Cancel the operator if the mesh only contains one Face Set to avoid deleting the
* entire object. */
return false;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index de9511bab6f..472ade36f7f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -107,7 +107,7 @@ static void color_filter_task_cb(void *__restrict userdata,
const int mode = data->filter_type;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
@@ -118,7 +118,7 @@ static void color_filter_task_cb(void *__restrict userdata,
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f) {
continue;
}
@@ -196,7 +196,7 @@ static void color_filter_task_cb(void *__restrict userdata,
case COLOR_FILTER_SMOOTH: {
fade = clamp_f(fade, -1.0f, 1.0f);
float smooth_color[4];
- SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex);
blend_color_interpolate_float(final_color, vd.col, smooth_color, fade);
break;
}
@@ -281,7 +281,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
if (!ss->pbvh) {
return OPERATOR_CANCELLED;
}
- if (BKE_pbvh_type(pbvh) != PBVH_FACES) {
+ if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
index 10f141e2311..4c6c156e361 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
@@ -119,7 +119,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
switch (mode) {
case MASK_FILTER_SMOOTH:
case MASK_FILTER_SHARPEN: {
- float val = SCULPT_neighbor_mask_average(ss, vd.index);
+ float val = SCULPT_neighbor_mask_average(ss, vd.vertex);
val -= *vd.mask;
@@ -139,7 +139,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
}
case MASK_FILTER_GROW:
max = 0.0f;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
if (vmask_f > max) {
max = vmask_f;
@@ -150,7 +150,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
break;
case MASK_FILTER_SHRINK:
min = 1.0f;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
if (vmask_f < min) {
min = vmask_f;
@@ -232,7 +232,9 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask");
for (int j = 0; j < num_verts; j++) {
- prev_mask[j] = SCULPT_vertex_mask_get(ss, j);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, j);
+
+ prev_mask[j] = SCULPT_vertex_mask_get(ss, vertex);
}
}
@@ -323,9 +325,9 @@ static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd)
zero_v3(avg);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) {
float normalized[3];
- sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.index), vd->co);
+ sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.vertex), vd->co);
normalize_v3(normalized);
add_v3_v3(avg, normalized);
total++;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index 3fc1a7674f7..c117a3a41f3 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -293,7 +293,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
const eSculptMeshFilterType filter_type = data->filter_type;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS);
/* When using the relax face sets meshes filter,
* each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */
@@ -308,7 +308,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) {
/* Surface Smooth can't skip the loop for this vertex as it needs to calculate its
@@ -326,7 +326,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
}
if (filter_type == MESH_FILTER_RELAX_FACE_SETS) {
- if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
+ if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) {
continue;
}
}
@@ -334,7 +334,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
switch (filter_type) {
case MESH_FILTER_SMOOTH:
fade = clamp_f(fade, -1.0f, 1.0f);
- SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, 0.0f);
sub_v3_v3v3(val, avg, orig_co);
madd_v3_v3v3fl(val, orig_co, val, fade);
sub_v3_v3v3(disp, val, orig_co);
@@ -397,9 +397,9 @@ static void mesh_filter_task_cb(void *__restrict userdata,
disp,
vd.co,
ss->filter_cache->surface_smooth_laplacian_disp,
- vd.index,
+ vd.vertex,
orig_data.co,
- ss->filter_cache->surface_smooth_shape_preservation);
+ ss->filter_cache->surface_smooth_shape_preservation, 0.0f);
break;
}
case MESH_FILTER_SHARPEN: {
@@ -411,10 +411,10 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float disp_sharpen[3] = {0.0f, 0.0f, 0.0f};
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float disp_n[3];
sub_v3_v3v3(
- disp_n, SCULPT_vertex_co_get(ss, ni.index), SCULPT_vertex_co_get(ss, vd.index));
+ disp_n, SCULPT_vertex_co_get(ss, ni.vertex), SCULPT_vertex_co_get(ss, vd.vertex));
mul_v3_fl(disp_n, ss->filter_cache->sharpen_factor[ni.index]);
add_v3_v3(disp_sharpen, disp_n);
}
@@ -424,7 +424,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float disp_avg[3];
float avg_co[3];
- SCULPT_neighbor_coords_average(ss, avg_co, vd.index);
+ SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex, 0.0f);
sub_v3_v3v3(disp_avg, avg_co, vd.co);
mul_v3_v3fl(
disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index]));
@@ -486,9 +486,11 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss)
filter_cache->detail_directions = MEM_malloc_arrayN(
totvert, sizeof(float[3]), "detail directions");
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f);
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -512,8 +514,10 @@ static void mesh_filter_init_limit_surface_co(SculptSession *ss)
filter_cache->limit_surface_co = MEM_malloc_arrayN(
sizeof(float[3]), totvert, "limit surface co");
+
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ SCULPT_vertex_limit_surface_get(ss, vertex, filter_cache->limit_surface_co[i]);
}
}
@@ -534,8 +538,10 @@ static void mesh_filter_sharpen_init(SculptSession *ss,
for (int i = 0; i < totvert; i++) {
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f);
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex));
filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]);
}
@@ -558,11 +564,12 @@ static void mesh_filter_sharpen_init(SculptSession *ss,
smooth_iterations++) {
for (int i = 0; i < totvert; i++) {
float direction_avg[3] = {0.0f, 0.0f, 0.0f};
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
float sharpen_avg = 0;
int total = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]);
sharpen_avg += filter_cache->sharpen_factor[ni.index];
total++;
@@ -589,7 +596,7 @@ static void mesh_filter_surface_smooth_displace_task_cb(
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f) {
continue;
}
@@ -597,7 +604,7 @@ static void mesh_filter_surface_smooth_displace_task_cb(
SCULPT_surface_smooth_displace_step(ss,
vd.co,
ss->filter_cache->surface_smooth_laplacian_disp,
- vd.index,
+ vd.vertex,
ss->filter_cache->surface_smooth_current_vertex,
clamp_f(fade, 0.0f, 1.0f));
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c
index d86d0938300..23efb04b316 100644
--- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c
+++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c
@@ -112,6 +112,48 @@ static bool sculpt_geodesic_mesh_test_dist_add(
return false;
}
+#define BMESH_INITIAL_VERT_TAG BM_ELEM_TAG_ALT
+
+static bool sculpt_geodesic_mesh_test_dist_add_bmesh(
+ BMVert *v0, BMVert *v1, BMVert *v2, float *dists, GSet *initial_vertices)
+{
+ const int v0_i = BM_elem_index_get(v0);
+ const int v1_i = BM_elem_index_get(v1);
+ const int v2_i = v2 ? BM_elem_index_get(v2) : SCULPT_GEODESIC_VERTEX_NONE;
+
+ if (BM_elem_flag_test(v0, BMESH_INITIAL_VERT_TAG)) {
+ return false;
+ }
+
+ BLI_assert(dists[v1_i] != FLT_MAX);
+ if (dists[v0_i] <= dists[v1_i]) {
+ return false;
+ }
+
+ float dist0;
+ if (v2_i != SCULPT_GEODESIC_VERTEX_NONE) {
+ BLI_assert(dists[v2_i] != FLT_MAX);
+ if (dists[v0_i] <= dists[v2_i]) {
+ return false;
+ }
+
+ dist0 = geodesic_distance_propagate_across_triangle(
+ v0->co, v1->co, v2->co, dists[v1_i], dists[v2_i]);
+ }
+ else {
+ float vec[3];
+ sub_v3_v3v3(vec, v1->co, v0->co);
+ dist0 = dists[v1_i] + len_v3(vec);
+ }
+
+ if (dist0 < dists[v0_i]) {
+ dists[v0_i] = dist0;
+ return true;
+ }
+
+ return false;
+}
+
static float *SCULPT_geodesic_mesh_create(Object *ob,
GSet *initial_vertices,
const float limit_radius)
@@ -271,6 +313,190 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
return dists;
}
+static float *SCULPT_geodesic_bmesh_create(Object *ob,
+ GSet *initial_vertices,
+ const float limit_radius)
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss->bm) {
+ return NULL;
+ }
+
+ const int totvert = ss->bm->totvert;
+ const int totedge = ss->bm->totedge;
+
+ const float limit_radius_sq = limit_radius * limit_radius;
+
+ float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
+ BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag");
+
+ BLI_LINKSTACK_DECLARE(queue, BMEdge *);
+ BLI_LINKSTACK_DECLARE(queue_next, BMEdge *);
+
+ BLI_LINKSTACK_INIT(queue);
+ BLI_LINKSTACK_INIT(queue_next);
+
+ for (int i = 0; i < totvert; i++) {
+ if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) {
+ dists[i] = 0.0f;
+ }
+ else {
+ dists[i] = FLT_MAX;
+ }
+ }
+
+ /* Masks vertices that are further than limit radius from an initial vertex. As there is no need
+ * to define a distance to them the algorithm can stop earlier by skipping them. */
+ BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex");
+ GSetIterator gs_iter;
+
+ SCULPT_vertex_random_access_ensure(ss);
+
+ BMVert *v;
+ BMIter iter;
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ BM_elem_flag_disable(v, BMESH_INITIAL_VERT_TAG);
+ }
+
+ if (limit_radius == FLT_MAX) {
+ /* In this case, no need to loop through all initial vertices to check distances as they are
+ * all going to be affected. */
+ BLI_bitmap_set_all(affected_vertex, true, totvert);
+ }
+ else {
+ /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When
+ * this optimization is needed, it is expected for the tool to request the distance to a low
+ * number of vertices (usually just 1 or 2). */
+ GSET_ITER (gs_iter, initial_vertices) {
+ const int v_i = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
+ BMVert *v = (BMVert *)BKE_pbvh_table_index_to_vertex(ss->pbvh, v_i).i;
+
+ BM_elem_flag_enable(v, BMESH_INITIAL_VERT_TAG);
+
+ for (int i = 0; i < totvert; i++) {
+ BMVert *v2 = (BMVert *)BKE_pbvh_table_index_to_vertex(ss->pbvh, i).i;
+
+ if (len_squared_v3v3(v->co, v2->co) <= limit_radius_sq) {
+ BLI_BITMAP_ENABLE(affected_vertex, i);
+ }
+ }
+ }
+ }
+
+ BMEdge *e;
+ int i = 0;
+
+ BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) {
+ const int v1_i = BM_elem_index_get(e->v1);
+ const int v2_i = BM_elem_index_get(e->v2);
+
+ if (!BLI_BITMAP_TEST(affected_vertex, v1_i) && !BLI_BITMAP_TEST(affected_vertex, v2_i)) {
+ i++;
+ continue;
+ }
+ if (dists[v1_i] != FLT_MAX || dists[v2_i] != FLT_MAX) {
+ BLI_LINKSTACK_PUSH(queue, e);
+ }
+
+ i++;
+ }
+
+ do {
+ while (BLI_LINKSTACK_SIZE(queue)) {
+ BMEdge *e = BLI_LINKSTACK_POP(queue);
+
+ BMVert *v1 = e->v1, *v2 = e->v2;
+ int v1_i = BM_elem_index_get(e->v1);
+ int v2_i = BM_elem_index_get(e->v2);
+
+ if (dists[v1_i] == FLT_MAX || dists[v2_i] == FLT_MAX) {
+ if (dists[v1_i] > dists[v2_i]) {
+ SWAP(BMVert *, v1, v2);
+ SWAP(int, v1_i, v2_i);
+ }
+ sculpt_geodesic_mesh_test_dist_add_bmesh(v2, v1, NULL, dists, initial_vertices);
+ }
+
+ BMLoop *l = e->l;
+ if (l) {
+ do {
+ BMFace *f = l->f;
+ BMLoop *l2 = f->l_first;
+
+ if (BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset) < 0) {
+ l = l->radial_next;
+ continue;
+ }
+
+ do {
+ BMVert *v_other = l2->v;
+
+ if (ELEM(v_other, v1, v2)) {
+ l2 = l2->next;
+ continue;
+ }
+
+ const int v_other_i = BM_elem_index_get(v_other);
+
+ if (sculpt_geodesic_mesh_test_dist_add_bmesh(
+ v_other, v1, v2, dists, initial_vertices)) {
+ BMIter eiter;
+ BMEdge *e_other;
+
+ BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) {
+ BMVert *ev_other;
+
+ if (e_other->v1 == v_other) {
+ ev_other = e_other->v2;
+ }
+ else {
+ ev_other = e_other->v1;
+ }
+
+ const int ev_other_i = BM_elem_index_get(ev_other);
+ const int e_other_i = BM_elem_index_get(e_other);
+
+ bool ok = e_other != e && !BLI_BITMAP_TEST(edge_tag, e_other_i);
+ ok = ok && (!e_other->l || dists[ev_other_i] != FLT_MAX);
+ ok = ok && (BLI_BITMAP_TEST(affected_vertex, v_other_i) ||
+ BLI_BITMAP_TEST(affected_vertex, ev_other_i));
+
+ if (ok) {
+ BLI_BITMAP_ENABLE(edge_tag, e_other_i);
+ BLI_LINKSTACK_PUSH(queue_next, e_other);
+ }
+ }
+ }
+
+ l2 = l2->next;
+ } while (l2 != f->l_first);
+
+ l = l->radial_next;
+ } while (l != e->l);
+ }
+ }
+
+ for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
+ BMEdge *e = (BMEdge *)lnk->link;
+ const int e_i = BM_elem_index_get(e);
+
+ BLI_BITMAP_DISABLE(edge_tag, e_i);
+ }
+
+ BLI_LINKSTACK_SWAP(queue, queue_next);
+
+ } while (BLI_LINKSTACK_SIZE(queue));
+
+ BLI_LINKSTACK_FREE(queue);
+ BLI_LINKSTACK_FREE(queue_next);
+ MEM_SAFE_FREE(edge_tag);
+ MEM_SAFE_FREE(affected_vertex);
+
+ return dists;
+}
+
/* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the
* distance to each vertex. In this case, only one of the initial vertices will be used to
* calculate the distance. */
@@ -281,14 +507,15 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
Mesh *mesh = BKE_object_get_original_mesh(ob);
const int totvert = mesh->totvert;
float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
- int first_affected = SCULPT_GEODESIC_VERTEX_NONE;
+ SculptVertRef first_affected = {SCULPT_GEODESIC_VERTEX_NONE};
+
GSetIterator gs_iter;
GSET_ITER (gs_iter, initial_vertices) {
- first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
+ first_affected.i = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
break;
}
- if (first_affected == SCULPT_GEODESIC_VERTEX_NONE) {
+ if (first_affected.i == SCULPT_GEODESIC_VERTEX_NONE) {
for (int i = 0; i < totvert; i++) {
dists[i] = FLT_MAX;
}
@@ -297,7 +524,8 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
const float *first_affected_co = SCULPT_vertex_co_get(ss, first_affected);
for (int i = 0; i < totvert; i++) {
- dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, i));
+ dists[i] = len_v3v3(first_affected_co,
+ SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)));
}
return dists;
@@ -312,6 +540,7 @@ float *SCULPT_geodesic_distances_create(Object *ob,
case PBVH_FACES:
return SCULPT_geodesic_mesh_create(ob, initial_vertices, limit_radius);
case PBVH_BMESH:
+ return SCULPT_geodesic_bmesh_create(ob, initial_vertices, limit_radius);
case PBVH_GRIDS:
return SCULPT_geodesic_fallback_create(ob, initial_vertices);
}
@@ -321,7 +550,7 @@ float *SCULPT_geodesic_distances_create(Object *ob,
float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
Object *ob,
- const int vertex,
+ const SculptVertRef vertex,
const float limit_radius)
{
SculptSession *ss = ob->sculpt;
@@ -330,7 +559,8 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char i = 0; i <= symm; ++i) {
if (SCULPT_is_symmetry_iteration_valid(i, symm)) {
- int v = -1;
+ SculptVertRef v = {-1};
+
if (i == 0) {
v = vertex;
}
@@ -339,8 +569,11 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i);
v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
- if (v != -1) {
- BLI_gset_add(initial_vertices, POINTER_FROM_INT(v));
+
+ const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
+
+ if (v_i != -1) {
+ BLI_gset_add(initial_vertices, POINTER_FROM_INT(v_i));
}
}
}
@@ -350,10 +583,19 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
return dists;
}
-float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius)
+float *SCULPT_geodesic_from_vertex(Object *ob,
+ const SculptVertRef vertex,
+ const float limit_radius)
{
+ SculptSession *ss = ob->sculpt;
+
+ SCULPT_vertex_random_access_ensure(ss);
+
GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
- BLI_gset_add(initial_vertices, POINTER_FROM_INT(vertex));
+
+ BLI_gset_add(initial_vertices,
+ POINTER_FROM_INT(BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)));
+
float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius);
BLI_gset_free(initial_vertices, NULL);
return dists;
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 696c3332a2b..f966e52ae02 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -30,12 +30,16 @@
#include "DNA_vec_types.h"
#include "BLI_bitmap.h"
+#include "BLI_compiler_compat.h"
#include "BLI_gsqueue.h"
#include "BLI_threads.h"
+#include "BKE_attribute.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
+#include "bmesh.h"
+
struct AutomaskingCache;
struct KeyBlock;
struct Object;
@@ -44,6 +48,22 @@ struct bContext;
enum ePaintSymmetryFlags;
+typedef struct SculptCustomLayer {
+ bool is_cdlayer; // false for multires data
+ void *data; // only valid for multires and face
+ int elemsize;
+ int cd_offset; // for bmesh
+ CustomDataLayer *layer; // not for multires
+} SculptCustomLayer;
+
+/*
+maximum symmetry passes returned by SCULPT_get_symmetry_pass.
+enough for about ~30 radial symmetry passes, which seems like plenty
+
+used by various code that needs to statically store per-pass state.
+*/
+#define SCULPT_MAX_SYMMETRY_PASSES 255
+
bool SCULPT_mode_poll(struct bContext *C);
bool SCULPT_mode_poll_view3d(struct bContext *C);
/* checks for a brush, not just sculpt mode */
@@ -51,6 +71,7 @@ bool SCULPT_poll(struct bContext *C);
bool SCULPT_poll_view3d(struct bContext *C);
bool SCULPT_vertex_colors_poll(struct bContext *C);
+bool SCULPT_vertex_colors_poll_no_bmesh(struct bContext *C);
/* Updates */
@@ -96,22 +117,28 @@ char SCULPT_mesh_symmetry_xyz_get(Object *object);
/* Sculpt PBVH abstraction API */
void SCULPT_vertex_random_access_ensure(struct SculptSession *ss);
+void SCULPT_face_random_access_ensure(struct SculptSession *ss);
int SCULPT_vertex_count_get(struct SculptSession *ss);
-const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index);
-void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]);
-float SCULPT_vertex_mask_get(struct SculptSession *ss, int index);
-const float *SCULPT_vertex_color_get(SculptSession *ss, int index);
-
-const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index);
-void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]);
+const float *SCULPT_vertex_co_get(struct SculptSession *ss, SculptVertRef index);
+void SCULPT_vertex_normal_get(SculptSession *ss, SculptVertRef index, float no[3]);
+float SCULPT_vertex_mask_get(struct SculptSession *ss, SculptVertRef index);
+const float *SCULPT_vertex_color_get(SculptSession *ss, SculptVertRef index);
+
+const float *SCULPT_vertex_persistent_co_get(SculptSession *ss,
+ SculptVertRef index,
+ int cd_pers_co);
+void SCULPT_vertex_persistent_normal_get(SculptSession *ss,
+ SculptVertRef index,
+ float no[3],
+ int cd_pers_no);
/* Coordinates used for manipulating the base mesh when Grab Active Vertex is enabled. */
-const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index);
+const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, SculptVertRef index);
/* Returns the info of the limit surface when Multires is available, otherwise it returns the
* current coordinate of the vertex. */
-void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]);
+void SCULPT_vertex_limit_surface_get(SculptSession *ss, SculptVertRef index, float r_co[3]);
/* Returns the pointer to the coordinates that should be edited from a brush tool iterator
* depending on the given deformation target. */
@@ -122,22 +149,26 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss,
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
typedef struct SculptVertexNeighborIter {
/* Storage */
- int *neighbors;
+ SculptVertRef *neighbors;
+ int *neighbor_indices;
+
int size;
int capacity;
- int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY];
+ SculptVertRef neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY];
+ int neighbor_indices_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY];
/* Internal iterator. */
int num_duplicates;
int i;
/* Public */
+ SculptVertRef vertex;
int index;
bool is_duplicate;
} SculptVertexNeighborIter;
void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
- const int index,
+ const SculptVertRef vref,
const bool include_duplicates,
SculptVertexNeighborIter *iter);
@@ -146,7 +177,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \
for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \
neighbor_iterator.i++) { \
- neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i];
+ neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \
+ neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i];
/* Iterate over neighboring and duplicate vertices (for PBVH_GRIDS). Duplicates come
* first since they are nearest for floodfill. */
@@ -154,7 +186,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
SCULPT_vertex_neighbors_get(ss, v_index, true, &neighbor_iterator); \
for (neighbor_iterator.i = neighbor_iterator.size - 1; neighbor_iterator.i >= 0; \
neighbor_iterator.i--) { \
- neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i]; \
+ neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \
+ neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; \
neighbor_iterator.is_duplicate = (neighbor_iterator.i >= \
neighbor_iterator.size - neighbor_iterator.num_duplicates);
@@ -165,8 +198,9 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
} \
((void)0)
-int SCULPT_active_vertex_get(SculptSession *ss);
+SculptVertRef SCULPT_active_vertex_get(SculptSession *ss);
const float *SCULPT_active_vertex_co_get(SculptSession *ss);
+const float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex);
void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]);
/* Returns PBVH deformed vertices array if shape keys or deform modifiers are used, otherwise
@@ -185,14 +219,14 @@ void SCULPT_fake_neighbors_free(struct Object *ob);
/* Vertex Info. */
void SCULPT_boundary_info_ensure(Object *object);
/* Boundary Info needs to be initialized in order to use this function. */
-bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index);
+bool SCULPT_vertex_is_boundary(const SculptSession *ss, const SculptVertRef index);
void SCULPT_connected_components_ensure(Object *ob);
/* Sculpt Visibility API */
-void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible);
-bool SCULPT_vertex_visible_get(SculptSession *ss, int index);
+void SCULPT_vertex_visible_set(SculptSession *ss, SculptVertRef index, bool visible);
+bool SCULPT_vertex_visible_get(SculptSession *ss, SculptVertRef index);
void SCULPT_visibility_sync_all_face_sets_to_vertices(struct Object *ob);
void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
@@ -200,21 +234,28 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
/* Face Sets API */
int SCULPT_active_face_set_get(SculptSession *ss);
-int SCULPT_vertex_face_set_get(SculptSession *ss, int index);
-void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set);
+int SCULPT_vertex_face_set_get(SculptSession *ss, SculptVertRef index);
+void SCULPT_vertex_face_set_set(SculptSession *ss, SculptVertRef index, int face_set);
-bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set);
-bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index);
+bool SCULPT_vertex_has_face_set(SculptSession *ss, SculptVertRef index, int face_set);
+bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, SculptVertRef index);
int SCULPT_face_set_next_available_get(SculptSession *ss);
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible);
-bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index);
-bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index);
+bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, SculptVertRef index);
+bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, SculptVertRef index);
void SCULPT_face_sets_visibility_invert(SculptSession *ss);
void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible);
+int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face);
+
+// returns previous face set
+int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset);
+int SCULPT_face_set_flag_get(SculptSession *ss, SculptFaceRef face, char flag);
+int SCULPT_face_set_flag_set(SculptSession *ss, SculptFaceRef face, char flag, bool state);
+
bool SCULPT_stroke_is_main_symmetry_pass(struct StrokeCache *cache);
bool SCULPT_stroke_is_first_brush_step(struct StrokeCache *cache);
bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cache);
@@ -224,24 +265,22 @@ typedef struct {
struct BMLog *bm_log;
struct SculptUndoNode *unode;
+ int datatype;
float (*coords)[3];
short (*normals)[3];
const float *vmasks;
float (*colors)[4];
+ short _no[3];
/* Original coordinate, normal, and mask. */
const float *co;
const short *no;
float mask;
const float *col;
+ struct PBVH *pbvh;
+ struct SculptSession *ss;
} SculptOrigVertData;
-void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
-void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
-void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data,
- Object *ob,
- struct SculptUndoNode *unode);
-
/* Utils. */
void SCULPT_calc_brush_plane(struct Sculpt *sd,
struct Object *ob,
@@ -253,11 +292,11 @@ void SCULPT_calc_brush_plane(struct Sculpt *sd,
void SCULPT_calc_area_normal(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]);
-int SCULPT_nearest_vertex_get(struct Sculpt *sd,
- struct Object *ob,
- const float co[3],
- float max_distance,
- bool use_original);
+SculptVertRef SCULPT_nearest_vertex_get(struct Sculpt *sd,
+ struct Object *ob,
+ const float co[3],
+ float max_distance,
+ bool use_original);
int SCULPT_plane_point_side(const float co[3], const float plane[4]);
int SCULPT_plane_trim(const struct StrokeCache *cache,
@@ -299,15 +338,21 @@ void SCULPT_floodfill_add_initial_with_symmetry(struct Sculpt *sd,
struct Object *ob,
struct SculptSession *ss,
SculptFloodFill *flood,
- int index,
+ SculptVertRef index,
float radius);
-void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index);
-void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index);
-void SCULPT_floodfill_execute(
- struct SculptSession *ss,
- SculptFloodFill *flood,
- bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata),
- void *userdata);
+
+void SCULPT_floodfill_add_initial(SculptFloodFill *flood, SculptVertRef index);
+void SCULPT_floodfill_add_and_skip_initial(struct SculptSession *ss,
+ SculptFloodFill *flood,
+ SculptVertRef vertex);
+void SCULPT_floodfill_execute(struct SculptSession *ss,
+ SculptFloodFill *flood,
+ bool (*func)(SculptSession *ss,
+ SculptVertRef from_v,
+ SculptVertRef to_v,
+ bool is_duplicate,
+ void *userdata),
+ void *userdata);
void SCULPT_floodfill_free(SculptFloodFill *flood);
/* Dynamic topology */
@@ -319,6 +364,11 @@ enum eDynTopoWarnFlag {
DYNTOPO_WARN_MODIFIER = (1 << 3),
};
+struct Mesh;
+
+void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss);
+void SCULPT_dynamic_topology_sync_layers(Object *ob, struct Mesh *me);
+
void SCULPT_dynamic_topology_enable_ex(struct Main *bmain,
struct Depsgraph *depsgraph,
Scene *scene,
@@ -331,8 +381,9 @@ void sculpt_dynamic_topology_disable_with_undo(struct Main *bmain,
bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush);
-void SCULPT_dynamic_topology_triangulate(struct BMesh *bm);
+void SCULPT_dynamic_topology_triangulate(struct SculptSession *ss, struct BMesh *bm);
void SCULPT_dyntopo_node_layers_add(struct SculptSession *ss);
+void SCULPT_dyntopo_save_origverts(struct SculptSession *ss);
enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob);
@@ -341,7 +392,7 @@ void SCULPT_pbvh_clear(Object *ob);
/* Auto-masking. */
float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking,
SculptSession *ss,
- int vert);
+ SculptVertRef vert);
/* Returns the automasking cache depending on the active tool. Used for code that can run both for
* brushes and filter. */
@@ -359,25 +410,28 @@ typedef enum eBoundaryAutomaskMode {
AUTOMASK_INIT_BOUNDARY_EDGES = 1,
AUTOMASK_INIT_BOUNDARY_FACE_SETS = 2,
} eBoundaryAutomaskMode;
-float *SCULPT_boundary_automasking_init(Object *ob,
- eBoundaryAutomaskMode mode,
- int propagation_steps,
- float *automask_factor);
+
+void SCULPT_boundary_automasking_init(Object *ob,
+ eBoundaryAutomaskMode mode,
+ int propagation_steps,
+ SculptCustomLayer *factorlayer);
/* Geodesic distances. */
/* Returns an array indexed by vertex index containing the geodesic distance to the closest vertex
in the initial vertex set. The caller is responsible for freeing the array.
-Geodesic distances will only work when used with PBVH_FACES, for other types of PBVH it will
-fallback to euclidean distances to one of the initial vertices in the set. */
+Geodesic distances will only work when used with PBVH_FACES or PBVH_BMESH, for other types of PBVH
+it will fallback to euclidean distances to one of the initial vertices in the set. */
float *SCULPT_geodesic_distances_create(struct Object *ob,
struct GSet *initial_vertices,
const float limit_radius);
float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd,
struct Object *ob,
- const int vertex,
+ const SculptVertRef vertex,
const float limit_radius);
-float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius);
+float *SCULPT_geodesic_from_vertex(Object *ob,
+ const SculptVertRef vertex,
+ const float limit_radius);
/* Filters. */
void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, const int undo_type);
@@ -515,7 +569,7 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain);
/* Boundary Brush. */
struct SculptBoundary *SCULPT_boundary_data_init(Object *object,
Brush *brush,
- const int initial_vertex,
+ const SculptVertRef initial_vertex,
const float radius);
void SCULPT_boundary_data_free(struct SculptBoundary *boundary);
void SCULPT_do_boundary_brush(struct Sculpt *sd,
@@ -545,23 +599,39 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* Smear Brush. */
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
-/* Smooth Brush. */
-void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct BMVert *v);
+/* Topology rake */
+void SCULPT_bmesh_four_neighbor_average(float avg[3],
+ float direction[3],
+ struct BMVert *v,
+ float projection);
-void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index);
-float SCULPT_neighbor_mask_average(SculptSession *ss, int index);
-void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index);
+/* Smoothing api */
+void SCULPT_neighbor_coords_average(SculptSession *ss,
+ float result[3],
+ SculptVertRef index,
+ float projection);
+float SCULPT_neighbor_mask_average(SculptSession *ss, SculptVertRef index);
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], SculptVertRef index);
/* Mask the mesh boundaries smoothing only the mesh surface without using automasking. */
-void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index);
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
+ float result[3],
+ SculptVertRef index,
+ float projection);
+
+void SCULPT_smooth_vcol_boundary(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength);
void SCULPT_smooth(Sculpt *sd,
Object *ob,
PBVHNode **nodes,
const int totnode,
float bstrength,
- const bool smooth_mask);
-void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+ const bool smooth_mask,
+ float projection);
+
+void SCULPT_do_smooth_brush(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection);
/* Surface Smooth Brush. */
@@ -569,13 +639,15 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
float *disp,
const float co[3],
float (*laplacian_disp)[3],
- const int v_index,
+ const SculptVertRef v_index,
const float origco[3],
- const float alpha);
+ const float alpha,
+ const float projection);
+
void SCULPT_surface_smooth_displace_step(SculptSession *ss,
float *co,
float (*laplacian_disp)[3],
- const int v_index,
+ const SculptVertRef v_index,
const float beta,
const float fade);
void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
@@ -634,8 +706,8 @@ typedef struct SculptUndoNode {
int totvert;
/* non-multires */
- int maxvert; /* to verify if totvert it still the same */
- int *index; /* to restore into right location */
+ int maxvert; /* to verify if totvert it still the same */
+ SculptVertRef *index; /* to restore into right location */
BLI_bitmap *vert_hidden;
/* multires */
@@ -672,7 +744,11 @@ typedef struct SculptUndoNode {
/* Sculpt Face Sets */
int *face_sets;
+ bool *nodemap;
+ int nodemap_size;
+
size_t undo_size;
+ // int gen, lasthash;
} SculptUndoNode;
/* Factor of brush to have rake point following behind
@@ -786,7 +862,7 @@ typedef struct SculptThreadedTaskData {
bool mask_by_color_preserve_mask;
/* Index of the vertex that is going to be used as a reference for the colors. */
- int mask_by_color_vertex;
+ SculptVertRef mask_by_color_vertex;
float *mask_by_color_floodfill;
int face_set;
@@ -797,6 +873,13 @@ typedef struct SculptThreadedTaskData {
ThreadMutex mutex;
+ // Layer brush
+ int cd_pers_co, cd_pers_no, cd_pers_disp;
+ int cd_layer_disp;
+
+ float smooth_projection;
+ float rake_projection;
+ SculptCustomLayer *scl;
} SculptThreadedTaskData;
/*************** Brush testing declarations ****************/
@@ -830,6 +913,8 @@ typedef struct {
bool original;
/* This ignores fully masked and fully hidden nodes. */
bool ignore_fully_ineffective;
+ struct Object *ob;
+ struct Brush *brush;
} SculptSearchSphereData;
typedef struct {
@@ -866,7 +951,7 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss,
const short vno[3],
const float fno[3],
const float mask,
- const int vertex_index,
+ const SculptVertRef vertex_index,
const int thread_id);
/* Tilts a normal by the x and y tilt values using the view axis. */
@@ -897,13 +982,15 @@ typedef struct AutomaskingSettings {
/* Flags from eAutomasking_flag. */
int flags;
int initial_face_set;
+ float concave_factor;
} AutomaskingSettings;
typedef struct AutomaskingCache {
AutomaskingSettings settings;
/* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
* initialized in #SCULPT_automasking_cache_init when needed. */
- float *factor;
+ // float *factor;
+ SculptCustomLayer *factorlayer;
} AutomaskingCache;
typedef struct StrokeCache {
@@ -1066,6 +1153,17 @@ typedef struct StrokeCache {
rcti previous_r; /* previous redraw rectangle */
rcti current_r; /* current redraw rectangle */
+ float stroke_distance; // copy of PaintStroke->stroke_distance
+ float stroke_distance_t; // copy of PaintStroke->stroke_distance_t
+
+ float last_dyntopo_t;
+ float last_smooth_t[SCULPT_MAX_SYMMETRY_PASSES];
+ float last_rake_t[SCULPT_MAX_SYMMETRY_PASSES];
+
+ int layer_disp_map_size;
+ BLI_bitmap *layer_disp_map;
+
+ struct PaintStroke *stroke;
} StrokeCache;
/* Sculpt Filters */
@@ -1141,7 +1239,7 @@ typedef struct ExpandCache {
* during the execution of Expand by moving the origin. */
float initial_mouse_move[2];
float initial_mouse[2];
- int initial_active_vertex;
+ SculptVertRef initial_active_vertex;
int initial_active_face_set;
/* Maximum number of vertices allowed in the SculptSession for previewing the falloff using
@@ -1302,8 +1400,17 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache,
const float angle);
void SCULPT_cache_free(StrokeCache *cache);
+void SCULPT_orig_vert_data_init(SculptOrigVertData *data,
+ Object *ob,
+ PBVHNode *node,
+ SculptUndoType type);
+void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data,
+ Object *ob,
+ struct SculptUndoNode *unode);
+
SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type);
-SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node);
+SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type);
SculptUndoNode *SCULPT_undo_get_first_node(void);
void SCULPT_undo_push_begin(struct Object *ob, const char *name);
void SCULPT_undo_push_end(void);
@@ -1370,3 +1477,99 @@ void SCULPT_OT_dyntopo_detail_size_edit(struct wmOperatorType *ot);
/* Dyntopo. */
void SCULPT_OT_dynamic_topology_toggle(struct wmOperatorType *ot);
+bool SCULPT_ensure_dyntopo_node_undo(struct Object *ob,
+ struct PBVHNode *node,
+ SculptUndoType type,
+ int extraType);
+
+float SCULPT_calc_concavity(SculptSession *ss, SculptVertRef vref);
+
+typedef struct SculptCurvatureData {
+ float ks[3];
+ float principle[3][3]; // normalized
+} SculptCurvatureData;
+
+/*
+If useAccurateSolver is false, a faster but less accurate
+power solver will be used. If true then BLI_eigen_solve_selfadjoint_m3
+will be called.
+*/
+bool SCULPT_calc_principle_curvatures(SculptSession *ss,
+ SculptVertRef vertex,
+ SculptCurvatureData *out,
+ bool useAccurateSolver);
+
+void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver);
+void SCULPT_curvature_dir_get(SculptSession *ss,
+ SculptVertRef v,
+ float dir[3],
+ bool useAccurateSolver);
+
+/*
+Ensure a named temporary layer exists, creating it if necassary.
+The layer will be marked with CD_FLAG_TEMPORARY.
+*/
+void SCULPT_dyntopo_ensure_templayer(SculptSession *ss, int type, const char *name);
+
+bool SCULPT_dyntopo_has_templayer(SculptSession *ss, int type, const char *name);
+
+/* Get a named temporary vertex customdata layer offset, if it exists. If not
+ -1 is returned.*/
+int SCULPT_dyntopo_get_templayer(SculptSession *ss, int type, const char *name);
+
+void SCULPT_dyntopo_save_persistent_base(SculptSession *ss);
+
+#define SCULPT_LAYER_PERS_CO "__dyntopo_layer_pers_co"
+#define SCULPT_LAYER_PERS_NO "__dyntopo_layer_pers_no"
+#define SCULPT_LAYER_PERS_DISP "__dyntopo_layer_pers_disp"
+#define SCULPT_LAYER_DISP "__dyntopo_layer_disp"
+
+// these tools don't support dynamic pbvh splitting during the stroke
+#define DYNTOPO_HAS_DYNAMIC_SPLIT(tool) \
+ (ELEM(tool, SCULPT_TOOL_DRAW_SHARP, SCULPT_TOOL_LAYER) == 0)
+
+/*get current symmetry pass index inclusive of both
+ mirror and radial symmetry*/
+int SCULPT_get_symmetry_pass(const SculptSession *ss);
+
+void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
+void SCULPT_reorder_bmesh(SculptSession *ss);
+
+// TODO: support faces
+static inline void *SCULPT_temp_cdata_get(SculptVertRef vertex, SculptCustomLayer *scl)
+{
+ if (scl->data) {
+ char *p = (char *)scl->data;
+ return p + scl->elemsize * (int)vertex.i;
+ }
+ else {
+ BMVert *v = (BMVert *)vertex.i;
+ return BM_ELEM_CD_GET_VOID_P(v, scl->cd_offset);
+ }
+}
+
+/*
+create a custom vertex or face attribute.
+always create all of your attributes together with SCULPT_temp_customlayer_ensure,
+
+then initialize their SculptCustomLayer's with SCULPT_temp_customlayer_get
+afterwards. Otherwise customdata offsets will be wrong (for PBVH_BMESH).
+
+return true on success. if false, layer was not created.
+
+Access per element data with SCULPT_temp_cdata_get.
+*/
+bool SCULPT_temp_customlayer_ensure(SculptSession *ss,
+ AttributeDomain domain,
+ int proptype,
+ char *name);
+bool SCULPT_temp_customlayer_get(
+ SculptSession *ss, AttributeDomain domain, int proptype, char *name, SculptCustomLayer *scl);
+
+bool SCULPT_dyntopo_automasking_init(const SculptSession *ss,
+ const Brush *br,
+ const Sculpt *sd,
+ DyntopoMaskCB *r_mask_cb,
+ void **r_mask_cb_data);
+void SCULPT_dyntopo_automasking_end(void *mask_data);
+void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
index 9b06b2ee5d5..cf34fdc1506 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
@@ -117,7 +117,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
int vi = vd.index;
float final_mask = *vd.mask;
if (data->mask_expand_use_normals) {
- if (ss->filter_cache->normal_factor[SCULPT_active_vertex_get(ss)] <
+ if (ss->filter_cache->normal_factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss))] <
ss->filter_cache->normal_factor[vd.index]) {
final_mask = 1.0f;
}
@@ -137,7 +137,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
if (data->mask_expand_create_face_set) {
if (final_mask == 1.0f) {
- SCULPT_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set);
+ SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set);
}
BKE_pbvh_node_mark_redraw(node);
}
@@ -188,7 +188,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
mouse[1] = event->mval[1];
if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) {
/* The cursor is over the mesh, get the update iteration from the updated active vertex. */
- mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)];
+ int vi = BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss));
+ mask_expand_update_it = ss->filter_cache->mask_update_it[vi];
}
else {
/* When the cursor is outside the mesh, affect the entire connected component. */
@@ -309,10 +310,13 @@ typedef struct MaskExpandFloodFillData {
} MaskExpandFloodFillData;
static bool mask_expand_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_vref, SculptVertRef to_vref, bool is_duplicate, void *userdata)
{
MaskExpandFloodFillData *data = userdata;
+ int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref);
+ int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref);
+
if (!is_duplicate) {
int to_it = ss->filter_cache->mask_update_it[from_v] + 1;
ss->filter_cache->mask_update_it[to_v] = to_it;
@@ -322,8 +326,8 @@ static bool mask_expand_floodfill_cb(
if (data->use_normals) {
float current_normal[3], prev_normal[3];
- SCULPT_vertex_normal_get(ss, to_v, current_normal);
- SCULPT_vertex_normal_get(ss, from_v, prev_normal);
+ SCULPT_vertex_normal_get(ss, to_vref, current_normal);
+ SCULPT_vertex_normal_get(ss, from_vref, prev_normal);
const float from_edge_factor = ss->filter_cache->edge_factor[from_v];
ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) *
from_edge_factor;
@@ -412,13 +416,15 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
else {
ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask");
for (int i = 0; i < vertex_count; i++) {
- ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, i);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, vertex);
}
}
ss->filter_cache->mask_update_last_it = 1;
ss->filter_cache->mask_update_current_it = 1;
- ss->filter_cache->mask_update_it[SCULPT_active_vertex_get(ss)] = 0;
+ ss->filter_cache->mask_update_it[BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss))] = 0;
copy_v3_v3(ss->filter_cache->mask_expand_initial_co, SCULPT_active_vertex_co_get(ss));
@@ -437,9 +443,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
if (use_normals) {
for (int repeat = 0; repeat < 2; repeat++) {
for (int i = 0; i < vertex_count; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
float avg = 0.0f;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
avg += ss->filter_cache->normal_factor[ni.index];
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c
index 0c383cdf035..349f781c7a1 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c
@@ -115,7 +115,7 @@ static void mask_init_task_cb(void *__restrict userdata,
*vd.mask = BLI_hash_int_01(vd.index + seed);
break;
case SCULPT_MASK_INIT_RANDOM_PER_FACE_SET: {
- const int face_set = SCULPT_vertex_face_set_get(ss, vd.index);
+ const int face_set = SCULPT_vertex_face_set_get(ss, vd.vertex);
*vd.mask = BLI_hash_int_01(face_set + seed);
break;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
index f78f30a2cfd..a7f42349777 100644
--- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
+++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
@@ -107,7 +107,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
/* Sample the normal and area of the +X and -X axis individually. */
@@ -166,7 +166,6 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
-
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@@ -215,7 +214,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
index c3977b28178..12934af943f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -96,11 +96,11 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float smooth_color[4];
- SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex);
blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade);
if (vd.mvert) {
@@ -123,7 +123,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
PBVHColorBufferNode *color_buffer;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+ orig_data.datatype = SCULPT_UNDO_COLOR;
color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]);
@@ -163,7 +164,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
/* Density. */
@@ -363,6 +364,10 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_paint_brush_task_cb_ex, &settings);
+
+ if (brush->vcol_boundary_factor > 0.0f) {
+ SCULPT_smooth_vcol_boundary(sd, ob, nodes, totnode, brush->vcol_boundary_factor);
+ }
}
static void do_smear_brush_task_cb_exec(void *__restrict userdata,
@@ -392,7 +397,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
@@ -415,10 +420,10 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
- sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co);
const float *neighbor_color = ss->cache->prev_colors[ni.index];
normalize_v3_v3(vertex_disp_norm, vertex_disp);
if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) {
@@ -451,7 +456,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.index));
+ copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.vertex));
}
BKE_pbvh_vertex_iter_end;
}
@@ -465,13 +470,17 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
return;
}
+ SCULPT_vertex_random_access_ensure(ss);
+
const int totvert = SCULPT_vertex_count_get(ss);
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
if (!ss->cache->prev_colors) {
ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors");
for (int i = 0; i < totvert; i++) {
- copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, vertex));
}
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index 587ce346428..07f0f763ff9 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -172,7 +172,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
float final_pos[3];
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
@@ -198,7 +198,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
/* Apply the vertex mask to the displacement. */
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
mul_v3_fl(disp, mask * automask);
/* Accumulate the displacement. */
@@ -237,7 +237,7 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata,
float max = 0.0f;
/* Grow the factor. */
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
max = MAX2(vmask_f, max);
}
@@ -383,7 +383,8 @@ typedef struct PoseFloodFillData {
int current_face_set;
int next_face_set;
int prev_face_set;
- int next_vertex;
+ SculptVertRef next_vertex;
+ int next_vertex_index;
bool next_face_set_found;
@@ -413,11 +414,16 @@ typedef struct PoseFloodFillData {
int target_face_set;
} PoseFloodFillData;
-static bool pose_topology_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata)
+static bool pose_topology_floodfill_cb(SculptSession *ss,
+ SculptVertRef UNUSED(from_v),
+ SculptVertRef to_vref,
+ bool is_duplicate,
+ void *userdata)
{
PoseFloodFillData *data = userdata;
- const float *co = SCULPT_vertex_co_get(ss, to_v);
+ int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref);
+
+ const float *co = SCULPT_vertex_co_get(ss, to_vref);
if (data->pose_factor) {
data->pose_factor[to_v] = 1.0f;
@@ -442,15 +448,18 @@ static bool pose_topology_floodfill_cb(
return false;
}
-static bool pose_face_sets_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata)
+static bool pose_face_sets_floodfill_cb(SculptSession *ss,
+ SculptVertRef UNUSED(from_v),
+ SculptVertRef to_v,
+ bool is_duplicate,
+ void *userdata)
{
PoseFloodFillData *data = userdata;
- const int index = to_v;
+ const int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
bool visit_next = false;
- const float *co = SCULPT_vertex_co_get(ss, index);
+ const float *co = SCULPT_vertex_co_get(ss, to_v);
const bool symmetry_check = SCULPT_check_vertex_pivot_symmetry(
co, data->pose_initial_co, data->symm) &&
!is_duplicate;
@@ -464,11 +473,11 @@ static bool pose_face_sets_floodfill_cb(
if (sculpt_pose_brush_is_vertex_inside_brush_radius(
co, data->pose_initial_co, data->radius, data->symm)) {
- const int visited_face_set = SCULPT_vertex_face_set_get(ss, index);
+ const int visited_face_set = SCULPT_vertex_face_set_get(ss, to_v);
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(visited_face_set));
}
else if (symmetry_check) {
- data->current_face_set = SCULPT_vertex_face_set_get(ss, index);
+ data->current_face_set = SCULPT_vertex_face_set_get(ss, to_v);
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(data->current_face_set));
}
return true;
@@ -482,11 +491,11 @@ static bool pose_face_sets_floodfill_cb(
GSetIterator gs_iter;
GSET_ITER (gs_iter, data->visited_face_sets) {
const int visited_face_set = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
- is_vertex_valid |= SCULPT_vertex_has_face_set(ss, index, visited_face_set);
+ is_vertex_valid |= SCULPT_vertex_has_face_set(ss, to_v, visited_face_set);
}
}
else {
- is_vertex_valid = SCULPT_vertex_has_face_set(ss, index, data->current_face_set);
+ is_vertex_valid = SCULPT_vertex_has_face_set(ss, to_v, data->current_face_set);
}
if (!is_vertex_valid) {
@@ -501,11 +510,11 @@ static bool pose_face_sets_floodfill_cb(
/* Fallback origin accumulation. */
if (symmetry_check) {
- add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, index));
+ add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, to_v));
data->fallback_count++;
}
- if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, index)) {
+ if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, to_v)) {
return visit_next;
}
@@ -514,15 +523,15 @@ static bool pose_face_sets_floodfill_cb(
bool count_as_boundary = false;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.index);
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, to_v, ni) {
+ int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.vertex);
/* Check if we can get a valid face set for the next iteration from this neighbor. */
- if (SCULPT_vertex_has_unique_face_set(ss, ni.index) &&
+ if (SCULPT_vertex_has_unique_face_set(ss, ni.vertex) &&
!BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(next_face_set_candidate))) {
if (!data->next_face_set_found) {
data->next_face_set = next_face_set_candidate;
- data->next_vertex = ni.index;
+ data->next_vertex = ni.vertex;
data->next_face_set_found = true;
}
count_as_boundary = true;
@@ -532,7 +541,7 @@ static bool pose_face_sets_floodfill_cb(
/* Origin accumulation. */
if (count_as_boundary) {
- add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, index));
+ add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, to_v));
data->tot_co++;
}
return visit_next;
@@ -556,8 +565,6 @@ void SCULPT_pose_calc_pose_data(Sculpt *sd,
float *r_pose_origin,
float *r_pose_factor)
{
- SCULPT_vertex_random_access_ensure(ss);
-
/* Calculate the pose rotation point based on the boundaries of the brush factor. */
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
@@ -608,7 +615,7 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata,
SculptVertexNeighborIter ni;
float avg = 0.0f;
int total = 0;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
avg += data->pose_factor[ni.index];
total++;
}
@@ -678,12 +685,15 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd,
const float initial_location[3],
const float radius)
{
+ SCULPT_vertex_random_access_ensure(ss);
const float chain_segment_len = radius * (1.0f + br->pose_offset);
float next_chain_segment_target[3];
int totvert = SCULPT_vertex_count_get(ss);
- int nearest_vertex_index = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true);
+ SculptVertRef nearest_vertex = SCULPT_nearest_vertex_get(
+ sd, ob, initial_location, FLT_MAX, true);
+ int nearest_vertex_index = BKE_pbvh_vertex_index_to_table(ss->pbvh, nearest_vertex);
/* Init the buffers used to keep track of the changes in the pose factors as more segments are
* added to the IK chain. */
@@ -768,7 +778,8 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
int current_face_set = SCULPT_FACE_SET_NONE;
int prev_face_set = SCULPT_FACE_SET_NONE;
- int current_vertex = SCULPT_active_vertex_get(ss);
+ SculptVertRef current_vertex = SCULPT_active_vertex_get(ss);
+ int current_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, current_vertex);
for (int s = 0; s < ik_chain->tot_segments; s++) {
@@ -797,6 +808,13 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
SCULPT_floodfill_execute(ss, &flood, pose_face_sets_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
+ if (!fdata.next_face_set_found) {
+ for (int i = s; i < ik_chain->tot_segments; i++) {
+ zero_v3(ik_chain->segments[i].orig);
+ }
+ break;
+ }
+
if (fdata.tot_co > 0) {
mul_v3_fl(fdata.pose_origin, 1.0f / (float)fdata.tot_co);
copy_v3_v3(ik_chain->segments[s].orig, fdata.pose_origin);
@@ -823,10 +841,15 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
return ik_chain;
}
-static bool pose_face_sets_fk_find_masked_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+static bool pose_face_sets_fk_find_masked_floodfill_cb(SculptSession *ss,
+ SculptVertRef from_vr,
+ SculptVertRef to_vr,
+ bool is_duplicate,
+ void *userdata)
{
PoseFloodFillData *data = userdata;
+ int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vr);
+ int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vr);
if (!is_duplicate) {
data->floodfill_it[to_v] = data->floodfill_it[from_v] + 1;
@@ -835,11 +858,11 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb(
data->floodfill_it[to_v] = data->floodfill_it[from_v];
}
- const int to_face_set = SCULPT_vertex_face_set_get(ss, to_v);
+ const int to_face_set = SCULPT_vertex_face_set_get(ss, to_vr);
if (!BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(to_face_set))) {
- if (SCULPT_vertex_has_unique_face_set(ss, to_v) &&
- !SCULPT_vertex_has_unique_face_set(ss, from_v) &&
- SCULPT_vertex_has_face_set(ss, from_v, to_face_set)) {
+ if (SCULPT_vertex_has_unique_face_set(ss, to_vr) &&
+ !SCULPT_vertex_has_unique_face_set(ss, from_vr) &&
+ SCULPT_vertex_has_face_set(ss, from_vr, to_face_set)) {
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(to_face_set));
@@ -854,14 +877,17 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb(
}
}
- return SCULPT_vertex_has_face_set(ss, to_v, data->initial_face_set);
+ return SCULPT_vertex_has_face_set(ss, to_vr, data->initial_face_set);
}
-static bool pose_face_sets_fk_set_weights_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool pose_face_sets_fk_set_weights_floodfill_cb(SculptSession *ss,
+ SculptVertRef UNUSED(from_v),
+ SculptVertRef to_v,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
PoseFloodFillData *data = userdata;
- data->fk_weights[to_v] = 1.0f;
+ data->fk_weights[BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v)] = 1.0f;
return !SCULPT_vertex_has_face_set(ss, to_v, data->masked_face_set);
}
@@ -872,7 +898,9 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
SculptPoseIKChain *ik_chain = pose_ik_chain_new(1, totvert);
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
+ const int active_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, active_vertex);
+
const int active_face_set = SCULPT_active_face_set_get(ss);
SculptFloodFill flood;
@@ -880,7 +908,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
SCULPT_floodfill_add_initial(&flood, active_vertex);
PoseFloodFillData fdata;
fdata.floodfill_it = MEM_calloc_arrayN(totvert, sizeof(int), "floodfill iteration");
- fdata.floodfill_it[active_vertex] = 1;
+ fdata.floodfill_it[active_vertex_i] = 1;
fdata.initial_face_set = active_face_set;
fdata.masked_face_set = SCULPT_FACE_SET_NONE;
fdata.target_face_set = SCULPT_FACE_SET_NONE;
@@ -893,9 +921,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
int origin_count = 0;
float origin_acc[3] = {0.0f};
for (int i = 0; i < totvert; i++) {
- if (fdata.floodfill_it[i] != 0 && SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) &&
- SCULPT_vertex_has_face_set(ss, i, fdata.masked_face_set)) {
- add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (fdata.floodfill_it[i] != 0 &&
+ SCULPT_vertex_has_face_set(ss, vref, fdata.initial_face_set) &&
+ SCULPT_vertex_has_face_set(ss, vref, fdata.masked_face_set)) {
+ add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, vref));
origin_count++;
}
}
@@ -904,10 +935,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
float target_acc[3] = {0.0f};
if (fdata.target_face_set != fdata.masked_face_set) {
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (fdata.floodfill_it[i] != 0 &&
- SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) &&
- SCULPT_vertex_has_face_set(ss, i, fdata.target_face_set)) {
- add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, i));
+ SCULPT_vertex_has_face_set(ss, vref, fdata.initial_face_set) &&
+ SCULPT_vertex_has_face_set(ss, vref, fdata.target_face_set)) {
+ add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, vref));
target_count++;
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index eabbfe43e03..6a37fd55562 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -24,6 +24,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_task.h"
@@ -32,6 +33,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_attribute.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
@@ -58,53 +60,177 @@
#include "RNA_define.h"
#include "bmesh.h"
-
+#ifdef PROXY_ADVANCED
+/* clang-format off */
+#include "BKE_DerivedMesh.h"
+#include "../../blenkernel/intern/pbvh_intern.h"
+/* clang-format on */
+#endif
#include <math.h>
#include <stdlib.h>
-void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index)
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection)
{
float avg[3] = {0.0f, 0.0f, 0.0f};
int total = 0;
int neighbor_count = 0;
- const bool is_boundary = SCULPT_vertex_is_boundary(ss, index);
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex);
+ const float *co = SCULPT_vertex_co_get(ss, vertex);
+ float no[3];
+
+ if (projection > 0.0f) {
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
neighbor_count++;
+
+ float tmp[3];
+ bool ok = false;
+
if (is_boundary) {
/* Boundary vertices use only other boundary vertices. */
- if (SCULPT_vertex_is_boundary(ss, ni.index)) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
+ copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
+ ok = true;
}
}
else {
/* Interior vertices use all neighbors. */
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
+ ok = true;
+ }
+
+ if (!ok) {
+ continue;
}
+
+ if (projection > 0.0f) {
+ sub_v3_v3(tmp, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+ add_v3_v3(avg, tmp);
+ }
+ else {
+ add_v3_v3(avg, tmp);
+ }
+
+ total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Do not modify corner vertices. */
if (neighbor_count <= 2) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
return;
}
/* Avoid division by 0 when there are no neighbors. */
if (total == 0) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
return;
}
mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0f) {
+ add_v3_v3(result, co);
+ }
}
+void SCULPT_neighbor_coords_average_interior_velocity(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection,
+ SculptCustomLayer *scl)
+{
+ float avg[3] = {0.0f, 0.0f, 0.0f};
+ int total = 0;
+ int neighbor_count = 0;
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex);
+ const float *co = SCULPT_vertex_co_get(ss, vertex);
+ float no[3];
+
+ if (projection > 0.0f) {
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+
+ float vel[3];
+
+ copy_v3_v3(vel, (float *)SCULPT_temp_cdata_get(vertex, scl));
+ mul_v3_fl(vel, 0.4f);
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ neighbor_count++;
+
+ float tmp[3];
+ bool ok = false;
+
+ float *vel2 = SCULPT_temp_cdata_get(ni.vertex, scl);
+
+ // propegate smooth velocities a bit
+ madd_v3_v3fl(vel2, vel, 1.0f / (float)ni.size);
+
+ if (is_boundary) {
+ /* Boundary vertices use only other boundary vertices. */
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
+ copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
+ ok = true;
+ }
+ }
+ else {
+ /* Interior vertices use all neighbors. */
+ copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
+ ok = true;
+ }
+
+ if (!ok) {
+ continue;
+ }
+
+ if (projection > 0.0f) {
+ sub_v3_v3(tmp, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+ add_v3_v3(avg, tmp);
+ }
+ else {
+ add_v3_v3(avg, tmp);
+ }
+
+ total++;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ /* Do not modify corner vertices. */
+ if (neighbor_count <= 2) {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
+ return;
+ }
+
+ /* Avoid division by 0 when there are no neighbors. */
+ if (total == 0) {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
+ return;
+ }
+
+ mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0f) {
+ add_v3_v3(result, co);
+ }
+}
/* For bmesh: Average surrounding verts based on an orthogonality measure.
* Naturally converges to a quad-like structure. */
-void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v)
+void SCULPT_bmesh_four_neighbor_average(float avg[3],
+ float direction[3],
+ BMVert *v,
+ float projection)
{
float avg_co[3] = {0.0f, 0.0f, 0.0f};
@@ -121,7 +247,8 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1;
float vec[3];
sub_v3_v3v3(vec, v_other->co, v->co);
- madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no));
+
+ madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no) * projection);
normalize_v3(vec);
/* fac is a measure of how orthogonal or parallel the edge is
@@ -140,7 +267,7 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
/* Preserve volume. */
float vec[3];
sub_v3_v3(avg, v->co);
- mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no));
+ mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no) * projection);
sub_v3_v3(avg, vec);
add_v3_v3(avg, v->co);
}
@@ -152,34 +279,60 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
/* Generic functions for laplacian smoothing. These functions do not take boundary vertices into
* account. */
-void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index)
+void SCULPT_neighbor_coords_average(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection)
{
float avg[3] = {0.0f, 0.0f, 0.0f};
+ float *co, no[3];
int total = 0;
+ if (projection > 0.0f) {
+ co = (float *)SCULPT_vertex_co_get(ss, vertex);
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex);
+
+ if (projection > 0.0f) {
+ float tmp[3];
+
+ sub_v3_v3v3(tmp, co2, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+
+ add_v3_v3(avg, tmp);
+ }
+ else {
+ add_v3_v3(avg, co2);
+ }
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0) {
+ add_v3_v3(result, co);
+ }
}
else {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
}
}
-float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
+float SCULPT_neighbor_mask_average(SculptSession *ss, SculptVertRef index)
{
float avg = 0.0f;
int total = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- avg += SCULPT_vertex_mask_get(ss, ni.index);
+ avg += SCULPT_vertex_mask_get(ss, ni.vertex);
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -190,14 +343,14 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
return SCULPT_vertex_mask_get(ss, index);
}
-void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], SculptVertRef index)
{
float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int total = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index));
+ add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.vertex));
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -241,7 +394,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp[3];
@@ -273,8 +426,10 @@ static void SCULPT_enhance_details_brush(Sculpt *sd,
for (int i = 0; i < totvert; i++) {
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f);
+ sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -290,6 +445,88 @@ static void SCULPT_enhance_details_brush(Sculpt *sd,
BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings);
}
+#ifdef PROXY_ADVANCED
+static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ const bool smooth_mask = data->smooth_mask;
+ float bstrength = data->strength;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ PBVHNode **nodes = data->nodes;
+ ProxyVertArray *p = &nodes[n]->proxyverts;
+
+ for (int i = 0; i < p->size; i++) {
+ float co[3] = {0.0f, 0.0f, 0.0f};
+ int ni = 0;
+
+# if 1
+ if (sculpt_brush_test_sq_fn(&test, p->co[i])) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(
+ ss,
+ brush,
+ p->co[i],
+ sqrtf(test.dist),
+ p->no[i],
+ p->fno[i],
+ smooth_mask ? 0.0f : (p->mask ? p->mask[i] : 0.0f),
+ p->index[i],
+ thread_id);
+# else
+ if (1) {
+ const float fade = 1.0;
+# endif
+
+ while (ni < MAX_PROXY_NEIGHBORS && p->neighbors[i][ni].node >= 0) {
+ ProxyKey *key = p->neighbors[i] + ni;
+ PBVHNode *n2 = ss->pbvh->nodes + key->node;
+
+ // printf("%d %d %d %p\n", key->node, key->pindex, ss->pbvh->totnode, n2);
+
+ if (key->pindex < 0 || key->pindex >= n2->proxyverts.size) {
+ printf("corruption!\n");
+ fflush(stdout);
+ ni++;
+ continue;
+ }
+
+ if (n2->proxyverts.co) {
+ add_v3_v3(co, n2->proxyverts.co[key->pindex]);
+ ni++;
+ }
+ }
+
+ // printf("ni %d\n", ni);
+
+ if (ni > 2) {
+ mul_v3_fl(co, 1.0f / (float)ni);
+ }
+ else {
+ copy_v3_v3(co, p->co[i]);
+ }
+
+ // printf("%f %f %f ", co[0], co[1], co[2]);
+
+ interp_v3_v3v3(p->co[i], p->co[i], co, fade);
+ }
+ }
+}
+
+#else
static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -300,6 +537,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const Brush *brush = data->brush;
const bool smooth_mask = data->smooth_mask;
float bstrength = data->strength;
+ float projection = data->smooth_projection;
PBVHVertexIter vd;
@@ -323,17 +561,17 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
- vd.index,
+ vd.vertex,
thread_id);
if (smooth_mask) {
- float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask;
+ float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask;
val *= fade * bstrength;
*vd.mask += val;
CLAMP(*vd.mask, 0.0f, 1.0f);
}
else {
float avg[3], val[3];
- SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection);
sub_v3_v3v3(val, avg, vd.co);
madd_v3_v3v3fl(val, vd.co, val, fade);
SCULPT_clip(sd, ss, vd.co, val);
@@ -344,13 +582,72 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
}
BKE_pbvh_vertex_iter_end;
}
+#endif
+
+static void do_smooth_brush_task_cb_ex_scl(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ float bstrength = data->strength;
+ float projection = data->smooth_projection;
+
+ SculptCustomLayer *scl = data->scl;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ (vd.mask ? *vd.mask : 0.0f),
+ vd.vertex,
+ thread_id);
+
+ float avg[3], val[3];
+
+ SCULPT_neighbor_coords_average_interior_velocity(ss, avg, vd.vertex, projection, scl);
+
+ sub_v3_v3v3(val, avg, vd.co);
+
+ float *vel = (float *)SCULPT_temp_cdata_get(vd.vertex, scl);
+ interp_v3_v3v3(vel, vel, val, 0.5);
+
+ madd_v3_v3v3fl(val, vd.co, vel, fade);
+
+ SCULPT_clip(sd, ss, vd.co, val);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
void SCULPT_smooth(Sculpt *sd,
Object *ob,
PBVHNode **nodes,
const int totnode,
float bstrength,
- const bool smooth_mask)
+ const bool smooth_mask,
+ float projection)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -366,6 +663,18 @@ void SCULPT_smooth(Sculpt *sd,
count = (int)(bstrength * max_iterations);
last = max_iterations * (bstrength - count * fract);
+ SculptCustomLayer scl;
+#if 0
+ bool have_scl = smooth_mask ? false :
+ SCULPT_temp_customlayer_ensure(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel");
+ if (have_scl) {
+ SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &scl);
+ }
+#else
+ bool have_scl = false;
+#endif
+
if (type == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "sculpt smooth: pmap missing");
return;
@@ -374,25 +683,41 @@ void SCULPT_smooth(Sculpt *sd,
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
+#ifdef PROXY_ADVANCED
+ int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK;
+ BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask);
+
+ BKE_pbvh_load_proxyarrays(ss->pbvh, nodes, totnode, PV_CO | PV_NO | PV_MASK);
+#endif
for (iteration = 0; iteration <= count; iteration++) {
const float strength = (iteration != count) ? 1.0f : last;
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .smooth_mask = smooth_mask,
- .strength = strength,
- };
+ SculptThreadedTaskData data = {.sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .smooth_mask = smooth_mask,
+ .strength = strength,
+ .smooth_projection = projection,
+ .scl = have_scl ? &scl : NULL};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
+ if (have_scl) {
+ BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex_scl, &settings);
+ }
+ else {
+ BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
+ }
+
+#ifdef PROXY_ADVANCED
+ BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode);
+#endif
}
}
-void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+void SCULPT_do_smooth_brush(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection)
{
SculptSession *ss = ob->sculpt;
if (ss->cache->bstrength <= 0.0f) {
@@ -401,7 +726,7 @@ void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
}
else {
/* Regular mode, smooth. */
- SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false);
+ SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false, projection);
}
}
@@ -412,18 +737,21 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
float *disp,
const float co[3],
float (*laplacian_disp)[3],
- const int v_index,
+ const SculptVertRef v_index,
const float origco[3],
- const float alpha)
+ const float alpha,
+ const float projection)
{
float laplacian_smooth_co[3];
float weigthed_o[3], weigthed_q[3], d[3];
- SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index);
+ SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index, projection);
+
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_index);
mul_v3_v3fl(weigthed_o, origco, alpha);
mul_v3_v3fl(weigthed_q, co, 1.0f - alpha);
add_v3_v3v3(d, weigthed_o, weigthed_q);
- sub_v3_v3v3(laplacian_disp[v_index], laplacian_smooth_co, d);
+ sub_v3_v3v3(laplacian_disp[index], laplacian_smooth_co, d);
sub_v3_v3v3(disp, laplacian_smooth_co, co);
}
@@ -431,22 +759,25 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
void SCULPT_surface_smooth_displace_step(SculptSession *ss,
float *co,
float (*laplacian_disp)[3],
- const int v_index,
+ const SculptVertRef v_index,
const float beta,
const float fade)
{
float b_avg[3] = {0.0f, 0.0f, 0.0f};
float b_current_vertex[3];
int total = 0;
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_index);
+
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_index, ni) {
add_v3_v3(b_avg, laplacian_disp[ni.index]);
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
if (total > 0) {
mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total);
- madd_v3_v3fl(b_current_vertex, laplacian_disp[v_index], beta);
+ madd_v3_v3fl(b_current_vertex, laplacian_disp[index], beta);
mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f));
sub_v3_v3(co, b_current_vertex);
}
@@ -469,10 +800,11 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
+
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@@ -483,12 +815,18 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp[3];
- SCULPT_surface_smooth_laplacian_step(
- ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, orig_data.co, alpha);
+ SCULPT_surface_smooth_laplacian_step(ss,
+ disp,
+ vd.co,
+ ss->cache->surface_smooth_laplacian_disp,
+ vd.vertex,
+ orig_data.co,
+ alpha,
+ data->smooth_projection);
madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f));
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@@ -524,10 +862,10 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
SCULPT_surface_smooth_displace_step(
- ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade);
+ ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade);
}
BKE_pbvh_vertex_iter_end;
}
@@ -544,12 +882,11 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
}
/* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ SculptThreadedTaskData data = {.sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .smooth_projection = brush->autosmooth_projection};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
@@ -560,3 +897,190 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
0, totnode, &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings);
}
}
+
+static void do_smooth_vcol_boundary_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ const bool smooth_mask = data->smooth_mask;
+ float bstrength = data->strength;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float tot = 0.0f;
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!vd.col) {
+ continue;
+ }
+
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(
+ ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
+ vd.vertex,
+ thread_id);
+
+ madd_v3_v3fl(avg, vd.col, fade);
+ tot += fade;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ if (tot == 0.0f) {
+ return;
+ }
+ tot = 1.0f / tot;
+
+ mul_v3_fl(avg, tot);
+
+ float exp = brush->vcol_boundary_exponent;
+ // detect bad value
+
+ if (exp == 0.0f) {
+ exp = 1.0f;
+ }
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(
+ ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
+ vd.vertex,
+ thread_id);
+ if (!vd.col) {
+ continue;
+ }
+
+ float avg2[3], avg3[3], val[3];
+ float tot2 = 0.0f, tot4 = 0.0f;
+
+ copy_v4_v4(avg, vd.col);
+
+ zero_v3(avg2);
+ zero_v3(avg3);
+
+ madd_v3_v3fl(avg2, vd.co, 0.5f);
+ tot2 += 0.5f;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
+ const float *col = SCULPT_vertex_color_get(ss, ni.vertex);
+ const float *co = SCULPT_vertex_co_get(ss, ni.vertex);
+
+ // simple color metric. TODO: plug in appropriate color space code?
+ float dv[4];
+ sub_v4_v4v4(dv, col, avg);
+ float w = (fabs(dv[0]) + fabs(dv[1]) + fabs(dv[2]) + fabs(dv[3])) / 4.0;
+
+ w = powf(w, exp);
+
+ madd_v3_v3fl(avg3, co, 1.0f);
+ tot4 += 1.0f;
+
+ madd_v3_v3fl(avg2, co, w);
+ tot2 += w;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (tot2 == 0.0f) {
+ continue;
+ }
+
+ if (tot4 > 0.0f) {
+ mul_v3_fl(avg3, 1.0f / tot4);
+ }
+
+ /* try to avoid perfectly colinear triangles, and the normal discontinuities they create,
+ by blending slightly with unweighted smoothed position */
+ mul_v3_fl(avg2, 1.0f / tot2);
+ interp_v3_v3v3(avg2, avg2, avg3, 0.025);
+
+ sub_v3_v3v3(val, avg2, vd.co);
+ madd_v3_v3v3fl(val, vd.co, val, fade);
+ SCULPT_clip(sd, ss, vd.co, val);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_smooth_vcol_boundary(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength)
+{
+ SculptSession *ss = ob->sculpt;
+
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ const int max_iterations = 4;
+ const float fract = 1.0f / max_iterations;
+ PBVHType type = BKE_pbvh_type(ss->pbvh);
+ int iteration, count;
+ float last;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ count = (int)(bstrength * max_iterations);
+ last = max_iterations * (bstrength - count * fract);
+
+ if (type == PBVH_FACES && !ss->pmap) {
+ BLI_assert(!"sculpt smooth: pmap missing");
+ return;
+ }
+
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_boundary_info_ensure(ob);
+
+#ifdef PROXY_ADVANCED
+ int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK;
+ BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask);
+
+ BKE_pbvh_load_proxyarrays(ss->pbvh, nodes, totnode, PV_CO | PV_NO | PV_MASK);
+#endif
+ for (iteration = 0; iteration <= count; iteration++) {
+ const float strength = (iteration != count) ? 1.0f : last;
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .smooth_mask = false,
+ .strength = strength,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_smooth_vcol_boundary_brush_task_cb_ex, &settings);
+
+#ifdef PROXY_ADVANCED
+ BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode);
+#endif
+ }
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index 3c0a591e8a7..8d9497f6bef 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -159,7 +159,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
PBVHNode *node = data->nodes[i];
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS);
PBVHVertexIter vd;
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 501a1e53276..df4b43d21a2 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -113,9 +113,11 @@ typedef struct UndoSculpt {
ListBase nodes;
size_t undo_size;
+ BMLog *bm_restore;
} UndoSculpt;
static UndoSculpt *sculpt_undo_get_nodes(void);
+void sculpt_undo_print_nodes(void *active);
static void update_cb(PBVHNode *node, void *rebuild)
{
@@ -133,6 +135,8 @@ struct PartialUpdateData {
char *modified_grids;
};
+static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p);
+
/**
* A version of #update_cb that tests for 'ME_VERT_PBVH_UPDATE'
*/
@@ -170,6 +174,8 @@ static bool test_swap_v3_v3(float a[3], float b[3])
return false;
}
+void pbvh_bmesh_check_nodes(PBVH *pbvh);
+
static bool sculpt_undo_restore_deformed(
const SculptSession *ss, SculptUndoNode *unode, int uindex, int oindex, float coord[3])
{
@@ -187,7 +193,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
SculptSession *ss = ob->sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
MVert *mvert;
- int *index;
+ SculptVertRef *index;
if (unode->maxvert) {
/* Regular mesh restore. */
@@ -221,18 +227,18 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
if (unode->orig_co) {
if (ss->deform_modifiers_active) {
for (int i = 0; i < unode->totvert; i++) {
- sculpt_undo_restore_deformed(ss, unode, i, index[i], vertCos[index[i]]);
+ sculpt_undo_restore_deformed(ss, unode, i, index[i].i, vertCos[index[i].i]);
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
- swap_v3_v3(vertCos[index[i]], unode->orig_co[i]);
+ swap_v3_v3(vertCos[index[i].i], unode->orig_co[i]);
}
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
- swap_v3_v3(vertCos[index[i]], unode->co[i]);
+ swap_v3_v3(vertCos[index[i].i], unode->co[i]);
}
}
@@ -249,21 +255,21 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
if (unode->orig_co) {
if (ss->deform_modifiers_active) {
for (int i = 0; i < unode->totvert; i++) {
- sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ sculpt_undo_restore_deformed(ss, unode, i, index[i].i, mvert[index[i].i].co);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
- swap_v3_v3(mvert[index[i]].co, unode->orig_co[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ swap_v3_v3(mvert[index[i].i].co, unode->orig_co[i]);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
- swap_v3_v3(mvert[index[i]].co, unode->co[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ swap_v3_v3(mvert[index[i].i].co, unode->co[i]);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
}
@@ -303,7 +309,7 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode)
MVert *mvert = ss->mvert;
for (int i = 0; i < unode->totvert; i++) {
- MVert *v = &mvert[unode->index[i]];
+ MVert *v = &mvert[unode->index[i].i];
if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) {
BLI_BITMAP_FLIP(unode->vert_hidden, i);
v->flag ^= ME_HIDE;
@@ -330,13 +336,13 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
if (unode->maxvert) {
/* regular mesh restore */
- int *index = unode->index;
+ SculptVertRef *index = unode->index;
MVert *mvert = ss->mvert;
MPropCol *vcol = ss->vcol;
for (int i = 0; i < unode->totvert; i++) {
- copy_v4_v4(vcol[index[i]].color, unode->col[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ swap_v4_v4(vcol[index[i].i].color, unode->col[i]);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
return true;
@@ -350,7 +356,7 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
MVert *mvert;
float *vmask;
- int *index;
+ SculptVertRef *index;
if (unode->maxvert) {
/* Regular mesh restore. */
@@ -360,9 +366,9 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
vmask = ss->vmask;
for (int i = 0; i < unode->totvert; i++) {
- if (vmask[index[i]] != unode->mask[i]) {
- SWAP(float, vmask[index[i]], unode->mask[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ if (vmask[index[i].i] != unode->mask[i]) {
+ SWAP(float, vmask[index[i].i], unode->mask[i]);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
}
@@ -397,7 +403,7 @@ static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode)
Mesh *me = BKE_object_get_original_mesh(ob);
int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
for (int i = 0; i < me->totpoly; i++) {
- face_sets[i] = unode->face_sets[i];
+ SWAP(int, face_sets[i], unode->face_sets[i]);
}
return false;
}
@@ -410,18 +416,152 @@ static void sculpt_undo_bmesh_restore_generic_task_cb(
BKE_pbvh_node_mark_redraw(nodes[n]);
}
+extern const char dyntopop_node_idx_layer_id[];
+
+typedef struct BmeshUndoData {
+ PBVH *pbvh;
+ BMesh *bm;
+ bool do_full_recalc;
+ bool balance_pbvh;
+ int cd_face_node_offset, cd_vert_node_offset;
+} BmeshUndoData;
+
+static void bmesh_undo_on_vert_kill(BMVert *v, void *userdata)
+{
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+ // data->do_full_recalc = true;
+
+ if (BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset) < 0) {
+ // something went wrong
+ printf("pbvh bmesh undo error\n");
+ data->do_full_recalc = true;
+ return;
+ }
+ else {
+ int ni = BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset);
+
+ /*
+ regenerate bm_unique_verts, which can end up with
+ freed verts for some reason. I've run this through
+ ASAN and fixed one likely cause, but it still happens.
+ - joeedh
+ */
+ if (ni >= 0) {
+ PBVHNode *node = BKE_pbvh_get_node(data->pbvh, ni);
+ BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node);
+ }
+ }
+
+ BKE_pbvh_bmesh_remove_vertex(data->pbvh, v, false);
+ data->balance_pbvh = true;
+}
+static void bmesh_undo_on_vert_add(BMVert *v, void *userdata)
+{
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+
+ BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1);
+ // data->do_full_recalc = true;
+ data->balance_pbvh = true;
+}
+static void bmesh_undo_on_face_kill(BMFace *f, void *userdata)
+{
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+ int ni = BM_ELEM_CD_GET_INT(f, data->cd_face_node_offset);
+
+ /*
+ regenerate bm_unique_verts, which can end up with
+ freed verts for some reason. I've run this through
+ ASAN and fixed one likely cause, but it still happens.
+ - joeedh
+ */
+ if (ni >= 0) {
+ PBVHNode *node = BKE_pbvh_get_node(data->pbvh, ni);
+ BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node);
+ }
+
+ BKE_pbvh_bmesh_remove_face(data->pbvh, f, false);
+
+ // data->do_full_recalc = true;
+ data->balance_pbvh = true;
+}
+static void bmesh_undo_on_face_add(BMFace *f, void *userdata)
+{
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+ // data->do_full_recalc = true;
+
+ BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, -1);
+ BKE_pbvh_bmesh_add_face(data->pbvh, f, false, true);
+
+ data->balance_pbvh = true;
+}
+static void bmesh_undo_full_mesh(void *userdata)
+{
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+ data->do_full_recalc = true;
+}
+
+static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_customdata)
+{
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+
+ if (!old_customdata) {
+ BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1);
+ return;
+ }
+
+ BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1);
+ return;
+ // preserve pbvh node references
+
+ BMVert h;
+ h.head.data = old_customdata;
+
+ int oldnode_i = BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset);
+
+ BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, oldnode_i);
+
+ if (oldnode_i >= 0) {
+ PBVHNode *node = BKE_pbvh_node_from_index(data->pbvh, oldnode_i);
+ BKE_pbvh_node_mark_update(node);
+ }
+}
+
+static void bmesh_undo_on_face_change(BMFace *f, void *userdata, void *old_customdata)
+{
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+
+ // vert will be added back to pbvh when its owning faces are
+ BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, -1);
+}
+
static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, SculptSession *ss)
{
+ BmeshUndoData data = {
+ ss->pbvh, ss->bm, false, false, ss->cd_face_node_offset, ss->cd_vert_node_offset};
+
+ BMLogCallbacks callbacks = {bmesh_undo_on_vert_add,
+ bmesh_undo_on_vert_kill,
+ bmesh_undo_on_vert_change,
+ bmesh_undo_on_face_add,
+ bmesh_undo_on_face_kill,
+ bmesh_undo_on_face_change,
+ bmesh_undo_full_mesh,
+ (void *)&data};
+
if (unode->applied) {
- BM_log_undo(ss->bm, ss->bm_log);
+ BM_log_undo(ss->bm, ss->bm_log, &callbacks, dyntopop_node_idx_layer_id);
unode->applied = false;
}
else {
- BM_log_redo(ss->bm, ss->bm_log);
+ BM_log_redo(ss->bm, ss->bm_log, &callbacks, dyntopop_node_idx_layer_id);
unode->applied = true;
}
- if (unode->type == SCULPT_UNDO_MASK) {
+ BKE_pbvh_bmesh_regen_node_verts(ss->pbvh);
+ pbvh_bmesh_check_nodes(ss->pbvh);
+
+ if (!data.do_full_recalc || unode->type == SCULPT_UNDO_MASK ||
+ unode->type == SCULPT_UNDO_COLOR) {
int totnode;
PBVHNode **nodes;
@@ -435,6 +575,12 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob,
if (nodes) {
MEM_freeN(nodes);
}
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ if (data.balance_pbvh) {
+ BKE_pbvh_bmesh_after_stroke(ss->pbvh);
+ }
}
else {
SCULPT_pbvh_clear(ob);
@@ -448,18 +594,27 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode)
Mesh *me = ob->data;
SCULPT_pbvh_clear(ob);
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
/* Create empty BMesh and enable logging. */
ss->bm = BM_mesh_create(&bm_mesh_allocsize_default,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
- BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK);
+ &((struct BMeshCreateParams){.use_toolflags = false,
+ .use_unique_ids = true,
+ .use_id_elem_mask = BM_VERT | BM_FACE,
+ .use_id_map = true}));
SCULPT_dyntopo_node_layers_add(ss);
+
me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
- /* Restore the BMLog using saved entries. */
- ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
+ ss->bm_log = BM_log_unfreeze(ss->bm, unode->bm_entry);
+
+ if (!ss->bm_log) {
+ /* Restore the BMLog using saved entries. */
+ ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
+ }
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
}
static void sculpt_undo_bmesh_restore_begin(bContext *C,
@@ -475,7 +630,7 @@ static void sculpt_undo_bmesh_restore_begin(bContext *C,
sculpt_undo_bmesh_enable(ob, unode);
/* Restore the mesh from the first log entry. */
- BM_log_redo(ss->bm, ss->bm_log);
+ BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
unode->applied = true;
}
@@ -486,11 +641,12 @@ static void sculpt_undo_bmesh_restore_end(bContext *C,
Object *ob,
SculptSession *ss)
{
+
if (unode->applied) {
sculpt_undo_bmesh_enable(ob, unode);
/* Restore the mesh from the last log entry. */
- BM_log_undo(ss->bm, ss->bm_log);
+ BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
unode->applied = false;
}
@@ -499,6 +655,10 @@ static void sculpt_undo_bmesh_restore_end(bContext *C,
SCULPT_dynamic_topology_disable(C, NULL);
unode->applied = true;
}
+
+ if (ss->bm) {
+ BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
+ }
}
static void sculpt_undo_geometry_store_data(SculptUndoNodeGeometry *geometry, Object *object)
@@ -590,23 +750,91 @@ static int sculpt_undo_bmesh_restore(bContext *C,
Object *ob,
SculptSession *ss)
{
+ if (ss->bm_log && ss->bm &&
+ !ELEM(unode->type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) {
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
+
+ if (ss->active_face_index.i && ss->active_face_index.i != -1LL) {
+ ss->active_face_index.i = (intptr_t)BM_log_face_id_get(ss->bm_log,
+ (BMFace *)ss->active_face_index.i);
+ }
+ else {
+ ss->active_face_index.i = -1;
+ }
+
+ if (ss->active_vertex_index.i && ss->active_vertex_index.i != -1LL) {
+ ss->active_vertex_index.i = (intptr_t)BM_log_vert_id_get(
+ ss->bm_log, (BMVert *)ss->active_vertex_index.i);
+ }
+ else {
+ ss->active_vertex_index.i = -1;
+ }
+ }
+ else {
+ ss->active_face_index.i = ss->active_vertex_index.i = -1;
+ }
+
+ bool ret = false;
+
switch (unode->type) {
case SCULPT_UNDO_DYNTOPO_BEGIN:
sculpt_undo_bmesh_restore_begin(C, unode, ob, ss);
- return true;
-
+ SCULPT_vertex_random_access_ensure(ss);
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
+ ret = true;
+ break;
case SCULPT_UNDO_DYNTOPO_END:
- sculpt_undo_bmesh_restore_end(C, unode, ob, ss);
- return true;
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
+
+ if (ss->bm) {
+ sculpt_undo_bmesh_restore_end(C, unode, ob, ss);
+ }
+ SCULPT_vertex_random_access_ensure(ss);
+ ret = true;
+ break;
default:
if (ss->bm_log) {
sculpt_undo_bmesh_restore_generic(unode, ob, ss);
- return true;
+ SCULPT_vertex_random_access_ensure(ss);
+ ret = true;
}
break;
}
- return false;
+ if (ss->bm_log && ss->bm) {
+ if (ss->active_face_index.i != -1) {
+ BMFace *f = BM_log_id_face_get(ss->bm_log, (uint)ss->active_face_index.i);
+ if (f && f->head.htype == BM_FACE) {
+ ss->active_face_index.i = (intptr_t)f;
+ }
+ else {
+ ss->active_face_index.i = 0LL;
+ }
+ }
+ else {
+ ss->active_face_index.i = 0LL;
+ }
+
+ if (ss->active_vertex_index.i != -1) {
+ BMVert *v = BM_log_id_vert_get(ss->bm_log, (uint)ss->active_vertex_index.i);
+
+ if (v && v->head.htype == BM_VERT) {
+ ss->active_vertex_index.i = (intptr_t)v;
+ }
+ else {
+ ss->active_vertex_index.i = 0LL;
+ }
+ }
+ else {
+ ss->active_vertex_index.i = 0LL;
+ }
+ }
+ else {
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
+ }
+
+ return ret;
}
/* Geometry updates (such as Apply Base, for example) will re-evaluate the object and refine its
@@ -647,8 +875,36 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
bool update = false, rebuild = false, update_mask = false, update_visibility = false;
bool need_mask = false;
bool need_refine_subdiv = false;
+ bool did_first_hack = false;
for (unode = lb->first; unode; unode = unode->next) {
+ if (unode->bm_entry && !ss->bm) {
+ // file loading breaks undo because the stack isn't initialized
+ // detect that case and try to fix it
+
+ did_first_hack = true;
+
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
+ SCULPT_dynamic_topology_enable_ex(CTX_data_main(C), depsgraph, scene, ob);
+
+ // see if we have a saved log in the entry
+ BMLog *log = BM_log_unfreeze(ss->bm, unode->bm_entry);
+
+ if (log) {
+ if (ss->bm_log) {
+ BM_log_free(ss->bm_log, false);
+ }
+
+ ss->bm_log = log;
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
+ }
+
+ // PBVH is corrupted at this point, destroy it
+ SCULPT_pbvh_clear(ob);
+ }
+
/* Restore pivot. */
copy_v3_v3(ss->pivot_pos, unode->pivot_pos);
copy_v3_v3(ss->pivot_rot, unode->pivot_rot);
@@ -664,7 +920,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
- if (lb->first) {
+ sculpt_undo_print_nodes(NULL);
+
+ if (!ss->bm && lb->first) {
unode = lb->first;
if (unode->type == SCULPT_UNDO_FACE_SETS) {
sculpt_undo_restore_face_sets(C, unode);
@@ -719,6 +977,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
* continue. */
if (unode->maxvert) {
if (ss->totvert != unode->maxvert) {
+ printf("error! %s\n", __func__);
continue;
}
}
@@ -859,6 +1118,9 @@ static void sculpt_undo_free_list(ListBase *lb)
if (unode->co) {
MEM_freeN(unode->co);
}
+ if (unode->nodemap) {
+ MEM_freeN(unode->nodemap);
+ }
if (unode->no) {
MEM_freeN(unode->no);
}
@@ -888,6 +1150,7 @@ static void sculpt_undo_free_list(ListBase *lb)
if (unode->bm_entry) {
BM_log_entry_drop(unode->bm_entry);
+ unode->bm_entry = NULL;
}
sculpt_undo_geometry_free_data(&unode->geometry_original);
@@ -927,7 +1190,28 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
}
#endif
-SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node)
+static int hash_sculpt_colors(SculptUndoNode *node)
+{
+ if (!node->col) {
+ return -1;
+ }
+
+ int i = 0;
+ int hash = 0;
+
+ for (i = 0; i < node->totvert; i++) {
+ float *col = node->col[i];
+
+ for (int j = 0; j < 4; j++) {
+ hash = hash ^ (int)(col[j] * 2048.0f * 2048.0f);
+ hash += (1 << 23) - 1;
+ }
+ }
+
+ return hash;
+}
+
+SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type)
{
UndoSculpt *usculpt = sculpt_undo_get_nodes();
@@ -935,7 +1219,19 @@ SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node)
return NULL;
}
- return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node));
+ if (type < 0) {
+ return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node));
+ }
+
+ SculptUndoNode *unode;
+
+ for (unode = usculpt->nodes.first; unode; unode = unode->next) {
+ if (unode->node == node && type == unode->type) {
+ return unode;
+ }
+ }
+
+ return NULL;
}
SculptUndoNode *SCULPT_undo_get_first_node()
@@ -1104,6 +1400,9 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode)
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
+ SculptOrigVertData orig_data;
+ SCULPT_orig_vert_data_unode_init(&orig_data, ob, unode);
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) {
copy_v3_v3(unode->co[vd.i], vd.co);
if (vd.no) {
@@ -1114,7 +1413,11 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode)
}
if (ss->deform_modifiers_active) {
- copy_v3_v3(unode->orig_co[vd.i], ss->orig_cos[unode->index[vd.i]]);
+ SCULPT_orig_vert_data_update(&orig_data, &vd);
+
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, unode->index[vd.i]);
+
+ copy_v3_v3(unode->orig_co[vd.i], orig_data.co);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1157,6 +1460,8 @@ static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode)
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
+ // unode->gen++;
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) {
copy_v4_v4(unode->col[vd.i], vd.col);
}
@@ -1217,7 +1522,10 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
SculptUndoNode *unode = usculpt->nodes.first;
+ bool new_node = false;
+
if (unode == NULL) {
+ new_node = true;
unode = MEM_callocN(sizeof(*unode), __func__);
BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
@@ -1225,8 +1533,10 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
unode->applied = true;
if (type == SCULPT_UNDO_DYNTOPO_END) {
- unode->bm_entry = BM_log_entry_add(ss->bm_log);
- BM_log_before_all_removed(ss->bm, ss->bm_log);
+ unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
+ BM_log_full_mesh(ss->bm, ss->bm_log);
+
+ // BM_log_before_all_removed(ss->bm, ss->bm_log);
}
else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) {
/* Store a copy of the mesh's current vertices, loops, and
@@ -1237,56 +1547,140 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter;
sculpt_undo_geometry_store_data(geometry, ob);
- unode->bm_entry = BM_log_entry_add(ss->bm_log);
- BM_log_all_added(ss->bm, ss->bm_log);
+ unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
+ // BM_log_all_added(ss->bm, ss->bm_log);
+ BM_log_full_mesh(ss->bm, ss->bm_log);
}
else {
- unode->bm_entry = BM_log_entry_add(ss->bm_log);
+ unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
}
BLI_addtail(&usculpt->nodes, unode);
}
if (node) {
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ unode->bm_entry = BM_log_entry_check_customdata(ss->bm, ss->bm_log);
+ }
+
switch (type) {
case SCULPT_UNDO_COORDS:
case SCULPT_UNDO_MASK:
- /* Before any vertex values get modified, ensure their
- * original positions are logged. */
- BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
- BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset);
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ float *dummy;
+ BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, false);
}
BKE_pbvh_vertex_iter_end;
break;
case SCULPT_UNDO_HIDDEN: {
- GSetIterator gs_iter;
- GSet *faces = BKE_pbvh_bmesh_node_faces(node);
- BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
- BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset);
+ TableGSet *faces = BKE_pbvh_bmesh_node_faces(node);
+ BMFace *f;
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, true);
}
BKE_pbvh_vertex_iter_end;
- GSET_ITER (gs_iter, faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
+ TGSET_ITER (f, faces) {
BM_log_face_modified(ss->bm_log, f);
}
+ TGSET_ITER_END
+ break;
+ }
+
+ case SCULPT_UNDO_COLOR: {
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ float *dummy;
+ BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, true);
+ }
+ BKE_pbvh_vertex_iter_end;
break;
}
+ case SCULPT_UNDO_FACE_SETS: {
+ TableGSet *faces = BKE_pbvh_bmesh_node_faces(node);
+ BMFace *f;
+ TGSET_ITER (f, faces) {
+ BM_log_face_modified(ss->bm_log, f);
+ }
+ TGSET_ITER_END
+
+ break;
+ }
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
case SCULPT_UNDO_GEOMETRY:
- case SCULPT_UNDO_FACE_SETS:
- case SCULPT_UNDO_COLOR:
break;
}
}
+ else {
+ switch (type) {
+ case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
+ case SCULPT_UNDO_GEOMETRY:
+ BM_log_full_mesh(ss->bm, ss->bm_log);
+ break;
+ }
+ }
+
+ if (new_node) {
+ sculpt_undo_print_nodes(NULL);
+ }
return unode;
}
+bool SCULPT_ensure_dyntopo_node_undo(Object *ob,
+ PBVHNode *node,
+ SculptUndoType type,
+ int extraType)
+{
+ SculptSession *ss = ob->sculpt;
+ UndoSculpt *usculpt = sculpt_undo_get_nodes();
+ SculptUndoNode *unode = usculpt->nodes.first;
+
+ if (!unode || unode->type != type) {
+ unode = sculpt_undo_alloc_node_type(ob, type);
+
+ BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
+
+ unode->type = type;
+ unode->applied = true;
+ unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
+
+ return SCULPT_ensure_dyntopo_node_undo(ob, node, type, extraType);
+ }
+
+ int n = BKE_pbvh_get_node_id(ss->pbvh, node);
+
+ if (unode->nodemap_size <= n) {
+ int newsize = (n + 1) * 2;
+
+ if (!unode->nodemap) {
+ unode->nodemap = MEM_callocN(sizeof(*unode->nodemap) * newsize, "unode->nodemap");
+ }
+ else {
+ unode->nodemap = MEM_recallocN(unode->nodemap, sizeof(*unode->nodemap) * newsize);
+ }
+
+ unode->nodemap_size = newsize;
+ }
+
+ if (unode->nodemap[n]) {
+ return false;
+ }
+
+ unode->nodemap[n] = 1;
+ sculpt_undo_bmesh_push(ob, node, type);
+
+ if (extraType >= 0) {
+ sculpt_undo_bmesh_push(ob, node, extraType);
+ }
+
+ return true;
+}
+
SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type)
{
SculptSession *ss = ob->sculpt;
@@ -1301,20 +1695,24 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
/* Dynamic topology stores only one undo node per stroke,
* regardless of the number of PBVH nodes modified. */
unode = sculpt_undo_bmesh_push(ob, node, type);
+ sculpt_undo_print_nodes(NULL);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
if (type == SCULPT_UNDO_GEOMETRY) {
unode = sculpt_undo_geometry_push(ob, type);
+ sculpt_undo_print_nodes(NULL);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
if (type == SCULPT_UNDO_FACE_SETS) {
unode = sculpt_undo_face_sets_push(ob, type);
+ sculpt_undo_print_nodes(NULL);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
- if ((unode = SCULPT_undo_get_node(node))) {
+ if ((unode = SCULPT_undo_get_node(node, type))) {
+ sculpt_undo_print_nodes(NULL);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
@@ -1336,7 +1734,11 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
int allvert;
BKE_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert);
BKE_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL);
- memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert);
+
+ for (int i = 0; i < unode->totvert; i++) {
+ unode->index[i].i = vert_indices[i];
+ }
+ // memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert);
}
switch (type) {
@@ -1373,6 +1775,8 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
unode->shapeName[0] = '\0';
}
+ sculpt_undo_print_nodes(NULL);
+
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
@@ -1434,6 +1838,7 @@ typedef struct SculptUndoStep {
UndoStep step;
/* NOTE: will split out into list for multi-object-sculpt-mode. */
UndoSculpt data;
+ int id;
} SculptUndoStep;
static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
@@ -1472,6 +1877,8 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C,
BLI_assert(us->step.is_applied == true);
sculpt_undo_restore_list(C, depsgraph, &us->data.nodes);
us->step.is_applied = false;
+
+ sculpt_undo_print_nodes(us);
}
static void sculpt_undosys_step_decode_redo_impl(struct bContext *C,
@@ -1481,6 +1888,8 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C,
BLI_assert(us->step.is_applied == false);
sculpt_undo_restore_list(C, depsgraph, &us->data.nodes);
us->step.is_applied = true;
+
+ sculpt_undo_print_nodes(us);
}
static void sculpt_undosys_step_decode_undo(struct bContext *C,
@@ -1632,6 +2041,10 @@ static UndoSculpt *sculpt_undo_get_nodes(void)
return sculpt_undosys_step_get_nodes(us);
}
+void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss)
+{
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1726,3 +2139,106 @@ void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str)
}
/** \} */
+
+#ifdef _
+# undef _
+#endif
+#define _(type) \
+ case type: \
+ return #type;
+static char *undo_type_to_str(int type)
+{
+ switch (type) {
+ _(SCULPT_UNDO_DYNTOPO_BEGIN)
+ _(SCULPT_UNDO_DYNTOPO_END)
+ _(SCULPT_UNDO_COORDS)
+ _(SCULPT_UNDO_GEOMETRY)
+ _(SCULPT_UNDO_DYNTOPO_SYMMETRIZE)
+ _(SCULPT_UNDO_FACE_SETS)
+ _(SCULPT_UNDO_HIDDEN)
+ _(SCULPT_UNDO_MASK)
+ _(SCULPT_UNDO_COLOR)
+ default:
+ return "unknown node type";
+ }
+}
+#undef _
+
+static int nodeidgen = 1;
+
+static void print_sculpt_node(SculptUndoNode *node)
+{
+ int hash = hash_sculpt_colors(node);
+
+ // if (node->lasthash == 0) {
+ // node->lasthash = hash;
+ // }
+
+ printf(" %s:%s {applied=%d gen=%d hash=%d}\n",
+ undo_type_to_str(node->type),
+ node->idname,
+ node->applied,
+ 0, // node->gen,
+ hash /*- node->lasthash*/);
+}
+
+static void print_sculpt_undo_step(UndoStep *us, UndoStep *active, int i)
+{
+ SculptUndoNode *node;
+
+ if (us->type != BKE_UNDOSYS_TYPE_SCULPT) {
+ return;
+ }
+
+ int id = -1;
+
+ SculptUndoStep *su = (SculptUndoStep *)us;
+ if (!su->id) {
+ su->id = nodeidgen++;
+ }
+
+ id = su->id;
+
+ printf("id=%d %s %d %s\n", id, us == active ? "->" : " ", i, us->name);
+
+ if (us->type == BKE_UNDOSYS_TYPE_SCULPT) {
+ UndoSculpt *usculpt = sculpt_undosys_step_get_nodes(us);
+
+ for (node = usculpt->nodes.first; node; node = node->next) {
+ print_sculpt_node(node);
+ }
+ }
+}
+void sculpt_undo_print_nodes(void *active)
+{
+#if 0
+ UndoStack *ustack = ED_undo_stack_get();
+ UndoStep *us = ustack->steps.first;
+ if (active == NULL) {
+ active = ustack->step_active;
+ }
+
+ SculptUndoNode *node;
+
+ if (!us) {
+ return;
+ }
+
+ printf("\n");
+ if (ustack->step_init) {
+ printf("===undo init===\n");
+ print_sculpt_undo_step(ustack->step_init, active, -1);
+ printf("===============\n");
+ }
+
+ int i = 0;
+ for (; us; us = us->next, i++) {
+ print_sculpt_undo_step(us, active, i);
+ }
+
+ if (ustack->step_active) {
+ print_sculpt_undo_step(ustack->step_active, active, i);
+ }
+
+#endif
+}
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index d7671a372c6..4f11765b10b 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -382,8 +382,10 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats)
stats->totfacesculpt = ss->totfaces;
break;
case PBVH_BMESH:
- stats->totvertsculpt = ob->sculpt->bm->totvert;
- stats->tottri = ob->sculpt->bm->totface;
+ if (ob->sculpt->bm) {
+ stats->totvertsculpt = ob->sculpt->bm->totvert;
+ stats->tottri = ob->sculpt->bm->totface;
+ }
break;
case PBVH_GRIDS:
stats->totvertsculpt = BKE_pbvh_get_grid_num_vertices(ss->pbvh);
@@ -443,15 +445,7 @@ static void stats_update(Depsgraph *depsgraph,
FOREACH_OBJECT_END;
}
else if (ob && (ob->mode & OB_MODE_SCULPT)) {
- /* Sculpt Mode. */
- if (stats_is_object_dynamic_topology_sculpt(ob)) {
- /* Dynamic topology. Do not count all vertices,
- * dynamic topology stats are initialized later as part of sculpt stats. */
- }
- else {
- /* When dynamic topology is not enabled both sculpt stats and scene stats are collected. */
- stats_object_sculpt(ob, stats);
- }
+ stats_object_sculpt(ob, stats);
}
else {
/* Objects. */
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 3d5dabda23d..84be69b7c4f 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -2897,7 +2897,8 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
ED_mesh_uv_texture_ensure(me, NULL);
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
me,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -2908,7 +2909,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
/* Set the margin really quickly before the packing operation. */
scene->toolsettings->uvcalc_margin = 0.001f;
uvedit_pack_islands(scene, ob, bm);
- BM_mesh_bm_to_me(bmain, bm, me, (&(struct BMeshToMeshParams){0}));
+ BM_mesh_bm_to_me(bmain, NULL, bm, me, (&(struct BMeshToMeshParams){0}));
BM_mesh_free(bm);
if (sync_selection) {