Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_kelvinlet.h77
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/kelvinlet.c215
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c179
5 files changed, 316 insertions, 161 deletions
diff --git a/source/blender/blenkernel/BKE_kelvinlet.h b/source/blender/blenkernel/BKE_kelvinlet.h
new file mode 100644
index 00000000000..fbf7d3ede1f
--- /dev/null
+++ b/source/blender/blenkernel/BKE_kelvinlet.h
@@ -0,0 +1,77 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) Blender Foundation.
+ * All rights reserved.
+ */
+#ifndef __BKE_KELVINLET_H__
+#define __BKE_KELVINLET_H__
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_math.h"
+
+/* Regularized Kelvinlets: Sculpting Brushes based on Fundamental Solutions of Elasticity
+ * Pixar Technical Memo #17-03 */
+
+#define KELVINLET_MAX_ITERATIONS 3
+
+typedef struct KelvinletParams {
+ float a;
+ float b;
+ float c;
+
+ float f;
+
+ float radius_scaled[KELVINLET_MAX_ITERATIONS];
+} KelvinletParams;
+
+/* Initialize KelvinletParams to store the parameters that will affect the deformation produced by
+ * a Kelvinlet */
+void BKE_kelvinlet_init_params(
+ KelvinletParams *params, float radius, float force, float shear_modulus, float poisson_ratio);
+
+/* Regularized Kelvinlets */
+/* All these functions output the displacement that should be applied to each element. */
+/* The initial coordinates of that element should not be modified during the transformation */
+void BKE_kelvinlet_grab(float r_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float brush_delta[3]);
+void BKE_kelvinlet_grab_biscale(float r_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float brush_delta[3]);
+void BKE_kelvinlet_grab_triscale(float r_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float brush_delta[3]);
+void BKE_kelvinlet_scale(float r_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float surface_normal[3]);
+void BKE_kelvinlet_twist(float r_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float surface_normal[3]);
+
+#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 883518b7a9d..88a3629ea3f 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -129,6 +129,7 @@ set(SRC
intern/image_gen.c
intern/image_save.c
intern/ipo.c
+ intern/kelvinlet.c
intern/key.c
intern/keyconfig.c
intern/lattice.c
@@ -292,6 +293,7 @@ set(SRC
BKE_image.h
BKE_image_save.h
BKE_ipo.h
+ BKE_kelvinlet.h
BKE_key.h
BKE_keyconfig.h
BKE_lattice.h
diff --git a/source/blender/blenkernel/intern/kelvinlet.c b/source/blender/blenkernel/intern/kelvinlet.c
new file mode 100644
index 00000000000..a7b48107873
--- /dev/null
+++ b/source/blender/blenkernel/intern/kelvinlet.c
@@ -0,0 +1,215 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_kelvinlet.h"
+
+/* Regularized Kelvinlets: Sculpting Brushes based on Fundamental Solutions of Elasticity
+ * Pixar Technical Memo #17-03 */
+
+void BKE_kelvinlet_init_params(
+ KelvinletParams *params, float radius, float force, float shear_modulus, float poisson_ratio)
+{
+ params->a = 1.0f / (4.0f * (float)M_PI * shear_modulus);
+ params->b = params->a / (4.0f * (1.0f - poisson_ratio));
+ params->c = 2 * (3.0f * params->a - 2.0f * params->b);
+
+ /* Used in scale and twist. */
+ params->f = force;
+
+ /* This can be exposed if needed */
+ const float radius_e[KELVINLET_MAX_ITERATIONS] = {1.0f, 2.0f, 2.0f};
+ params->radius_scaled[0] = radius * radius_e[0];
+ params->radius_scaled[1] = params->radius_scaled[0] * radius_e[1];
+ params->radius_scaled[2] = params->radius_scaled[1] * radius_e[2];
+}
+
+static void init_kelvinlet_grab(float radius_e[3],
+ float kelvinlet[3],
+ const float radius,
+ const KelvinletParams *params,
+ const int num_iterations)
+{
+ const float a = params->a;
+ const float b = params->b;
+ const float *radius_scaled = params->radius_scaled;
+
+ for (int i = 0; i < num_iterations; i++) {
+ radius_e[i] = sqrtf(pow2f(radius) + pow2f(params->radius_scaled[i]));
+ }
+
+ /* Regularized Kelvinlets: Formula (6) */
+ for (int i = 0; i < num_iterations; i++) {
+ kelvinlet[i] = ((a - b) / radius_e[i]) + ((b * pow2f(radius)) / pow3f(radius_e[i])) +
+ ((a * pow2f(radius_scaled[i])) / (2.0f * pow3f(radius_e[i])));
+ }
+}
+
+void BKE_kelvinlet_grab(float radius_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float brush_delta[3])
+{
+ float radius_e[3], kelvinlet[3];
+ const float c = params->c;
+ const float radius = len_v3v3(brush_location, elem_orig_co);
+
+ init_kelvinlet_grab(radius_e, kelvinlet, radius, params, 1);
+
+ const float fade = kelvinlet[0] * c;
+
+ mul_v3_v3fl(radius_elem_disp, brush_delta, fade);
+}
+
+void BKE_kelvinlet_grab_biscale(float radius_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float brush_delta[3])
+{
+ float radius_e[3], kelvinlet[3];
+ const float c = params->c;
+ const float *radius_scaled = params->radius_scaled;
+ float radius = len_v3v3(brush_location, elem_orig_co);
+
+ init_kelvinlet_grab(radius_e, kelvinlet, radius, params, 2);
+
+ const float u = kelvinlet[0] - kelvinlet[1];
+ const float fade = u * c / ((1.0f / radius_scaled[0]) - (1.0f / radius_scaled[1]));
+
+ mul_v3_v3fl(radius_elem_disp, brush_delta, fade);
+}
+
+void BKE_kelvinlet_grab_triscale(float radius_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float brush_delta[3])
+{
+ float radius_e[3], kelvinlet[3], weights[3];
+ const float c = params->c;
+ const float *radius_scaled = params->radius_scaled;
+ const float radius = len_v3v3(brush_location, elem_orig_co);
+
+ init_kelvinlet_grab(radius_e, kelvinlet, radius, params, 3);
+
+ weights[0] = 1.0f;
+ weights[1] = -((pow2f(radius_scaled[2]) - pow2f(radius_scaled[0])) /
+ (pow2f(radius_scaled[2]) - pow2f(radius_scaled[1])));
+ weights[2] = ((pow2f(radius_scaled[1]) - pow2f(radius_scaled[0])) /
+ (pow2f(radius_scaled[2]) - pow2f(radius_scaled[1])));
+
+ const float u = weights[0] * kelvinlet[0] + weights[1] * kelvinlet[1] +
+ weights[2] * kelvinlet[2];
+ const float fade = u * c /
+ (weights[0] / radius_scaled[0] + weights[1] / radius_scaled[1] +
+ weights[2] / radius_scaled[2]);
+
+ mul_v3_v3fl(radius_elem_disp, brush_delta, fade);
+}
+
+typedef void (*kelvinlet_fn)(
+ float[3], const float *, const float *, const float *, const KelvinletParams *);
+
+static void sculpt_kelvinet_integrate(kelvinlet_fn kelvinlet,
+ float r_disp[3],
+ const float vertex_co[3],
+ const float location[3],
+ const float normal[3],
+ const 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.0f);
+ madd_v3_v3fl(r_disp, k[2], 2.0f);
+ add_v3_v3(r_disp, k[3]);
+ mul_v3_fl(r_disp, 1.0f / 6.0f);
+}
+
+/* Regularized Kelvinlets: Formula (16) */
+static void kelvinlet_scale(float disp[3],
+ const float vertex_co[3],
+ const float location[3],
+ const float UNUSED(normal[3]),
+ const KelvinletParams *p)
+{
+ float radius_vertex[3];
+ sub_v3_v3v3(radius_vertex, vertex_co, location);
+ const float radius = len_v3(radius_vertex);
+ const float radius_e = sqrtf(pow2f(radius) + pow2f(p->radius_scaled[0]));
+ const float u = (2.0f * p->b - p->a) * ((1.0f / pow3f(radius_e))) +
+ ((3.0f * pow2f(p->radius_scaled[0])) / (2.0f * pow5f(radius_e)));
+ const float fade = u * p->c;
+ mul_v3_v3fl(disp, radius_vertex, fade * p->f);
+}
+
+void BKE_kelvinlet_scale(float radius_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float surface_normal[3])
+{
+ sculpt_kelvinet_integrate(
+ kelvinlet_scale, radius_elem_disp, elem_orig_co, brush_location, surface_normal, params);
+}
+
+/* Regularized Kelvinlets: Formula (15) */
+static void kelvinlet_twist(float disp[3],
+ const float vertex_co[3],
+ const float location[3],
+ const float normal[3],
+ const KelvinletParams *p)
+{
+ float radius_vertex[3], q_r[3];
+ sub_v3_v3v3(radius_vertex, vertex_co, location);
+ const float radius = len_v3(radius_vertex);
+ const float radius_e = sqrtf(pow2f(radius) + pow2f(p->radius_scaled[0]));
+ const float u = -p->a * ((1.0f / pow3f(radius_e))) +
+ ((3.0f * pow2f(p->radius_scaled[0])) / (2.0f * pow5f(radius_e)));
+ const float fade = u * p->c;
+ cross_v3_v3v3(q_r, normal, radius_vertex);
+ mul_v3_v3fl(disp, q_r, fade * p->f);
+}
+
+void BKE_kelvinlet_twist(float radius_elem_disp[3],
+ const KelvinletParams *params,
+ const float elem_orig_co[3],
+ const float brush_location[3],
+ const float surface_normal[3])
+{
+ sculpt_kelvinet_integrate(
+ kelvinlet_twist, radius_elem_disp, elem_orig_co, brush_location, surface_normal, params);
+}
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 4c86b9d3396..85c6425bb2f 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -58,6 +58,10 @@ MINLINE float pow4f(float x)
{
return pow2f(pow2f(x));
}
+MINLINE float pow5f(float x)
+{
+ return pow4f(x) * x;
+}
MINLINE float pow7f(float x)
{
return pow2f(pow3f(x)) * x;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 2964d53bc48..9d0048ff847 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -48,6 +48,7 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_image.h"
+#include "BKE_kelvinlet.h"
#include "BKE_key.h"
#include "BKE_library.h"
#include "BKE_main.h"
@@ -3404,98 +3405,6 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
BKE_pbvh_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 UNUSED(tls))
@@ -3516,23 +3425,6 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
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_volume_preservation;
-
- 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;
@@ -3547,73 +3439,38 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
dir = -dir;
}
}
+
+ KelvinletParams params;
+ float force = len_v3(grab_delta) * dir * bstrength;
+ BKE_kelvinlet_init_params(
+ &params, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation);
+
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
sculpt_orig_vert_data_update(&orig_data, &vd);
- float fade, 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]));
- }
-
+ float final_disp[3];
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);
+ BKE_kelvinlet_grab(final_disp, &params, orig_data.co, location, grab_delta);
+ mul_v3_fl(final_disp, bstrength * 20.0f);
break;
case BRUSH_ELASTIC_DEFORM_GRAB_BISCALE: {
- const float 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);
+ BKE_kelvinlet_grab_biscale(final_disp, &params, orig_data.co, location, grab_delta);
+ mul_v3_fl(final_disp, 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]));
-
- const 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);
+ BKE_kelvinlet_grab_triscale(final_disp, &params, orig_data.co, location, grab_delta);
+ mul_v3_fl(final_disp, 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,
- &params);
+ BKE_kelvinlet_scale(
+ final_disp, &params, orig_data.co, location, ss->cache->sculpt_normal_symm);
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,
- &params);
+ BKE_kelvinlet_twist(
+ final_disp, &params, orig_data.co, location, ss->cache->sculpt_normal_symm);
break;
}