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:
-rw-r--r--source/blender/blenkernel/BKE_mesh_fair.h56
-rw-r--r--source/blender/blenkernel/CMakeLists.txt3
-rw-r--r--source/blender/blenkernel/intern/mesh_fair.cc505
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c91
5 files changed, 655 insertions, 1 deletions
diff --git a/source/blender/blenkernel/BKE_mesh_fair.h b/source/blender/blenkernel/BKE_mesh_fair.h
new file mode 100644
index 00000000000..2d5c85d4129
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_fair.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
+ * https://github.com/fedackb/mesh-fairing.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_utildefines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Mesh Fairing. */
+/* Creates a smooth as possible geometry patch in a defined area. Different values of depth allow
+ * to minimize changes in the vertex positions or tangency in the affected area. */
+
+typedef enum eMeshFairingDepth {
+ MESH_FAIRING_DEPTH_POSITION = 1,
+ MESH_FAIRING_DEPTH_TANGENCY = 2,
+} eMeshFairingDepth;
+
+/* affect_vertices is used to define the fairing area. Indexed by vertex index, set to true when
+ * the vertex should be modified by fairing. */
+void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm,
+ bool *affect_vertices,
+ const eMeshFairingDepth depth);
+
+/* This function can optionally use the MVert coordinates of deform_mverts to read and write the
+ * fairing result. When NULL, the function will use mesh->mverts directly. */
+void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh,
+ struct MVert *deform_mverts,
+ bool *affect_vertices,
+ const eMeshFairingDepth depth);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 0067e1c8209..ae0a180311c 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -42,6 +42,7 @@ set(INC
../simulation
../../../intern/ghost
../../../intern/glew-mx
+ ../../../intern/eigen
../../../intern/guardedalloc
../../../intern/iksolver/extern
../../../intern/atomic
@@ -170,6 +171,7 @@ set(SRC
intern/mesh.c
intern/mesh_convert.c
intern/mesh_evaluate.c
+ intern/mesh_fair.cc
intern/mesh_iterators.c
intern/mesh_mapping.c
intern/mesh_merge.c
@@ -353,6 +355,7 @@ set(SRC
BKE_mball_tessellate.h
BKE_mesh.h
BKE_mesh_iterators.h
+ BKE_mesh_fair.h
BKE_mesh_mapping.h
BKE_mesh_mirror.h
BKE_mesh_remap.h
diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc
new file mode 100644
index 00000000000..527288d06cf
--- /dev/null
+++ b/source/blender/blenkernel/intern/mesh_fair.cc
@@ -0,0 +1,505 @@
+/*
+ * 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
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
+ * https://github.com/fedackb/mesh-fairing.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_map.hh"
+#include "BLI_math.h"
+#include "BLI_vector.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_fair.h"
+#include "BKE_mesh_mapping.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include "MEM_guardedalloc.h"
+#include "eigen_capi.h"
+
+using blender::Map;
+using blender::Vector;
+using std::array;
+
+class VertexWeight {
+ public:
+ virtual float weight_at_index(const int index) = 0;
+ virtual ~VertexWeight() = default;
+};
+
+class LoopWeight {
+ public:
+ virtual float weight_at_index(const int index) = 0;
+ virtual ~LoopWeight() = default;
+};
+
+class FairingContext {
+ public:
+ /* Get coordinates of vertices which are adjacent to the loop with specified index. */
+ virtual void adjacents_coords_from_loop(const int loop,
+ float r_adj_next[3],
+ float r_adj_prev[3]) = 0;
+
+ /* Get the other vertex index for a loop. */
+ virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) = 0;
+
+ int vertex_count_get()
+ {
+ return totvert_;
+ }
+
+ int loop_count_get()
+ {
+ return totvert_;
+ }
+
+ MeshElemMap *vertex_loop_map_get(const int v)
+ {
+ return &vlmap_[v];
+ }
+
+ float *vertex_deformation_co_get(const int v)
+ {
+ return co_[v];
+ }
+
+ virtual ~FairingContext() = default;
+
+ void fair_vertices(bool *affected,
+ const eMeshFairingDepth depth,
+ VertexWeight *vertex_weight,
+ LoopWeight *loop_weight)
+ {
+
+ fair_vertices_ex(affected, (int)depth, vertex_weight, loop_weight);
+ }
+
+ protected:
+ Vector<float *> co_;
+
+ int totvert_;
+ int totloop_;
+
+ MeshElemMap *vlmap_;
+ int *vlmap_mem_;
+
+ private:
+ void fair_setup_fairing(const int v,
+ const int i,
+ LinearSolver *solver,
+ float multiplier,
+ const int depth,
+ Map<int, int> &vert_col_map,
+ VertexWeight *vertex_weight,
+ LoopWeight *loop_weight)
+ {
+ if (depth == 0) {
+ if (vert_col_map.contains(v)) {
+ const int j = vert_col_map.lookup(v);
+ EIG_linear_solver_matrix_add(solver, i, j, -multiplier);
+ return;
+ }
+ for (int j = 0; j < 3; j++) {
+ EIG_linear_solver_right_hand_side_add(solver, j, i, multiplier * co_[v][j]);
+ }
+ return;
+ }
+
+ float w_ij_sum = 0;
+ const float w_i = vertex_weight->weight_at_index(v);
+ MeshElemMap *vlmap_elem = &vlmap_[v];
+ for (int l = 0; l < vlmap_elem->count; l++) {
+ const int l_index = vlmap_elem->indices[l];
+ const int other_vert = other_vertex_index_from_loop(l_index, v);
+ const float w_ij = loop_weight->weight_at_index(l_index);
+ w_ij_sum += w_ij;
+ fair_setup_fairing(other_vert,
+ i,
+ solver,
+ w_i * w_ij * multiplier,
+ depth - 1,
+ vert_col_map,
+ vertex_weight,
+ loop_weight);
+ }
+ fair_setup_fairing(v,
+ i,
+ solver,
+ -1 * w_i * w_ij_sum * multiplier,
+ depth - 1,
+ vert_col_map,
+ vertex_weight,
+ loop_weight);
+ }
+
+ void fair_vertices_ex(bool *affected,
+ const int order,
+ VertexWeight *vertex_weight,
+ LoopWeight *loop_weight)
+ {
+ Map<int, int> vert_col_map;
+ int num_affected_vertices = 0;
+ for (int i = 0; i < totvert_; i++) {
+ if (!affected[i]) {
+ continue;
+ }
+ vert_col_map.add(i, num_affected_vertices);
+ num_affected_vertices++;
+ }
+
+ /* Early return, nothing to do. */
+ if (num_affected_vertices == 0 || num_affected_vertices == totvert_) {
+ return;
+ }
+
+ /* Setup fairing matrices */
+ LinearSolver *solver = EIG_linear_solver_new(num_affected_vertices, num_affected_vertices, 3);
+ for (auto item : vert_col_map.items()) {
+ const int v = item.key;
+ const int col = item.value;
+ fair_setup_fairing(v, col, solver, 1.0f, order, vert_col_map, vertex_weight, loop_weight);
+ }
+
+ /* Solve linear system */
+ EIG_linear_solver_solve(solver);
+
+ /* Copy the result back to the mesh */
+ for (auto item : vert_col_map.items()) {
+ const int v = item.key;
+ const int col = item.value;
+ for (int j = 0; j < 3; j++) {
+ co_[v][j] = EIG_linear_solver_variable_get(solver, j, col);
+ }
+ }
+
+ /* Free solver data */
+ EIG_linear_solver_delete(solver);
+ }
+};
+
+class MeshFairingContext : public FairingContext {
+ public:
+ MeshFairingContext(Mesh *mesh, MVert *deform_mverts)
+ {
+ totvert_ = mesh->totvert;
+ totloop_ = mesh->totloop;
+
+ medge_ = mesh->medge;
+ mpoly_ = mesh->mpoly;
+ mloop_ = mesh->mloop;
+ BKE_mesh_vert_loop_map_create(&vlmap_,
+ &vlmap_mem_,
+ mesh->mpoly,
+ mesh->mloop,
+ mesh->totvert,
+ mesh->totpoly,
+ mesh->totloop);
+
+ /* Deformation coords. */
+ co_.reserve(mesh->totvert);
+ if (deform_mverts) {
+ for (int i = 0; i < mesh->totvert; i++) {
+ co_[i] = deform_mverts[i].co;
+ }
+ }
+ else {
+ for (int i = 0; i < mesh->totvert; i++) {
+ co_[i] = mesh->mvert[i].co;
+ }
+ }
+
+ loop_to_poly_map_.reserve(mesh->totloop);
+ for (int i = 0; i < mesh->totpoly; i++) {
+ for (int l = 0; l < mesh->mpoly[i].totloop; l++) {
+ loop_to_poly_map_[l + mesh->mpoly[i].loopstart] = i;
+ }
+ }
+ }
+
+ ~MeshFairingContext()
+ {
+ MEM_SAFE_FREE(vlmap_);
+ MEM_SAFE_FREE(vlmap_mem_);
+ }
+
+ virtual void adjacents_coords_from_loop(const int loop,
+ float r_adj_next[3],
+ float r_adj_prev[3]) override
+ {
+ const int vert = mloop_[loop].v;
+ const MPoly *p = &mpoly_[loop_to_poly_map_[loop]];
+ const int corner = poly_find_loop_from_vert(p, &mloop_[p->loopstart], vert);
+ copy_v3_v3(r_adj_next, co_[ME_POLY_LOOP_NEXT(mloop_, p, corner)->v]);
+ copy_v3_v3(r_adj_prev, co_[ME_POLY_LOOP_PREV(mloop_, p, corner)->v]);
+ }
+
+ virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) override
+ {
+ MEdge *e = &medge_[mloop_[loop].e];
+ if (e->v1 == v) {
+ return e->v2;
+ }
+ return e->v1;
+ }
+
+ protected:
+ Mesh *mesh_;
+ MLoop *mloop_;
+ MPoly *mpoly_;
+ MEdge *medge_;
+ Vector<int> loop_to_poly_map_;
+};
+
+class BMeshFairingContext : public FairingContext {
+ public:
+ BMeshFairingContext(BMesh *bm)
+ {
+ this->bm = bm;
+ totvert_ = bm->totvert;
+ totloop_ = bm->totloop;
+
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ /* Deformation coords. */
+ co_.reserve(bm->totvert);
+ for (int i = 0; i < bm->totvert; i++) {
+ BMVert *v = BM_vert_at_index(bm, i);
+ co_[i] = v->co;
+ }
+
+ bmloop_.reserve(bm->totloop);
+ vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(sizeof(MeshElemMap), bm->totvert, "bmesh loop map");
+ vlmap_mem_ = (int *)MEM_malloc_arrayN(sizeof(int), bm->totloop, "bmesh loop map mempool");
+
+ BMVert *v;
+ BMLoop *l;
+ BMIter iter;
+ BMIter loop_iter;
+ int index_iter = 0;
+
+ /* This initializes both the bmloop and the vlmap for bmesh in a single loop. */
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ int loop_count = 0;
+ const int vert_index = BM_elem_index_get(v);
+ vlmap_[vert_index].indices = &vlmap_mem_[index_iter];
+ BM_ITER_ELEM (l, &loop_iter, v, BM_LOOPS_OF_VERT) {
+ const int loop_index = BM_elem_index_get(l);
+ bmloop_[loop_index] = l;
+ vlmap_mem_[index_iter] = loop_index;
+ index_iter++;
+ loop_count++;
+ }
+ vlmap_[vert_index].count = loop_count;
+ }
+ }
+
+ ~BMeshFairingContext()
+ {
+ MEM_SAFE_FREE(vlmap_);
+ MEM_SAFE_FREE(vlmap_mem_);
+ }
+
+ virtual void adjacents_coords_from_loop(const int loop,
+ float r_adj_next[3],
+ float r_adj_prev[3]) override
+ {
+ copy_v3_v3(r_adj_next, bmloop_[loop]->next->v->co);
+ copy_v3_v3(r_adj_prev, bmloop_[loop]->prev->v->co);
+ }
+
+ virtual int other_vertex_index_from_loop(const int loop, const unsigned int v) override
+ {
+ BMLoop *l = bmloop_[loop];
+ BMVert *bmvert = BM_vert_at_index(bm, v);
+ BMVert *bm_other_vert = BM_edge_other_vert(l->e, bmvert);
+ return BM_elem_index_get(bm_other_vert);
+ }
+
+ protected:
+ BMesh *bm;
+ Vector<BMLoop *> bmloop_;
+};
+
+class UniformVertexWeight : public VertexWeight {
+ public:
+ UniformVertexWeight(FairingContext *fairing_context)
+ {
+ const int totvert = fairing_context->vertex_count_get();
+ vertex_weights_.reserve(totvert);
+ for (int i = 0; i < totvert; i++) {
+ const int tot_loop = fairing_context->vertex_loop_map_get(i)->count;
+ if (tot_loop != 0) {
+ vertex_weights_[i] = 1.0f / tot_loop;
+ }
+ else {
+ vertex_weights_[i] = FLT_MAX;
+ }
+ }
+ }
+ ~UniformVertexWeight() = default;
+
+ float weight_at_index(const int index) override
+ {
+ return vertex_weights_[index];
+ }
+
+ private:
+ Vector<float> vertex_weights_;
+};
+
+class VoronoiVertexWeight : public VertexWeight {
+
+ public:
+ VoronoiVertexWeight(FairingContext *fairing_context)
+ {
+
+ const int totvert = fairing_context->vertex_count_get();
+ vertex_weights_.reserve(totvert);
+ for (int i = 0; i < totvert; i++) {
+
+ float area = 0.0f;
+ float a[3];
+ copy_v3_v3(a, fairing_context->vertex_deformation_co_get(i));
+ const float acute_threshold = M_PI_2;
+
+ MeshElemMap *vlmap_elem = fairing_context->vertex_loop_map_get(i);
+ for (int l = 0; l < vlmap_elem->count; l++) {
+ const int l_index = vlmap_elem->indices[l];
+
+ float b[3], c[3], d[3];
+ fairing_context->adjacents_coords_from_loop(l_index, b, c);
+
+ if (angle_v3v3v3(c, fairing_context->vertex_deformation_co_get(i), b) < acute_threshold) {
+ calc_circumcenter(d, a, b, c);
+ }
+ else {
+ add_v3_v3v3(d, b, c);
+ mul_v3_fl(d, 0.5f);
+ }
+
+ float t[3];
+ add_v3_v3v3(t, a, b);
+ mul_v3_fl(t, 0.5f);
+ area += area_tri_v3(a, t, d);
+
+ add_v3_v3v3(t, a, c);
+ mul_v3_fl(t, 0.5f);
+ area += area_tri_v3(a, d, t);
+ }
+
+ vertex_weights_[i] = area != 0.0f ? 1.0f / area : 1e12;
+ }
+ }
+ ~VoronoiVertexWeight() = default;
+
+ float weight_at_index(const int index) override
+ {
+ return vertex_weights_[index];
+ }
+
+ private:
+ Vector<float> vertex_weights_;
+
+ void calc_circumcenter(float r[3], const float a[3], const float b[3], const float c[3])
+ {
+ float ab[3];
+ sub_v3_v3v3(ab, b, a);
+
+ float ac[3];
+ sub_v3_v3v3(ac, c, a);
+
+ float ab_cross_ac[3];
+ cross_v3_v3v3(ab_cross_ac, ab, ac);
+
+ if (len_squared_v3(ab_cross_ac) > 0.0f) {
+ float d[3];
+ cross_v3_v3v3(d, ab_cross_ac, ab);
+ mul_v3_fl(d, len_squared_v3(ac));
+
+ float t[3];
+ cross_v3_v3v3(t, ac, ab_cross_ac);
+ mul_v3_fl(t, len_squared_v3(ab));
+
+ add_v3_v3(d, t);
+
+ mul_v3_fl(d, 1.0f / (2.0f * len_squared_v3(ab_cross_ac)));
+
+ add_v3_v3v3(r, a, d);
+ return;
+ }
+ copy_v3_v3(r, a);
+ }
+};
+
+class UniformLoopWeight : public LoopWeight {
+ public:
+ float weight_at_index(const int UNUSED(index)) override
+ {
+ return 1.0f;
+ }
+};
+
+static void prefair_and_fair_vertices(FairingContext *fairing_context,
+ bool *affected_vertices,
+ const eMeshFairingDepth depth)
+{
+ /* Prefair. */
+ UniformVertexWeight *uniform_vertex_weights = new UniformVertexWeight(fairing_context);
+ UniformLoopWeight *uniform_loop_weights = new UniformLoopWeight();
+ fairing_context->fair_vertices(
+ affected_vertices, depth, uniform_vertex_weights, uniform_loop_weights);
+ delete uniform_vertex_weights;
+
+ /* Fair. */
+ VoronoiVertexWeight *voronoi_vertex_weights = new VoronoiVertexWeight(fairing_context);
+ /* TODO: Implemente cotangent loop weights. */
+ fairing_context->fair_vertices(
+ affected_vertices, depth, voronoi_vertex_weights, uniform_loop_weights);
+
+ delete uniform_loop_weights;
+ delete voronoi_vertex_weights;
+}
+
+void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh,
+ struct MVert *deform_mverts,
+ bool *affect_vertices,
+ const eMeshFairingDepth depth)
+{
+ MeshFairingContext *fairing_context = new MeshFairingContext(mesh, deform_mverts);
+ prefair_and_fair_vertices(fairing_context, affect_vertices, depth);
+ delete fairing_context;
+}
+
+void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm,
+ bool *affect_vertices,
+ const eMeshFairingDepth depth)
+{
+ BMeshFairingContext *fairing_context = new BMeshFairingContext(bm);
+ prefair_and_fair_vertices(fairing_context, affect_vertices, depth);
+ delete fairing_context;
+}
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 9bf3d2610d8..fff8d27ef5b 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -32,6 +32,7 @@ set(INC
../../windowmanager
../../../../intern/atomic
../../../../intern/clog
+ ../../../../intern/eigen
../../../../intern/glew-mx
../../../../intern/guardedalloc
)
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index ad42750bb92..1fba958d695 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -41,6 +41,7 @@
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_fair.h"
#include "BKE_mesh_mapping.h"
#include "BKE_multires.h"
#include "BKE_node.h"
@@ -1024,6 +1025,8 @@ typedef enum eSculptFaceSetEditMode {
SCULPT_FACE_SET_EDIT_GROW = 0,
SCULPT_FACE_SET_EDIT_SHRINK = 1,
SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2,
+ SCULPT_FACE_SET_EDIT_FAIR_POSITIONS = 3,
+ SCULPT_FACE_SET_EDIT_FAIR_TANGENCY = 4,
} eSculptFaceSetEditMode;
static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
@@ -1048,6 +1051,22 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
"Delete Geometry",
"Deletes the faces that are assigned to the Face Set",
},
+ {
+ SCULPT_FACE_SET_EDIT_FAIR_POSITIONS,
+ "FAIR_POSITIONS",
+ 0,
+ "Fair Positions",
+ "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
+ "vertex positions",
+ },
+ {
+ SCULPT_FACE_SET_EDIT_FAIR_TANGENCY,
+ "FAIR_TANGENCY",
+ 0,
+ "Fair Tangency",
+ "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
+ "vertex tangents",
+ },
{0, NULL, 0, NULL, NULL},
};
@@ -1181,6 +1200,29 @@ static void sculpt_face_set_delete_geometry(Object *ob,
BM_mesh_free(bm);
}
+static void sculpt_face_set_edit_fair_face_set(Object *ob,
+ const int active_face_set_id,
+ const int fair_order)
+{
+ SculptSession *ss = ob->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ Mesh *mesh = ob->data;
+ bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices");
+
+ SCULPT_boundary_info_ensure(ob);
+
+ for (int i = 0; i < totvert; i++) {
+ fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) &&
+ SCULPT_vertex_has_face_set(ss, i, active_face_set_id) &&
+ SCULPT_vertex_has_unique_face_set(ss, i);
+ }
+
+ MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
+ BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order);
+ MEM_freeN(fair_vertices);
+}
+
static void sculpt_face_set_apply_edit(Object *ob,
const int active_face_set_id,
const int mode,
@@ -1204,6 +1246,12 @@ static void sculpt_face_set_apply_edit(Object *ob,
case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY:
sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden);
break;
+ case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
+ sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_POSITION);
+ break;
+ case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
+ sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY);
+ break;
}
}
@@ -1230,6 +1278,16 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss,
return false;
}
}
+
+ if (ELEM(mode, SCULPT_FACE_SET_EDIT_FAIR_POSITIONS, SCULPT_FACE_SET_EDIT_FAIR_TANGENCY)) {
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ /* TODO: Multires topology representation using grids and duplicates can't be used directly
+ * by the fair algorithm. Multires topology needs to be exposed in a different way or
+ * converted to a mesh for this operation. */
+ return false;
+ }
+ }
+
return true;
}
@@ -1287,11 +1345,38 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob,
MEM_freeN(nodes);
}
+static void sculpt_face_set_edit_modify_coordinates(bContext *C,
+ Object *ob,
+ const int active_face_set,
+ const eSculptFaceSetEditMode mode)
+{
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ SculptSession *ss = ob->sculpt;
+ PBVH *pbvh = ss->pbvh;
+ PBVHNode **nodes;
+ int totnode;
+ BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ SCULPT_undo_push_begin(ob, "face set edit");
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_update(nodes[i]);
+ SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS);
+ }
+ sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, false);
+
+ if (ss->deform_modifiers_active || ss->shapekey_active) {
+ SCULPT_flush_stroke_deform(sd, ob, true);
+ }
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
+ SCULPT_undo_push_end();
+ MEM_freeN(nodes);
+}
+
static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const int mode = RNA_enum_get(op->ptr, "mode");
const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
@@ -1320,6 +1405,10 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
case SCULPT_FACE_SET_EDIT_SHRINK:
sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden);
break;
+ case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
+ case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
+ sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode);
+ break;
}
SCULPT_tag_update_overlays(C);