Standardize naming, use spans and references for input parameters, and improve documentation. Now the functions expect the lookups to succeed as well, they will fail and assert otherwise. The functions are also simple enough that it likely makes sense to keep them all inlined
495 lines
14 KiB
C++
495 lines
14 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
* Mesh Fairing algorithm designed by Brett Fedack, used in the addon "Mesh Fairing":
|
|
* https://github.com/fedackb/mesh-fairing.
|
|
*/
|
|
|
|
#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.hh"
|
|
#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::Array;
|
|
using blender::float3;
|
|
using blender::Map;
|
|
using blender::MutableSpan;
|
|
using blender::Span;
|
|
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 uint 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_verts(bool *affected,
|
|
const eMeshFairingDepth depth,
|
|
VertexWeight *vertex_weight,
|
|
LoopWeight *loop_weight)
|
|
{
|
|
|
|
fair_verts_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_verts_ex(const bool *affected,
|
|
const int order,
|
|
VertexWeight *vertex_weight,
|
|
LoopWeight *loop_weight)
|
|
{
|
|
Map<int, int> vert_col_map;
|
|
int affected_verts_num = 0;
|
|
for (int i = 0; i < totvert_; i++) {
|
|
if (!affected[i]) {
|
|
continue;
|
|
}
|
|
vert_col_map.add(i, affected_verts_num);
|
|
affected_verts_num++;
|
|
}
|
|
|
|
/* Early return, nothing to do. */
|
|
if (ELEM(affected_verts_num, 0, totvert_)) {
|
|
return;
|
|
}
|
|
|
|
/* Setup fairing matrices */
|
|
LinearSolver *solver = EIG_linear_solver_new(affected_verts_num, affected_verts_num, 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, MutableSpan<float3> deform_positions)
|
|
{
|
|
totvert_ = mesh->totvert;
|
|
totloop_ = mesh->totloop;
|
|
|
|
MutableSpan<float3> positions = mesh->vert_positions_for_write();
|
|
edges_ = mesh->edges();
|
|
polys = mesh->polys();
|
|
corner_verts_ = mesh->corner_verts();
|
|
corner_edges_ = mesh->corner_edges();
|
|
BKE_mesh_vert_loop_map_create(&vlmap_,
|
|
&vlmap_mem_,
|
|
polys.data(),
|
|
corner_verts_.data(),
|
|
mesh->totvert,
|
|
mesh->totpoly,
|
|
mesh->totloop);
|
|
|
|
/* Deformation coords. */
|
|
co_.reserve(mesh->totvert);
|
|
if (!deform_positions.is_empty()) {
|
|
for (int i = 0; i < mesh->totvert; i++) {
|
|
co_[i] = deform_positions[i];
|
|
}
|
|
}
|
|
else {
|
|
for (int i = 0; i < mesh->totvert; i++) {
|
|
co_[i] = positions[i];
|
|
}
|
|
}
|
|
|
|
loop_to_poly_map_ = blender::bke::mesh_topology::build_loop_to_poly_map(polys,
|
|
corner_verts_.size());
|
|
}
|
|
|
|
~MeshFairingContext() override
|
|
{
|
|
MEM_SAFE_FREE(vlmap_);
|
|
MEM_SAFE_FREE(vlmap_mem_);
|
|
}
|
|
|
|
void adjacents_coords_from_loop(const int loop,
|
|
float r_adj_next[3],
|
|
float r_adj_prev[3]) override
|
|
{
|
|
using namespace blender;
|
|
const int vert = corner_verts_[loop];
|
|
const MPoly &poly = polys[loop_to_poly_map_[loop]];
|
|
const int2 adjecent_verts = bke::mesh::poly_find_adjecent_verts(poly, corner_verts_, vert);
|
|
copy_v3_v3(r_adj_next, co_[adjecent_verts[0]]);
|
|
copy_v3_v3(r_adj_prev, co_[adjecent_verts[1]]);
|
|
}
|
|
|
|
int other_vertex_index_from_loop(const int loop, const uint v) override
|
|
{
|
|
const MEdge *edge = &edges_[corner_edges_[loop]];
|
|
if (edge->v1 == v) {
|
|
return edge->v2;
|
|
}
|
|
return edge->v1;
|
|
}
|
|
|
|
protected:
|
|
Mesh *mesh_;
|
|
Span<int> corner_verts_;
|
|
Span<int> corner_edges_;
|
|
Span<MPoly> polys;
|
|
Span<MEdge> edges_;
|
|
Array<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(bm->totvert, sizeof(MeshElemMap), "bmesh loop map");
|
|
vlmap_mem_ = (int *)MEM_malloc_arrayN(bm->totloop, sizeof(int), "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() override
|
|
{
|
|
MEM_SAFE_FREE(vlmap_);
|
|
MEM_SAFE_FREE(vlmap_mem_);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
int other_vertex_index_from_loop(const int loop, const uint 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 /*index*/) override
|
|
{
|
|
return 1.0f;
|
|
}
|
|
};
|
|
|
|
static void prefair_and_fair_verts(FairingContext *fairing_context,
|
|
bool *affected_verts,
|
|
const eMeshFairingDepth depth)
|
|
{
|
|
/* Pre-fair. */
|
|
UniformVertexWeight *uniform_vertex_weights = new UniformVertexWeight(fairing_context);
|
|
UniformLoopWeight *uniform_loop_weights = new UniformLoopWeight();
|
|
fairing_context->fair_verts(affected_verts, depth, uniform_vertex_weights, uniform_loop_weights);
|
|
delete uniform_vertex_weights;
|
|
|
|
/* Fair. */
|
|
VoronoiVertexWeight *voronoi_vertex_weights = new VoronoiVertexWeight(fairing_context);
|
|
/* TODO: Implement cotangent loop weights. */
|
|
fairing_context->fair_verts(affected_verts, depth, voronoi_vertex_weights, uniform_loop_weights);
|
|
|
|
delete uniform_loop_weights;
|
|
delete voronoi_vertex_weights;
|
|
}
|
|
|
|
void BKE_mesh_prefair_and_fair_verts(struct Mesh *mesh,
|
|
float (*deform_vert_positions)[3],
|
|
bool *affect_verts,
|
|
const eMeshFairingDepth depth)
|
|
{
|
|
MutableSpan<float3> deform_positions_span;
|
|
if (deform_vert_positions) {
|
|
deform_positions_span = {reinterpret_cast<float3 *>(deform_vert_positions), mesh->totvert};
|
|
}
|
|
MeshFairingContext *fairing_context = new MeshFairingContext(mesh, deform_positions_span);
|
|
prefair_and_fair_verts(fairing_context, affect_verts, depth);
|
|
delete fairing_context;
|
|
}
|
|
|
|
void BKE_bmesh_prefair_and_fair_verts(struct BMesh *bm,
|
|
bool *affect_verts,
|
|
const eMeshFairingDepth depth)
|
|
{
|
|
BMeshFairingContext *fairing_context = new BMeshFairingContext(bm);
|
|
prefair_and_fair_verts(fairing_context, affect_verts, depth);
|
|
delete fairing_context;
|
|
}
|