This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

563 lines
18 KiB
C
Raw Permalink Normal View History

/*
* 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
2020-05-09 17:14:35 +10:00
* 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) 2020 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup edsculpt
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_task.h"
#include "DNA_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BKE_scene.h"
#include "DEG_depsgraph.h"
#include "WM_api.h"
#include "WM_message.h"
#include "WM_toolsystem.h"
#include "WM_types.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "paint_intern.h"
#include "sculpt_intern.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "bmesh.h"
#include <math.h>
#include <stdlib.h>
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index)
{
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
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);
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
neighbor_count++;
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++;
}
}
else {
/* Interior vertices use all neighbors. */
add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
total++;
}
}
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Do not modify corner vertices. */
if (neighbor_count <= 2) {
copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
return;
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
}
/* Avoid division by 0 when there are no neighbors. */
if (total == 0) {
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
return;
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
}
mul_v3_v3fl(result, avg, 1.0f / total);
}
/* 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)
{
float avg_co[3] = {0.0f, 0.0f, 0.0f};
float tot_co = 0.0f;
BMIter eiter;
BMEdge *e;
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
if (BM_edge_is_boundary(e)) {
copy_v3_v3(avg, v->co);
return;
}
BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1;
float vec[3];
sub_v3_v3v3(vec, v_other->co, v->co);
madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no));
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);
fac = fac * fac - 0.5f;
fac *= fac;
madd_v3_v3fl(avg_co, v_other->co, fac);
tot_co += fac;
}
/* In case vert has no Edge s. */
if (tot_co > 0.0f) {
mul_v3_v3fl(avg, avg_co, 1.0f / tot_co);
/* 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, vec);
add_v3_v3(avg, v->co);
}
else {
zero_v3(avg);
}
}
/* 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)
{
float avg[3] = {0.0f, 0.0f, 0.0f};
int total = 0;
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_END(ni);
if (total > 0) {
mul_v3_v3fl(result, avg, 1.0f / total);
}
else {
copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
}
}
float SCULPT_neighbor_mask_average(SculptSession *ss, int 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);
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
return avg / total;
}
return SCULPT_vertex_mask_get(ss, index);
}
void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int 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));
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
mul_v4_v4fl(result, avg, 1.0f / total);
}
else {
copy_v4_v4(result, SCULPT_vertex_color_get(ss, index));
}
}
static void do_enhance_details_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;
PBVHVertexIter vd;
float bstrength = ss->cache->bstrength;
CLAMP(bstrength, -1.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.index,
thread_id);
float disp[3];
madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade);
SCULPT_clip(sd, ss, vd.co, disp);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
}
static void SCULPT_enhance_details_brush(Sculpt *sd,
Object *ob,
PBVHNode **nodes,
const int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
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));
}
}
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
};
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);
}
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
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)) {
continue;
}
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.index) - *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 (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
}
void SCULPT_smooth(Sculpt *sd,
Object *ob,
PBVHNode **nodes,
const int totnode,
float bstrength,
const bool smooth_mask)
{
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);
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
SCULPT_boundary_info_ensure(ob);
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 = smooth_mask,
.strength = strength,
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
Fix T78747: Fix mesh boundary detection and automasking This issue was produced by a hack in the sculpt mode code from 2.80 when the sculpt API for connectivity info was not available. The smooth brush was the only brush that needed connectivity info, so there were 3 different smooth functions with the connectivity queries implemented for dyntopo, meshes and grids. The mesh version of smoothing was checking the number of connected faces to a vertex to mask the mesh boundaries, which was not covering all cases and was hardcoded in the smooth function itself. This patch removes all those legacy functions and unifies all smooth functions into a single one using the new API and the automasking system. In order to achieve this, there were needed some extra changes: - The smooth brush now does not automasks the boundaries by default, so its default preset needs to be updated to enable automasking - The mesh boundary info is extracted once and cached in a bitmap, similar to the disconnected elements IDs. This makes boundary detection work as expected in all cases, solving a lot of known issues with the smooth brush. In multires, this info is extracted and cached only at the base mesh level, so it is much more memory efficient than the previous automasking system. - In order to keep the brushes responsive as they were before, the automasking system can now skip creating the cache when it is not needed for the requested options. This means that for high poly meshes and simple automasking options the brushes won't lag on start. Reviewed By: sergey Maniphest Tasks: T78747 Differential Revision: https://developer.blender.org/D8260
2020-07-15 16:24:03 +02:00
BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
}
}
void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
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);
}
}
/* HC Smooth Algorithm. */
/* From: Improved Laplacian Smoothing of Noisy Surface Meshes */
void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
float *disp,
const float co[3],
float (*laplacian_disp)[3],
const int v_index,
const float origco[3],
const float alpha)
{
float laplacian_smooth_co[3];
float weigthed_o[3], weigthed_q[3], d[3];
SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, 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(disp, laplacian_smooth_co, co);
}
void SCULPT_surface_smooth_displace_step(SculptSession *ss,
float *co,
float (*laplacian_disp)[3],
const int 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;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_index, ni) {
add_v3_v3(b_avg, laplacian_disp[ni.index]);
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);
mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f));
sub_v3_v3(co, b_current_vertex);
}
}
static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength;
float alpha = brush->surface_smooth_shape_preservation;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
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);
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
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.index,
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);
madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f));
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
}
static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength;
const float beta = brush->surface_smooth_current_vertex;
PBVHVertexIter vd;
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.index,
thread_id);
SCULPT_surface_smooth_displace_step(
ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade);
}
BKE_pbvh_vertex_iter_end;
}
void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
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");
}
/* Threaded loop over nodes. */
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
.brush = brush,
.nodes = nodes,
};
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_surface_smooth_brush_laplacian_task_cb_ex, &settings);
BLI_task_parallel_range(
0, totnode, &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings);
}
}