Sculpt: integrate instant meshes
Still not particularly usable, but much faster then Quadriflow.
This commit is contained in:
parent
95b84b5e13
commit
779aeb72ab
|
@ -22,6 +22,7 @@ set(INC
|
|||
.
|
||||
../atomic
|
||||
../eigen
|
||||
../guardedalloc
|
||||
../../extern/Eigen3
|
||||
ext/half
|
||||
ext/dset
|
||||
|
@ -53,6 +54,10 @@ set(SRC
|
|||
src/extract.h
|
||||
src/field.cpp
|
||||
src/field.h
|
||||
src/meshstats.cpp
|
||||
src/meshstats.h
|
||||
src/normal.cpp
|
||||
src/normal.h
|
||||
src/hierarchy.cpp
|
||||
src/hierarchy.h
|
||||
src/reorder.cpp
|
||||
|
@ -61,6 +66,8 @@ set(SRC
|
|||
src/subdivide.h
|
||||
src/smoothcurve.cpp
|
||||
src/smoothcurve.h
|
||||
src/c_api.cpp
|
||||
instant_meshes_c_api.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
This is a fork of instant-meshes, an alternative to QuadriFlow.
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
/* clang-format off */
|
||||
//remeshedge->flag
|
||||
enum {
|
||||
REMESH_EDGE_BOUNDARY = (1<<0),
|
||||
REMESH_EDGE_USE_DIR = (1<<1)
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
typedef struct RemeshVertex {
|
||||
float co[3], no[3];
|
||||
} RemeshVertex;
|
||||
|
||||
// edge constraint
|
||||
typedef struct RemeshEdge {
|
||||
int v1, v2, flag;
|
||||
float dir[3];
|
||||
} RemeshEdge;
|
||||
|
||||
typedef struct RemeshTri {
|
||||
int v1, v2, v3;
|
||||
int eflags[3];
|
||||
} RemeshTri;
|
||||
|
||||
typedef struct RemeshOutFace {
|
||||
int *verts;
|
||||
int totvert;
|
||||
} RemeshOutFace;
|
||||
|
||||
typedef struct RemeshMesh {
|
||||
RemeshTri *tris;
|
||||
RemeshVertex *verts;
|
||||
RemeshEdge *edges; // list of constrained edges, need not be all edges in the mesh
|
||||
|
||||
int tottri, totvert, totedge;
|
||||
|
||||
RemeshOutFace *outfaces;
|
||||
RemeshVertex *outverts;
|
||||
int *out_scracth;
|
||||
|
||||
int totoutface;
|
||||
int totoutvert;
|
||||
} RemeshMesh;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void instant_meshes_run(RemeshMesh *mesh);
|
||||
void instant_meshes_finish(RemeshMesh *mesh);
|
||||
void instant_meshes_set_number_of_threads(int n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,347 @@
|
|||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "adjacency.h"
|
||||
#include "cleanup.h"
|
||||
#include "common.h"
|
||||
#include "dedge.h"
|
||||
#include "extract.h"
|
||||
#include "field.h"
|
||||
#include "hierarchy.h"
|
||||
#include "normal.h"
|
||||
#include "smoothcurve.h"
|
||||
#include "subdivide.h"
|
||||
|
||||
#include "meshstats.h"
|
||||
|
||||
#include "../instant_meshes_c_api.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
int instant_meshes_nprocs = 1;
|
||||
|
||||
const float len_v3v3(const float *a, const float *b)
|
||||
{
|
||||
float dx = a[0] - b[0];
|
||||
float dy = a[1] - b[1];
|
||||
float dz = a[2] - b[2];
|
||||
|
||||
float len = dx * dx + dy * dy + dz * dz;
|
||||
|
||||
return len > 0.0f ? sqrtf(len) : 0.0f;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void instant_meshes_run(RemeshMesh *mesh)
|
||||
{
|
||||
MatrixXu F;
|
||||
MatrixXf V;
|
||||
MatrixXf N;
|
||||
|
||||
float elen = 0.0f;
|
||||
int totlen = 0;
|
||||
float scale = 0.1f;
|
||||
|
||||
std::set<uint32_t> creaseSet;
|
||||
|
||||
F.resize(3, mesh->tottri);
|
||||
V.resize(3, mesh->totvert);
|
||||
N.resize(3, mesh->totvert);
|
||||
|
||||
// F.resize(mesh->tottri * 3);
|
||||
// V.resize(mesh->totvert * 3);
|
||||
// N.resize(mesh->totvert * 3);
|
||||
|
||||
for (int i = 0; i < mesh->tottri; i++) {
|
||||
RemeshTri *tri = mesh->tris + i;
|
||||
|
||||
elen += len_v3v3(mesh->verts[tri->v1].co, mesh->verts[tri->v2].co);
|
||||
elen += len_v3v3(mesh->verts[tri->v2].co, mesh->verts[tri->v3].co);
|
||||
elen += len_v3v3(mesh->verts[tri->v3].co, mesh->verts[tri->v1].co);
|
||||
totlen += 3;
|
||||
|
||||
F(0, i) = tri->v1;
|
||||
F(1, i) = tri->v2;
|
||||
F(2, i) = tri->v3;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mesh->totvert; i++) {
|
||||
RemeshVertex *v = mesh->verts + i;
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
V(j, i) = v->co[j];
|
||||
N(j, i) = v->no[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (totlen > 0) {
|
||||
elen /= totlen;
|
||||
scale = elen * 4.0f;
|
||||
}
|
||||
|
||||
const bool extrinsic = true;
|
||||
const bool deterministic = false;
|
||||
const int rosy = 4, posy = 4;
|
||||
|
||||
printf("scale: %.5f\n", scale);
|
||||
|
||||
VectorXu V2E, E2E;
|
||||
VectorXb boundary;
|
||||
VectorXb nonManifold;
|
||||
|
||||
nonManifold.resize(V.cols());
|
||||
|
||||
build_dedge(F, V, V2E, E2E, boundary, nonManifold);
|
||||
// AdjacencyMatrix adj = generate_adjacency_matrix_cotan(F, V, V2E, E2E, nonManifold);
|
||||
AdjacencyMatrix adj = generate_adjacency_matrix_uniform(F, V2E, E2E, nonManifold);
|
||||
|
||||
VectorXf A;
|
||||
|
||||
generate_smooth_normals(F, V, V2E, E2E, nonManifold, N);
|
||||
compute_dual_vertex_areas(F, V, V2E, E2E, nonManifold, A);
|
||||
|
||||
MultiResolutionHierarchy mRes;
|
||||
|
||||
mRes.setF(std::move(F));
|
||||
mRes.setV(std::move(V));
|
||||
mRes.setN(std::move(N));
|
||||
mRes.setE2E(std::move(E2E));
|
||||
mRes.setAdj(std::move(adj));
|
||||
mRes.setA(std::move(A));
|
||||
mRes.setScale(scale);
|
||||
|
||||
printf("building multiresolution hierarchy\n");
|
||||
mRes.build(deterministic);
|
||||
mRes.resetSolution();
|
||||
|
||||
/* set up edge constraints */
|
||||
mRes.clearConstraints();
|
||||
|
||||
for (int i = 0; i < mesh->totedge; i++) {
|
||||
RemeshEdge *e = mesh->edges + i;
|
||||
|
||||
const MatrixXu &F = mRes.F();
|
||||
const MatrixXf &N = mRes.N(), &V = mRes.V();
|
||||
const VectorXu &E2E = mRes.E2E();
|
||||
|
||||
uint32_t i0 = (uint32_t)e->v1;
|
||||
uint32_t i1 = (uint32_t)e->v2;
|
||||
Vector3f p0 = V.col(i0), p1 = V.col(i1);
|
||||
|
||||
if (0 && (e->flag & REMESH_EDGE_USE_DIR)) {
|
||||
Vector3f dir(e->dir);
|
||||
|
||||
if (dir.squaredNorm() > 0) {
|
||||
mRes.CO().col(i0) = p0;
|
||||
mRes.CO().col(i1) = p1;
|
||||
mRes.CQ().col(i0) = mRes.CQ().col(i1) = dir;
|
||||
mRes.CQw()[i0] = mRes.CQw()[i1] = mRes.COw()[i0] = mRes.COw()[i1] = 0.05f;
|
||||
}
|
||||
}
|
||||
else if (e->flag & REMESH_EDGE_BOUNDARY) {
|
||||
Vector3f edge = p1 - p0;
|
||||
|
||||
creaseSet.insert((uint32_t)e->v1);
|
||||
creaseSet.insert((uint32_t)e->v2);
|
||||
|
||||
if (edge.squaredNorm() > 0) {
|
||||
edge.normalize();
|
||||
mRes.CO().col(i0) = p0;
|
||||
mRes.CO().col(i1) = p1;
|
||||
mRes.CQ().col(i0) = mRes.CQ().col(i1) = edge;
|
||||
mRes.CQw()[i0] = mRes.CQw()[i1] = mRes.COw()[i0] = mRes.COw()[i1] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mRes.propagateConstraints(rosy, posy);
|
||||
|
||||
Optimizer opt(mRes, false);
|
||||
|
||||
opt.setExtrinsic(extrinsic);
|
||||
opt.setRoSy(rosy);
|
||||
opt.setPoSy(posy);
|
||||
|
||||
printf("optimizing orientation field\n");
|
||||
for (int step = 0; step < 1; step++) {
|
||||
opt.optimizeOrientations(-1);
|
||||
opt.notify();
|
||||
opt.wait();
|
||||
}
|
||||
|
||||
std::map<uint32_t, uint32_t> sing;
|
||||
compute_orientation_singularities(mRes, sing, extrinsic, rosy);
|
||||
cout << "Orientation field has " << sing.size() << " singularities." << endl;
|
||||
|
||||
printf("optimizing position field\n");
|
||||
for (int step = 0; step < 1; step++) {
|
||||
opt.optimizePositions(-1);
|
||||
opt.notify();
|
||||
opt.wait();
|
||||
}
|
||||
|
||||
std::map<uint32_t, Vector2i> pos_sing;
|
||||
compute_position_singularities(mRes, sing, pos_sing, extrinsic, rosy, posy);
|
||||
cout << "Position field has " << pos_sing.size() << " singularities." << endl;
|
||||
|
||||
opt.shutdown();
|
||||
|
||||
std::vector<std::vector<TaggedLink>> adj_extracted;
|
||||
|
||||
MatrixXu F_extracted;
|
||||
MatrixXf V_extracted;
|
||||
MatrixXf N_extracted;
|
||||
MatrixXf Nf_extracted;
|
||||
|
||||
std::set<uint32_t> creaseOut;
|
||||
|
||||
printf("extracting mesh\n");
|
||||
|
||||
extract_graph(mRes,
|
||||
extrinsic,
|
||||
rosy,
|
||||
posy,
|
||||
adj_extracted,
|
||||
V_extracted,
|
||||
N_extracted,
|
||||
creaseSet,
|
||||
creaseOut,
|
||||
deterministic,
|
||||
true,
|
||||
true,
|
||||
true);
|
||||
|
||||
extract_faces(adj_extracted,
|
||||
V_extracted,
|
||||
N_extracted,
|
||||
Nf_extracted,
|
||||
F_extracted,
|
||||
posy,
|
||||
mRes.scale(),
|
||||
creaseOut,
|
||||
true,
|
||||
false,
|
||||
nullptr,
|
||||
0);
|
||||
|
||||
// F_extracted = mRes.F();
|
||||
// V_extracted = mRes.V();
|
||||
|
||||
mesh->totoutface = F_extracted.cols();
|
||||
mesh->totoutvert = V_extracted.cols();
|
||||
|
||||
int sides = F_extracted.rows();
|
||||
|
||||
mesh->outfaces = (RemeshOutFace *)MEM_malloc_arrayN(
|
||||
mesh->totoutface, sizeof(RemeshOutFace), "RemeshOutFaces");
|
||||
mesh->outverts = (RemeshVertex *)MEM_malloc_arrayN(
|
||||
mesh->totoutvert, sizeof(RemeshVertex), "RemeshOutVerts");
|
||||
mesh->out_scracth = (int *)MEM_malloc_arrayN(
|
||||
mesh->totoutface * sides, sizeof(int), "out_scratch");
|
||||
|
||||
printf(
|
||||
"sides:%d\ntotal faces: %d\ntotal verts: %d\n", sides, mesh->totoutface, mesh->totoutvert);
|
||||
|
||||
RemeshOutFace *f = mesh->outfaces;
|
||||
int totface = mesh->totoutface;
|
||||
int i = 0, fi = 0, li = 0;
|
||||
|
||||
/* Check for irregular faces */
|
||||
std::map<uint32_t, std::pair<uint32_t, std::map<uint32_t, uint32_t>>> irregular;
|
||||
size_t nIrregular = 0;
|
||||
|
||||
for (; fi < totface; fi++) {
|
||||
if (F_extracted(2, fi) == F_extracted(3, fi)) {
|
||||
nIrregular++;
|
||||
auto &value = irregular[F_extracted(2, fi)];
|
||||
value.first = fi;
|
||||
value.second[F_extracted(0, fi)] = F_extracted(1, fi);
|
||||
continue;
|
||||
}
|
||||
|
||||
f->verts = mesh->out_scracth + li;
|
||||
li += sides;
|
||||
|
||||
int j = 0;
|
||||
|
||||
for (j = 0; j < sides; j++) {
|
||||
f->verts[j] = F_extracted(j, fi);
|
||||
}
|
||||
|
||||
// if (j != sides) {
|
||||
// i--;
|
||||
//}
|
||||
|
||||
f->totvert = sides;
|
||||
i++;
|
||||
f++;
|
||||
}
|
||||
|
||||
for (auto item : irregular) {
|
||||
auto face = item.second;
|
||||
uint32_t v = face.second.begin()->first, first = v, k = 0;
|
||||
|
||||
int j = 0;
|
||||
f->verts = mesh->out_scracth + li;
|
||||
|
||||
while (true) {
|
||||
v = face.second[v];
|
||||
f->verts[j] = (int)v;
|
||||
|
||||
li++;
|
||||
j++;
|
||||
|
||||
if (v == first || ++k == face.second.size())
|
||||
break;
|
||||
}
|
||||
|
||||
f->totvert = j;
|
||||
|
||||
f++;
|
||||
i++;
|
||||
}
|
||||
|
||||
printf("final totface: %d, alloc totface: %d\n", i, mesh->totoutface);
|
||||
printf("final totloop: %d, alloc totloop: %d\n", li, mesh->totoutface * sides);
|
||||
|
||||
mesh->totoutface = i;
|
||||
|
||||
RemeshVertex *v = mesh->outverts;
|
||||
for (int i = 0; i < mesh->totoutvert; i++, v++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
v->co[j] = V_extracted(j, i);
|
||||
v->no[j] = N_extracted(j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void instant_meshes_finish(RemeshMesh *mesh)
|
||||
{
|
||||
/*
|
||||
if (mesh->verts) {
|
||||
MEM_freeN((void *)mesh->verts);
|
||||
}
|
||||
|
||||
if (mesh->edges) {
|
||||
MEM_freeN((void *)mesh->edges);
|
||||
}
|
||||
|
||||
if (mesh->tris) {
|
||||
MEM_freeN((void *)mesh->tris);
|
||||
}*/
|
||||
|
||||
if (mesh->outverts) {
|
||||
MEM_freeN((void *)mesh->outverts);
|
||||
}
|
||||
if (mesh->outfaces) {
|
||||
MEM_freeN((void *)mesh->outfaces);
|
||||
}
|
||||
if (mesh->out_scracth) {
|
||||
MEM_freeN((void *)mesh->out_scracth);
|
||||
}
|
||||
}
|
||||
|
||||
void instant_meshes_set_number_of_threads(int n)
|
||||
{
|
||||
instant_meshes_nprocs = n;
|
||||
}
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -15,7 +15,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "hierarchy.h"
|
||||
#include <map>
|
||||
|
||||
|
@ -29,133 +28,204 @@ extern Vector3f rotate180_by(const Vector3f &d, const Vector3f &n, int amount);
|
|||
extern Vector2i rshift60(Vector2i shift, int amount);
|
||||
extern Vector2i rshift90(Vector2i shift, int amount);
|
||||
extern Vector2i rshift180(Vector2i shift, int amount);
|
||||
extern Vector3f rotate_vector_into_plane(Vector3f q, const Vector3f &source_normal, const Vector3f &target_normal);
|
||||
extern Vector3f rotate_vector_into_plane(Vector3f q,
|
||||
const Vector3f &source_normal,
|
||||
const Vector3f &target_normal);
|
||||
|
||||
/* Extrinsic & intrinsic orientation symmetry functors */
|
||||
extern std::pair<Vector3f, Vector3f>
|
||||
compat_orientation_intrinsic_2(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_2(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f>
|
||||
compat_orientation_intrinsic_4(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_4(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f>
|
||||
compat_orientation_intrinsic_4_knoeppel(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_4_knoeppel(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f>
|
||||
compat_orientation_intrinsic_6(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_6(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f>
|
||||
compat_orientation_extrinsic_2(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<Vector3f, Vector3f> compat_orientation_extrinsic_2(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f>
|
||||
compat_orientation_extrinsic_4(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<Vector3f, Vector3f> compat_orientation_extrinsic_4(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f>
|
||||
compat_orientation_extrinsic_6(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<Vector3f, Vector3f> compat_orientation_extrinsic_6(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<int, int>
|
||||
compat_orientation_extrinsic_index_2(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<int, int> compat_orientation_extrinsic_index_2(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<int, int>
|
||||
compat_orientation_extrinsic_index_4(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<int, int> compat_orientation_extrinsic_index_4(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<int, int>
|
||||
compat_orientation_extrinsic_index_6(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<int, int> compat_orientation_extrinsic_index_6(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<int, int>
|
||||
compat_orientation_intrinsic_index_2(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<int, int> compat_orientation_intrinsic_index_2(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<int, int>
|
||||
compat_orientation_intrinsic_index_4(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<int, int> compat_orientation_intrinsic_index_4(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
extern std::pair<int, int>
|
||||
compat_orientation_intrinsic_index_6(const Vector3f &q0, const Vector3f &n0,
|
||||
const Vector3f &q1, const Vector3f &n1);
|
||||
extern std::pair<int, int> compat_orientation_intrinsic_index_6(const Vector3f &q0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &n1);
|
||||
|
||||
/* Extrinsic & intrinsic position symmetry functors */
|
||||
extern std::pair<Vector3f, Vector3f> compat_position_extrinsic_3(
|
||||
const Vector3f &p0, const Vector3f &n0, const Vector3f &q0,
|
||||
const Vector3f &o0, const Vector3f &p1, const Vector3f &n1,
|
||||
const Vector3f &q1, const Vector3f &o1, Float scale, Float inv_scale);
|
||||
extern std::pair<Vector3f, Vector3f> compat_position_extrinsic_3(const Vector3f &p0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q0,
|
||||
const Vector3f &o0,
|
||||
const Vector3f &p1,
|
||||
const Vector3f &n1,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &o1,
|
||||
Float scale,
|
||||
Float inv_scale);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f> compat_position_extrinsic_4(
|
||||
const Vector3f &p0, const Vector3f &n0, const Vector3f &q0,
|
||||
const Vector3f &o0, const Vector3f &p1, const Vector3f &n1,
|
||||
const Vector3f &q1, const Vector3f &o1, Float scale, Float inv_scale);
|
||||
extern std::pair<Vector3f, Vector3f> compat_position_extrinsic_4(const Vector3f &p0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q0,
|
||||
const Vector3f &o0,
|
||||
const Vector3f &p1,
|
||||
const Vector3f &n1,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &o1,
|
||||
Float scale,
|
||||
Float inv_scale);
|
||||
|
||||
extern std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_3(
|
||||
const Vector3f &p0, const Vector3f &n0, const Vector3f &q0,
|
||||
const Vector3f &o0, const Vector3f &p1, const Vector3f &n1,
|
||||
const Vector3f &q1, const Vector3f &o1, Float scale, Float inv_scale,
|
||||
Float *error = nullptr);
|
||||
extern std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_3(const Vector3f &p0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q0,
|
||||
const Vector3f &o0,
|
||||
const Vector3f &p1,
|
||||
const Vector3f &n1,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &o1,
|
||||
Float scale,
|
||||
Float inv_scale,
|
||||
Float *error = nullptr);
|
||||
|
||||
extern std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_4(
|
||||
const Vector3f &p0, const Vector3f &n0, const Vector3f &q0,
|
||||
const Vector3f &o0, const Vector3f &p1, const Vector3f &n1,
|
||||
const Vector3f &q1, const Vector3f &o1, Float scale, Float inv_scale,
|
||||
Float *error = nullptr);
|
||||
extern std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_4(const Vector3f &p0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q0,
|
||||
const Vector3f &o0,
|
||||
const Vector3f &p1,
|
||||
const Vector3f &n1,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &o1,
|
||||
Float scale,
|
||||
Float inv_scale,
|
||||
Float *error = nullptr);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f> compat_position_intrinsic_3(
|
||||
const Vector3f &p0, const Vector3f &n0, const Vector3f &q0,
|
||||
const Vector3f &o0, const Vector3f &p1, const Vector3f &n1,
|
||||
const Vector3f &q1, const Vector3f &o1, Float scale, Float inv_scale);
|
||||
extern std::pair<Vector3f, Vector3f> compat_position_intrinsic_3(const Vector3f &p0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q0,
|
||||
const Vector3f &o0,
|
||||
const Vector3f &p1,
|
||||
const Vector3f &n1,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &o1,
|
||||
Float scale,
|
||||
Float inv_scale);
|
||||
|
||||
extern std::pair<Vector3f, Vector3f> compat_position_intrinsic_4(
|
||||
const Vector3f &p0, const Vector3f &n0, const Vector3f &q0,
|
||||
const Vector3f &o0, const Vector3f &p1, const Vector3f &n1,
|
||||
const Vector3f &q1, const Vector3f &o1, Float scale, Float inv_scale);
|
||||
extern std::pair<Vector3f, Vector3f> compat_position_intrinsic_4(const Vector3f &p0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q0,
|
||||
const Vector3f &o0,
|
||||
const Vector3f &p1,
|
||||
const Vector3f &n1,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &o1,
|
||||
Float scale,
|
||||
Float inv_scale);
|
||||
|
||||
extern std::pair<Vector2i, Vector2i> compat_position_intrinsic_index_3(
|
||||
const Vector3f &p0, const Vector3f &n0, const Vector3f &q0,
|
||||
const Vector3f &o0, const Vector3f &p1, const Vector3f &n1,
|
||||
const Vector3f &q1, const Vector3f &o1, Float scale, Float inv_scale,
|
||||
Float *error = nullptr);
|
||||
extern std::pair<Vector2i, Vector2i> compat_position_intrinsic_index_3(const Vector3f &p0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q0,
|
||||
const Vector3f &o0,
|
||||
const Vector3f &p1,
|
||||
const Vector3f &n1,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &o1,
|
||||
Float scale,
|
||||
Float inv_scale,
|
||||
Float *error = nullptr);
|
||||
|
||||
extern std::pair<Vector2i, Vector2i> compat_position_intrinsic_index_4(
|
||||
const Vector3f &p0, const Vector3f &n0, const Vector3f &q0,
|
||||
const Vector3f &o0, const Vector3f &p1, const Vector3f &n1,
|
||||
const Vector3f &q1, const Vector3f &o1, Float scale, Float inv_scale,
|
||||
Float *error = nullptr);
|
||||
extern std::pair<Vector2i, Vector2i> compat_position_intrinsic_index_4(const Vector3f &p0,
|
||||
const Vector3f &n0,
|
||||
const Vector3f &q0,
|
||||
const Vector3f &o0,
|
||||
const Vector3f &p1,
|
||||
const Vector3f &n1,
|
||||
const Vector3f &q1,
|
||||
const Vector3f &o1,
|
||||
Float scale,
|
||||
Float inv_scale,
|
||||
Float *error = nullptr);
|
||||
|
||||
/* Optimization kernels */
|
||||
|
||||
extern Float optimize_orientations(
|
||||
MultiResolutionHierarchy &mRes, int level, bool extrinsic, int rosy,
|
||||
const std::function<void(uint32_t)> &progress);
|
||||
extern Float optimize_orientations(MultiResolutionHierarchy &mRes,
|
||||
int level,
|
||||
bool extrinsic,
|
||||
int rosy,
|
||||
const std::function<void(uint32_t)> &progress);
|
||||
|
||||
extern Float optimize_positions(
|
||||
MultiResolutionHierarchy &mRes, int level, bool extrinsic, int posy,
|
||||
const std::function<void(uint32_t)> &progress);
|
||||
extern Float optimize_positions(MultiResolutionHierarchy &mRes,
|
||||
int level,
|
||||
bool extrinsic,
|
||||
int posy,
|
||||
const std::function<void(uint32_t)> &progress);
|
||||
|
||||
/* Singularity computation */
|
||||
|
||||
extern void compute_orientation_singularities(
|
||||
const MultiResolutionHierarchy &mRes, std::map<uint32_t, uint32_t> &sing,
|
||||
bool extrinsic, int rosy);
|
||||
extern void compute_orientation_singularities(const MultiResolutionHierarchy &mRes,
|
||||
std::map<uint32_t, uint32_t> &sing,
|
||||
bool extrinsic,
|
||||
int rosy);
|
||||
|
||||
extern void
|
||||
compute_position_singularities(const MultiResolutionHierarchy &mRes,
|
||||
const std::map<uint32_t, uint32_t> &orient_sing,
|
||||
std::map<uint32_t, Vector2i> &pos_sing,
|
||||
bool extrinsic, int rosy, int posy);
|
||||
extern void compute_position_singularities(const MultiResolutionHierarchy &mRes,
|
||||
const std::map<uint32_t, uint32_t> &orient_sing,
|
||||
std::map<uint32_t, Vector2i> &pos_sing,
|
||||
bool extrinsic,
|
||||
int rosy,
|
||||
int posy);
|
||||
|
||||
/* Field optimizer (invokes optimization kernels in a separate thread) */
|
||||
|
||||
class Serializer;
|
||||
class Optimizer {
|
||||
public:
|
||||
public:
|
||||
/* clang-format off */
|
||||
|
||||
Optimizer(MultiResolutionHierarchy &mRes, bool interactive);
|
||||
void save(Serializer &state);
|
||||
void load(const Serializer &state);
|
||||
|
@ -193,30 +263,34 @@ public:
|
|||
const VectorXf &error() { return mError; }
|
||||
#endif
|
||||
|
||||
void moveSingularity(const std::vector<uint32_t> &path, bool orientations) {
|
||||
std::lock_guard<ordered_lock> lock(mRes.mutex());
|
||||
mAttractorStrokes.push_back(std::make_pair(orientations, path));
|
||||
setLevel(0);
|
||||
}
|
||||
/* clang-format on */
|
||||
|
||||
void run();
|
||||
protected:
|
||||
MultiResolutionHierarchy &mRes;
|
||||
std::vector<std::pair<bool, std::vector<uint32_t>>> mAttractorStrokes;
|
||||
bool mRunning;
|
||||
bool mOptimizeOrientations;
|
||||
bool mOptimizePositions;
|
||||
std::thread mThread;
|
||||
std::condition_variable_any mCond;
|
||||
int mLevel, mLevelIterations;
|
||||
bool mHierarchical;
|
||||
int mRoSy, mPoSy;
|
||||
bool mExtrinsic;
|
||||
bool mInteractive;
|
||||
double mLastUpdate;
|
||||
Float mProgress;
|
||||
void moveSingularity(const std::vector<uint32_t> &path, bool orientations)
|
||||
{
|
||||
std::lock_guard<ordered_lock> lock(mRes.mutex());
|
||||
mAttractorStrokes.push_back(std::make_pair(orientations, path));
|
||||
setLevel(0);
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
protected:
|
||||
MultiResolutionHierarchy &mRes;
|
||||
std::vector<std::pair<bool, std::vector<uint32_t>>> mAttractorStrokes;
|
||||
bool mRunning;
|
||||
bool mOptimizeOrientations;
|
||||
bool mOptimizePositions;
|
||||
std::thread mThread;
|
||||
std::condition_variable_any mCond;
|
||||
int mLevel, mLevelIterations;
|
||||
bool mHierarchical;
|
||||
int mRoSy, mPoSy;
|
||||
bool mExtrinsic;
|
||||
bool mInteractive;
|
||||
double mLastUpdate;
|
||||
Float mProgress;
|
||||
#ifdef VISUALIZE_ERROR
|
||||
VectorXf mError;
|
||||
VectorXf mError;
|
||||
#endif
|
||||
Timer<> mTimer;
|
||||
Timer<> mTimer;
|
||||
};
|
||||
|
|
|
@ -18,114 +18,250 @@
|
|||
|
||||
class Serializer;
|
||||
|
||||
extern AdjacencyMatrix
|
||||
downsample_graph(const AdjacencyMatrix adj, const MatrixXf &V,
|
||||
const MatrixXf &N, const VectorXf &areas, MatrixXf &V_p,
|
||||
MatrixXf &V_n, VectorXf &areas_p, MatrixXu &to_upper,
|
||||
VectorXu &to_lower, bool deterministic = false,
|
||||
const ProgressCallback &progress = ProgressCallback());
|
||||
extern AdjacencyMatrix downsample_graph(const AdjacencyMatrix adj,
|
||||
const MatrixXf &V,
|
||||
const MatrixXf &N,
|
||||
const VectorXf &areas,
|
||||
MatrixXf &V_p,
|
||||
MatrixXf &V_n,
|
||||
VectorXf &areas_p,
|
||||
MatrixXu &to_upper,
|
||||
VectorXu &to_lower,
|
||||
bool deterministic = false,
|
||||
const ProgressCallback &progress = ProgressCallback());
|
||||
|
||||
struct MultiResolutionHierarchy {
|
||||
enum { MAX_DEPTH = 25 };
|
||||
public:
|
||||
MultiResolutionHierarchy();
|
||||
void free();
|
||||
void save(Serializer &state);
|
||||
void load(const Serializer &state);
|
||||
enum { MAX_DEPTH = 25 };
|
||||
|
||||
int levels() const { return (int) mV.size(); }
|
||||
public:
|
||||
MultiResolutionHierarchy();
|
||||
void free();
|
||||
void save(Serializer &state);
|
||||
void load(const Serializer &state);
|
||||
|
||||
void build(bool deterministic = false,
|
||||
const ProgressCallback &progress = ProgressCallback());
|
||||
int levels() const
|
||||
{
|
||||
return (int)mV.size();
|
||||
}
|
||||
|
||||
void printStatistics() const;
|
||||
void resetSolution();
|
||||
void build(bool deterministic = false, const ProgressCallback &progress = ProgressCallback());
|
||||
|
||||
inline ordered_lock &mutex() { return mMutex; }
|
||||
void printStatistics() const;
|
||||
void resetSolution();
|
||||
|
||||
inline const std::vector<std::vector<uint32_t>> &phases(int level) const { return mPhases[level]; }
|
||||
inline const AdjacencyMatrix &adj(int level = 0) const { return mAdj[level]; }
|
||||
inline AdjacencyMatrix &adj(int level = 0) { return mAdj[level]; }
|
||||
inline const MatrixXf &V(int level = 0) const { return mV[level]; }
|
||||
inline const MatrixXf &N(int level = 0) const { return mN[level]; }
|
||||
inline const VectorXf &A(int level = 0) const { return mA[level]; }
|
||||
inline const MatrixXu &toUpper(int level) const { return mToUpper[level]; }
|
||||
inline const VectorXu &toLower(int level) const { return mToLower[level]; }
|
||||
inline const MatrixXf &Q(int level = 0) const { return mQ[level]; }
|
||||
inline const MatrixXf &O(int level = 0) const { return mO[level]; }
|
||||
inline const MatrixXf &CQ(int level = 0) const { return mCQ[level]; }
|
||||
inline const MatrixXf &CO(int level = 0) const { return mCO[level]; }
|
||||
inline const VectorXf &CQw(int level = 0) const { return mCQw[level]; }
|
||||
inline const VectorXf &COw(int level = 0) const { return mCOw[level]; }
|
||||
inline const MatrixXu &F() const { return mF; }
|
||||
inline const VectorXu &E2E() const { return mE2E; }
|
||||
inline MatrixXf &Q(int level = 0) { return mQ[level]; }
|
||||
inline MatrixXf &O(int level = 0) { return mO[level]; }
|
||||
inline MatrixXf &CQ(int level = 0) { return mCQ[level]; }
|
||||
inline MatrixXf &CO(int level = 0) { return mCO[level]; }
|
||||
inline VectorXf &CQw(int level = 0) { return mCQw[level]; }
|
||||
inline VectorXf &COw(int level = 0) { return mCOw[level]; }
|
||||
inline ordered_lock &mutex()
|
||||
{
|
||||
return mMutex;
|
||||
}
|
||||
|
||||
inline void setF(MatrixXu &&F) { mF = std::move(F); }
|
||||
inline void setE2E(VectorXu &&E2E) { mE2E = std::move(E2E); }
|
||||
inline void setV(MatrixXf &&V) { mV.clear(); mV.push_back(std::move(V)); }
|
||||
inline void setN(MatrixXf &&N) { mN.clear(); mN.push_back(std::move(N)); }
|
||||
inline void setA(MatrixXf &&A) { mA.clear(); mA.push_back(std::move(A)); }
|
||||
inline void setAdj(AdjacencyMatrix &&adj) { mAdj.clear(); mAdj.push_back(std::move(adj)); }
|
||||
inline const std::vector<std::vector<uint32_t>> &phases(int level) const
|
||||
{
|
||||
return mPhases[level];
|
||||
}
|
||||
inline const AdjacencyMatrix &adj(int level = 0) const
|
||||
{
|
||||
return mAdj[level];
|
||||
}
|
||||
inline AdjacencyMatrix &adj(int level = 0)
|
||||
{
|
||||
return mAdj[level];
|
||||
}
|
||||
inline const MatrixXf &V(int level = 0) const
|
||||
{
|
||||
return mV[level];
|
||||
}
|
||||
inline const MatrixXf &N(int level = 0) const
|
||||
{
|
||||
return mN[level];
|
||||
}
|
||||
inline const VectorXf &A(int level = 0) const
|
||||
{
|
||||
return mA[level];
|
||||
}
|
||||
inline const MatrixXu &toUpper(int level) const
|
||||
{
|
||||
return mToUpper[level];
|
||||
}
|
||||
inline const VectorXu &toLower(int level) const
|
||||
{
|
||||
return mToLower[level];
|
||||
}
|
||||
inline const MatrixXf &Q(int level = 0) const
|
||||
{
|
||||
return mQ[level];
|
||||
}
|
||||
inline const MatrixXf &O(int level = 0) const
|
||||
{
|
||||
return mO[level];
|
||||
}
|
||||
inline const MatrixXf &CQ(int level = 0) const
|
||||
{
|
||||
return mCQ[level];
|
||||
}
|
||||
inline const MatrixXf &CO(int level = 0) const
|
||||
{
|
||||
return mCO[level];
|
||||
}
|
||||
inline const VectorXf &CQw(int level = 0) const
|
||||
{
|
||||
return mCQw[level];
|
||||
}
|
||||
inline const VectorXf &COw(int level = 0) const
|
||||
{
|
||||
return mCOw[level];
|
||||
}
|
||||
inline const MatrixXu &F() const
|
||||
{
|
||||
return mF;
|
||||
}
|
||||
inline const VectorXu &E2E() const
|
||||
{
|
||||
return mE2E;
|
||||
}
|
||||
inline MatrixXf &Q(int level = 0)
|
||||
{
|
||||
return mQ[level];
|
||||
}
|
||||
inline MatrixXf &O(int level = 0)
|
||||
{
|
||||
return mO[level];
|
||||
}
|
||||
inline MatrixXf &CQ(int level = 0)
|
||||
{
|
||||
return mCQ[level];
|
||||
}
|
||||
inline MatrixXf &CO(int level = 0)
|
||||
{
|
||||
return mCO[level];
|
||||
}
|
||||
inline VectorXf &CQw(int level = 0)
|
||||
{
|
||||
return mCQw[level];
|
||||
}
|
||||
inline VectorXf &COw(int level = 0)
|
||||
{
|
||||
return mCOw[level];
|
||||
}
|
||||
|
||||
inline uint32_t size(int level = 0) const { return mV[level].cols(); }
|
||||
inline void setF(MatrixXu &&F)
|
||||
{
|
||||
mF = std::move(F);
|
||||
}
|
||||
inline void setE2E(VectorXu &&E2E)
|
||||
{
|
||||
mE2E = std::move(E2E);
|
||||
}
|
||||
inline void setV(MatrixXf &&V)
|
||||
{
|
||||
mV.clear();
|
||||
mV.push_back(std::move(V));
|
||||
}
|
||||
inline void setN(MatrixXf &&N)
|
||||
{
|
||||
mN.clear();
|
||||
mN.push_back(std::move(N));
|
||||
}
|
||||
inline void setA(MatrixXf &&A)
|
||||
{
|
||||
mA.clear();
|
||||
mA.push_back(std::move(A));
|
||||
}
|
||||
inline void setAdj(AdjacencyMatrix &&adj)
|
||||
{
|
||||
mAdj.clear();
|
||||
mAdj.push_back(std::move(adj));
|
||||
}
|
||||
|
||||
inline Float scale() const { return mScale; }
|
||||
inline void setScale(Float scale) { mScale = scale; }
|
||||
inline int iterationsQ() const { return mIterationsQ; }
|
||||
inline void setIterationsQ(int iterationsQ) { mIterationsQ = iterationsQ; }
|
||||
inline int iterationsO() const { return mIterationsO; }
|
||||
inline void setIterationsO(int iterationsO) { mIterationsO = iterationsO; }
|
||||
inline size_t totalSize() const { return mTotalSize; }
|
||||
inline uint32_t size(int level = 0) const
|
||||
{
|
||||
return mV[level].cols();
|
||||
}
|
||||
|
||||
void clearConstraints();
|
||||
void propagateConstraints(int rosy, int posy);
|
||||
void propagateSolution(int rosy);
|
||||
inline Float scale() const
|
||||
{
|
||||
return mScale;
|
||||
}
|
||||
inline void setScale(Float scale)
|
||||
{
|
||||
mScale = scale;
|
||||
}
|
||||
inline int iterationsQ() const
|
||||
{
|
||||
return mIterationsQ;
|
||||
}
|
||||
inline void setIterationsQ(int iterationsQ)
|
||||
{
|
||||
mIterationsQ = iterationsQ;
|
||||
}
|
||||
inline int iterationsO() const
|
||||
{
|
||||
return mIterationsO;
|
||||
}
|
||||
inline void setIterationsO(int iterationsO)
|
||||
{
|
||||
mIterationsO = iterationsO;
|
||||
}
|
||||
inline size_t totalSize() const
|
||||
{
|
||||
return mTotalSize;
|
||||
}
|
||||
|
||||
inline Vector3f faceCenter(uint32_t idx) const {
|
||||
Vector3f pos = Vector3f::Zero();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
pos += mV[0].col(mF(i, idx));
|
||||
return pos * (1.0f / 3.0f);
|
||||
}
|
||||
void clearConstraints();
|
||||
void propagateConstraints(int rosy, int posy);
|
||||
void propagateSolution(int rosy);
|
||||
|
||||
inline Vector3f faceNormal(uint32_t idx) const {
|
||||
Vector3f p0 = mV[0].col(mF(0, idx)),
|
||||
p1 = mV[0].col(mF(1, idx)),
|
||||
p2 = mV[0].col(mF(2, idx));
|
||||
return (p1-p0).cross(p2-p0).normalized();
|
||||
}
|
||||
inline Vector3f faceCenter(uint32_t idx) const
|
||||
{
|
||||
Vector3f pos = Vector3f::Zero();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
pos += mV[0].col(mF(i, idx));
|
||||
return pos * (1.0f / 3.0f);
|
||||
}
|
||||
|
||||
/* Flags which indicate whether the integer variables are froen */
|
||||
bool frozenQ() const { return mFrozenQ; }
|
||||
bool frozenO() const { return mFrozenO; }
|
||||
void setFrozenQ(bool frozen) { mFrozenQ = frozen; }
|
||||
void setFrozenO(bool frozen) { mFrozenO = frozen; }
|
||||
public:
|
||||
MatrixXu mF;
|
||||
VectorXu mE2E;
|
||||
std::vector<std::vector<std::vector<uint32_t>>> mPhases;
|
||||
std::vector<AdjacencyMatrix> mAdj;
|
||||
std::vector<MatrixXf> mV;
|
||||
std::vector<MatrixXf> mN;
|
||||
std::vector<VectorXf> mA;
|
||||
std::vector<VectorXu> mToLower;
|
||||
std::vector<MatrixXu> mToUpper;
|
||||
std::vector<MatrixXf> mO;
|
||||
std::vector<MatrixXf> mQ;
|
||||
std::vector<MatrixXf> mCQ;
|
||||
std::vector<MatrixXf> mCO;
|
||||
std::vector<VectorXf> mCQw;
|
||||
std::vector<VectorXf> mCOw;
|
||||
bool mFrozenQ, mFrozenO;
|
||||
ordered_lock mMutex;
|
||||
Float mScale;
|
||||
int mIterationsQ;
|
||||
int mIterationsO;
|
||||
uint32_t mTotalSize;
|
||||
inline Vector3f faceNormal(uint32_t idx) const
|
||||
{
|
||||
Vector3f p0 = mV[0].col(mF(0, idx)), p1 = mV[0].col(mF(1, idx)), p2 = mV[0].col(mF(2, idx));
|
||||
return (p1 - p0).cross(p2 - p0).normalized();
|
||||
}
|
||||
|
||||
/* Flags which indicate whether the integer variables are froen */
|
||||
bool frozenQ() const
|
||||
{
|
||||
return mFrozenQ;
|
||||
}
|
||||
bool frozenO() const
|
||||
{
|
||||
return mFrozenO;
|
||||
}
|
||||
void setFrozenQ(bool frozen)
|
||||
{
|
||||
mFrozenQ = frozen;
|
||||
}
|
||||
void setFrozenO(bool frozen)
|
||||
{
|
||||
mFrozenO = frozen;
|
||||
}
|
||||
|
||||
public:
|
||||
MatrixXu mF;
|
||||
VectorXu mE2E;
|
||||
std::vector<std::vector<std::vector<uint32_t>>> mPhases;
|
||||
std::vector<AdjacencyMatrix> mAdj;
|
||||
std::vector<MatrixXf> mV;
|
||||
std::vector<MatrixXf> mN;
|
||||
std::vector<VectorXf> mA;
|
||||
std::vector<VectorXu> mToLower;
|
||||
std::vector<MatrixXu> mToUpper;
|
||||
std::vector<MatrixXf> mO;
|
||||
std::vector<MatrixXf> mQ;
|
||||
std::vector<MatrixXf> mCQ;
|
||||
std::vector<MatrixXf> mCO;
|
||||
std::vector<VectorXf> mCQw;
|
||||
std::vector<VectorXf> mCOw;
|
||||
bool mFrozenQ, mFrozenO;
|
||||
ordered_lock mMutex;
|
||||
Float mScale;
|
||||
int mIterationsQ;
|
||||
int mIterationsO;
|
||||
uint32_t mTotalSize;
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -43,12 +43,19 @@ struct Mesh *BKE_mesh_remesh_quadriflow(const struct Mesh *mesh,
|
|||
void (*update_cb)(void *, float progress, int *cancel),
|
||||
void *update_cb_data);
|
||||
|
||||
struct Mesh *BKE_mesh_remesh_instant_meshes(const Mesh *input_mesh,
|
||||
int target_faces,
|
||||
void (*update_cb)(void *, float progress, int *cancel),
|
||||
void *update_cb_data);
|
||||
|
||||
/* Data reprojection functions */
|
||||
void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source);
|
||||
void BKE_remesh_reproject_vertex_paint(struct Mesh *target, const struct Mesh *source);
|
||||
void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, struct Mesh *source);
|
||||
void BKE_remesh_reproject_materials(struct Mesh *target, struct Mesh *source);
|
||||
void BKE_mesh_remesh_sculpt_array_update(struct Object *ob, struct Mesh *target, struct Mesh *source);
|
||||
void BKE_mesh_remesh_sculpt_array_update(struct Object *ob,
|
||||
struct Mesh *target,
|
||||
struct Mesh *source);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -760,6 +760,16 @@ if(WITH_OPENVDB)
|
|||
add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
|
||||
endif()
|
||||
|
||||
if(WITH_INSTANT_MESHES)
|
||||
list(APPEND INC
|
||||
../../../intern/instant-meshes
|
||||
)
|
||||
list(APPEND LIB
|
||||
bf_intern_instant_meshes
|
||||
)
|
||||
add_definitions(-DWITH_QUADRIFLOW)
|
||||
endif()
|
||||
|
||||
if(WITH_QUADRIFLOW)
|
||||
list(APPEND INC
|
||||
../../../intern/quadriflow
|
||||
|
|
|
@ -2496,6 +2496,11 @@ void BKE_brush_color_set(struct Scene *scene,
|
|||
|
||||
ch = BRUSHSET_LOOKUP(brush->channels, color);
|
||||
|
||||
if (!ch) {
|
||||
BKE_brush_channelset_add_builtin(brush->channels, "color");
|
||||
ch = BRUSHSET_LOOKUP(brush->channels, color);
|
||||
}
|
||||
|
||||
if ((ch->flag & BRUSH_CHANNEL_INHERIT) && scene->toolsettings->sculpt &&
|
||||
scene->toolsettings->sculpt->channels) {
|
||||
BrushChannel *pch = BRUSHSET_LOOKUP(scene->toolsettings->sculpt->channels, color);
|
||||
|
|
|
@ -1825,6 +1825,8 @@ void BKE_brush_builtin_create(Brush *brush, int tool)
|
|||
GETCH(dyntopo_disabled)->ivalue = 1;
|
||||
break;
|
||||
case SCULPT_TOOL_SNAKE_HOOK:
|
||||
GETCH(strength)->mappings[BRUSH_MAPPING_PRESSURE].flag &= ~BRUSH_MAPPING_ENABLED;
|
||||
|
||||
GETCH(dyntopo_mode)->ivalue = DYNTOPO_LOCAL_COLLAPSE | DYNTOPO_SUBDIVIDE;
|
||||
GETCH(dyntopo_mode)->flag = BRUSH_CHANNEL_INHERIT_IF_UNSET;
|
||||
GETCH(strength)->fvalue = 1.0f;
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
|
@ -34,6 +38,7 @@
|
|||
#include "BLI_float3.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
@ -61,6 +66,8 @@
|
|||
# include "quadriflow_capi.hpp"
|
||||
#endif
|
||||
|
||||
#include "instant_meshes_c_api.h"
|
||||
|
||||
using blender::Array;
|
||||
using blender::float3;
|
||||
using blender::IndexRange;
|
||||
|
@ -230,6 +237,259 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh,
|
|||
}
|
||||
#endif
|
||||
|
||||
struct HashEdge {
|
||||
std::size_t operator()(std::tuple<int, int> const &edge) const noexcept
|
||||
{
|
||||
int v1 = std::get<0>(edge);
|
||||
int v2 = std::get<1>(edge);
|
||||
|
||||
if (v1 > v2) {
|
||||
std::swap(v1, v2);
|
||||
}
|
||||
|
||||
return std::size_t(v1);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: move from sculpt_smooth.c to blenlib or somewhere
|
||||
extern "C" int closest_vec_to_perp(
|
||||
float dir[3], float r_dir2[3], float no[3], float *buckets, float w);
|
||||
|
||||
ATTR_NO_OPT Mesh *BKE_mesh_remesh_instant_meshes(const Mesh *input_mesh,
|
||||
int target_faces,
|
||||
void (*update_cb)(void *,
|
||||
float progress,
|
||||
int *cancel),
|
||||
void *update_cb_data)
|
||||
{
|
||||
/* Ensure that the triangulated mesh data is up to data */
|
||||
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh);
|
||||
MeshElemMap *epmap = nullptr;
|
||||
int *epmem = nullptr;
|
||||
|
||||
instant_meshes_set_number_of_threads(BLI_system_thread_count());
|
||||
|
||||
BKE_mesh_edge_poly_map_create(&epmap,
|
||||
&epmem,
|
||||
input_mesh->medge,
|
||||
input_mesh->totedge,
|
||||
input_mesh->mpoly,
|
||||
input_mesh->totpoly,
|
||||
input_mesh->mloop,
|
||||
input_mesh->totloop);
|
||||
|
||||
/* Gather the required data for export to the internal quadriflow mesh format. */
|
||||
MVertTri *verttri = (MVertTri *)MEM_callocN(
|
||||
sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh), "remesh_looptri");
|
||||
BKE_mesh_runtime_verttri_from_looptri(
|
||||
verttri, input_mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(input_mesh));
|
||||
|
||||
MPropCol *dirs = NULL;
|
||||
|
||||
int idx = CustomData_get_named_layer_index(&input_mesh->vdata, CD_PROP_COLOR, "_rake_temp");
|
||||
if (idx >= 0) {
|
||||
dirs = (MPropCol *)input_mesh->vdata.layers[idx].data;
|
||||
}
|
||||
|
||||
const int totfaces = BKE_mesh_runtime_looptri_len(input_mesh);
|
||||
const int totverts = input_mesh->totvert;
|
||||
|
||||
std::unordered_map<std::tuple<int, int>, int, HashEdge> eflags;
|
||||
auto make_edge_pair = [](int v1, int v2) {
|
||||
return std::tuple<int, int>(std::min(v1, v2), std::max(v1, v2));
|
||||
};
|
||||
|
||||
Array<RemeshVertex> verts(totverts);
|
||||
Array<RemeshTri> faces(totfaces);
|
||||
std::vector<RemeshEdge> edges;
|
||||
|
||||
for (int i : IndexRange(totverts)) {
|
||||
copy_v3_v3(verts[i].co, input_mesh->mvert[i].co);
|
||||
normal_short_to_float_v3(verts[i].no, input_mesh->mvert[i].no);
|
||||
}
|
||||
|
||||
int *fsets = (int *)CustomData_get_layer(&input_mesh->pdata, CD_SCULPT_FACE_SETS);
|
||||
|
||||
for (const int i : IndexRange(input_mesh->totedge)) {
|
||||
MEdge *me = input_mesh->medge + i;
|
||||
MeshElemMap *mep = epmap + i;
|
||||
|
||||
bool ok = mep->count == 1;
|
||||
ok = ok || (me->flag & (ME_SEAM | ME_SHARP));
|
||||
|
||||
if (fsets && !ok) {
|
||||
int last_fset;
|
||||
|
||||
// try face sets
|
||||
for (int j = 0; j < mep->count; j++) {
|
||||
int fset = abs(fsets[mep->indices[j]]);
|
||||
|
||||
if (j > 0 && last_fset != fset) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
|
||||
last_fset = fset;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok || dirs) {
|
||||
RemeshEdge e;
|
||||
|
||||
e.flag = ok ? REMESH_EDGE_BOUNDARY : 0;
|
||||
e.v1 = me->v1;
|
||||
e.v2 = me->v2;
|
||||
|
||||
if (dirs && !ok) {
|
||||
e.flag |= REMESH_EDGE_USE_DIR;
|
||||
float d1[3], d2[3], vec[3], no1[3], no2[3];
|
||||
|
||||
// get rake directions from verts
|
||||
copy_v3_v3(d1, dirs[me->v1].color);
|
||||
copy_v3_v3(d2, dirs[me->v2].color);
|
||||
|
||||
// edge vec
|
||||
sub_v3_v3v3(vec, input_mesh->mvert[me->v2].co, input_mesh->mvert[me->v1].co);
|
||||
normalize_v3(vec);
|
||||
|
||||
// build edge normal
|
||||
normal_short_to_float_v3(no1, input_mesh->mvert[me->v1].no);
|
||||
normal_short_to_float_v3(no2, input_mesh->mvert[me->v2].no);
|
||||
|
||||
add_v3_v3(no1, no2);
|
||||
normalize_v3(no1);
|
||||
|
||||
float buckets[8];
|
||||
|
||||
// find closest of four 90 degree rotations to vec for d1, d2
|
||||
closest_vec_to_perp(vec, d1, no1, buckets, 1.0f);
|
||||
closest_vec_to_perp(vec, d2, no1, buckets, 1.0f);
|
||||
|
||||
// build final direction
|
||||
add_v3_v3(d1, d2);
|
||||
normalize_v3(d1);
|
||||
|
||||
copy_v3_v3(e.dir, d1);
|
||||
}
|
||||
|
||||
eflags[make_edge_pair(me->v1, me->v2)] = i;
|
||||
edges.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i : IndexRange(totfaces)) {
|
||||
MVertTri *mtri = verttri + i;
|
||||
|
||||
faces[i].v1 = mtri->tri[0];
|
||||
faces[i].v2 = mtri->tri[1];
|
||||
faces[i].v3 = mtri->tri[2];
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int v1 = mtri->tri[j];
|
||||
int v2 = mtri->tri[(j + 1) % 3];
|
||||
|
||||
auto item = eflags.find(make_edge_pair(v1, v2));
|
||||
if (item != eflags.end()) {
|
||||
int flag = item->second;
|
||||
faces[i].eflags[j] = flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RemeshMesh remesh;
|
||||
remesh.tris = faces.data();
|
||||
remesh.tottri = (int)faces.size();
|
||||
|
||||
remesh.verts = verts.data();
|
||||
remesh.edges = edges.data();
|
||||
remesh.totedge = (int)edges.size();
|
||||
remesh.totvert = (int)verts.size();
|
||||
|
||||
instant_meshes_run(&remesh);
|
||||
|
||||
int totloop = 0;
|
||||
for (int i : IndexRange(remesh.totoutface)) {
|
||||
totloop += remesh.outfaces[i].totvert;
|
||||
}
|
||||
|
||||
Mesh *mesh = BKE_mesh_new_nomain(remesh.totoutvert, 0, 0, totloop, remesh.totoutface);
|
||||
|
||||
for (int i : IndexRange(remesh.totoutvert)) {
|
||||
MVert *mv = mesh->mvert + i;
|
||||
RemeshVertex *v = remesh.outverts + i;
|
||||
|
||||
copy_v3_v3(mv->co, v->co);
|
||||
normal_float_to_short_v3(mv->no, v->no);
|
||||
}
|
||||
|
||||
int li = 0;
|
||||
MLoop *ml = mesh->mloop;
|
||||
|
||||
for (int i : IndexRange(remesh.totoutface)) {
|
||||
RemeshOutFace *f = remesh.outfaces + i;
|
||||
MPoly *mp = mesh->mpoly + i;
|
||||
|
||||
mp->loopstart = li;
|
||||
mp->totloop = f->totvert;
|
||||
|
||||
for (int j = 0; j < f->totvert; j++, ml++, li++) {
|
||||
ml->v = f->verts[j];
|
||||
}
|
||||
}
|
||||
|
||||
instant_meshes_finish(&remesh);
|
||||
|
||||
BKE_mesh_calc_edges(mesh, false, false);
|
||||
BKE_mesh_calc_normals(mesh);
|
||||
|
||||
// C++ doesn't seem to like the MEM_SAFE_FREE macro
|
||||
if (epmap) {
|
||||
MEM_freeN((void *)epmap);
|
||||
}
|
||||
|
||||
if (epmem) {
|
||||
MEM_freeN((void *)epmem);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
#if 0
|
||||
|
||||
/* Construct the new output mesh */
|
||||
Mesh *mesh = BKE_mesh_new_nomain(qrd.out_totverts, 0, 0, qrd.out_totfaces * 4, qrd.out_totfaces);
|
||||
|
||||
for (const int i : IndexRange(qrd.out_totverts)) {
|
||||
copy_v3_v3(mesh->mvert[i].co, &qrd.out_verts[i * 3]);
|
||||
}
|
||||
|
||||
for (const int i : IndexRange(qrd.out_totfaces)) {
|
||||
MPoly &poly = mesh->mpoly[i];
|
||||
const int loopstart = i * 4;
|
||||
poly.loopstart = loopstart;
|
||||
poly.totloop = 4;
|
||||
mesh->mloop[loopstart].v = qrd.out_faces[loopstart];
|
||||
mesh->mloop[loopstart + 1].v = qrd.out_faces[loopstart + 1];
|
||||
mesh->mloop[loopstart + 2].v = qrd.out_faces[loopstart + 2];
|
||||
mesh->mloop[loopstart + 3].v = qrd.out_faces[loopstart + 3];
|
||||
}
|
||||
|
||||
BKE_mesh_calc_edges(mesh, false, false);
|
||||
BKE_mesh_calc_normals(mesh);
|
||||
|
||||
MEM_freeN(qrd.out_faces);
|
||||
MEM_freeN(qrd.out_verts);
|
||||
|
||||
if (epmap) {
|
||||
MEM_freeN((void *)epmap);
|
||||
}
|
||||
|
||||
if (epmem) {
|
||||
MEM_freeN((void *)epmem);
|
||||
}
|
||||
return mesh;
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mesh *BKE_mesh_remesh_quadriflow(const Mesh *mesh,
|
||||
int target_faces,
|
||||
int seed,
|
||||
|
|
|
@ -315,6 +315,7 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot);
|
|||
void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_voxel_size_edit(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot);
|
||||
void OBJECT_OT_instant_meshes_remesh(struct wmOperatorType *ot);
|
||||
|
||||
/* object_transfer_data.c */
|
||||
void OBJECT_OT_data_transfer(struct wmOperatorType *ot);
|
||||
|
|
|
@ -285,6 +285,7 @@ void ED_operatortypes_object(void)
|
|||
WM_operatortype_append(OBJECT_OT_voxel_size_edit);
|
||||
|
||||
WM_operatortype_append(OBJECT_OT_quadriflow_remesh);
|
||||
WM_operatortype_append(OBJECT_OT_instant_meshes_remesh);
|
||||
}
|
||||
|
||||
void ED_operatormacros_object(void)
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "BLI_math.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
@ -801,8 +802,8 @@ static Mesh *remesh_symmetry_bisect(Object *ob, Mesh *mesh, eSymmetryAxes symmet
|
|||
plane_no[axis] = -1.0f;
|
||||
mesh_bisect_temp = mesh_bisect;
|
||||
|
||||
mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(ob,
|
||||
&mmd, mesh_bisect, axis, plane_co, plane_no);
|
||||
mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(
|
||||
ob, &mmd, mesh_bisect, axis, plane_co, plane_no);
|
||||
|
||||
if (mesh_bisect_temp != mesh_bisect) {
|
||||
BKE_id_free(nullptr, mesh_bisect_temp);
|
||||
|
@ -1231,4 +1232,84 @@ void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot)
|
|||
255);
|
||||
}
|
||||
|
||||
static void instant_mesh_update_cb(void *data, float progress, int *cancel)
|
||||
{
|
||||
}
|
||||
static int instant_meshes_remesh_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Mesh *mesh = BKE_object_get_original_mesh(ob);
|
||||
|
||||
/* Run Quadriflow bisect operations on a copy of the mesh to keep the code readable without
|
||||
* freeing the original ID */
|
||||
Mesh *bisect_mesh = BKE_mesh_copy_for_eval(mesh, false);
|
||||
|
||||
/* Bisect the input mesh using the paint symmetry settings */
|
||||
bisect_mesh = remesh_symmetry_bisect(
|
||||
ob, bisect_mesh, (eSymmetryAxes)0); //(eSymmetryAxes)mesh->symmetry);
|
||||
|
||||
int target_faces = RNA_int_get(op->ptr, "target_faces");
|
||||
|
||||
Mesh *new_mesh = BKE_mesh_remesh_instant_meshes(
|
||||
bisect_mesh, target_faces, instant_mesh_update_cb, nullptr);
|
||||
|
||||
if (!new_mesh) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Remesher failed to create mesh");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_VOLUME || mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK ||
|
||||
mesh->flag & ME_REMESH_REPROJECT_SCULPT_FACE_SETS ||
|
||||
mesh->flag & ME_REMESH_REPROJECT_MATERIALS) {
|
||||
BKE_mesh_runtime_clear_geometry(mesh);
|
||||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) {
|
||||
BKE_shrinkwrap_remesh_target_project(new_mesh, mesh, ob);
|
||||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) {
|
||||
BKE_mesh_remesh_reproject_paint_mask(new_mesh, mesh);
|
||||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_SCULPT_FACE_SETS) {
|
||||
BKE_remesh_reproject_sculpt_face_sets(new_mesh, mesh);
|
||||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_MATERIALS) {
|
||||
BKE_remesh_reproject_materials(new_mesh, mesh);
|
||||
}
|
||||
|
||||
if (mesh->flag & ME_REMESH_REPROJECT_VERTEX_COLORS) {
|
||||
BKE_mesh_runtime_clear_geometry(mesh);
|
||||
BKE_remesh_reproject_vertex_paint(new_mesh, mesh);
|
||||
}
|
||||
|
||||
BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
|
||||
BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(ob->data), BKE_MESH_BATCH_DIRTY_ALL);
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void OBJECT_OT_instant_meshes_remesh(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Instant Meshes Remesh";
|
||||
ot->description =
|
||||
"Create a new quad based mesh using the surface data of the current mesh. All data "
|
||||
"layers will be lost";
|
||||
ot->idname = "OBJECT_OT_instant_meshes_remesh";
|
||||
|
||||
/* api callbacks */
|
||||
ot->poll = object_remesh_poll;
|
||||
ot->exec = instant_meshes_remesh_exec;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
PropertyRNA *prop;
|
||||
prop = RNA_def_int(ot->srna, "target_faces", 5000, 8, 1 << 23, "Face Count", "", 8, 100000);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -369,16 +369,20 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, SculptVertRef index)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
const float *SCULPT_vertex_color_get(SculptSession *ss, SculptVertRef index)
|
||||
const float *SCULPT_vertex_color_get(SculptSession *ss, SculptVertRef vertex)
|
||||
{
|
||||
if (vertex.i == SCULPT_REF_NONE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (BKE_pbvh_type(ss->pbvh)) {
|
||||
case PBVH_FACES:
|
||||
if (ss->vcol) {
|
||||
return ss->vcol[index.i].color;
|
||||
return ss->vcol[vertex.i].color;
|
||||
}
|
||||
break;
|
||||
case PBVH_BMESH: {
|
||||
BMVert *v = (BMVert *)index.i;
|
||||
BMVert *v = (BMVert *)vertex.i;
|
||||
|
||||
if (ss->cd_vcol_offset >= 0) {
|
||||
MPropCol *col = BM_ELEM_CD_GET_VOID_P(v, ss->cd_vcol_offset);
|
||||
|
@ -962,7 +966,7 @@ SculptVertRef SCULPT_active_vertex_get(SculptSession *ss)
|
|||
if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) {
|
||||
return ss->active_vertex_index;
|
||||
}
|
||||
return BKE_pbvh_make_vref(0);
|
||||
return BKE_pbvh_make_vref(SCULPT_REF_NONE);
|
||||
}
|
||||
|
||||
const float *SCULPT_active_vertex_co_get(SculptSession *ss)
|
||||
|
@ -8735,8 +8739,11 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene,
|
|||
if (ob->sculpt != NULL) {
|
||||
BKE_sculptsession_free(ob);
|
||||
}
|
||||
|
||||
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
|
||||
ob->sculpt->mode_type = OB_MODE_SCULPT;
|
||||
ob->sculpt->active_face_index.i = SCULPT_REF_NONE;
|
||||
ob->sculpt->active_vertex_index.i = SCULPT_REF_NONE;
|
||||
|
||||
BKE_sculpt_ensure_orig_mesh_data(scene, ob);
|
||||
|
||||
|
|
Loading…
Reference in New Issue