diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 32b386bf0ab..a13f39da4a9 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -600,19 +600,10 @@ def brush_settings(layout, context, brush, popover=False): # use_persistent, set_persistent_base if capabilities.has_persistence: ob = context.sculpt_object - do_persistent = True - - # not supported yet for this case - for md in ob.modifiers: - if md.type == 'MULTIRES': - do_persistent = False - break - - if do_persistent: - layout.separator() - layout.prop(brush, "use_persistent") - layout.operator("sculpt.set_persistent_base") - layout.separator() + layout.separator() + layout.prop(brush, "use_persistent") + layout.operator("sculpt.set_persistent_base") + layout.separator() if brush.sculpt_tool == 'CLAY_STRIPS': row = layout.row() diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 012188bed96..f78a142704b 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -279,6 +279,12 @@ typedef struct SculptClothSimulation { } SculptClothSimulation; +typedef struct SculptLayerPersistentBase { + float co[3]; + float no[3]; + float disp; +} SculptLayerPersistentBase; + /* Session data (mode-specific) */ typedef struct SculptSession { @@ -329,9 +335,6 @@ typedef struct SculptSession { unsigned int texcache_side, *texcache, texcache_actual; struct ImagePool *tex_pool; - /* Layer brush persistence between strokes */ - float (*layer_co)[3]; /* Copy of the mesh vertices' locations */ - struct StrokeCache *cache; struct FilterCache *filter_cache; @@ -359,6 +362,10 @@ typedef struct SculptSession { float pose_origin[3]; SculptPoseIKChain *pose_ik_chain_preview; + /* Layer brush persistence between strokes */ + /* This is freed with the PBVH, so it is always in sync with the mesh. */ + SculptLayerPersistentBase *layer_base; + /* Transform operator */ float pivot_pos[3]; float pivot_rot[4]; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index a59ce816e26..94f0e544a6b 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -122,7 +122,6 @@ void BKE_pbvh_build_bmesh(PBVH *bvh, const int cd_vert_node_offset, const int cd_face_node_offset); void BKE_pbvh_free(PBVH *bvh); -void BKE_pbvh_free_layer_disp(PBVH *bvh); /* Hierarchical Search in the BVH, two methods: * - for each hit calling a callback @@ -310,14 +309,6 @@ void BKE_pbvh_face_sets_set(PBVH *bvh, int *face_sets); void BKE_pbvh_face_sets_color_set(PBVH *bvh, int seed, int color_default); -/* Layer displacement */ - -/* Get the node's displacement layer, creating it if necessary */ -float *BKE_pbvh_node_layer_disp_get(PBVH *pbvh, PBVHNode *node); - -/* If the node has a displacement layer, free it and set to null */ -void BKE_pbvh_node_layer_disp_free(PBVHNode *node); - /* vertex deformer */ float (*BKE_pbvh_vert_coords_alloc(struct PBVH *pbvh))[3]; void BKE_pbvh_vert_coords_apply(struct PBVH *pbvh, const float (*vertCos)[3], const int totvert); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index e1303b8d723..d775598f420 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1422,6 +1422,12 @@ void BKE_brush_sculpt_reset(Brush *br) br->cloth_deform_type = BRUSH_CLOTH_DEFORM_DRAG; br->flag &= ~(BRUSH_ALPHA_PRESSURE | BRUSH_SIZE_PRESSURE); break; + case SCULPT_TOOL_LAYER: + br->flag &= ~BRUSH_SPACE_ATTEN; + br->hardness = 0.35f; + br->alpha = 1.0f; + br->height = 0.05f; + break; default: break; } diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index c5d60b29aca..420538061bb 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1306,9 +1306,10 @@ static void sculptsession_free_pbvh(Object *object) } MEM_SAFE_FREE(ss->pmap); - MEM_SAFE_FREE(ss->pmap_mem); + MEM_SAFE_FREE(ss->layer_base); + MEM_SAFE_FREE(ss->preview_vert_index_list); ss->preview_vert_index_count = 0; } @@ -1353,31 +1354,17 @@ void BKE_sculptsession_free(Object *ob) BM_log_free(ss->bm_log); } - if (ss->texcache) { - MEM_freeN(ss->texcache); - } + MEM_SAFE_FREE(ss->texcache); if (ss->tex_pool) { BKE_image_pool_free(ss->tex_pool); } - if (ss->layer_co) { - MEM_freeN(ss->layer_co); - } + MEM_SAFE_FREE(ss->orig_cos); + MEM_SAFE_FREE(ss->deform_cos); + MEM_SAFE_FREE(ss->deform_imats); - if (ss->orig_cos) { - MEM_freeN(ss->orig_cos); - } - if (ss->deform_cos) { - MEM_freeN(ss->deform_cos); - } - if (ss->deform_imats) { - MEM_freeN(ss->deform_imats); - } - - if (ss->preview_vert_index_list) { - MEM_freeN(ss->preview_vert_index_list); - } + MEM_SAFE_FREE(ss->preview_vert_index_list); if (ss->pose_ik_chain_preview) { for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) { diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 06622f0d009..977e70745a3 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -685,8 +685,6 @@ void BKE_pbvh_free(PBVH *bvh) if (node->face_vert_indices) { MEM_freeN((void *)node->face_vert_indices); } - BKE_pbvh_node_layer_disp_free(node); - if (node->bm_faces) { BLI_gset_free(node->bm_faces, NULL); } @@ -722,13 +720,6 @@ void BKE_pbvh_free(PBVH *bvh) MEM_freeN(bvh); } -void BKE_pbvh_free_layer_disp(PBVH *bvh) -{ - for (int i = 0; i < bvh->totnode; i++) { - BKE_pbvh_node_layer_disp_free(&bvh->nodes[i]); - } -} - static void pbvh_iter_begin(PBVHIter *iter, PBVH *bvh, BKE_pbvh_SearchCallback scb, @@ -2768,26 +2759,6 @@ void BKE_pbvh_grids_update( } } -/* Get the node's displacement layer, creating it if necessary */ -float *BKE_pbvh_node_layer_disp_get(PBVH *bvh, PBVHNode *node) -{ - if (!node->layer_disp) { - int totvert = 0; - BKE_pbvh_node_num_verts(bvh, node, &totvert, NULL); - node->layer_disp = MEM_callocN(sizeof(float) * totvert, "layer disp"); - } - return node->layer_disp; -} - -/* If the node has a displacement layer, free it and set to null */ -void BKE_pbvh_node_layer_disp_free(PBVHNode *node) -{ - if (node->layer_disp) { - MEM_freeN(node->layer_disp); - node->layer_disp = NULL; - } -} - float (*BKE_pbvh_vert_coords_alloc(PBVH *pbvh))[3] { float(*vertCos)[3] = NULL; diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 04ca5770a97..33372a0d7cf 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1216,6 +1216,34 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, SculptSession } } +void SCULPT_layer_brush_height_preview_draw(const uint gpuattr, + const Brush *brush, + const float obmat[4][4], + const float location[3], + const float normal[3], + const float rds, + const float line_width, + const float outline_col[3], + const float alpha) +{ + float cursor_trans[4][4], cursor_rot[4][4]; + float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; + float quat[4]; + float height_preview_trans[3]; + copy_m4_m4(cursor_trans, obmat); + madd_v3_v3v3fl(height_preview_trans, location, normal, brush->height); + translate_m4( + cursor_trans, height_preview_trans[0], height_preview_trans[1], height_preview_trans[2]); + rotation_between_vecs_to_quat(quat, z_axis, normal); + quat_to_mat4(cursor_rot, quat); + GPU_matrix_mul(cursor_trans); + GPU_matrix_mul(cursor_rot); + + GPU_line_width(line_width); + immUniformColor3fvAlpha(outline_col, alpha * 0.5f); + imm_draw_circle_wire_3d(gpuattr, 0, 0, rds, 80); +} + static bool paint_use_2d_cursor(ePaintMode mode) { if (mode >= PAINT_MODE_TEXTURE_3D) { @@ -1476,6 +1504,21 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) GPU_matrix_pop(); } + /* Layer brush height. */ + if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { + GPU_matrix_push(); + SCULPT_layer_brush_height_preview_draw(pos, + brush, + vc.obact->obmat, + gi.location, + gi.normal, + rds, + 1.0f, + outline_col, + outline_alpha); + GPU_matrix_pop(); + } + /* Update and draw dynamic mesh preview lines. */ GPU_matrix_push(); GPU_matrix_mul(vc.obact->obmat); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index b8c6e97fcc1..66f66bfceb8 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1709,10 +1709,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, /* Update the test radius to sample the normal using the normal radius of the brush. */ if (data->brush->ob_mode == OB_MODE_SCULPT) { float test_radius = sqrtf(normal_test.radius_squared); - /* Layer brush produces artifacts with normal and area radius. */ - if (!(ss->cache && data->brush->sculpt_tool == SCULPT_TOOL_LAYER)) { - test_radius *= data->brush->normal_radius_factor; - } + test_radius *= data->brush->normal_radius_factor; normal_test.radius = test_radius; normal_test.radius_squared = test_radius * test_radius; } @@ -1724,15 +1721,13 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, if (data->brush->ob_mode == OB_MODE_SCULPT) { float test_radius = sqrtf(area_test.radius_squared); /* Layer brush produces artifacts with normal and area radius */ - if (!(ss->cache && data->brush->sculpt_tool == SCULPT_TOOL_LAYER)) { - /* Enable area radius control only on Scrape for now */ - if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) && - data->brush->area_radius_factor > 0.0f) { - test_radius *= data->brush->area_radius_factor; - } - else { - test_radius *= data->brush->normal_radius_factor; - } + /* Enable area radius control only on Scrape for now */ + if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) && + data->brush->area_radius_factor > 0.0f) { + test_radius *= data->brush->area_radius_factor; + } + else { + test_radius *= data->brush->normal_radius_factor; } area_test.radius = test_radius; area_test.radius_squared = test_radius * test_radius; @@ -4072,23 +4067,14 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; Sculpt *sd = data->sd; const Brush *brush = data->brush; - const float *offset = data->offset; + + const bool use_persistent_base = ss->layer_base && brush->flag & BRUSH_PERSISTENT; PBVHVertexIter vd; SculptOrigVertData orig_data; - float *layer_disp; const float bstrength = ss->cache->bstrength; - const float lim = (bstrength < 0.0f) ? -data->brush->height : data->brush->height; - /* XXX: layer brush needs conversion to proxy but its more complicated */ - /* proxy = BKE_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; */ - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - /* Why does this have to be thread-protected? */ - BLI_mutex_lock(&data->mutex); - layer_disp = BKE_pbvh_node_layer_disp_get(ss->pbvh, data->nodes[n]); - BLI_mutex_unlock(&data->mutex); - SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); @@ -4098,38 +4084,65 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, SCULPT_orig_vert_data_update(&orig_data, &vd); if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - 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.index, - tls->thread_id); - float *disp = &layer_disp[vd.i]; - float val[3]; + const float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.index, + tls->thread_id); - *disp += fade; - - /* Don't let the displacement go past the limit. */ - if ((lim < 0.0f && *disp < lim) || (lim >= 0.0f && *disp > lim)) { - *disp = lim; - } - - mul_v3_v3fl(val, offset, *disp); - - if (!ss->multires.active && !ss->bm && ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { - int index = vd.vert_indices[vd.i]; - - /* Persistent base. */ - add_v3_v3(val, ss->layer_co[index]); + const int vi = vd.index; + float *disp_factor; + if (use_persistent_base) { + disp_factor = &ss->layer_base[vi].disp; } else { - add_v3_v3(val, orig_data.co); + disp_factor = &ss->cache->layer_displacement_factor[vi]; } - SCULPT_clip(sd, ss, vd.co, val); + /* When using persistent base, the layer brush Ctrl invert mode resets the height of the + * layer to 0. This makes possible to clean edges of previously added layers on top of the + * base. */ + /* The main direction of the layers is inverted using the regular brush strength with the + * brush direction property. */ + if (use_persistent_base && ss->cache->invert) { + (*disp_factor) += fabsf(fade * bstrength * (*disp_factor)) * + ((*disp_factor) > 0.0f ? -1.0f : 1.0f); + } + else { + (*disp_factor) += fade * bstrength * (1.05f - fabsf(*disp_factor)); + } + if (vd.mask) { + const float clamp_mask = 1.0f - *vd.mask; + CLAMP(*disp_factor, -clamp_mask, clamp_mask); + } + else { + CLAMP(*disp_factor, -1.0f, 1.0f); + } + + float final_co[3]; + float normal[3]; + + if (use_persistent_base) { + copy_v3_v3(normal, ss->layer_base[vi].no); + mul_v3_fl(normal, brush->height); + madd_v3_v3v3fl(final_co, ss->layer_base[vi].co, normal, *disp_factor); + } + else { + normal_short_to_float_v3(normal, orig_data.no); + mul_v3_fl(normal, brush->height); + madd_v3_v3v3fl(final_co, orig_data.co, normal, *disp_factor); + } + + float vdisp[3]; + sub_v3_v3v3(vdisp, final_co, vd.co); + mul_v3_fl(vdisp, fabsf(fade)); + add_v3_v3v3(final_co, vd.co, vdisp); + + SCULPT_clip(sd, ss, vd.co, final_co); if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; @@ -4143,24 +4156,23 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - mul_v3_v3v3(offset, ss->cache->scale, ss->cache->sculpt_normal_symm); + if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 && + ss->cache->first_time) { + ss->cache->layer_displacement_factor = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss), + "layer displacement factor"); + } SculptThreadedTaskData data = { .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .offset = offset, }; - BLI_mutex_init(&data.mutex); PBVHParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); BKE_pbvh_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); - - BLI_mutex_end(&data.mutex); } static void do_inflate_brush_task_cb_ex(void *__restrict userdata, @@ -5948,6 +5960,7 @@ void SCULPT_cache_free(StrokeCache *cache) { MEM_SAFE_FREE(cache->dial); MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp); + MEM_SAFE_FREE(cache->layer_displacement_factor); if (cache->pose_ik_chain) { SCULPT_pose_ik_chain_free(cache->pose_ik_chain); @@ -6006,14 +6019,9 @@ static void sculpt_update_cache_invariants( ss->cache = cache; /* Set scaling adjustment. */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { - max_scale = 1.0f; - } - else { - max_scale = 0.0f; - for (int i = 0; i < 3; i++) { - max_scale = max_ff(max_scale, fabsf(ob->scale[i])); - } + max_scale = 0.0f; + for (int i = 0; i < 3; i++) { + max_scale = max_ff(max_scale, fabsf(ob->scale[i])); } cache->scale[0] = max_scale / ob->scale[0]; cache->scale[1] = max_scale / ob->scale[1]; @@ -6129,32 +6137,6 @@ static void sculpt_update_cache_invariants( normalize_v3(cache->true_gravity_direction); } - /* Initialize layer brush displacements and persistent coords. */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { - /* Not supported yet for multires or dynamic topology. */ - if (!ss->multires.active && !ss->bm && !ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { - if (!ss->layer_co) { - ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert, "sculpt mesh vertices copy"); - } - - if (ss->deform_cos) { - memcpy(ss->layer_co, ss->deform_cos, ss->totvert); - } - else { - for (int i = 0; i < ss->totvert; i++) { - copy_v3_v3(ss->layer_co[i], ss->mvert[i].co); - } - } - } - - if (ss->bm) { - /* Free any remaining layer displacements from nodes. If not and topology changes - * from using another tool, then next layer toolstroke - * can access past disp array bounds. */ - BKE_pbvh_free_layer_disp(ss->pbvh); - } - } - /* Make copies of the mesh vertex locations and normals for some tools. */ if (brush->flag & BRUSH_ANCHORED) { cache->original = true; @@ -7290,13 +7272,25 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot) static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) { - SculptSession *ss = CTX_data_active_object(C)->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; if (ss) { - if (ss->layer_co) { - MEM_freeN(ss->layer_co); + SCULPT_vertex_random_access_init(ss); + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false); + + MEM_SAFE_FREE(ss->layer_base); + + const int totvert = SCULPT_vertex_count_get(ss); + ss->layer_base = MEM_mallocN(sizeof(SculptLayerPersistentBase) * totvert, + "layer persistent base"); + + for (int i = 0; i < totvert; i++) { + copy_v3_v3(ss->layer_base[i].co, SCULPT_vertex_co_get(ss, i)); + SCULPT_vertex_normal_get(ss, i, ss->layer_base[i].no); + ss->layer_base[i].disp = 0.0f; } - ss->layer_co = NULL; } return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 8189ca8c551..c1e7508f98b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -781,6 +781,9 @@ typedef struct StrokeCache { /* Stores the displacement produced by the laplacian step of HC smooth. */ float (*surface_smooth_laplacian_disp)[3]; + /* Layer brush */ + float *layer_displacement_factor; + float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ struct Dial *dial; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index c07ebf790d4..414a6101071 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -1314,10 +1314,6 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo) MEM_freeN(unode->no); unode->no = NULL; } - - if (unode->node) { - BKE_pbvh_node_layer_disp_free(unode->node); - } } /* We could remove this and enforce all callers run in an operator using 'OPTYPE_UNDO'. */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 87f0de6daa2..041943bc5e4 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -689,7 +689,6 @@ typedef enum eBrushUVSculptTool { SCULPT_TOOL_SLIDE_RELAX, \ SCULPT_TOOL_CREASE, \ SCULPT_TOOL_BLOB, \ - SCULPT_TOOL_LAYER, \ SCULPT_TOOL_INFLATE, \ SCULPT_TOOL_CLAY, \ SCULPT_TOOL_CLAY_STRIPS, \ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index b5ed49b3476..1286d752b62 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -2094,6 +2094,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "height"); RNA_def_property_float_default(prop, 0.5f); RNA_def_property_range(prop, 0, 1.0f); + RNA_def_property_ui_range(prop, 0, 0.2f, 1, 3); RNA_def_property_ui_text( prop, "Brush Height", "Affectable height of brush (layer height for layer tool, i.e.)"); RNA_def_property_update(prop, 0, "rna_Brush_update");