Sculpt: memory fixes

* Removed a pointer from a sculpt cloth struct.
  Due to padding this doubled the size of the
  struct.  Hopefully this will be nicer on the L3 cache.
* Fixed a nasty memory leak in the smoothing code with multires.
This commit is contained in:
Joseph Eagar 2021-11-02 21:55:18 -07:00
parent 87c6d95603
commit 04d1bdbf36
7 changed files with 201 additions and 89 deletions

View File

@ -302,6 +302,8 @@ typedef enum eSculptClothConstraintType {
SCULPT_CLOTH_CONSTRAINT_PIN = 3,
} eSculptClothConstraintType;
#define CLOTH_NO_POS_PTR
typedef struct SculptClothConstraint {
signed char ctype, thread_nr;
@ -328,18 +330,30 @@ typedef struct SculptClothConstraint {
struct {
int index;
float *co;
#ifndef CLOTH_NO_POS_PTR
float *position;
#endif
} elems[];
} SculptClothConstraint;
#define MAKE_CONSTRAINT_STRUCT(totelem) \
signed char ctype, thread_nr; \
short node; \
float strength; \
struct { \
int index; \
float *position; \
} elems[totelem]
#ifndef CLOTH_NO_POS_PTR
# define MAKE_CONSTRAINT_STRUCT(totelem) \
signed char ctype, thread_nr; \
short node; \
float strength; \
struct { \
int index; \
float *position; \
} elems[totelem]
#else
# define MAKE_CONSTRAINT_STRUCT(totelem) \
signed char ctype, thread_nr; \
short node; \
float strength; \
struct { \
int index; \
} elems[totelem]
#endif
typedef struct SculptClothLengthConstraint {
MAKE_CONSTRAINT_STRUCT(2);
@ -375,12 +389,6 @@ typedef struct SculptClothSimulation {
*/
int tot_constraint_tasks;
/* Position anchors for deformation brushes. These positions are modified by the brush and the
* final positions of the simulated vertices are updated with constraints that use these points
* as targets. */
float (*deformation_pos)[3];
float *deformation_strength;
float mass;
float damping;
float softbody_strength;
@ -392,9 +400,17 @@ typedef struct SculptClothSimulation {
float sim_falloff;
float (*acceleration)[3];
float (*pos)[3];
float (*init_pos)[3];
float (*softbody_pos)[3];
/* Position anchors for deformation brushes. These positions are modified by the brush and the
* final positions of the simulated vertices are updated with constraints that use these points
* as targets. */
float (*deformation_pos)[3];
float *deformation_strength;
float (*prev_pos)[3];
float (*last_iteration_pos)[3];
float (*init_normal)[3];
@ -602,7 +618,9 @@ enum {
SCULPT_SCL_LAYER_DISP,
SCULPT_SCL_LAYER_STROKE_ID,
SCULPT_SCL_ORIG_FSETS,
SCULPT_SCL_LAYER_MAX
SCULPT_SCL_SMOOTH_VEL,
SCULPT_SCL_SMOOTH_BDIS,
SCULPT_SCL_LAYER_MAX,
};
typedef struct SculptSession {

View File

@ -6945,6 +6945,22 @@ void SCULPT_cache_free(SculptSession *ss, StrokeCache *cache)
MEM_freeN(cache);
}
void SCULPT_release_customlayers(SculptSession *ss, bool non_customdata_only)
{
for (int i = 0; i < SCULPT_SCL_LAYER_MAX; i++) {
if (ss->custom_layers[i]) {
if (non_customdata_only && !ss->custom_layers[i]->params.simple_array) {
continue;
}
SCULPT_temp_customlayer_release(ss, ss->custom_layers[i]);
MEM_freeN(ss->custom_layers[i]);
ss->custom_layers[i] = NULL;
}
}
}
void SCULPT_clear_scl_pointers(SculptSession *ss)
{
for (int i = 0; i < SCULPT_SCL_LAYER_MAX; i++) {

View File

@ -127,6 +127,26 @@ struct {
#endif
};
/* clang-format off */
enum {
CLOTH_POS_POS,
CLOTH_POS_INIT,
CLOTH_POS_SOFT,
CLOTH_POS_DEF
};
/* clang-format on */
#ifdef CLOTH_NO_POS_PTR
# define PACK_POS_TYPE(index, type) ((index) | ((type) << 26))
# define UNPACK_POS_TYPE(index) ((index) >> 26)
# define UNPACK_POS_INDEX(index) ((index) & ~(3 << 26))
# define GET_POS_PTR(index) (&cloth_sim->pos)[UNPACK_POS_TYPE(index)][UNPACK_POS_INDEX(index)]
//# define GET_POS_PTR_TYPE(index, type) (&cloth_sim->pos)[type][index]
#else
# define PACK_POS_TYPE(index, type) index
# define UNPACK_POS_INDEX(index) index
#endif
/* C port of:
https://github.com/InteractiveComputerGraphics/PositionBasedDynamics/blob/master/PositionBasedDynamics/PositionBasedDynamics.cpp
MIT license
@ -254,17 +274,25 @@ static bool calc_bending_gradients(const SculptClothBendConstraint *constraint,
return true;
}
# else
static bool calc_bending_gradients(const SculptClothBendConstraint *constraint,
static bool calc_bending_gradients(const SculptClothSimulation *cloth_sim,
const SculptClothBendConstraint *constraint,
float gradients[4][3])
{
// derivatives from Bridson, Simulation of Clothing with Folds and Wrinkles
// his modes correspond to the derivatives of the bending angle arccos(n1 dot n2) with correct
// scaling
const float invMass0 = 1.0f, invMass1 = 1.0f, invMass2 = 1.0f, invMass3 = 1.0f;
# ifndef CLOTH_NO_POS_PTR
float *p0 = constraint->elems[0].position;
float *p1 = constraint->elems[1].position;
float *p2 = constraint->elems[2].position;
float *p3 = constraint->elems[3].position;
# else
float *p0 = GET_POS_PTR(constraint->elems[0].index);
float *p1 = GET_POS_PTR(constraint->elems[1].index);
float *p2 = GET_POS_PTR(constraint->elems[2].index);
float *p3 = GET_POS_PTR(constraint->elems[3].index);
# endif
float *d0 = gradients[0];
float *d1 = gradients[1];
@ -602,17 +630,19 @@ static void cloth_brush_add_bend_constraint(SculptSession *ss,
v3 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v3i);
v4 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v4i);
bend_constraint->elems[0].index = v1i;
bend_constraint->elems[1].index = v2i;
bend_constraint->elems[2].index = v3i;
bend_constraint->elems[3].index = v4i;
bend_constraint->elems[0].index = PACK_POS_TYPE(v1i, CLOTH_POS_POS);
bend_constraint->elems[1].index = PACK_POS_TYPE(v2i, CLOTH_POS_POS);
bend_constraint->elems[2].index = PACK_POS_TYPE(v3i, CLOTH_POS_POS);
bend_constraint->elems[3].index = PACK_POS_TYPE(v4i, CLOTH_POS_POS);
bend_constraint->node = node_index;
# ifndef CLOTH_NO_POS_PTR
bend_constraint->elems[0].position = cloth_sim->pos[v1i];
bend_constraint->elems[1].position = cloth_sim->pos[v2i];
bend_constraint->elems[2].position = cloth_sim->pos[v3i];
bend_constraint->elems[3].position = cloth_sim->pos[v4i];
# endif
const float *co1, *co2, *co3, *co4;
@ -642,12 +672,12 @@ static void cloth_brush_add_bend_constraint(SculptSession *ss,
}
#endif
static void cloth_brush_add_length_constraint(SculptSession *ss,
SculptClothSimulation *cloth_sim,
const int node_index,
const int v1i,
const int v2i,
const bool use_persistent)
ATTR_NO_OPT static void cloth_brush_add_length_constraint(SculptSession *ss,
SculptClothSimulation *cloth_sim,
const int node_index,
const int v1i,
const int v2i,
const bool use_persistent)
{
SculptClothLengthConstraint *length_constraint = cloth_add_constraint(cloth_sim, CON_LENGTH);
SculptVertRef v1, v2;
@ -655,13 +685,15 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
v1 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v1i);
v2 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v2i);
length_constraint->elems[0].index = v1i;
length_constraint->elems[1].index = v2i;
length_constraint->elems[0].index = PACK_POS_TYPE(v1i, CLOTH_POS_POS);
length_constraint->elems[1].index = PACK_POS_TYPE(v2i, CLOTH_POS_POS);
length_constraint->node = node_index;
#ifndef CLOTH_NO_POS_PTR
length_constraint->elems[0].position = cloth_sim->pos[v1i];
length_constraint->elems[1].position = cloth_sim->pos[v2i];
#endif
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL;
@ -689,13 +721,15 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim
{
SculptClothLengthConstraint *length_constraint = cloth_add_constraint(cloth_sim, CON_LENGTH);
length_constraint->elems[0].index = v;
length_constraint->elems[1].index = v;
length_constraint->elems[0].index = PACK_POS_TYPE(v, CLOTH_POS_POS);
length_constraint->elems[1].index = PACK_POS_TYPE(v, CLOTH_POS_SOFT);
length_constraint->node = node_index;
#ifndef CLOTH_NO_POS_PTR
length_constraint->elems[0].position = cloth_sim->pos[v];
length_constraint->elems[1].position = cloth_sim->softbody_pos[v];
#endif
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_SOFTBODY;
@ -713,13 +747,15 @@ static void cloth_brush_add_pin_constraint(SculptClothSimulation *cloth_sim,
{
SculptClothLengthConstraint *length_constraint = cloth_add_constraint(cloth_sim, CON_LENGTH);
length_constraint->elems[0].index = v;
length_constraint->elems[1].index = v;
length_constraint->elems[0].index = PACK_POS_TYPE(v, CLOTH_POS_POS);
length_constraint->elems[1].index = PACK_POS_TYPE(v, CLOTH_POS_INIT);
length_constraint->node = node_index;
#ifndef CLOTH_NO_POS_PTR
length_constraint->elems[0].position = cloth_sim->pos[v];
length_constraint->elems[1].position = cloth_sim->init_pos[v];
#endif
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_PIN;
@ -730,22 +766,24 @@ static void cloth_brush_add_pin_constraint(SculptClothSimulation *cloth_sim,
cloth_brush_reallocate_constraints(cloth_sim);
}
static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_sim,
const int node_index,
const int v,
const float strength)
ATTR_NO_OPT static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_sim,
const int node_index,
const int v,
const float strength)
{
SculptClothLengthConstraint *length_constraint = cloth_add_constraint(cloth_sim, CON_LENGTH);
length_constraint->elems[0].index = v;
length_constraint->elems[1].index = v;
length_constraint->elems[0].index = PACK_POS_TYPE(v, CLOTH_POS_POS);
length_constraint->elems[1].index = PACK_POS_TYPE(v, CLOTH_POS_DEF);
length_constraint->node = node_index;
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_DEFORMATION;
#ifndef CLOTH_NO_POS_PTR
length_constraint->elems[0].position = cloth_sim->pos[v];
length_constraint->elems[1].position = cloth_sim->deformation_pos[v];
#endif
length_constraint->length = 0.0f;
length_constraint->strength = strength;
@ -814,6 +852,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
build_indices[tot_indices] = vd.index;
tot_indices++;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
build_indices[tot_indices] = ni.index;
@ -1630,10 +1669,10 @@ static void cloth_free_tasks(SculptClothSimulation *cloth_sim)
cloth_sim->tot_constraint_tasks = 0;
}
static void cloth_sort_constraints_for_tasks(SculptSession *ss,
Brush *brush,
SculptClothSimulation *cloth_sim,
int totthread)
ATTR_NO_OPT static void cloth_sort_constraints_for_tasks(SculptSession *ss,
Brush *brush,
SculptClothSimulation *cloth_sim,
int totthread)
{
SculptClothTaskData *tasks = MEM_calloc_arrayN(
totthread + 1, sizeof(SculptClothTaskData), "SculptClothTaskData");
@ -1675,7 +1714,7 @@ static void cloth_sort_constraints_for_tasks(SculptSession *ss,
int last = 0, same = true;
for (int j = 0; j < totelem; j++) {
int threadnr = vthreads[con->elems[j].index];
int threadnr = vthreads[UNPACK_POS_INDEX(con->elems[j].index)];
if (threadnr) {
ok = false;
@ -1695,14 +1734,14 @@ static void cloth_sort_constraints_for_tasks(SculptSession *ss,
tasknr = BLI_rng_get_int(rng) % totthread;
for (int j = 0; j < totelem; j++) {
vthreads[con->elems[j].index] = tasknr + 1;
vthreads[UNPACK_POS_INDEX(con->elems[j].index)] = tasknr + 1;
}
}
else if (same) {
tasknr = last - 1;
for (int j = 0; j < totelem; j++) {
vthreads[con->elems[j].index] = tasknr + 1;
vthreads[UNPACK_POS_INDEX(con->elems[j].index)] = tasknr + 1;
}
}
else {
@ -1719,7 +1758,7 @@ static void cloth_sort_constraints_for_tasks(SculptSession *ss,
dynamic mode where the performance benefits are
not worth it*/
for (int step = 0; not_dynamic && step < totelem; step++) {
int v = con->elems[step].index;
int v = UNPACK_POS_INDEX(con->elems[step].index);
SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, v);
SculptVertexNeighborIter ni;
@ -1764,6 +1803,11 @@ static void cloth_sort_constraints_for_tasks(SculptSession *ss,
cloth_sim->tot_constraint_tasks = totthread + 1;
#if 1
unsigned int size = sizeof(SculptClothLengthConstraint) * cloth_sim->tot_constraints[0];
size += sizeof(SculptClothBendConstraint) * cloth_sim->tot_constraints[1];
printf("%.2fmb", (float)size / 1024.0f / 1024.0f);
for (int i = 0; i < totthread + 1; i++) {
printf("%d: ", i);
@ -1802,10 +1846,13 @@ static void cloth_brush_satisfy_constraints_intern(SculptSession *ss,
continue;
}
# ifndef CLOTH_NO_POS_PTR
for (int j = 0; j < 4; j++) {
constraint->elems[j].position = cloth_sim->pos[constraint->elems[j].index];
}
if (!calc_bending_gradients(constraint, gradients)) {
# endif
if (!calc_bending_gradients(cloth_sim, constraint, gradients)) {
continue;
}
@ -1813,9 +1860,14 @@ static void cloth_brush_satisfy_constraints_intern(SculptSession *ss,
cloth_brush_simulation_location_get(ss, brush, sim_location);
for (int j = 0; j < 4; j++) {
int vi = constraint->elems[j].index;
int vi = UNPACK_POS_INDEX(constraint->elems[j].index);
# ifndef CLOTH_NO_POS_PTR
float *pos = constraint->elems[j].position;
# else
float *pos = GET_POS_PTR(constraint->elems[j].index);
# endif
# if 1
float sim_factor = ss->cache ? cloth_brush_simulation_falloff_get(cloth_sim,
brush,
ss->cache->radius,
@ -1823,11 +1875,8 @@ static void cloth_brush_satisfy_constraints_intern(SculptSession *ss,
cloth_sim->init_pos[vi]) :
1.0f;
/*increase strength of bending constraints*/
/* increase strength of bending constraints */
sim_factor = sqrtf(sim_factor);
# else
float sim_factor = 0.9f;
# endif
if (sim_factor == 0.0f) {
continue;
@ -1838,7 +1887,7 @@ static void cloth_brush_satisfy_constraints_intern(SculptSession *ss,
sim_factor *= SCULPT_automasking_factor_get(automasking, ss, vref) * 1.0f -
SCULPT_vertex_mask_get(ss, vref);
madd_v3_v3fl(constraint->elems[j].position, gradients[j], sim_factor);
madd_v3_v3fl(pos, gradients[j], sim_factor);
if (USE_SOLVER_RIPPLE_CONSTRAINT) {
cloth_brush_constraint_pos_to_line(cloth_sim, vi);
@ -1855,14 +1904,23 @@ static void cloth_brush_satisfy_constraints_intern(SculptSession *ss,
continue;
}
const int v1 = constraint->elems[0].index;
const int v2 = constraint->elems[1].index;
#ifndef CLOTH_NO_POS_PTR
float *pos1 = constraint->elems[0].position;
float *pos2 = constraint->elems[1].position;
#else
float *pos1 = GET_POS_PTR(constraint->elems[0].index);
float *pos2 = GET_POS_PTR(constraint->elems[1].index);
#endif
const int v1 = UNPACK_POS_INDEX(constraint->elems[0].index);
const int v2 = UNPACK_POS_INDEX(constraint->elems[1].index);
const SculptVertRef v1ref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v1);
const SculptVertRef v2ref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v2);
float v1_to_v2[3];
sub_v3_v3v3(v1_to_v2, constraint->elems[1].position, constraint->elems[0].position);
sub_v3_v3v3(v1_to_v2, pos2, pos1);
const float current_distance = len_v3(v1_to_v2);
float correction_vector[3];
float correction_vector_half[3];

View File

@ -777,6 +777,12 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
}
}
/* destroy non-customdata temporary layers (which are rarely used) */
SCULPT_release_customlayers(ss, true);
/* clear all the other temporary layer references, that point to customdata layers*/
SCULPT_clear_scl_pointers(ss);
if (!ss->bm || !ss->pbvh || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
SCULPT_pbvh_clear(ob);
}
@ -923,6 +929,11 @@ static void SCULPT_dynamic_topology_disable_ex(
Mesh *me = ob->data;
SCULPT_pbvh_clear(ob);
/* destroy non-customdata temporary layers (which are rarely used) */
SCULPT_release_customlayers(ss, true);
/* free all the other pointers in ss->custom_layers*/
SCULPT_clear_scl_pointers(ss);
BKE_sculptsession_bm_to_me(ob, true);

View File

@ -395,14 +395,14 @@ static void mesh_filter_task_cb(void *__restrict userdata,
ss,
avg,
vd.vertex,
&((SculptSmoothArgs){.projection = 0.0f,
.slide_fset = slide_fset, // 1.0f - hard_edge_fac,
.bound_smooth = bsmooth,
.preserve_fset_boundaries = preserve_fset_boundaries,
.do_weighted_smooth = weighted,
.bound_smooth_radius = bound_smooth_radius,
.bound_scl = bsmooth > 0.0f ? &ss->filter_cache->bound_scl :
NULL}));
&((SculptSmoothArgs){
.projection = 0.0f,
.slide_fset = slide_fset, // 1.0f - hard_edge_fac,
.bound_smooth = bsmooth,
.preserve_fset_boundaries = preserve_fset_boundaries,
.do_weighted_smooth = weighted,
.bound_smooth_radius = bound_smooth_radius,
.bound_scl = bsmooth > 0.0f ? ss->custom_layers[SCULPT_SCL_SMOOTH_BDIS] : NULL}));
sub_v3_v3v3(val, avg, orig_co);
madd_v3_v3v3fl(val, orig_co, val, fade);
@ -910,7 +910,8 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
ss->filter_cache->bound_smooth_radius = RNA_float_get(op->ptr, "bound_smooth_radius");
if (filter_type == MESH_FILTER_SMOOTH && ss->filter_cache->bound_smooth_radius != 0.0f) {
SCULPT_bound_smooth_init(ss, &ss->filter_cache->bound_scl);
/*ensure ss->custom_layers[SCULPT_SCL_SMOOTH_BDIS] exists*/
SCULPT_bound_smooth_ensure(ss);
}
WM_event_add_modal_handler(C, op);

View File

@ -1722,8 +1722,6 @@ typedef struct FilterCache {
float hard_edge_fac;
bool hard_edge_mode;
float bound_smooth_radius;
struct SculptCustomLayer bound_scl;
} FilterCache;
void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache,
@ -1959,6 +1957,7 @@ bool SCULPT_temp_customlayer_has(SculptSession *ss,
AttributeDomain domain,
int proptype,
const char *name);
void SCULPT_release_customlayers(SculptSession *ss, bool non_customdata_only);
bool SCULPT_dyntopo_automasking_init(const SculptSession *ss,
Sculpt *sd,
@ -2037,7 +2036,7 @@ struct BMesh *SCULPT_dyntopo_empty_bmesh();
/* initializes customdata layer used by SCULPT_neighbor_coords_average_interior when bound_smooth >
* 0.0f*/
void SCULPT_bound_smooth_init(SculptSession *ss, SculptCustomLayer *r_bound_scl);
void SCULPT_bound_smooth_ensure(SculptSession *ss);
#define SCULPT_stroke_needs_original(brush) \
ELEM(brush->sculpt_tool, \

View File

@ -1540,11 +1540,21 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
}
#endif
void SCULPT_bound_smooth_init(SculptSession *ss, SculptCustomLayer *r_bound_scl)
void SCULPT_bound_smooth_ensure(SculptSession *ss)
{
SculptLayerParams params = {.permanent = true, .simple_array = false};
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_COLOR, "t__smooth_bdist", r_bound_scl, &params);
if (!ss->custom_layers[SCULPT_SCL_SMOOTH_BDIS]) {
ss->custom_layers[SCULPT_SCL_SMOOTH_BDIS] = MEM_callocN(sizeof(SculptCustomLayer),
"bound_scl");
SCULPT_temp_customlayer_get(ss,
ATTR_DOMAIN_POINT,
CD_PROP_COLOR,
"t__smooth_bdist",
ss->custom_layers[SCULPT_SCL_SMOOTH_BDIS],
&params);
}
}
void SCULPT_smooth(Sculpt *sd,
@ -1575,13 +1585,19 @@ void SCULPT_smooth(Sculpt *sd,
}
SculptLayerParams params = {.permanent = false, .simple_array = false};
SculptCustomLayer vel_scl;
if (do_vel_smooth) {
SCULPT_temp_customlayer_ensure(
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &params);
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &vel_scl, &params);
if (!ss->custom_layers[SCULPT_SCL_SMOOTH_VEL]) {
ss->custom_layers[SCULPT_SCL_SMOOTH_VEL] = MEM_callocN(sizeof(SculptCustomLayer),
"vel_smooth_scl");
SCULPT_temp_customlayer_get(ss,
ATTR_DOMAIN_POINT,
CD_PROP_FLOAT3,
"__scl_smooth_vel",
ss->custom_layers[SCULPT_SCL_SMOOTH_VEL],
&params);
}
}
float bstrength2 = bstrength;
@ -1618,8 +1634,6 @@ void SCULPT_smooth(Sculpt *sd,
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
SculptCustomLayer _scl, *bound_scl = NULL;
float bound_smooth = SCULPT_get_float(ss, boundary_smooth, sd, brush);
float fset_slide = SCULPT_get_float(ss, fset_slide, sd, brush);
@ -1627,13 +1641,8 @@ void SCULPT_smooth(Sculpt *sd,
if (bound_smooth > 0.0f) {
bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP);
bound_scl = &_scl;
SCULPT_bound_smooth_init(ss, bound_scl);
if (do_vel_smooth) {
SCULPT_temp_customlayer_get(
ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &vel_scl, &params);
}
/* ensure ss->custom_layers[SCULPT_SCL_SMOOTH_BDIS] exists */
SCULPT_bound_smooth_ensure(ss);
}
#ifdef PROXY_ADVANCED
@ -1672,8 +1681,8 @@ void SCULPT_smooth(Sculpt *sd,
.smooth_projection = projection,
.fset_slide = fset_slide,
.bound_smooth = bound_smooth,
.scl = do_vel ? &vel_scl : NULL,
.scl2 = bound_scl,
.scl = do_vel ? ss->custom_layers[SCULPT_SCL_SMOOTH_VEL] : NULL,
.scl2 = ss->custom_layers[SCULPT_SCL_SMOOTH_BDIS],
.vel_smooth_fac = vel_fac,
.do_origco = do_origco,
.iterations = count + 1,