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:
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_smooth.c')
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c1369
1 files changed, 1264 insertions, 105 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 4006359e0e4..c4aec5f9dd0 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -1,3 +1,4 @@
+
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -23,15 +24,20 @@
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
#include "BLI_blenlib.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_hash.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
#include "BLI_task.h"
+#include "BLI_threads.h"
#include "DNA_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_attribute.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
@@ -57,79 +63,607 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "atomic_ops.h"
#include "bmesh.h"
-
+#ifdef PROXY_ADVANCED
+/* clang-format off */
+#include "BKE_DerivedMesh.h"
+#include "../../blenkernel/intern/pbvh_intern.h"
+/* clang-format on */
+#endif
#include <math.h>
#include <stdlib.h>
-void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index)
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection,
+ SculptCustomLayer *bound_scl,
+ bool do_origco)
+{
+ float avg[3] = {0.0f, 0.0f, 0.0f};
+
+ MDynTopoVert *mv = SCULPT_vertex_get_mdyntopo(ss, vertex);
+
+ if (do_origco) {
+ SCULPT_vertex_check_origdata(ss, vertex);
+ }
+
+ float total = 0.0f;
+ int neighbor_count = 0;
+ bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+
+ int bflag = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP;
+ float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP);
+ float slide_fset = BKE_brush_fset_slide_get(ss->scene, ss->cache->brush);
+
+ slide_fset = MAX2(slide_fset, bound_smooth);
+
+ if (check_fsets) {
+ bflag |= SCULPT_BOUNDARY_FACE_SET;
+ }
+
+ const SculptBoundaryType is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bflag);
+
+ const float *co = do_origco ? mv->origco : SCULPT_vertex_co_get(ss, vertex);
+ float no[3];
+
+ if (true || projection > 0.0f) {
+ if (do_origco) {
+ copy_v3_v3(no, mv->origno);
+ }
+ else {
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+ }
+
+ const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) && !is_boundary;
+ float *areas = NULL;
+
+ SculptCornerType ctype = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP;
+ if (check_fsets) {
+ ctype |= SCULPT_CORNER_FACE_SET;
+ }
+
+ // bool have_bmesh = ss->bm;
+
+ if (weighted || bound_scl) {
+ int val = SCULPT_vertex_valence_get(ss, vertex);
+ areas = BLI_array_alloca(areas, val);
+
+ BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
+ }
+
+ float *b1 = NULL, btot = 0.0f, b1_orig;
+
+ if (bound_scl) {
+ b1 = SCULPT_temp_cdata_get(vertex, bound_scl);
+ b1_orig = *b1;
+ *b1 = 0.0f;
+ }
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ MDynTopoVert *mv2 = SCULPT_vertex_get_mdyntopo(ss, ni.vertex);
+ const float *co2;
+
+ if (!do_origco || mv2->stroke_id != ss->stroke_id) {
+ co2 = SCULPT_vertex_co_get(ss, ni.vertex);
+ }
+ else {
+ co2 = mv2->origco;
+ }
+
+ neighbor_count++;
+
+ float tmp[3], w;
+ bool ok = false;
+
+ if (weighted) {
+ w = areas[ni.i];
+ }
+ else {
+ w = 1.0f;
+ }
+
+ bool do_diffuse = false;
+
+ /*use the new edge api if edges are available, if not estimate boundary
+ from verts*/
+
+ SculptBoundaryType final_boundary = 0;
+
+ if (ni.has_edge) {
+ final_boundary = SCULPT_edge_is_boundary(ss, ni.edge, bflag);
+
+#ifdef SCULPT_DIAGONAL_EDGE_MARKS
+ if (ss->bm) {
+ BMEdge *e = (BMEdge *)ni.edge.i;
+ if (!(e->head.hflag & BM_ELEM_DRAW)) {
+ neighbor_count--;
+ continue;
+ }
+ }
+#endif
+ }
+ else {
+ final_boundary = is_boundary & SCULPT_vertex_is_boundary(ss, ni.vertex, bflag);
+ }
+
+ do_diffuse = bound_scl != NULL;
+
+ if (is_boundary) {
+ /* Boundary vertices use only other boundary vertices.
+
+ This if statement needs to be refactored a bit, it's confusing.
+
+ */
+
+ bool slide = (slide_fset > 0.0f && is_boundary == SCULPT_BOUNDARY_FACE_SET) ||
+ bound_smooth > 0.0f;
+ slide = slide && !final_boundary;
+
+ if (slide) {
+ // project non-boundary offset onto boundary normal
+ float t[3];
+
+ w *= slide_fset;
+
+ sub_v3_v3v3(t, co2, co);
+ madd_v3_v3v3fl(tmp, co, no, dot_v3v3(t, no));
+ ok = true;
+ }
+ else if (final_boundary) {
+ copy_v3_v3(tmp, co2);
+ ok = true;
+ do_diffuse = false;
+ }
+ else {
+ ok = false;
+ }
+ }
+ else {
+ copy_v3_v3(tmp, co2);
+ ok = true;
+ }
+
+ if (do_diffuse && bound_scl && !is_boundary) {
+ /*
+ simple boundary inflator using an ad-hoc diffusion-based pseudo-geodesic field
+
+ makes more rounded edges.
+ */
+ copy_v3_v3(tmp, co2);
+ ok = true;
+
+ float len = len_v3v3(co, tmp);
+ float w2 = 1.0f;
+
+ float *b2 = SCULPT_temp_cdata_get(ni.vertex, bound_scl);
+ float b2_val = *b2 + len;
+
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex, bflag)) {
+ w2 = 1000.0f;
+ b2_val = len;
+ }
+
+ *b1 += b2_val * w2;
+ btot += w2;
+
+ float no2[3];
+
+ if (!do_origco || mv2->stroke_id != ss->stroke_id) {
+ SCULPT_vertex_normal_get(ss, ni.vertex, no2);
+ }
+ else {
+ copy_v3_v3(no2, mv2->origno);
+ }
+
+ float radius = ss->cache->radius * 10.0f;
+
+ float th = radius - b1_orig;
+ th = MAX2(th, 0.0f);
+ th /= radius;
+
+#if 0
+ float *color = (float *)SCULPT_vertex_color_get(ss, ni.vertex);
+ color[0] = color[1] = color[2] = th;
+ color[3] = 1.0f;
+#endif
+
+ float fac = ss->cache->brush->boundary_smooth_factor;
+ fac = MIN2(fac * 4.0f, 1.0f);
+ fac = powf(fac, 0.2);
+ th *= fac;
+
+ sub_v3_v3(tmp, co);
+ madd_v3_v3fl(tmp, no2, th * dot_v3v3(no2, tmp));
+ add_v3_v3(tmp, co);
+ }
+
+ if (!ok) {
+ continue;
+ }
+
+ if (projection > 0.0f) {
+ sub_v3_v3(tmp, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+ madd_v3_v3fl(avg, tmp, w);
+ }
+ else {
+ madd_v3_v3fl(avg, tmp, w);
+ }
+
+ total += w;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (btot != 0.0f) {
+ *b1 /= btot;
+ //*b1 += (b1_orig - *b1) * 0.95f;
+ }
+ else if (b1) {
+ *b1 = b1_orig;
+ }
+
+ /* Do not modify corner vertices. */
+ if (neighbor_count <= 2 && is_boundary) {
+ copy_v3_v3(result, co);
+ return;
+ }
+
+ /* Avoid division by 0 when there are no neighbors. */
+ if (total == 0.0f) {
+ copy_v3_v3(result, co);
+ return;
+ }
+
+ mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0f) {
+ add_v3_v3(result, co);
+ }
+
+ SculptCornerType c = SCULPT_vertex_is_corner(ss, vertex, ctype);
+ float corner_smooth;
+
+ if (c == 0) {
+ return;
+ }
+
+ if (c & SCULPT_CORNER_FACE_SET) {
+ corner_smooth = MAX2(slide_fset, bound_smooth);
+ }
+ else {
+ corner_smooth = bound_smooth;
+ }
+
+ interp_v3_v3v3(result, result, co, 1.0f - corner_smooth);
+}
+
+void SCULPT_neighbor_coords_average_interior_velocity(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection,
+ SculptCustomLayer *scl)
{
float avg[3] = {0.0f, 0.0f, 0.0f};
int total = 0;
int neighbor_count = 0;
- const bool is_boundary = SCULPT_vertex_is_boundary(ss, index);
+ bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+ int bflag = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP;
+
+ if (check_fsets) {
+ bflag |= SCULPT_BOUNDARY_FACE_SET;
+ }
+
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bflag);
+ const float *co = SCULPT_vertex_co_get(ss, vertex);
+ float no[3];
+
+ if (projection > 0.0f) {
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+
+ float vel[3];
+
+ copy_v3_v3(vel, (float *)SCULPT_temp_cdata_get(vertex, scl));
+ mul_v3_fl(vel, 0.4f);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
neighbor_count++;
+
+ float tmp[3];
+ bool ok = false;
+
+ float *vel2 = SCULPT_temp_cdata_get(ni.vertex, scl);
+
+ // propegate smooth velocities a bit
+ madd_v3_v3fl(vel2, vel, 1.0f / (float)ni.size);
+
if (is_boundary) {
/* Boundary vertices use only other boundary vertices. */
- if (SCULPT_vertex_is_boundary(ss, ni.index)) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex, bflag)) {
+ copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
+ ok = true;
}
}
else {
/* Interior vertices use all neighbors. */
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
+ ok = true;
+ }
+
+ if (!ok) {
+ continue;
+ }
+
+ if (projection > 0.0f) {
+ sub_v3_v3(tmp, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+ add_v3_v3(avg, tmp);
}
+ else {
+ add_v3_v3(avg, tmp);
+ }
+
+ total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Do not modify corner vertices. */
- if (neighbor_count <= 2 && is_boundary) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ if (neighbor_count <= 2) {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
return;
}
/* Avoid division by 0 when there are no neighbors. */
if (total == 0) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
return;
}
mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0f) {
+ add_v3_v3(result, co);
+ }
+}
+
+int closest_vec_to_perp(float dir[3], float r_dir2[3], float no[3], float *buckets, float w)
+{
+ int bits = 0;
+
+ if (dot_v3v3(r_dir2, dir) < 0.0f) {
+ negate_v3(r_dir2);
+ bits |= 1;
+ }
+
+ float dir4[3];
+ cross_v3_v3v3(dir4, r_dir2, no);
+ normalize_v3(dir4);
+
+ if (dot_v3v3(dir4, dir) < 0.0f) {
+ negate_v3(dir4);
+ bits |= 2;
+ }
+
+ if (dot_v3v3(dir4, dir) > dot_v3v3(r_dir2, dir)) {
+ copy_v3_v3(r_dir2, dir4);
+ bits |= 4;
+ }
+
+ buckets[bits] += w;
+
+ return bits;
+}
+
+void vec_transform(float r_dir2[3], float no[3], int bits)
+{
+ if (bits & 4) {
+ float dir4[3];
+
+ copy_v3_v3(dir4, r_dir2);
+
+ if (bits & 2) {
+ negate_v3(dir4);
+ }
+
+ float dir5[3];
+
+ cross_v3_v3v3(dir5, no, dir4);
+ normalize_v3(dir5);
+
+ copy_v3_v3(r_dir2, dir5);
+ }
+
+ if (bits & 1) {
+ negate_v3(r_dir2);
+ }
+}
+
+volatile int blehrand = 0;
+static int blehrand_get()
+{
+ int i = blehrand;
+ i = (i * 124325 + 231423322) & 524287;
+
+ blehrand = i;
+ return i;
}
/* For bmesh: Average surrounding verts based on an orthogonality measure.
* Naturally converges to a quad-like structure. */
-void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v)
+void SCULPT_bmesh_four_neighbor_average(SculptSession *ss,
+ float avg[3],
+ float direction[3],
+ BMVert *v,
+ float projection,
+ bool check_fsets,
+ int cd_temp,
+ int cd_dyn_vert,
+ bool do_origco)
{
-
float avg_co[3] = {0.0f, 0.0f, 0.0f};
float tot_co = 0.0f;
+ float buckets[8] = {0};
+
+ // zero_v3(direction);
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
+
+ float *col = BM_ELEM_CD_GET_VOID_P(v, cd_temp);
+ float dir[3];
+ float dir3[3] = {0.0f, 0.0f, 0.0f};
+
+ const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT);
+ float *areas;
+
+ SCULPT_vertex_check_origdata(ss, (SculptVertRef){.i = (intptr_t)v});
+
+ if (do_origco) {
+ // SCULPT_vertex_check_origdata(ss, (SculptVertRef){.i = (intptr_t)v});
+ madd_v3_v3fl(direction, mv->origno, -dot_v3v3(mv->origno, direction));
+ normalize_v3(direction);
+ }
+
+ float *co1 = do_origco ? mv->origco : v->co;
+ float *no1 = do_origco ? mv->origno : v->no;
+
+ if (weighted) {
+ SculptVertRef vertex = {.i = (intptr_t)v};
+
+ int val = SCULPT_vertex_valence_get(ss, vertex);
+ areas = BLI_array_alloca(areas, val * 2);
+
+ BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
+ }
+
+ copy_v3_v3(dir, col);
+
+ if (dot_v3v3(dir, dir) == 0.0f) {
+ copy_v3_v3(dir, direction);
+ }
+ else {
+ closest_vec_to_perp(dir, direction, no1, buckets, 1.0f); // col[3]);
+ }
+
+ float totdir3 = 0.0f;
+
+ const float selfw = (float)mv->valence * 0.0025f;
+ madd_v3_v3fl(dir3, direction, selfw);
+ totdir3 += selfw;
+
BMIter eiter;
BMEdge *e;
+ bool had_bound = false;
+ int area_i = 0;
- BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
- if (BM_edge_is_boundary(e)) {
- copy_v3_v3(avg, v->co);
- return;
- }
+ BM_ITER_ELEM_INDEX (e, &eiter, v, BM_EDGES_OF_VERT, area_i) {
BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1;
+
+ float dir2[3];
+ float *col2 = BM_ELEM_CD_GET_VOID_P(v_other, cd_temp);
+
+ float bucketw = 1.0f; // col2[3] < col[3] ? 2.0f : 1.0f;
+ // bucketw /= 0.00001f + len_v3v3(e->v1->co, e->v2->co);
+ // if (weighted) {
+ // bucketw = 1.0 / (0.000001 + areas[area_i]);
+ //}
+ // if (e == v->e) {
+ // bucketw *= 2.0;
+ //}
+
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v_other);
+ float *co2;
+ float *no2;
+
+ if (!do_origco || mv2->stroke_id != ss->stroke_id) {
+ co2 = v_other->co;
+ no2 = v_other->no;
+ }
+ else {
+ co2 = mv2->origco;
+ no2 = mv2->origno;
+ }
+
+ // bool bound = (mv2->flag &
+ // (DYNVERT_BOUNDARY)); // | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY));
+ // bool bound2 = (mv2->flag &
+ // (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY));
+
+ SculptBoundaryType bflag = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH |
+ SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM;
+
+ int bound = SCULPT_edge_is_boundary(ss, (SculptEdgeRef){.i = (intptr_t)e}, bflag);
+ float dirw = 1.0f;
+
+ if (bound) {
+ had_bound = true;
+
+ sub_v3_v3v3(dir2, co2, co1);
+ madd_v3_v3fl(dir2, no1, -dot_v3v3(no1, dir2));
+ normalize_v3(dir2);
+ dirw = 100000.0f;
+ }
+ else {
+ dirw = col2[3];
+
+ copy_v3_v3(dir2, col2);
+ if (dot_v3v3(dir2, dir2) == 0.0f) {
+ copy_v3_v3(dir2, dir);
+ }
+ }
+
+ closest_vec_to_perp(dir, dir2, no1, buckets, bucketw); // col2[3]);
+
+ madd_v3_v3fl(dir3, dir2, dirw);
+ totdir3 += dirw;
+
+ if (had_bound) {
+ tot_co = 0.0f;
+ continue;
+ }
+
float vec[3];
- sub_v3_v3v3(vec, v_other->co, v->co);
- madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no));
+ sub_v3_v3v3(vec, co2, co1);
+
+ madd_v3_v3fl(vec, no1, -dot_v3v3(vec, no1) * projection);
normalize_v3(vec);
/* fac is a measure of how orthogonal or parallel the edge is
* relative to the direction. */
- float fac = dot_v3v3(vec, direction);
+ float fac = dot_v3v3(vec, dir);
+#ifdef SCULPT_DIAGONAL_EDGE_MARKS
+ float th = fabsf(saacos(fac)) / M_PI + 0.5f;
+ th -= floorf(th);
+
+ const float limit = 0.045;
+
+ if (fabsf(th - 0.25) < limit || fabsf(th - 0.75) < limit) {
+ BMEdge enew = *e, eold = *e;
+
+ enew.head.hflag &= ~BM_ELEM_DRAW;
+ // enew.head.hflag |= BM_ELEM_SEAM; // XXX debug
+
+ atomic_cas_int64((intptr_t *)(&e->head.index),
+ *(intptr_t *)(&eold.head.index),
+ *(intptr_t *)(&enew.head.index));
+ }
+#endif
+
fac = fac * fac - 0.5f;
fac *= fac;
- madd_v3_v3fl(avg_co, v_other->co, fac);
+
+ if (weighted) {
+ fac *= areas[area_i];
+ }
+
+ madd_v3_v3fl(avg_co, co2, fac);
tot_co += fac;
}
@@ -139,47 +673,201 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
/* Preserve volume. */
float vec[3];
- sub_v3_v3(avg, v->co);
- mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no));
+ sub_v3_v3(avg, co1);
+ mul_v3_v3fl(vec, no1, dot_v3v3(avg, no1) * projection);
sub_v3_v3(avg, vec);
- add_v3_v3(avg, v->co);
+ add_v3_v3(avg, co1);
}
else {
- zero_v3(avg);
+ // zero_v3(avg);
+ copy_v3_v3(avg, co1);
+ }
+
+ // do not update in do_origco
+ if (do_origco) {
+ return;
+ }
+
+ if (totdir3 > 0.0f) {
+ float outdir = totdir3 / (float)mv->valence;
+
+ // mul_v3_fl(dir3, 1.0 / totdir3);
+ normalize_v3(dir3);
+ if (had_bound) {
+ copy_v3_v3(col, dir3);
+ col[3] = 1000.0f;
+ }
+ else {
+
+ mul_v3_fl(col, col[3]);
+ madd_v3_v3fl(col, dir3, outdir);
+
+ col[3] = (col[3] + outdir) * 0.4;
+ normalize_v3(col);
+ }
+
+ float maxb = 0.0f;
+ int bi = 0;
+ for (int i = 0; i < 8; i++) {
+ if (buckets[i] > maxb) {
+ maxb = buckets[i];
+ bi = i;
+ }
+ }
+
+ // negate_v3(col);
+ vec_transform(col, no1, bi);
+ // negate_v3(col);
}
}
+static void sculpt_neighbor_coords_average_fset(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection)
+{
+ float avg[3] = {0.0f, 0.0f, 0.0f};
+ float *co, no[3];
+ float total = 0.0f;
+
+ bool boundary = !SCULPT_vertex_has_unique_face_set(ss, vertex);
+
+ if (projection > 0.0f) {
+ co = (float *)SCULPT_vertex_co_get(ss, vertex);
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+
+ const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) && !boundary;
+ float *areas;
+
+ if (weighted) {
+ int val = SCULPT_vertex_valence_get(ss, vertex);
+ areas = BLI_array_alloca(areas, val);
+
+ BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
+ }
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex);
+ float w;
+
+ if (weighted) {
+ w = areas[ni.i];
+ }
+ else {
+ w = 1.0f;
+ }
+
+ if (boundary && SCULPT_vertex_has_unique_face_set(ss, ni.vertex)) {
+ continue;
+ }
+
+ if (projection > 0.0f) {
+ float tmp[3];
+
+ sub_v3_v3v3(tmp, co2, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+
+ madd_v3_v3fl(avg, tmp, w);
+ }
+ else {
+ madd_v3_v3fl(avg, co2, w);
+ }
+ total += w;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (total > (boundary ? 1.0f : 0.0f)) {
+ mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0) {
+ add_v3_v3(result, co);
+ }
+ }
+ else {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
+ }
+}
/* Generic functions for laplacian smoothing. These functions do not take boundary vertices into
* account. */
-void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index)
+void SCULPT_neighbor_coords_average(
+ SculptSession *ss, float result[3], SculptVertRef vertex, float projection, bool check_fsets)
{
+ if (check_fsets) {
+ sculpt_neighbor_coords_average_fset(ss, result, vertex, projection);
+ return;
+ }
+
float avg[3] = {0.0f, 0.0f, 0.0f};
- int total = 0;
+ float *co, no[3];
+ float total = 0.0f;
+
+ if (projection > 0.0f) {
+ co = (float *)SCULPT_vertex_co_get(ss, vertex);
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+
+ const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
+ float *areas;
+
+ if (weighted) {
+ int val = SCULPT_vertex_valence_get(ss, vertex);
+ areas = BLI_array_alloca(areas, val);
+
+ BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
+ }
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex);
+ float w;
+
+ if (weighted) {
+ w = areas[ni.i];
+ }
+ else {
+ w = 1.0f;
+ }
+
+ if (projection > 0.0f) {
+ float tmp[3];
+
+ sub_v3_v3v3(tmp, co2, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+
+ madd_v3_v3fl(avg, tmp, w);
+ }
+ else {
+ madd_v3_v3fl(avg, co2, w);
+ }
+ total += w;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (total > 0) {
+ if (total > 0.0f) {
mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0) {
+ add_v3_v3(result, co);
+ }
}
else {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
}
}
-float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
+float SCULPT_neighbor_mask_average(SculptSession *ss, SculptVertRef index)
{
float avg = 0.0f;
int total = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- avg += SCULPT_vertex_mask_get(ss, ni.index);
+ avg += SCULPT_vertex_mask_get(ss, ni.vertex);
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -190,14 +878,14 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
return SCULPT_vertex_mask_get(ss, index);
}
-void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], SculptVertRef index)
{
float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int total = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index));
+ add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.vertex));
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -241,11 +929,13 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp[3];
- madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade);
+ float *dir = SCULPT_temp_cdata_get(vd.vertex, data->scl);
+
+ madd_v3_v3v3fl(disp, vd.co, dir, fade);
SCULPT_clip(sd, ss, vd.co, disp);
if (vd.mvert) {
@@ -263,33 +953,42 @@ static void SCULPT_enhance_details_brush(Sculpt *sd,
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptCustomLayer scl;
+
+ SCULPT_temp_customlayer_ensure(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_detail_dir", false);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_detail_dir", &scl, false);
+
+ if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
+ (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) {
+ BKE_pbvh_update_all_tri_areas(ss->pbvh);
+ }
+
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
const int totvert = SCULPT_vertex_count_get(ss);
- ss->cache->detail_directions = MEM_malloc_arrayN(
- totvert, 3 * sizeof(float), "details directions");
for (int i = 0; i < totvert; i++) {
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ float *dir = SCULPT_temp_cdata_get(vertex, &scl);
+
+ SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, false);
+ sub_v3_v3v3(dir, avg, SCULPT_vertex_co_get(ss, vertex));
}
}
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ SculptThreadedTaskData data = {.sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .scl = &scl};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings);
}
+#ifdef PROXY_ADVANCED
static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -311,6 +1010,107 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
+ PBVHNode **nodes = data->nodes;
+ ProxyVertArray *p = &nodes[n]->proxyverts;
+
+ for (int i = 0; i < p->size; i++) {
+ float co[3] = {0.0f, 0.0f, 0.0f};
+ int ni = 0;
+
+# if 1
+ if (sculpt_brush_test_sq_fn(&test, p->co[i])) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(
+ ss,
+ brush,
+ p->co[i],
+ sqrtf(test.dist),
+ p->no[i],
+ p->fno[i],
+ smooth_mask ? 0.0f : (p->mask ? p->mask[i] : 0.0f),
+ p->index[i],
+ thread_id);
+# else
+ if (1) {
+ const float fade = 1.0;
+# endif
+
+ while (ni < MAX_PROXY_NEIGHBORS && p->neighbors[i][ni].node >= 0) {
+ ProxyKey *key = p->neighbors[i] + ni;
+ PBVHNode *n2 = ss->pbvh->nodes + key->node;
+
+ // printf("%d %d %d %p\n", key->node, key->pindex, ss->pbvh->totnode, n2);
+
+ if (key->pindex < 0 || key->pindex >= n2->proxyverts.size) {
+ printf("corruption!\n");
+ fflush(stdout);
+ ni++;
+ continue;
+ }
+
+ if (n2->proxyverts.co) {
+ add_v3_v3(co, n2->proxyverts.co[key->pindex]);
+ ni++;
+ }
+ }
+
+ // printf("ni %d\n", ni);
+
+ if (ni > 2) {
+ mul_v3_fl(co, 1.0f / (float)ni);
+ }
+ else {
+ copy_v3_v3(co, p->co[i]);
+ }
+
+ // printf("%f %f %f ", co[0], co[1], co[2]);
+
+ interp_v3_v3v3(p->co[i], p->co[i], co, fade);
+ }
+ }
+}
+
+#else
+
+static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ const bool smooth_mask = data->smooth_mask;
+ float bstrength = data->strength;
+ float projection = data->smooth_projection;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+ const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
+ const bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+
+ SculptCornerType ctype = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP;
+ if (check_fsets) {
+ ctype |= SCULPT_CORNER_FACE_SET;
+ }
+
+ if (weighted || ss->cache->brush->boundary_smooth_factor > 0.0f) {
+ BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]);
+ }
+
+ bool modified = false;
+ // const float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor,
+ // BOUNDARY_SMOOTH_EXP);
+ // const float slide_fset = BKE_brush_fset_slide_get(ss->scene, ss->cache->brush);
+
+ SculptCustomLayer *bound_scl = data->scl2;
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@@ -323,24 +1123,99 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
- vd.index,
+ vd.vertex,
thread_id);
if (smooth_mask) {
- float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask;
+ float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask;
val *= fade * bstrength;
*vd.mask += val;
CLAMP(*vd.mask, 0.0f, 1.0f);
}
else {
float avg[3], val[3];
- SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
- sub_v3_v3v3(val, avg, vd.co);
- madd_v3_v3v3fl(val, vd.co, val, fade);
- SCULPT_clip(sd, ss, vd.co, val);
+
+ // if (SCULPT_vertex_is_corner(ss, vd.vertex, ctype) & ~SCULPT_CORNER_FACE_SET) {
+ // continue;
+ //}
+
+ int steps = data->do_origco ? 2 : 1;
+ for (int step = 0; step < steps; step++) {
+ float *co = step ? (float *)SCULPT_vertex_origco_get(ss, vd.vertex) : vd.co;
+
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, bound_scl, step);
+
+ sub_v3_v3v3(val, avg, co);
+ madd_v3_v3v3fl(val, co, val, fade);
+ SCULPT_clip(sd, ss, co, val);
+ }
}
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
+
+ modified = true;
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ if (modified && weighted) {
+ BKE_pbvh_node_mark_update_tri_area(data->nodes[n]);
+ }
+}
+#endif
+
+static void do_smooth_brush_task_cb_ex_scl(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ float bstrength = data->strength;
+ float projection = data->smooth_projection;
+
+ SculptCustomLayer *scl = data->scl;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ (vd.mask ? *vd.mask : 0.0f),
+ vd.vertex,
+ thread_id);
+
+ float avg[3], val[3];
+
+ SCULPT_neighbor_coords_average_interior_velocity(ss, avg, vd.vertex, projection, scl);
+
+ sub_v3_v3v3(val, avg, vd.co);
+
+ float *vel = (float *)SCULPT_temp_cdata_get(vd.vertex, scl);
+ interp_v3_v3v3(vel, vel, val, 0.5);
+
+ madd_v3_v3v3fl(val, vd.co, vel, fade);
+
+ SCULPT_clip(sd, ss, vd.co, val);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
}
BKE_pbvh_vertex_iter_end;
}
@@ -350,7 +1225,9 @@ void SCULPT_smooth(Sculpt *sd,
PBVHNode **nodes,
const int totnode,
float bstrength,
- const bool smooth_mask)
+ const bool smooth_mask,
+ float projection,
+ bool do_origco)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -361,11 +1238,29 @@ void SCULPT_smooth(Sculpt *sd,
int iteration, count;
float last;
+ if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
+ ((ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) ||
+ ss->cache->brush->boundary_smooth_factor > 0.0f)) {
+ BKE_pbvh_update_all_tri_areas(ss->pbvh);
+ }
+
CLAMP(bstrength, 0.0f, 1.0f);
count = (int)(bstrength * max_iterations);
last = max_iterations * (bstrength - count * fract);
+ SculptCustomLayer scl;
+#if 0
+ bool have_scl = smooth_mask ? false :
+ SCULPT_temp_customlayer_ensure(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel");
+ if (have_scl) {
+ SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &scl, false);
+ }
+#else
+ bool have_scl = false;
+#endif
+
if (type == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "sculpt smooth: pmap missing");
return;
@@ -374,6 +1269,24 @@ void SCULPT_smooth(Sculpt *sd,
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
+ SculptCustomLayer _scl, *bound_scl = NULL;
+
+ /* create temp layer for psuedo-geodesic field */
+ if (ss->cache->brush->boundary_smooth_factor > 0.0f) {
+ float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP);
+
+ bound_scl = &_scl;
+ SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist", false);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist", bound_scl, false);
+ }
+
+#ifdef PROXY_ADVANCED
+ int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK;
+ BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask);
+
+ BKE_pbvh_load_proxyarrays(ss->pbvh, nodes, totnode, PV_CO | PV_NO | PV_MASK);
+#endif
for (iteration = 0; iteration <= count; iteration++) {
const float strength = (iteration != count) ? 1.0f : last;
@@ -384,24 +1297,45 @@ void SCULPT_smooth(Sculpt *sd,
.nodes = nodes,
.smooth_mask = smooth_mask,
.strength = strength,
+ .smooth_projection = projection,
+ .scl = have_scl ? &scl : NULL,
+ .scl2 = bound_scl,
+ .do_origco = SCULPT_stroke_needs_original(ss->cache->brush),
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
+ if (0) { // have_scl) {
+ BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex_scl, &settings);
+ }
+ else {
+ BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
+ }
+
+#ifdef PROXY_ADVANCED
+ BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode);
+#endif
}
}
-void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+void SCULPT_do_smooth_brush(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection)
{
SculptSession *ss = ob->sculpt;
+
+ if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
+ ((ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) ||
+ ss->cache->brush->boundary_smooth_factor > 0.0f)) {
+ BKE_pbvh_update_all_tri_areas(ss->pbvh);
+ }
+
if (ss->cache->bstrength <= 0.0f) {
/* Invert mode, intensify details. */
SCULPT_enhance_details_brush(sd, ob, nodes, totnode);
}
else {
/* Regular mode, smooth. */
- SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false);
+ SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false, projection, false);
}
}
@@ -411,42 +1345,49 @@ void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
float *disp,
const float co[3],
- float (*laplacian_disp)[3],
- const int v_index,
+ SculptCustomLayer *scl,
+ const SculptVertRef v_index,
const float origco[3],
- const float alpha)
+ const float alpha,
+ const float projection,
+ bool check_fsets)
{
float laplacian_smooth_co[3];
float weigthed_o[3], weigthed_q[3], d[3];
- SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index);
+ SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index, projection, check_fsets);
+
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_index);
mul_v3_v3fl(weigthed_o, origco, alpha);
mul_v3_v3fl(weigthed_q, co, 1.0f - alpha);
add_v3_v3v3(d, weigthed_o, weigthed_q);
- sub_v3_v3v3(laplacian_disp[v_index], laplacian_smooth_co, d);
+ sub_v3_v3v3((float *)SCULPT_temp_cdata_get(v_index, scl), laplacian_smooth_co, d);
sub_v3_v3v3(disp, laplacian_smooth_co, co);
}
void SCULPT_surface_smooth_displace_step(SculptSession *ss,
float *co,
- float (*laplacian_disp)[3],
- const int v_index,
+ SculptCustomLayer *scl,
+ const SculptVertRef v_index,
const float beta,
const float fade)
{
float b_avg[3] = {0.0f, 0.0f, 0.0f};
float b_current_vertex[3];
int total = 0;
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_index);
+
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_index, ni) {
- add_v3_v3(b_avg, laplacian_disp[ni.index]);
+ add_v3_v3(b_avg, (float *)SCULPT_temp_cdata_get(ni.vertex, scl));
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
if (total > 0) {
mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total);
- madd_v3_v3fl(b_current_vertex, laplacian_disp[v_index], beta);
+ madd_v3_v3fl(b_current_vertex, (float *)SCULPT_temp_cdata_get(v_index, scl), beta);
mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f));
sub_v3_v3(co, b_current_vertex);
}
@@ -469,10 +1410,20 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
+
+ if (weighted) {
+ BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]);
+ }
+
+ bool modified = false;
+
+ bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- SCULPT_orig_vert_data_update(&orig_data, &vd);
+ SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
+
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@@ -483,18 +1434,31 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp[3];
- SCULPT_surface_smooth_laplacian_step(
- ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, orig_data.co, alpha);
+ SCULPT_surface_smooth_laplacian_step(ss,
+ disp,
+ vd.co,
+ data->scl,
+ vd.vertex,
+ orig_data.co,
+ alpha,
+ data->smooth_projection,
+ check_fsets);
madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f));
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
+
+ modified = true;
}
BKE_pbvh_vertex_iter_end;
+
+ if (modified && weighted) {
+ BKE_pbvh_node_mark_update_tri_area(data->nodes[n]);
+ }
}
static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
@@ -524,10 +1488,9 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
- SCULPT_surface_smooth_displace_step(
- ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade);
+ SCULPT_surface_smooth_displace_step(ss, vd.co, data->scl, vd.vertex, beta, fade);
}
BKE_pbvh_vertex_iter_end;
}
@@ -537,19 +1500,31 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
+ SculptCustomLayer scl;
+
+ SCULPT_temp_customlayer_ensure(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_lapsmooth", false);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_lapsmooth", &scl, false);
+
+ if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
+ (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) {
+ BKE_pbvh_update_all_tri_areas(ss->pbvh);
+ }
+
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
- BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL);
- ss->cache->surface_smooth_laplacian_disp = MEM_callocN(
- sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b");
+ // BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL);
+ // ss->cache->surface_smooth_laplacian_disp = MEM_callocN(
+ // sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b");
}
/* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ SculptThreadedTaskData data = {.sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .smooth_projection = brush->autosmooth_projection,
+ .scl = &scl};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
@@ -588,7 +1563,7 @@ static void SCULPT_do_directional_smooth_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float stroke_disp[3];
@@ -599,9 +1574,9 @@ static void SCULPT_do_directional_smooth_task_cb_ex(void *__restrict userdata,
int neighbor_count = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_neighbor_disp[3];
- const float *neighbor_co = SCULPT_vertex_co_get(ss, ni.index);
+ const float *neighbor_co = SCULPT_vertex_co_get(ss, ni.vertex);
sub_v3_v3v3(vertex_neighbor_disp, neighbor_co, vd.co);
normalize_v3(vertex_neighbor_disp);
if (fabsf(dot_v3v3(stroke_disp, vertex_neighbor_disp)) > 0.6f) {
@@ -651,8 +1626,8 @@ void SCULPT_do_directional_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes
}
static void SCULPT_do_uniform_weigths_smooth_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
+ const int n,
+ const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
@@ -677,18 +1652,16 @@ static void SCULPT_do_uniform_weigths_smooth_task_cb_ex(void *__restrict userdat
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
-
-
-
float len_accum = 0;
int tot_neighbors = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
- len_accum += len_v3v3(SCULPT_vertex_co_get(ss, vd.index), SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
+ len_accum += len_v3v3(SCULPT_vertex_co_get(ss, vd.vertex),
+ SCULPT_vertex_co_get(ss, ni.vertex));
tot_neighbors++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -700,16 +1673,16 @@ static void SCULPT_do_uniform_weigths_smooth_task_cb_ex(void *__restrict userdat
const float len_avg = bstrength * len_accum / tot_neighbors;
-
float co_accum[3] = {0.0f};
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
const float neighbor_co[3];
const float neighbor_disp[3];
- sub_v3_v3v3(neighbor_disp, SCULPT_vertex_co_get(ss, ni.index), SCULPT_vertex_co_get(ss, vd.index));
+ sub_v3_v3v3(
+ neighbor_disp, SCULPT_vertex_co_get(ss, ni.vertex), SCULPT_vertex_co_get(ss, vd.vertex));
normalize_v3(neighbor_disp);
mul_v3_fl(neighbor_disp, len_avg);
- add_v3_v3v3(neighbor_co, SCULPT_vertex_co_get(ss, vd.index), neighbor_disp);
+ add_v3_v3v3(neighbor_co, SCULPT_vertex_co_get(ss, vd.vertex), neighbor_disp);
add_v3_v3(co_accum, neighbor_co);
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -729,8 +1702,6 @@ static void SCULPT_do_uniform_weigths_smooth_task_cb_ex(void *__restrict userdat
}
}
-
-
void SCULPT_do_uniform_weights_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -746,6 +1717,194 @@ void SCULPT_do_uniform_weights_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **n
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
for (int i = 0; i < brush->surface_smooth_iterations; i++) {
- BLI_task_parallel_range(0, totnode, &data, SCULPT_do_uniform_weigths_smooth_task_cb_ex, &settings);
+ BLI_task_parallel_range(
+ 0, totnode, &data, SCULPT_do_uniform_weigths_smooth_task_cb_ex, &settings);
+ }
+}
+
+static void do_smooth_vcol_boundary_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ const bool smooth_mask = data->smooth_mask;
+ float bstrength = data->strength;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float tot = 0.0f;
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!vd.col) {
+ continue;
+ }
+
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(
+ ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
+ vd.vertex,
+ thread_id);
+
+ madd_v3_v3fl(avg, vd.col, fade);
+ tot += fade;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ if (tot == 0.0f) {
+ return;
+ }
+ tot = 1.0f / tot;
+
+ mul_v3_fl(avg, tot);
+
+ float exp = brush->vcol_boundary_exponent;
+ // detect bad value
+
+ if (exp == 0.0f) {
+ exp = 1.0f;
}
-} \ No newline at end of file
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(
+ ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
+ vd.vertex,
+ thread_id);
+ if (!vd.col) {
+ continue;
+ }
+
+ float avg2[3], avg3[3], val[3];
+ float tot2 = 0.0f, tot4 = 0.0f;
+
+ copy_v4_v4(avg, vd.col);
+
+ zero_v3(avg2);
+ zero_v3(avg3);
+
+ madd_v3_v3fl(avg2, vd.co, 0.5f);
+ tot2 += 0.5f;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
+ const float *col = SCULPT_vertex_color_get(ss, ni.vertex);
+ const float *co = SCULPT_vertex_co_get(ss, ni.vertex);
+
+ // simple color metric. TODO: plug in appropriate color space code?
+ float dv[4];
+ sub_v4_v4v4(dv, col, avg);
+ float w = (fabs(dv[0]) + fabs(dv[1]) + fabs(dv[2]) + fabs(dv[3])) / 4.0;
+
+ w = powf(w, exp);
+
+ madd_v3_v3fl(avg3, co, 1.0f);
+ tot4 += 1.0f;
+
+ madd_v3_v3fl(avg2, co, w);
+ tot2 += w;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (tot2 == 0.0f) {
+ continue;
+ }
+
+ if (tot4 > 0.0f) {
+ mul_v3_fl(avg3, 1.0f / tot4);
+ }
+
+ /* try to avoid perfectly colinear triangles, and the normal discontinuities they create,
+ by blending slightly with unweighted smoothed position */
+ mul_v3_fl(avg2, 1.0f / tot2);
+ interp_v3_v3v3(avg2, avg2, avg3, 0.025);
+
+ sub_v3_v3v3(val, avg2, vd.co);
+ madd_v3_v3v3fl(val, vd.co, val, fade);
+ SCULPT_clip(sd, ss, vd.co, val);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_smooth_vcol_boundary(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength)
+{
+ SculptSession *ss = ob->sculpt;
+
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ const int max_iterations = 4;
+ const float fract = 1.0f / max_iterations;
+ PBVHType type = BKE_pbvh_type(ss->pbvh);
+ int iteration, count;
+ float last;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ count = (int)(bstrength * max_iterations);
+ last = max_iterations * (bstrength - count * fract);
+
+ if (type == PBVH_FACES && !ss->pmap) {
+ BLI_assert(!"sculpt smooth: pmap missing");
+ return;
+ }
+
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_boundary_info_ensure(ob);
+
+#ifdef PROXY_ADVANCED
+ int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK;
+ BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask);
+
+ BKE_pbvh_load_proxyarrays(ss->pbvh, nodes, totnode, PV_CO | PV_NO | PV_MASK);
+#endif
+ for (iteration = 0; iteration <= count; iteration++) {
+ const float strength = (iteration != count) ? 1.0f : last;
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .smooth_mask = false,
+ .strength = strength,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_smooth_vcol_boundary_brush_task_cb_ex, &settings);
+
+#ifdef PROXY_ADVANCED
+ BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode);
+#endif
+ }
+}