diff options
-rw-r--r-- | source/blender/blenkernel/BKE_kelvinlet.h | 77 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/kelvinlet.c | 215 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_base_inline.c | 4 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 179 |
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( + ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, - ¶ms); + BKE_kelvinlet_scale( + final_disp, ¶ms, 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, - ¶ms); + BKE_kelvinlet_twist( + final_disp, ¶ms, orig_data.co, location, ss->cache->sculpt_normal_symm); break; } |