Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_smooth.c
| Show First 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "bmesh.h" | #include "bmesh.h" | ||||
| #include <math.h> | #include <math.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| /* For the smooth brush, uses the neighboring vertices around vert to calculate | void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index) | ||||
| * a smoothed location for vert. Skips corner vertices (used by only one | { | ||||
| * polygon). */ | float avg[3] = {0.0f, 0.0f, 0.0f}; | ||||
| void SCULPT_neighbor_average(SculptSession *ss, float avg[3], uint vert) | |||||
| { | |||||
| const MeshElemMap *vert_map = &ss->pmap[vert]; | |||||
| const MVert *mvert = ss->mvert; | |||||
| float(*deform_co)[3] = ss->deform_cos; | |||||
| /* Don't modify corner vertices. */ | |||||
| if (vert_map->count > 1) { | |||||
| int total = 0; | int total = 0; | ||||
| zero_v3(avg); | if (SCULPT_vertex_is_boundary(ss, index)) { | ||||
| copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); | |||||
| for (int i = 0; i < vert_map->count; i++) { | |||||
| const MPoly *p = &ss->mpoly[vert_map->indices[i]]; | |||||
| uint f_adj_v[2]; | |||||
| if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { | |||||
| for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { | |||||
| if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { | |||||
| add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] : mvert[f_adj_v[j]].co); | |||||
| total++; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (total > 0) { | |||||
| mul_v3_fl(avg, 1.0f / total); | |||||
| return; | return; | ||||
| } | } | ||||
| } | |||||
| copy_v3_v3(avg, deform_co ? deform_co[vert] : mvert[vert].co); | |||||
| } | |||||
| /* Same logic as neighbor_average(), but for bmesh rather than mesh. */ | |||||
| void SCULPT_bmesh_neighbor_average(float avg[3], BMVert *v) | |||||
| { | |||||
| /* logic for 3 or more is identical. */ | |||||
| const int vfcount = BM_vert_face_count_at_most(v, 3); | |||||
| /* Don't modify corner vertices. */ | SculptVertexNeighborIter ni; | ||||
| if (vfcount > 1) { | SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { | ||||
| BMIter liter; | add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); | ||||
| BMLoop *l; | |||||
| int total = 0; | |||||
| zero_v3(avg); | |||||
| BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { | |||||
| const BMVert *adj_v[2] = {l->prev->v, l->next->v}; | |||||
| for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { | |||||
| const BMVert *v_other = adj_v[i]; | |||||
| if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { | |||||
| add_v3_v3(avg, v_other->co); | |||||
| total++; | total++; | ||||
| } | } | ||||
| } | SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); | ||||
| } | |||||
| if (total > 0) { | if (total > 0) { | ||||
| mul_v3_fl(avg, 1.0f / total); | mul_v3_v3fl(result, avg, 1.0f / total); | ||||
sergey: You do not need to cast both operands for floating point division. `1.0 / int` will give you… | |||||
| return; | |||||
| } | } | ||||
| else { | |||||
| copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); | |||||
| } | } | ||||
| copy_v3_v3(avg, v->co); | |||||
| } | } | ||||
| /* For bmesh: Average surrounding verts based on an orthogonality measure. | /* For bmesh: Average surrounding verts based on an orthogonality measure. | ||||
| * Naturally converges to a quad-like structure. */ | * 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(float avg[3], float direction[3], BMVert *v) | ||||
| { | { | ||||
| float avg_co[3] = {0.0f, 0.0f, 0.0f}; | float avg_co[3] = {0.0f, 0.0f, 0.0f}; | ||||
| ▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index) | ||||
| if (total > 0) { | if (total > 0) { | ||||
| mul_v4_v4fl(result, avg, 1.0f / (float)total); | mul_v4_v4fl(result, avg, 1.0f / (float)total); | ||||
| } | } | ||||
| else { | else { | ||||
| copy_v4_v4(result, SCULPT_vertex_color_get(ss, index)); | copy_v4_v4(result, SCULPT_vertex_color_get(ss, index)); | ||||
| } | } | ||||
| } | } | ||||
| static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata, | 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; | |||||
| 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)) { | |||||
| 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.index, | |||||
| thread_id); | |||||
| if (smooth_mask) { | |||||
| float val = SCULPT_neighbor_mask_average(ss, vd.vert_indices[vd.i]) - *vd.mask; | |||||
| val *= fade * bstrength; | |||||
| *vd.mask += val; | |||||
| CLAMP(*vd.mask, 0.0f, 1.0f); | |||||
| } | |||||
| else { | |||||
| float avg[3], val[3]; | |||||
| SCULPT_neighbor_average(ss, avg, vd.vert_indices[vd.i]); | |||||
| sub_v3_v3v3(val, avg, 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; | |||||
| } | |||||
| static void do_smooth_brush_bmesh_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); | |||||
| 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.index, | |||||
| thread_id); | |||||
| if (smooth_mask) { | |||||
| float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask; | |||||
| val *= fade * bstrength; | |||||
| *vd.mask += val; | |||||
| CLAMP(*vd.mask, 0.0f, 1.0f); | |||||
| } | |||||
| else { | |||||
| float avg[3], val[3]; | |||||
| SCULPT_bmesh_neighbor_average(avg, vd.bm_vert); | |||||
| sub_v3_v3v3(val, avg, 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; | |||||
| } | |||||
| static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, | |||||
| const int n, | const int n, | ||||
| const TaskParallelTLS *__restrict tls) | const TaskParallelTLS *__restrict tls) | ||||
| { | { | ||||
| SculptThreadedTaskData *data = userdata; | SculptThreadedTaskData *data = userdata; | ||||
| SculptSession *ss = data->ob->sculpt; | SculptSession *ss = data->ob->sculpt; | ||||
| Sculpt *sd = data->sd; | Sculpt *sd = data->sd; | ||||
| const Brush *brush = data->brush; | const Brush *brush = data->brush; | ||||
| const bool smooth_mask = data->smooth_mask; | const bool smooth_mask = data->smooth_mask; | ||||
| float bstrength = data->strength; | float bstrength = data->strength; | ||||
| ▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | void SCULPT_smooth(Sculpt *sd, | ||||
| count = (int)(bstrength * max_iterations); | count = (int)(bstrength * max_iterations); | ||||
| last = max_iterations * (bstrength - count * fract); | last = max_iterations * (bstrength - count * fract); | ||||
| if (type == PBVH_FACES && !ss->pmap) { | if (type == PBVH_FACES && !ss->pmap) { | ||||
| BLI_assert(!"sculpt smooth: pmap missing"); | BLI_assert(!"sculpt smooth: pmap missing"); | ||||
| return; | return; | ||||
| } | } | ||||
| SCULPT_boundary_info_ensure(ob); | |||||
| for (iteration = 0; iteration <= count; iteration++) { | for (iteration = 0; iteration <= count; iteration++) { | ||||
| const float strength = (iteration != count) ? 1.0f : last; | const float strength = (iteration != count) ? 1.0f : last; | ||||
| SculptThreadedTaskData data = { | SculptThreadedTaskData data = { | ||||
| .sd = sd, | .sd = sd, | ||||
| .ob = ob, | .ob = ob, | ||||
| .brush = brush, | .brush = brush, | ||||
| .nodes = nodes, | .nodes = nodes, | ||||
| .smooth_mask = smooth_mask, | .smooth_mask = smooth_mask, | ||||
| .strength = strength, | .strength = strength, | ||||
| }; | }; | ||||
| TaskParallelSettings settings; | TaskParallelSettings settings; | ||||
| BKE_pbvh_parallel_range_settings(&settings, true, totnode); | BKE_pbvh_parallel_range_settings(&settings, true, totnode); | ||||
| BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings); | |||||
| switch (type) { | |||||
| case PBVH_GRIDS: | |||||
| BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_multires_task_cb_ex, &settings); | |||||
| break; | |||||
| case PBVH_FACES: | |||||
| BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_mesh_task_cb_ex, &settings); | |||||
| break; | |||||
| case PBVH_BMESH: | |||||
| BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_bmesh_task_cb_ex, &settings); | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| 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) | ||||
| { | { | ||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||
| SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); | SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 161 Lines • Show Last 20 Lines | |||||
You do not need to cast both operands for floating point division. 1.0 / int will give you what you want.