diff options
author | Pablo Dobarro <pablodp606> | 2022-06-04 05:21:04 +0300 |
---|---|---|
committer | Joseph Eagar <joeedh@gmail.com> | 2022-06-04 23:50:28 +0300 |
commit | e90ba74d3eb826abab4ec65b82fe0176ce3b7d1b (patch) | |
tree | 10f0d095e4aae8c50614b65ba2b71af4bc013002 /source/blender/editors/sculpt_paint/sculpt_transform.c | |
parent | e230ccaf8c3b62f8c1a322d525084226136578ba (diff) |
D15041: Sculpt: Elastic Transform
This implements transform modes for the transform tool and Elastic
Transform. This mode uses the Kelvinlets from elastic deform to apply
the transformation to the mesh, using the cursor radius to control the
elasticity falloff.
{F9269771}
In order for this to work, the transform tool uses incremental mode when
elastic transform is enabled. This allows to integrate the displacement of
the Kelvinet in multiple steps.
Review By: Sergey Sharbin & Daniel Bystedt & Julian Kaspar & Campbell
Barton
Differential Revision: https://developer.blender.org/D9653
Ref D15041
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_transform.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_transform.c | 171 |
1 files changed, 163 insertions, 8 deletions
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); |