tornavis/source/blender/blenkernel/intern/kelvinlet.cc

203 lines
7.6 KiB
C++

/* SPDX-FileCopyrightText: Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "BKE_kelvinlet.h"
#include "BLI_math_base.h"
#include "BLI_math_vector.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);
}
using kelvinlet_fn =
void (*)(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[3] /*normal*/,
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);
}