diff options
author | Pablo Dobarro <pablodp606@gmail.com> | 2019-09-07 00:14:57 +0300 |
---|---|---|
committer | Pablo Dobarro <pablodp606@gmail.com> | 2019-09-09 16:34:25 +0300 |
commit | 70c1aaf59ba91eede3d519caf9052ca3ef94ea9b (patch) | |
tree | 88b2d726783a147e58b6160af2d9464b5a3ac13d | |
parent | 8127bbbe39cb10aae65883db94684791fbbd5aa9 (diff) |
Sculpt: Elastic Deform Brush
This patch implements the paper "Regularized Kelvinlets: Sculpting Brushes based on Fundamental Solutions of Elasticity" https://graphics.pixar.com/library/Kelvinlets/paper.pdf
It includes grab, biscale grab, triscale grab, scale and twist.
All deformation modes are accessible under the same tool. This helps to keep the code organized and it should not make any difference to the user when a better brush management system is implemented.
Reviewed By: brecht
Differential Revision: https://developer.blender.org/D5634
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush.c | 1 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_280.c | 7 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_stroke.c | 8 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 297 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 13 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 27 |
7 files changed, 349 insertions, 11 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 89d771b7026..5bc4883d1ea 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -369,6 +369,13 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): row = col.row() row.prop(brush, "normal_radius_factor", slider=True) + if brush.sculpt_tool == 'ELASTIC_DEFORM': + col.separator() + row = col.row() + row.prop(brush, "elastic_deform_type") + row = col.row() + row.prop(brush, "elastic_deform_compressibility", slider=True) + # topology_rake_factor if ( capabilities.has_topology_rake and diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index cb408d25a01..f5f7fb71a64 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -901,6 +901,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->add_col[2] = 0.750000; break; case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_ELASTIC_DEFORM: case SCULPT_TOOL_SNAKE_HOOK: case SCULPT_TOOL_THUMB: br->size = 75; diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 1784cd0ab95..3bccf65f4e7 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3834,5 +3834,12 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Elatic deform brush */ + for (Brush *br = bmain->brushes.first; br; br = br->id.next) { + if (br->ob_mode & OB_MODE_SCULPT && br->elastic_deform_compressibility == 0.0f) { + br->elastic_deform_compressibility = 0.5f; + } + } } } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 18f087e8c7b..0072888cb8d 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -221,6 +221,7 @@ static bool paint_tool_require_location(Brush *brush, ePaintMode mode) case PAINT_MODE_SCULPT: if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, + SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB)) { @@ -251,7 +252,11 @@ static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode m { switch (mode) { case PAINT_MODE_SCULPT: - if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB)) { + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM)) { return false; } else { @@ -949,6 +954,7 @@ static bool sculpt_is_grab_tool(Brush *br) { return ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, + SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 4ad69e5de2a..dce8ea105e4 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -358,7 +358,8 @@ static bool sculpt_tool_needs_original(const char sculpt_tool) SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER, - SCULPT_TOOL_DRAW_SHARP); + SCULPT_TOOL_DRAW_SHARP, + SCULPT_TOOL_ELASTIC_DEFORM); } static bool sculpt_tool_is_proxy_used(const char sculpt_tool) @@ -388,6 +389,7 @@ static int sculpt_brush_needs_normal(const SculptSession *ss, const Brush *brush SCULPT_TOOL_LAYER, SCULPT_TOOL_NUDGE, SCULPT_TOOL_ROTATE, + SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_THUMB) || (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)) || @@ -1445,6 +1447,9 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_ROTATE: return alpha * pressure * feather; + case SCULPT_TOOL_ELASTIC_DEFORM: + return root_alpha * feather; + default: return 0; } @@ -2918,6 +2923,252 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); } +/* Regularized Kelvinlets: Sculpting Brushes based on Fundamental Solutions of Elasticity + * Pixar Technical Memo #17-03 */ + +typedef struct KelvinletParams { + float f; + float a; + float b; + float c; + float radius_scaled; +} KelvinletParams; + +static int sculpt_kelvinlet_get_scale_iteration_count(eBrushElasticDeformType type) +{ + if (type == BRUSH_ELASTIC_DEFORM_GRAB) { + return 1; + } + if (type == BRUSH_ELASTIC_DEFORM_GRAB_BISCALE) { + return 2; + } + if (type == BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE) { + return 3; + } + return 0; +} + +static void sculpt_kelvinet_integrate(void (*kelvinlet)(float disp[3], + const float vertex_co[3], + const float location[3], + float normal[3], + KelvinletParams *p), + float r_disp[3], + const float vertex_co[3], + const float location[3], + float normal[3], + KelvinletParams *p) +{ + float k[4][3], k_it[4][3]; + kelvinlet(k[0], vertex_co, location, normal, p); + copy_v3_v3(k_it[0], k[0]); + mul_v3_fl(k_it[0], 0.5f); + add_v3_v3v3(k_it[0], vertex_co, k_it[0]); + kelvinlet(k[1], k_it[0], location, normal, p); + copy_v3_v3(k_it[1], k[1]); + mul_v3_fl(k_it[1], 0.5f); + add_v3_v3v3(k_it[1], vertex_co, k_it[1]); + kelvinlet(k[2], k_it[1], location, normal, p); + copy_v3_v3(k_it[2], k[2]); + add_v3_v3v3(k_it[2], vertex_co, k_it[2]); + sub_v3_v3v3(k_it[2], k_it[2], location); + kelvinlet(k[3], k_it[2], location, normal, p); + copy_v3_v3(r_disp, k[0]); + madd_v3_v3fl(r_disp, k[1], 2); + madd_v3_v3fl(r_disp, k[2], 2); + add_v3_v3(r_disp, k[3]); + mul_v3_fl(r_disp, 1.0f / 6.0f); +} + +/* Regularized Kelvinlets: Formula (16) */ +static void sculpt_kelvinlet_scale(float disp[3], + const float vertex_co[3], + const float location[3], + float UNUSED(normal[3]), + KelvinletParams *p) +{ + float r_v[3]; + sub_v3_v3v3(r_v, vertex_co, location); + float r = len_v3(r_v); + float r_e = sqrtf(r * r + p->radius_scaled * p->radius_scaled); + float u = (2.0f * p->b - p->a) * ((1.0f / (r_e * r_e * r_e))) + + ((3.0f * p->radius_scaled * p->radius_scaled) / (2.0f * r_e * r_e * r_e * r_e * r_e)); + float fade = u * p->c; + mul_v3_v3fl(disp, r_v, fade * p->f); +} + +/* Regularized Kelvinlets: Formula (15) */ +static void sculpt_kelvinlet_twist(float disp[3], + const float vertex_co[3], + const float location[3], + float normal[3], + KelvinletParams *p) +{ + float r_v[3], q_r[3]; + sub_v3_v3v3(r_v, vertex_co, location); + float r = len_v3(r_v); + float r_e = sqrtf(r * r + p->radius_scaled * p->radius_scaled); + float u = -p->a * ((1.0f / (r_e * r_e * r_e))) + + ((3.0f * p->radius_scaled * p->radius_scaled) / (2.0f * r_e * r_e * r_e * r_e * r_e)); + float fade = u * p->c; + cross_v3_v3v3(q_r, normal, r_v); + mul_v3_v3fl(disp, q_r, fade * p->f); +} + +static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *grab_delta = data->grab_delta; + const float *location = ss->cache->location; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + + const float bstrength = ss->cache->bstrength; + + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + /* Maybe this can be exposed to the user */ + float radius_e[3] = {1.0f, 2.0f, 2.0f}; + float r_e[3]; + float kvl[3]; + float radius_scaled[3]; + + radius_scaled[0] = ss->cache->radius * radius_e[0]; + radius_scaled[1] = radius_scaled[0] * radius_e[1]; + radius_scaled[2] = radius_scaled[1] * radius_e[2]; + + float shear_modulus = 1.0f; + float poisson_ratio = brush->elastic_deform_compressibility; + + float a = 1.0f / (4.0f * (float)M_PI * shear_modulus); + float b = a / (4.0f * (1.0f - poisson_ratio)); + float c = 2 * (3.0f * a - 2.0f * b); + + float dir; + if (ss->cache->mouse[0] > ss->cache->initial_mouse[0]) { + dir = 1.0f; + } + else { + dir = -1.0f; + } + + if (brush->elastic_deform_type == BRUSH_ELASTIC_DEFORM_TWIST) { + int symm = ss->cache->mirror_symmetry_pass; + if (symm == 1 || symm == 2 || symm == 4 || symm == 7) { + dir = -dir; + } + } + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); + float fade, u, final_disp[3], weights[3]; + float r = len_v3v3(location, orig_data.co); + KelvinletParams params; + params.a = a; + params.b = b; + params.c = c; + params.radius_scaled = radius_scaled[0]; + + int multi_scale_it = sculpt_kelvinlet_get_scale_iteration_count(brush->elastic_deform_type); + for (int it = 0; it < max_ii(1, multi_scale_it); it++) { + r_e[it] = sqrtf(r * r + radius_scaled[it] * radius_scaled[it]); + } + + /* Regularized Kelvinlets: Formula (6) */ + for (int s_it = 0; s_it < multi_scale_it; s_it++) { + kvl[s_it] = ((a - b) / r_e[s_it]) + ((b * r * r) / (r_e[s_it] * r_e[s_it] * r_e[s_it])) + + ((a * radius_scaled[s_it] * radius_scaled[s_it]) / + (2.0f * r_e[s_it] * r_e[s_it] * r_e[s_it])); + } + + switch (brush->elastic_deform_type) { + /* Regularized Kelvinlets: Multi-scale extrapolation. Formula (11) */ + case BRUSH_ELASTIC_DEFORM_GRAB: + fade = kvl[0] * c; + mul_v3_v3fl(final_disp, grab_delta, fade * bstrength * 20.f); + break; + case BRUSH_ELASTIC_DEFORM_GRAB_BISCALE: + u = kvl[0] - kvl[1]; + fade = u * c / ((1.0f / radius_scaled[0]) - (1.0f / radius_scaled[1])); + mul_v3_v3fl(final_disp, grab_delta, fade * bstrength * 20.0f); + break; + case BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE: + weights[0] = 1.0f; + weights[1] = -( + (radius_scaled[2] * radius_scaled[2] - radius_scaled[0] * radius_scaled[0]) / + (radius_scaled[2] * radius_scaled[2] - radius_scaled[1] * radius_scaled[1])); + weights[2] = ((radius_scaled[1] * radius_scaled[1] - radius_scaled[0] * radius_scaled[0]) / + (radius_scaled[2] * radius_scaled[2] - radius_scaled[1] * radius_scaled[1])); + + float u = weights[0] * kvl[0] + weights[1] * kvl[1] + weights[2] * kvl[2]; + fade = u * c / + (weights[0] / radius_scaled[0] + weights[1] / radius_scaled[1] + + weights[2] / radius_scaled[2]); + mul_v3_v3fl(final_disp, grab_delta, fade * bstrength * 20.0f); + break; + case BRUSH_ELASTIC_DEFORM_SCALE: + params.f = len_v3(grab_delta) * dir * bstrength; + sculpt_kelvinet_integrate(sculpt_kelvinlet_scale, + final_disp, + orig_data.co, + location, + ss->cache->sculpt_normal_symm, + ¶ms); + break; + case BRUSH_ELASTIC_DEFORM_TWIST: + params.f = len_v3(grab_delta) * dir * bstrength; + sculpt_kelvinet_integrate(sculpt_kelvinlet_twist, + final_disp, + orig_data.co, + location, + ss->cache->sculpt_normal_symm, + ¶ms); + break; + } + + if (vd.mask) { + mul_v3_fl(final_disp, 1.0f - *vd.mask); + } + + copy_v3_v3(proxy[vd.i], final_disp); + + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .grab_delta = grab_delta, + }; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); +} + static void do_nudge_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) @@ -4219,13 +4470,26 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe { SculptSession *ss = ob->sculpt; int totnode; + PBVHNode **nodes; /* 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.0f; - PBVHNode **nodes = sculpt_pbvh_gather_generic( - ob, sd, brush, use_original, radius_scale, &totnode); + + /* These brushes need to update all nodes as they are not constrained by the brush radius */ + if (brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) { + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = FLT_MAX, + .original = true, + }; + BKE_pbvh_search_gather(ss->pbvh, NULL, &data, &nodes, &totnode); + } + else { + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; + const float radius_scale = 1.0f; + 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) { @@ -4309,6 +4573,8 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe break; case SCULPT_TOOL_DRAW_SHARP: do_draw_sharp_brush(sd, ob, nodes, totnode); + case SCULPT_TOOL_ELASTIC_DEFORM: + do_elastic_deform_brush(sd, ob, nodes, totnode); break; } @@ -4374,8 +4640,11 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata, Object *ob = data->ob; /* these brushes start from original coordinates */ - const bool use_orco = ELEM( - data->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB); + const bool use_orco = ELEM(data->brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM); PBVHVertexIter vd; PBVHProxyNode *proxies; @@ -4817,6 +5086,8 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Simplify Brush"; case SCULPT_TOOL_DRAW_SHARP: return "Draw Sharp Brush"; + case SCULPT_TOOL_ELASTIC_DEFORM: + return "Elastic Deform Brush"; } return "Sculpting"; @@ -5055,6 +5326,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru if (ELEM(tool, SCULPT_TOOL_GRAB, + SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_NUDGE, SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK, @@ -5078,6 +5350,7 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru switch (tool) { case SCULPT_TOOL_GRAB: case SCULPT_TOOL_THUMB: + case SCULPT_TOOL_ELASTIC_DEFORM: sub_v3_v3v3(delta, grab_location, cache->old_grab_location); invert_m4_m4(imat, ob->obmat); mul_mat3_m4_v3(imat, delta); @@ -5116,11 +5389,14 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru if (tool == SCULPT_TOOL_GRAB) { copy_v3_v3(cache->anchored_location, cache->true_location); } + else if (tool == SCULPT_TOOL_ELASTIC_DEFORM) { + copy_v3_v3(cache->anchored_location, cache->true_location); + } else if (tool == SCULPT_TOOL_THUMB) { copy_v3_v3(cache->anchored_location, cache->orig_grab_location); } - if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { + if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ELASTIC_DEFORM)) { /* location stays the same for finding vertices in brush radius */ copy_v3_v3(cache->true_location, cache->orig_grab_location); @@ -5636,7 +5912,8 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob) /* Restore the mesh before continuing with anchored stroke */ if ((brush->flag & BRUSH_ANCHORED) || - (brush->sculpt_tool == SCULPT_TOOL_GRAB && + ((brush->sculpt_tool == SCULPT_TOOL_GRAB || + brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) && BKE_brush_use_size_pressure(ss->cache->vc->scene, brush)) || (brush->flag & BRUSH_DRAG_DOT)) { paint_mesh_restore_co(sd, ob); diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 747c7cfb842..c5d741fd25a 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -198,6 +198,14 @@ typedef enum eBrushCurvePreset { BRUSH_CURVE_CONSTANT = 8, } eBrushCurvePreset; +typedef enum eBrushElasticDeformType { + BRUSH_ELASTIC_DEFORM_GRAB = 0, + BRUSH_ELASTIC_DEFORM_GRAB_BISCALE = 1, + BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE = 2, + BRUSH_ELASTIC_DEFORM_SCALE = 3, + BRUSH_ELASTIC_DEFORM_TWIST = 4, +} eBrushElasticDeformType; + typedef struct Brush { ID id; @@ -305,6 +313,9 @@ typedef struct Brush { int curve_preset; + int elastic_deform_type; + float elastic_deform_compressibility; + /* overlay */ int texture_overlay_alpha; int mask_overlay_alpha; @@ -454,6 +465,7 @@ typedef enum eBrushSculptTool { SCULPT_TOOL_CLAY_STRIPS = 18, SCULPT_TOOL_MASK = 19, SCULPT_TOOL_DRAW_SHARP = 20, + SCULPT_TOOL_ELASTIC_DEFORM = 21, } eBrushSculptTool; /* Brush.uv_sculpt_tool */ @@ -488,6 +500,7 @@ typedef enum eBrushUVSculptTool { SCULPT_TOOL_THUMB, \ SCULPT_TOOL_LAYER, \ SCULPT_TOOL_DRAW_SHARP, \ + SCULPT_TOOL_ELASTIC_DEFORM, \ \ /* These brushes could handle dynamic topology, \ * but user feedback indicates it's better not to */ \ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 58771023aab..5f574cc0f8e 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -92,6 +92,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""}, {SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""}, {SCULPT_TOOL_DRAW_SHARP, "DRAW_SHARP", ICON_BRUSH_SCULPT_DRAW, "Draw Sharp", ""}, + {SCULPT_TOOL_ELASTIC_DEFORM, "ELASTIC_DEFORM", ICON_BRUSH_GRAB, "Elastic Deform", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -1595,6 +1596,19 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem brush_elastic_deform_type_items[] = { + {BRUSH_ELASTIC_DEFORM_GRAB, "ELASTIC_DEFORM_GRAB", 0, "Grab", ""}, + {BRUSH_ELASTIC_DEFORM_GRAB_BISCALE, "ELASTIC_DEFORM_GRAB_BISCALE", 0, "Bi-scale Grab", ""}, + {BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE, + "ELASTIC_DEFORM_GRAB_TRISCALE", + 0, + "Tri-scale Grab", + ""}, + {BRUSH_ELASTIC_DEFORM_SCALE, "ELASTIC_DEFORM_SCALE", 0, "Scale", ""}, + {BRUSH_ELASTIC_DEFORM_TWIST, "ELASTIC_DEFORM_TWIST", 0, "Twist", ""}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "Brush", "ID"); RNA_def_struct_ui_text( srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); @@ -1675,6 +1689,11 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Curve Preset", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "elastic_deform_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_elastic_deform_type_items); + RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + /* number values */ prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL); RNA_def_property_int_funcs(prop, NULL, "rna_Brush_set_size", NULL); @@ -1810,6 +1829,14 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Normal Weight", "How much grab will pull vertexes out of surface during a grab"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "elastic_deform_compressibility", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "elastic_deform_compressibility"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01f, 3); + RNA_def_property_ui_text( + prop, "Compressibility", "Material compressibility when simulating the elasticity"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "rake_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "rake_factor"); RNA_def_property_float_default(prop, 0); |