diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_toolsystem_toolbar.py | 9 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_paint.h | 4 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 44 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 21 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_transform.c | 171 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 11 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_sculpt_paint.c | 20 |
7 files changed, 262 insertions, 18 deletions
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 31f25d016a6..5831aa52cc1 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -261,9 +261,15 @@ class _defs_annotate: class _defs_transform: + def draw_transform_sculpt_tool_settings(context, layout): + if context.mode != 'SCULPT': + return + layout.prop(context.tool_settings.sculpt, "transform_mode") + @ToolDef.from_fn def translate(): def draw_settings(context, layout, _tool): + _defs_transform.draw_transform_sculpt_tool_settings(context, layout) _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1) return dict( idname="builtin.move", @@ -279,6 +285,7 @@ class _defs_transform: @ToolDef.from_fn def rotate(): def draw_settings(context, layout, _tool): + _defs_transform.draw_transform_sculpt_tool_settings(context, layout) _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2) return dict( idname="builtin.rotate", @@ -294,6 +301,7 @@ class _defs_transform: @ToolDef.from_fn def scale(): def draw_settings(context, layout, _tool): + _defs_transform.draw_transform_sculpt_tool_settings(context, layout) _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3) return dict( idname="builtin.scale", @@ -349,6 +357,7 @@ class _defs_transform: props = tool.gizmo_group_properties("VIEW3D_GGT_xform_gizmo") layout.prop(props, "drag_action") + _defs_transform.draw_transform_sculpt_tool_settings(context, layout) _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1) return dict( diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 9e0ea5e13d5..a47e4a24f75 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -613,6 +613,10 @@ typedef struct SculptSession { float init_pivot_rot[4]; float init_pivot_scale[3]; + float prev_pivot_pos[3]; + float prev_pivot_rot[4]; + float prev_pivot_scale[3]; + union { struct { struct SculptVertexPaintGeomMap gmap; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 2c6c4c82676..d9209b65276 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3535,15 +3535,7 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; Sculpt *sd = data->sd; 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, - SCULPT_TOOL_ELASTIC_DEFORM, - SCULPT_TOOL_BOUNDARY, - SCULPT_TOOL_POSE); + const bool use_orco = data->use_proxies_orco; PBVHVertexIter vd; PBVHProxyNode *proxies; @@ -3598,17 +3590,49 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) return; } + /* First line is tools that don't support proxies. */ + const bool use_orco = ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM, + SCULPT_TOOL_BOUNDARY, + SCULPT_TOOL_POSE); + + BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .use_proxies_orco = use_orco, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); + MEM_SAFE_FREE(nodes); +} + +void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob) +{ + SculptSession *ss = ob->sculpt; + PBVHNode **nodes; + int totnode; + BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); SculptThreadedTaskData data = { .sd = sd, .ob = ob, - .brush = brush, .nodes = nodes, + .use_proxies_orco = false, }; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); + MEM_SAFE_FREE(nodes); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 5fa115ed629..0693b445fe5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -247,6 +247,10 @@ typedef struct SculptThreadedTaskData { float (*mat)[4]; float (*vertCos)[3]; + /* When true, the displacement stored in the proxies will be aplied to the original coordinates + * instead of to the current coordinates. */ + bool use_proxies_orco; + /* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors to * the stroke direction are needed. */ float (*stroke_xz)[3]; @@ -290,6 +294,10 @@ typedef struct SculptThreadedTaskData { bool mask_expand_create_face_set; float transform_mats[8][4][4]; + float elastic_transform_mat[4][4]; + float elastic_transform_pivot[3]; + float elastic_transform_pivot_init[3]; + float elastic_transform_radius; /* Boundary brush */ float boundary_deform_strength; @@ -372,6 +380,14 @@ typedef enum SculptFilterOrientation { SCULPT_FILTER_ORIENTATION_VIEW = 2, } SculptFilterOrientation; +/* Defines how transform tools are going to apply its displacement. */ +typedef enum SculptTransformDisplacementMode { + /* Displaces the elements from their original coordinates. */ + SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL = 0, + /* Displaces the elements incrementally from their previous position. */ + SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL = 1, +} SculptTransformDisplacementMode; + #define SCULPT_CLAY_STABILIZER_LEN 10 typedef struct AutomaskingSettings { @@ -440,6 +456,8 @@ typedef struct FilterCache { int active_face_set; + SculptTransformDisplacementMode transform_displacement_mode; + /* Auto-masking. */ AutomaskingCache *automasking; @@ -1137,12 +1155,15 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v); */ bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v); +void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob); + /** * Initialize a point-in-brush test with a given falloff shape. * * \param falloff_shape: #PAINT_FALLOFF_SHAPE_SPHERE or #PAINT_FALLOFF_SHAPE_TUBE. * \return The brush falloff function. */ + SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, SculptBrushTest *test, char falloff_shape); diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index b3616254b26..5096d5fe3fd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -16,6 +16,7 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_kelvinlet.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_object.h" @@ -33,6 +34,7 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" +#include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -54,6 +56,10 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob) copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot); copy_v3_v3(ss->init_pivot_scale, ss->pivot_scale); + copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos); + copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot); + copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale); + SCULPT_undo_push_begin(ob, "Transform"); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); @@ -61,10 +67,18 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob) SCULPT_vertex_random_access_ensure(ss); SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); + + if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) { + ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL; + } + else { + ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL; + } } static void sculpt_transform_matrices_init(SculptSession *ss, const char symm, + const SculptTransformDisplacementMode t_mode, float r_transform_mats[8][4][4]) { @@ -73,9 +87,18 @@ static void sculpt_transform_matrices_init(SculptSession *ss, transform_mat[4][4]; float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3]; - copy_v3_v3(start_pivot_pos, ss->init_pivot_pos); - copy_v4_v4(start_pivot_rot, ss->init_pivot_rot); - copy_v3_v3(start_pivot_scale, ss->init_pivot_scale); + switch (t_mode) { + case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL: + copy_v3_v3(start_pivot_pos, ss->init_pivot_pos); + copy_v4_v4(start_pivot_rot, ss->init_pivot_rot); + copy_v3_v3(start_pivot_scale, ss->init_pivot_scale); + break; + case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL: + copy_v3_v3(start_pivot_pos, ss->prev_pivot_pos); + copy_v4_v4(start_pivot_rot, ss->prev_pivot_rot); + copy_v3_v3(start_pivot_scale, ss->prev_pivot_scale); + break; + } for (int i = 0; i < PAINT_SYMM_AREAS; i++) { ePaintSymmetryAreas v_symm = i; @@ -134,16 +157,26 @@ static void sculpt_transform_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + float *start_co; float transformed_co[3], orig_co[3], disp[3]; float fade = vd.mask ? *vd.mask : 0.0f; copy_v3_v3(orig_co, orig_data.co); char symm_area = SCULPT_get_vertex_symm_area(orig_co); - copy_v3_v3(transformed_co, orig_co); + switch (ss->filter_cache->transform_displacement_mode) { + case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL: + start_co = orig_co; + break; + case SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL: + start_co = vd.co; + break; + } + + copy_v3_v3(transformed_co, start_co); mul_m4_v3(data->transform_mats[(int)symm_area], transformed_co); - sub_v3_v3v3(disp, transformed_co, orig_co); + sub_v3_v3v3(disp, transformed_co, start_co); mul_v3_fl(disp, 1.0f - fade); - add_v3_v3v3(vd.co, orig_co, disp); + add_v3_v3v3(vd.co, start_co, disp); if (vd.mvert) { BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); @@ -165,7 +198,8 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob) .nodes = ss->filter_cache->nodes, }; - sculpt_transform_matrices_init(ss, symm, data.transform_mats); + sculpt_transform_matrices_init( + ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats); /* Regular transform applies all symmetry passes at once as it is split by symmetry areas * (each vertex can only be transformed once by the transform matrix of its area). */ @@ -175,6 +209,98 @@ static void sculpt_transform_all_vertices(Sculpt *sd, Object *ob) 0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings); } +static void sculpt_elastic_transform_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + + float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[i])->co; + + SculptOrigVertData orig_data; + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); + + KelvinletParams params; + /* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume + * preservation like in the elastic deform brushes. Setting them to the same default as elastic + * deform triscale grab because they work well in most cases. */ + const float force = 1.0f; + const float shear_modulus = 1.0f; + const float poisson_ratio = 0.4f; + BKE_kelvinlet_init_params( + ¶ms, data->elastic_transform_radius, force, shear_modulus, poisson_ratio); + + SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, &vd); + float transformed_co[3], orig_co[3], disp[3]; + const float fade = vd.mask ? *vd.mask : 0.0f; + copy_v3_v3(orig_co, orig_data.co); + + copy_v3_v3(transformed_co, vd.co); + mul_m4_v3(data->elastic_transform_mat, transformed_co); + sub_v3_v3v3(disp, transformed_co, vd.co); + + float final_disp[3]; + BKE_kelvinlet_grab_triscale(final_disp, ¶ms, vd.co, data->elastic_transform_pivot, disp); + mul_v3_fl(final_disp, 20.0f * (1.0f - fade)); + + copy_v3_v3(proxy[vd.i], final_disp); + + if (vd.mvert) { + BKE_pbvh_vert_mark_update(ss->pbvh, vd.index); + } + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_update(node); +} + +static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float transform_radius) +{ + BLI_assert(ss->filter_cache->transform_displacement_mode == + SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL); + + SculptSession *ss = ob->sculpt; + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = ss->filter_cache->nodes, + .elastic_transform_radius = transform_radius, + }; + + sculpt_transform_matrices_init( + ss, symm, ss->filter_cache->transform_displacement_mode, data.transform_mats); + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode); + + /* Elastic transform needs to apply all transform matrices to all vertices and then combine the + * displacement proxies as all vertices are modified by all symmetry passes. */ + for (ePaintSymmetryFlags symmpass = 0; symmpass <= symm; symmpass++) { + if (SCULPT_is_symmetry_iteration_valid(symmpass, symm)) { + flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass); + flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass); + + printf( + "%.2f %.2f %.2f\n", ss->init_pivot_pos[0], ss->init_pivot_pos[1], ss->init_pivot_pos[2]); + + const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot); + copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]); + BLI_task_parallel_range( + 0, ss->filter_cache->totnode, &data, sculpt_elastic_transform_task_cb, &settings); + } + } + SCULPT_combine_transform_proxies(sd, ob); +} + void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; @@ -184,7 +310,36 @@ void ED_sculpt_update_modal_transform(struct bContext *C, Object *ob) SCULPT_vertex_random_access_ensure(ss); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); - sculpt_transform_all_vertices(sd, ob); + switch (sd->transform_mode) { + case SCULPT_TRANSFORM_MODE_ALL_VERTICES: { + sculpt_transform_all_vertices(sd, ob); + break; + } + case SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC: { + Brush *brush = BKE_paint_brush(&sd->paint); + Scene *scene = CTX_data_scene(C); + float transform_radius; + + if (BKE_brush_use_locked_size(scene, brush)) { + transform_radius = BKE_brush_unprojected_radius_get(scene, brush); + } + else { + ViewContext vc; + + ED_view3d_viewcontext_init(C, &vc, depsgraph); + + transform_radius = paint_calc_object_space_radius( + &vc, ss->init_pivot_pos, BKE_brush_size_get(scene, brush)); + } + + sculpt_transform_radius_elastic(sd, ob, transform_radius); + break; + } + } + + copy_v3_v3(ss->prev_pivot_pos, ss->pivot_pos); + copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot); + copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale); if (ss->deform_modifiers_active || ss->shapekey_active) { SCULPT_flush_stroke_deform(sd, ob, true); diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 1be27e0354c..47463638706 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -991,6 +991,9 @@ typedef struct Sculpt { // float pivot[3]; XXX not used? int flags; + /* Transform tool. */ + int transform_mode; + int automasking_flags; /* Control tablet input */ @@ -1011,6 +1014,8 @@ typedef struct Sculpt { float constant_detail; float detail_percent; + char _pad[4]; + struct Object *gravity_object; } Sculpt; @@ -2279,6 +2284,12 @@ typedef enum eSculptFlags { SCULPT_HIDE_FACE_SETS = (1 << 17), } eSculptFlags; +/* Sculpt.transform_mode */ +typedef enum eSculptTransformMode { + SCULPT_TRANSFORM_MODE_ALL_VERTICES = 0, + SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC = 1, +} eSculptTrasnformMode; + /** PaintModeSettings.mode */ typedef enum ePaintCanvasSource { /** Paint on the active node of the active material slot. */ diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index ab5e4c2f0d5..3de9e632ff6 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -774,6 +774,20 @@ static void rna_def_sculpt(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem sculpt_transform_mode_items[] = { + {SCULPT_TRANSFORM_MODE_ALL_VERTICES, + "ALL_VERTICES", + 0, + "All Vertices", + "Applies the transformation to all vertices in the mesh"}, + {SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC, + "RADIUS_ELASTIC", + 0, + "Elastic", + "Applies the transformation simulating elasticity using the radius of the cursor"}, + {0, NULL, 0, NULL, NULL}, + }; + StructRNA *srna; PropertyRNA *prop; @@ -912,6 +926,12 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Gravity", "Amount of gravity after each dab"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "transform_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, sculpt_transform_mode_items); + RNA_def_property_ui_text( + prop, "Transform Mode", "How the transformation is going to be applied to the target"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "gravity_object", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( |