projects.blender.org

Sculpt: New Layer Brush · 47f46637be

  • ️blender
  • ️Tue Apr 14 2020
@ -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()

@ -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];

@ -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);

@ -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;
}

@ -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++) {

@ -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;

@ -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);

@ -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;

@ -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;

@ -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'. */

@ -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, \

@ -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");