Skip to content

Commit

Permalink
Merge branch 'master' into zeno2-fixbug
Browse files Browse the repository at this point in the history
  • Loading branch information
legobadman committed Nov 2, 2023
2 parents 24d57c2 + 53645cd commit fbcd578
Show file tree
Hide file tree
Showing 37 changed files with 2,508 additions and 771 deletions.
93 changes: 80 additions & 13 deletions projects/CUDA/remesh/SurfaceMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,23 @@ bool SurfaceMesh::is_flip_ok(int e) const {
if (find_halfedge(v0, v1) != PMP_MAX_INDEX)
return false;

// check about normals for the flip
// if the normals change more than a given threshold angle,
// it is very likely to be topologically incorrect

auto& pos = prim_->attr<vec3f>("pos");
int old_v0 = to_vertex(h0);
int old_v1 = to_vertex(h1);

vec3f old_n0 = normalize(cross(pos[old_v0] - pos[v0], pos[old_v1] - pos[v0]));
vec3f old_n1 = normalize(cross(pos[old_v1] - pos[v1], pos[old_v0] - pos[v1]));
vec3f new_n0 = normalize(cross(pos[v1] - pos[old_v0], pos[v0] - pos[old_v0]));
vec3f new_n1 = normalize(cross(pos[v0] - pos[old_v1], pos[v1] - pos[old_v1]));
if(acos(clamp(dot(old_n0, new_n0), -1, 1)) > angle_thrd) return false;
if(acos(clamp(dot(old_n0, new_n1), -1, 1)) > angle_thrd) return false;
if(acos(clamp(dot(old_n1, new_n0), -1, 1)) > angle_thrd) return false;
if(acos(clamp(dot(old_n1, new_n1), -1, 1)) > angle_thrd) return false;

return true;
}

Expand Down Expand Up @@ -472,7 +489,7 @@ void SurfaceMesh::flip(int e) {
vconn_[vb0].halfedge_ = b1;
}

bool SurfaceMesh::is_collapse_ok(int v0v1) {
void SurfaceMesh::is_collapse_ok(int v0v1, bool &hcol01, bool &hcol10) {
int v1v0 = v0v1 ^ 1;
int v0 = to_vertex(v1v0);
int v1 = to_vertex(v0v1);
Expand All @@ -485,8 +502,10 @@ bool SurfaceMesh::is_collapse_ok(int v0v1) {
h1 = next_halfedge(v0v1);
h2 = next_halfedge(h1);
if (hconn_[h1^1].face_ == PMP_MAX_INDEX &&
hconn_[h2^1].face_ == PMP_MAX_INDEX)
return false;
hconn_[h2^1].face_ == PMP_MAX_INDEX) {
hcol01 = hcol10 = false;
return;
}
}

// the edges v0-vr and vr-v1 must not be both boundary edges
Expand All @@ -495,28 +514,75 @@ bool SurfaceMesh::is_collapse_ok(int v0v1) {
h1 = next_halfedge(v1v0);
h2 = next_halfedge(h1);
if (hconn_[h1^1].face_ == PMP_MAX_INDEX &&
hconn_[h2^1].face_ == PMP_MAX_INDEX)
return false;
hconn_[h2^1].face_ == PMP_MAX_INDEX) {
hcol01 = hcol10 = false;
return;
}
}

// if vl and vr are equal or both invalid -> fail
if (vl == vr)
return false;
if (vl == vr) {
hcol01 = hcol10 = false;
return;
}

// edge between two boundary vertices should be a boundary edge
if (is_boundary_v(v0) && is_boundary_v(v1) && hconn_[v0v1].face_ != PMP_MAX_INDEX &&
hconn_[v1v0].face_ != PMP_MAX_INDEX)
return false;
hconn_[v1v0].face_ != PMP_MAX_INDEX) {
hcol01 = hcol10 = false;
return;
}

// test intersection of the one-rings of v0 and v1
for (int vv : vertices(v0)) {
if (vv != v1 && vv != vl && vv != vr)
if (find_halfedge(vv, v1) != PMP_MAX_INDEX)
return false;
if (find_halfedge(vv, v1) != PMP_MAX_INDEX) {
hcol01 = hcol10 = false;
return;
}
}

// passed all tests
return true;
// check whether there are triangles flipped after collapsing
auto& pos = prim_->attr<vec3f>("pos");
vec3f pos0 = pos[v0], pos1 = pos[v1];

int hv0 = halfedge(v0);
const int hhv0 = hv0;
if (hv0 != PMP_MAX_INDEX) {
do {
if (hconn_[hv0].face_ == PMP_MAX_INDEX) {
break;
}

int v01 = to_vertex(hv0);
int v02 = to_vertex(next_halfedge(hv0));
vec3f nv0 = normalize(cross(pos[v01] - pos0, pos[v02] - pos0));
vec3f nv1 = normalize(cross(pos[v01] - pos1, pos[v02] - pos1));
if(acos(clamp(dot(nv0, nv1), -1, 1)) > angle_thrd) hcol01 = false;

hv0 = next_halfedge(hv0 ^ 1);
} while (hv0 != hhv0);
}

int hv1 = halfedge(v1);
const int hhv1 = hv1;
if (hv1 != PMP_MAX_INDEX) {
do {
if (hconn_[hv1].face_ == PMP_MAX_INDEX) {
break;
}

int v11 = to_vertex(hv1);
int v12 = to_vertex(next_halfedge(hv1));
vec3f nv1 = normalize(cross(pos[v11] - pos1, pos[v12] - pos1));
vec3f nv0 = normalize(cross(pos[v11] - pos0, pos[v12] - pos0));
if(acos(clamp(dot(nv1, nv0), -1, 1)) > angle_thrd) hcol10 = false;

hv1 = next_halfedge(hv1 ^ 1);
} while (hv1 != hhv1);
}

return;
}

void SurfaceMesh::collapse(int h) {
Expand Down Expand Up @@ -710,6 +776,7 @@ void SurfaceMesh::garbage_collection() {
// remember new size
nV = (vdeleted[i0] == 1) ? i0 : i0 + 1;
}

// remove deleted edges
if (nE > 0) {
i0 = 0;
Expand Down
75 changes: 74 additions & 1 deletion projects/CUDA/remesh/SurfaceMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,72 @@ class SurfaceMesh {
bool is_active_; // helper for C++11 range-based for-loops
};

class FaceAroundVertexCirculator {
public:
FaceAroundVertexCirculator(const SurfaceMesh* mesh = nullptr,
int v = PMP_MAX_INDEX)
: mesh_(mesh), is_active_(true) {
if (mesh_) {
halfedge_ = mesh_->vconn_[v].halfedge_;
if (halfedge_ != PMP_MAX_INDEX && mesh_->hconn_[halfedge_].face_ == PMP_MAX_INDEX)
operator++();
}
}

bool operator==(const FaceAroundVertexCirculator& rhs) const {
assert(mesh_ == rhs.mesh_);
return (is_active_ && (halfedge_ == rhs.halfedge_));
}

bool operator!=(const FaceAroundVertexCirculator& rhs) const {
return !operator==(rhs);
}

// pre-increment (rotate couter-clockwise)
FaceAroundVertexCirculator& operator++() {
assert(mesh_ && (halfedge_ != PMP_MAX_INDEX));
do {
halfedge_ = mesh_->hconn_[halfedge_].prev_halfedge_ ^ 1;
} while (mesh_->hconn_[halfedge_].face_ == PMP_MAX_INDEX);
is_active_ = true;
return *this;
}

// pre-decrement (rotate clockwise)
FaceAroundVertexCirculator& operator--() {
assert(mesh_ && (halfedge_ != PMP_MAX_INDEX));
do {
halfedge_ = mesh_->hconn_[halfedge_^1].next_halfedge_;
} while (mesh_->hconn_[halfedge_].face_ == PMP_MAX_INDEX);
return *this;
}

// get the halfedge the circulator refers to
int operator*() const {
assert(mesh_ && (halfedge_ != PMP_MAX_INDEX));
return mesh_->hconn_[halfedge_].face_;
}

// cast to bool: true if vertex is not isolated
operator bool() const { return halfedge_ != PMP_MAX_INDEX; }

// helper for C++11 range-based for-loops
FaceAroundVertexCirculator& begin() {
is_active_ = (halfedge_ == PMP_MAX_INDEX);
return *this;
}
// helper for C++11 range-based for-loops
FaceAroundVertexCirculator& end() {
is_active_ = true;
return *this;
}

private:
const SurfaceMesh* mesh_;
int halfedge_;
bool is_active_; // helper for C++11 range-based for-loops
};

SurfaceMesh(std::shared_ptr<zeno::PrimitiveObject> prim,
std::string line_pick_tag);
SurfaceMesh(const SurfaceMesh& rhs);
Expand Down Expand Up @@ -171,8 +237,12 @@ class SurfaceMesh {
return HalfedgeAroundVertexCirculator(this, v);
}

FaceAroundVertexCirculator faces(int v) const {
return FaceAroundVertexCirculator(this, v);
}

int find_halfedge(int start, int end) const;
bool is_collapse_ok(int v0v1);
void is_collapse_ok(int v0v1, bool &hcol01, bool &hcol10);
void collapse(int h);
void garbage_collection();
int split(int e, int v, int& new_lines, int& new_faces);
Expand Down Expand Up @@ -422,6 +492,9 @@ class SurfaceMesh {
// indicate garbage present
bool has_garbage_;

// a constant for collapse and flip checks
const float angle_thrd = (M_PI * 5.0f / 180.0f);

// helper data for add_tri()
typedef std::pair<int, int> NextCacheEntry;
typedef std::vector<NextCacheEntry> NextCache;
Expand Down
Loading

0 comments on commit fbcd578

Please sign in to comment.