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/sculpt_paint/sculpt_ops.c')
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_ops.c704
1 files changed, 451 insertions, 253 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c
index f16763be735..a740ec2773b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_ops.c
+++ b/source/blender/editors/sculpt_paint/sculpt_ops.c
@@ -8,33 +8,20 @@
#include "MEM_guardedalloc.h"
-#include "BLI_array.h"
-#include "BLI_blenlib.h"
-#include "BLI_dial_2d.h"
#include "BLI_ghash.h"
#include "BLI_gsqueue.h"
-#include "BLI_hash.h"
-#include "BLI_link_utils.h"
-#include "BLI_linklist.h"
-#include "BLI_linklist_stack.h"
-#include "BLI_listbase.h"
#include "BLI_math.h"
-#include "BLI_math_color_blend.h"
-#include "BLI_memarena.h"
-#include "BLI_rand.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
-#include "atomic_ops.h"
#include "BLT_translation.h"
-#include "PIL_time.h"
-
#include "DNA_brush_types.h"
#include "DNA_customdata_types.h"
#include "DNA_listBase.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -42,43 +29,23 @@
#include "BKE_attribute.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_layer.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
-#include "BKE_mesh_fair.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 "DEG_depsgraph_query.h"
#include "IMB_colormanagement.h"
-#include "GPU_batch.h"
-#include "GPU_batch_presets.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"
@@ -88,9 +55,6 @@
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
-#include "ED_space_api.h"
-#include "ED_transform_snap_object_context.h"
-#include "ED_view3d.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
@@ -102,7 +66,6 @@
#include "UI_resources.h"
#include "bmesh.h"
-#include "bmesh_tools.h"
#include <math.h>
#include <stdlib.h>
@@ -116,22 +79,33 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
- if (!ss) {
+ /* Do not allow in DynTopo just yet. */
+ if (!ss || (ss && ss->bm)) {
return OPERATOR_FINISHED;
}
SCULPT_vertex_random_access_ensure(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
- MEM_SAFE_FREE(ss->persistent_base);
+ SculptAttributeParams params = {0};
+ params.permanent = true;
+
+ ss->attrs.persistent_co = BKE_sculpt_attribute_ensure(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co), &params);
+ ss->attrs.persistent_no = BKE_sculpt_attribute_ensure(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no), &params);
+ ss->attrs.persistent_disp = BKE_sculpt_attribute_ensure(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp), &params);
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);
- ss->persistent_base[i].disp = 0.0f;
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3((float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co),
+ SCULPT_vertex_co_get(ss, vertex));
+ SCULPT_vertex_normal_get(
+ ss, vertex, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no));
+ (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_disp)) = 0.0f;
}
return OPERATOR_FINISHED;
@@ -213,7 +187,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
* as deleted, then after symmetrize operation all BMesh elements
* are logged as added (as opposed to attempting to store just the
* parts that symmetrize modifies). */
- SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize");
+ SCULPT_undo_push_begin(ob, op);
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE);
BM_log_before_all_removed(ss->bm, ss->bm_log);
@@ -240,7 +214,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
break;
case PBVH_FACES:
/* Mesh Symmetrize. */
- ED_sculpt_undo_geometry_begin(ob, "mesh symmetrize");
+ ED_sculpt_undo_geometry_begin(ob, op);
Mesh *mesh = ob->data;
BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist);
@@ -297,28 +271,34 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene,
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = OB_MODE_SCULPT;
- BKE_sculpt_ensure_orig_mesh_data(scene, ob);
+ /* Trigger evaluation of modifier stack to ensure
+ * multires modifier sets .runtime.ccg in
+ * the evaluated mesh.
+ */
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
/* This function expects a fully evaluated depsgraph. */
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
- /* 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. */
- /* 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. */
SculptSession *ss = ob->sculpt;
- const int new_face_set = SCULPT_face_set_next_available_get(ss);
- for (int i = 0; i < ss->totfaces; i++) {
- if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
- ss->face_sets[i] = new_face_set;
+ if (ss->face_sets) {
+ /* 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. */
+ /* 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. */
+ const int new_face_set = SCULPT_face_set_next_available_get(ss);
+ for (int i = 0; i < ss->totfaces; i++) {
+ if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
+ ss->face_sets[i] = new_face_set;
+ }
}
}
}
@@ -392,7 +372,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain,
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_undo_push_begin_ex(ob, "Dynamic topology enable");
}
SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob);
if (has_undo) {
@@ -416,7 +396,8 @@ void ED_object_sculptmode_enter(struct bContext *C, Depsgraph *depsgraph, Report
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports);
}
@@ -468,7 +449,8 @@ void ED_object_sculptmode_exit(bContext *C, Depsgraph *depsgraph)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob);
}
@@ -480,7 +462,8 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
const int mode_flag = OB_MODE_SCULPT;
const bool is_mode_set = (ob->mode & mode_flag) != 0;
@@ -508,7 +491,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
* while it works it causes lag when undoing the first undo step, see T71564. */
wmWindowManager *wm = CTX_wm_manager(C);
if (wm->op_undo_depth <= 1) {
- SCULPT_undo_push_begin(ob, op->type->name);
+ SCULPT_undo_push_begin(ob, op);
SCULPT_undo_push_end(ob);
}
}
@@ -543,7 +526,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
- ss->preview_vert_index_count = 0;
+ ss->preview_vert_count = 0;
int totpoints = 0;
/* This function is called from the cursor drawing code, so the PBVH may not be build yet. */
@@ -568,193 +551,50 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
float brush_co[3];
copy_v3_v3(brush_co, SCULPT_active_vertex_co_get(ss));
- BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
+ BLI_bitmap *visited_verts = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_verts");
/* Assuming an average of 6 edges per vertex in a triangulated mesh. */
- const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2;
+ const int max_preview_verts = 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");
+ if (ss->preview_vert_list == NULL) {
+ ss->preview_vert_list = MEM_callocN(max_preview_verts * sizeof(PBVHVertRef), "preview lines");
}
- GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int));
- int active_v = SCULPT_active_vertex_get(ss);
- BLI_gsqueue_push(not_visited_vertices, &active_v);
+ GSQueue *non_visited_verts = BLI_gsqueue_new(sizeof(PBVHVertRef));
+ PBVHVertRef active_v = SCULPT_active_vertex_get(ss);
+ BLI_gsqueue_push(non_visited_verts, &active_v);
- while (!BLI_gsqueue_is_empty(not_visited_vertices)) {
- int from_v;
- BLI_gsqueue_pop(not_visited_vertices, &from_v);
+ while (!BLI_gsqueue_is_empty(non_visited_verts)) {
+ PBVHVertRef from_v;
+
+ BLI_gsqueue_pop(non_visited_verts, &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;
- ss->preview_vert_index_list[totpoints] = from_v;
+ if (totpoints + (ni.size * 2) < max_preview_verts) {
+ PBVHVertRef to_v = ni.vertex;
+ int to_v_i = ni.index;
+ ss->preview_vert_list[totpoints] = from_v;
totpoints++;
- ss->preview_vert_index_list[totpoints] = to_v;
+ ss->preview_vert_list[totpoints] = to_v;
totpoints++;
- if (BLI_BITMAP_TEST(visited_vertices, to_v)) {
+ if (BLI_BITMAP_TEST(visited_verts, to_v_i)) {
continue;
}
- BLI_BITMAP_ENABLE(visited_vertices, to_v);
+ BLI_BITMAP_ENABLE(visited_verts, 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);
+ BLI_gsqueue_push(non_visited_verts, &to_v);
}
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
}
- BLI_gsqueue_free(not_visited_vertices);
-
- MEM_freeN(visited_vertices);
-
- ss->preview_vert_index_count = totpoints;
-}
-
-static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_active_object(C);
-
- ID *data;
- data = ob->data;
- if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) {
- return OPERATOR_CANCELLED;
- }
-
- if (ob->type != OB_MESH) {
- return OPERATOR_CANCELLED;
- }
-
- Mesh *mesh = ob->data;
-
- const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR);
- if (mloopcol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n);
-
- const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
- if (MPropCol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- const MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
-
- const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
- const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
-
- for (int i = 0; i < mesh->totpoly; i++) {
- const MPoly *c_poly = &polys[i];
- for (int j = 0; j < c_poly->totloop; j++) {
- int loop_index = c_poly->loopstart + j;
- const MLoop *c_loop = &loops[c_poly->loopstart + j];
- float srgb_color[4];
- linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color);
- loopcols[loop_index].r = (char)(srgb_color[0] * 255);
- loopcols[loop_index].g = (char)(srgb_color[1] * 255);
- loopcols[loop_index].b = (char)(srgb_color[2] * 255);
- loopcols[loop_index].a = (char)(srgb_color[3] * 255);
- }
- }
-
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
-
- return OPERATOR_FINISHED;
-}
-
-static bool sculpt_colors_poll(bContext *C)
-{
- if (!SCULPT_mode_poll(C)) {
- return false;
- }
-
- Object *ob = CTX_data_active_object(C);
-
- if (!ob->sculpt || !ob->sculpt->pbvh || BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_FACES) {
- return false;
- }
-
- return SCULPT_has_colors(ob->sculpt);
-}
-
-static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Sculpt Vertex Color to Vertex Color";
- ot->description = "Copy the Sculpt Vertex Color to a regular color layer";
- ot->idname = "SCULPT_OT_vertex_to_loop_colors";
-
- /* api callbacks */
- ot->poll = sculpt_colors_poll;
- ot->exec = vertex_to_loop_colors_exec;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_active_object(C);
-
- ID *data;
- data = ob->data;
- if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) {
- return OPERATOR_CANCELLED;
- }
-
- if (ob->type != OB_MESH) {
- return OPERATOR_CANCELLED;
- }
-
- Mesh *mesh = ob->data;
+ BLI_gsqueue_free(non_visited_verts);
- const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR);
- if (mloopcol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- const MLoopCol *loopcols = CustomData_get_layer_n(
- &mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n);
+ MEM_freeN(visited_verts);
- const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
- if (MPropCol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
-
- const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
- const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
-
- for (int i = 0; i < mesh->totpoly; i++) {
- const MPoly *c_poly = &polys[i];
- for (int j = 0; j < c_poly->totloop; j++) {
- int loop_index = c_poly->loopstart + j;
- const MLoop *c_loop = &loops[c_poly->loopstart + j];
- vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
- vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
- vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
- vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f);
- srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color);
- }
- }
-
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Vertex Color to Sculpt Vertex Color";
- ot->description = "Copy the active loop color layer to the vertex color";
- ot->idname = "SCULPT_OT_loop_to_vertex_colors";
-
- /* api callbacks */
- ot->poll = sculpt_colors_poll;
- ot->exec = loop_to_vertex_colors_exec;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ss->preview_vert_count = totpoints;
}
static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e))
@@ -764,7 +604,7 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent
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);
+ PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
float active_vertex_color[4];
if (!SCULPT_handles_colors_report(ss, op->reports)) {
@@ -882,9 +722,6 @@ static void do_mask_by_color_contiguous_update_nodes_cb(
continue;
}
update_node = true;
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
}
BKE_pbvh_vertex_iter_end;
if (update_node) {
@@ -893,8 +730,11 @@ 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, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
MaskByColorContiguousFloodFillData *data = userdata;
float current_color[4];
@@ -902,10 +742,10 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb(
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);
@@ -914,7 +754,7 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb(
}
static void sculpt_mask_by_color_contiguous(Object *object,
- const int vertex,
+ const PBVHVertRef vertex,
const float threshold,
const bool invert,
const bool preserve_mask)
@@ -991,7 +831,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
float col[4];
- SCULPT_vertex_color_get(ss, vd.index, col);
+ SCULPT_vertex_color_get(ss, vd.vertex, col);
const float current_mask = *vd.mask;
const float new_mask = sculpt_mask_by_color_delta_get(active_color, col, threshold, invert);
@@ -1001,18 +841,15 @@ static void do_mask_by_color_task_cb(void *__restrict userdata,
continue;
}
update_node = true;
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
}
BKE_pbvh_vertex_iter_end;
if (update_node) {
- BKE_pbvh_node_mark_redraw(data->nodes[n]);
+ BKE_pbvh_node_mark_update_mask(data->nodes[n]);
}
}
static void sculpt_mask_by_color_full_mesh(Object *object,
- const int vertex,
+ const PBVHVertRef vertex,
const float threshold,
const bool invert,
const bool preserve_mask)
@@ -1064,10 +901,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
const float mval_fl[2] = {UNPACK2(event->mval)};
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
- SCULPT_undo_push_begin(ob, "Mask by color");
+ SCULPT_undo_push_begin(ob, op);
BKE_sculpt_color_layer_create_if_needed(ob);
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const PBVHVertRef 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");
@@ -1127,6 +964,368 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
1.0f);
}
+typedef enum {
+ AUTOMASK_BAKE_MIX,
+ AUTOMASK_BAKE_MULTIPLY,
+ AUTOMASK_BAKE_DIVIDE,
+ AUTOMASK_BAKE_ADD,
+ AUTOMASK_BAKE_SUBTRACT,
+} CavityBakeMixMode;
+
+typedef struct AutomaskBakeTaskData {
+ SculptSession *ss;
+ AutomaskingCache *automasking;
+ PBVHNode **nodes;
+ CavityBakeMixMode mode;
+ float factor;
+ Object *ob;
+} AutomaskBakeTaskData;
+
+static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ AutomaskBakeTaskData *tdata = userdata;
+ SculptSession *ss = tdata->ss;
+ PBVHNode *node = tdata->nodes[n];
+ PBVHVertexIter vd;
+ const CavityBakeMixMode mode = tdata->mode;
+ const float factor = tdata->factor;
+
+ SCULPT_undo_push_node(tdata->ob, node, SCULPT_UNDO_MASK);
+
+ AutomaskingNodeData automask_data;
+ SCULPT_automasking_node_begin(tdata->ob, ss, tdata->automasking, &automask_data, node);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ SCULPT_automasking_node_update(ss, &automask_data, &vd);
+
+ float automask = SCULPT_automasking_factor_get(
+ tdata->automasking, ss, vd.vertex, &automask_data);
+ float mask;
+
+ switch (mode) {
+ case AUTOMASK_BAKE_MIX:
+ mask = automask;
+ break;
+ case AUTOMASK_BAKE_MULTIPLY:
+ mask = *vd.mask * automask;
+ break;
+ break;
+ case AUTOMASK_BAKE_DIVIDE:
+ mask = automask > 0.00001f ? *vd.mask / automask : 0.0f;
+ break;
+ break;
+ case AUTOMASK_BAKE_ADD:
+ mask = *vd.mask + automask;
+ break;
+ case AUTOMASK_BAKE_SUBTRACT:
+ mask = *vd.mask - automask;
+ break;
+ }
+
+ mask = *vd.mask + (mask - *vd.mask) * factor;
+ CLAMP(mask, 0.0f, 1.0f);
+
+ *vd.mask = mask;
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ BKE_pbvh_node_mark_update_mask(node);
+}
+
+static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ MultiresModifierData *mmd = BKE_sculpt_multires_active(CTX_data_scene(C), ob);
+ BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd);
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
+ SCULPT_vertex_random_access_ensure(ss);
+
+ SCULPT_undo_push_begin(ob, op);
+
+ CavityBakeMixMode mode = RNA_enum_get(op->ptr, "mix_mode");
+ float factor = RNA_float_get(op->ptr, "mix_factor");
+
+ PBVHNode **nodes;
+ int totnode;
+
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ AutomaskBakeTaskData tdata;
+
+ /* Set up automasking settings.
+ */
+ Sculpt sd2 = *sd;
+
+ /* Override cavity mask settings if use_automask_settings is false. */
+ if (!RNA_boolean_get(op->ptr, "use_automask_settings")) {
+ if (RNA_boolean_get(op->ptr, "invert")) {
+ sd2.automasking_flags = BRUSH_AUTOMASKING_CAVITY_INVERTED;
+ }
+ else {
+ sd2.automasking_flags = BRUSH_AUTOMASKING_CAVITY_NORMAL;
+ }
+
+ if (RNA_boolean_get(op->ptr, "use_curve")) {
+ sd2.automasking_flags |= BRUSH_AUTOMASKING_CAVITY_USE_CURVE;
+ }
+
+ sd2.automasking_cavity_blur_steps = RNA_int_get(op->ptr, "blur_steps");
+ sd2.automasking_cavity_factor = RNA_float_get(op->ptr, "factor");
+
+ sd2.automasking_cavity_curve = sd->automasking_cavity_curve_op;
+ }
+ else {
+ sd2.automasking_flags &= BRUSH_AUTOMASKING_CAVITY_ALL | BRUSH_AUTOMASKING_CAVITY_USE_CURVE;
+
+ /* Ensure cavity mask is actually enabled. */
+ if (!(sd2.automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
+ sd2.automasking_flags |= BRUSH_AUTOMASKING_CAVITY_NORMAL;
+ }
+ }
+
+ /* Create copy of brush with cleared automasking settings. */
+ Brush brush2 = *brush;
+ brush2.automasking_flags = 0;
+ brush2.automasking_boundary_edges_propagation_steps = 1;
+ brush2.automasking_cavity_curve = sd2.automasking_cavity_curve;
+
+ SCULPT_stroke_id_next(ob);
+
+ tdata.ob = ob;
+ tdata.mode = mode;
+ tdata.factor = factor;
+ tdata.ss = ss;
+ tdata.nodes = nodes;
+ tdata.automasking = SCULPT_automasking_cache_init(&sd2, &brush2, ob);
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &tdata, sculpt_bake_cavity_exec_task_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+ SCULPT_automasking_cache_free(tdata.automasking);
+
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
+ SCULPT_undo_push_end(ob);
+
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
+ SCULPT_tag_update_overlays(C);
+
+ return OPERATOR_FINISHED;
+}
+
+static void cavity_bake_ui(bContext *C, wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ Scene *scene = CTX_data_scene(C);
+ Sculpt *sd = scene->toolsettings ? scene->toolsettings->sculpt : NULL;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ bool use_curve = false;
+
+ if (!sd || !RNA_boolean_get(op->ptr, "use_automask_settings")) {
+ uiItemR(layout, op->ptr, "mix_mode", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "mix_factor", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_automask_settings", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "factor", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "blur_steps", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "invert", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_curve", 0, NULL, ICON_NONE);
+
+ use_curve = RNA_boolean_get(op->ptr, "use_curve");
+ }
+ else {
+ PointerRNA sculpt_ptr;
+
+ RNA_pointer_create(&scene->id, &RNA_Sculpt, sd, &sculpt_ptr);
+ uiItemR(layout, op->ptr, "mix_mode", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "mix_factor", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_automask_settings", 0, NULL, ICON_NONE);
+
+ use_curve = false;
+ }
+
+ if (use_curve) {
+ PointerRNA sculpt_ptr;
+
+ const char *curve_prop;
+
+ if (RNA_boolean_get(op->ptr, "use_automask_settings")) {
+ curve_prop = "automasking_cavity_curve";
+ }
+ else {
+ curve_prop = "automasking_cavity_curve_op";
+ }
+
+ if (scene->toolsettings && scene->toolsettings->sculpt) {
+ RNA_pointer_create(&scene->id, &RNA_Sculpt, scene->toolsettings->sculpt, &sculpt_ptr);
+ uiTemplateCurveMapping(layout, &sculpt_ptr, curve_prop, 'v', false, false, false, false);
+ }
+ }
+}
+
+static void SCULPT_OT_mask_from_cavity(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Mask From Cavity";
+ ot->idname = "SCULPT_OT_mask_from_cavity";
+ ot->description = "Creates a mask based on the curvature of the surface";
+ ot->ui = cavity_bake_ui;
+
+ static EnumPropertyItem mix_modes[] = {
+ {AUTOMASK_BAKE_MIX, "MIX", ICON_NONE, "Mix", ""},
+ {AUTOMASK_BAKE_MULTIPLY, "MULTIPLY", ICON_NONE, "Multiply", ""},
+ {AUTOMASK_BAKE_DIVIDE, "DIVIDE", ICON_NONE, "Divide", ""},
+ {AUTOMASK_BAKE_ADD, "ADD", ICON_NONE, "Add", ""},
+ {AUTOMASK_BAKE_SUBTRACT, "SUBTRACT", ICON_NONE, "Subtract", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* api callbacks */
+ ot->exec = sculpt_bake_cavity_exec;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "mix_mode", mix_modes, AUTOMASK_BAKE_MIX, "Mode", "Mix mode");
+ RNA_def_float(ot->srna, "mix_factor", 1.0f, 0.0f, 5.0f, "Mix Factor", "", 0.0f, 1.0f);
+
+ RNA_def_boolean(ot->srna,
+ "use_automask_settings",
+ false,
+ "Use Automask Settings",
+ "Use default settings from Options panel in sculpt mode");
+
+ RNA_def_float(ot->srna,
+ "factor",
+ 0.5f,
+ 0.0f,
+ 5.0f,
+ "Cavity Factor",
+ "The contrast of the cavity mask",
+ 0.0f,
+ 1.0f);
+ RNA_def_int(ot->srna,
+ "blur_steps",
+ 2,
+ 0,
+ 25,
+ "Cavity Blur",
+ "The number of times the cavity mask is blurred",
+ 0,
+ 25);
+ RNA_def_boolean(ot->srna, "use_curve", false, "Use Curve", "");
+
+ RNA_def_boolean(ot->srna, "invert", false, "Cavity (Inverted)", "");
+}
+
+static int sculpt_reveal_all_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+
+ Mesh *mesh = BKE_object_get_original_mesh(ob);
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
+
+ if (!ss->pbvh) {
+ return OPERATOR_CANCELLED;
+ }
+
+ PBVHNode **nodes;
+ int totnode;
+ bool with_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH;
+
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ if (!totnode) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Propagate face hide state to verts for undo. */
+ SCULPT_visibility_sync_all_from_faces(ob);
+
+ SCULPT_undo_push_begin(ob, op);
+
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_update_visibility(nodes[i]);
+
+ if (!with_bmesh) {
+ SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_HIDDEN);
+ }
+ }
+
+ if (!with_bmesh) {
+ /* As an optimization, free the hide attribute when making all geometry visible. This allows
+ * reduced memory usage without manually clearing it later, and allows sculpt operations to
+ * avoid checking element's hide status. */
+ CustomData_free_layer_named(&mesh->pdata, ".hide_poly", mesh->totpoly);
+ ss->hide_poly = NULL;
+ }
+ else {
+ SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_HIDDEN);
+
+ BMIter iter;
+ BMFace *f;
+ BMVert *v;
+ const int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK);
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ BM_log_vert_before_modified(ss->bm_log, v, cd_mask);
+ }
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ BM_log_face_modified(ss->bm_log, f);
+ }
+
+ SCULPT_face_visibility_all_set(ss, true);
+ }
+
+ SCULPT_visibility_sync_all_from_faces(ob);
+
+ /* NOTE: #SCULPT_visibility_sync_all_from_faces may have deleted
+ * `pbvh->hide_vert` if hide_poly did not exist, which is why
+ * we call #BKE_pbvh_update_hide_attributes_from_mesh here instead of
+ * after #CustomData_free_layer_named above. */
+ if (!with_bmesh) {
+ BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh);
+ }
+
+ BKE_pbvh_update_visibility(ss->pbvh);
+
+ SCULPT_undo_push_end(ob);
+ MEM_SAFE_FREE(nodes);
+
+ SCULPT_tag_update_overlays(C);
+ DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_reveal_all(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Reveal All";
+ ot->idname = "SCULPT_OT_reveal_all";
+ ot->description = "Unhide all geometry";
+
+ /* Api callbacks. */
+ ot->exec = sculpt_reveal_all_exec;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
void ED_operatortypes_sculpt(void)
{
WM_operatortype_append(SCULPT_OT_brush_stroke);
@@ -1140,7 +1339,6 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_set_detail_size);
WM_operatortype_append(SCULPT_OT_mesh_filter);
WM_operatortype_append(SCULPT_OT_mask_filter);
- WM_operatortype_append(SCULPT_OT_dirty_mask);
WM_operatortype_append(SCULPT_OT_mask_expand);
WM_operatortype_append(SCULPT_OT_set_pivot_position);
WM_operatortype_append(SCULPT_OT_face_sets_create);
@@ -1156,12 +1354,12 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_project_line_gesture);
WM_operatortype_append(SCULPT_OT_sample_color);
- WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
- WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
WM_operatortype_append(SCULPT_OT_color_filter);
WM_operatortype_append(SCULPT_OT_mask_by_color);
WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit);
WM_operatortype_append(SCULPT_OT_mask_init);
WM_operatortype_append(SCULPT_OT_expand);
+ WM_operatortype_append(SCULPT_OT_mask_from_cavity);
+ WM_operatortype_append(SCULPT_OT_reveal_all);
}