Commit e6a0f2fc authored by Christian Mattes's avatar Christian Mattes
Browse files

Merge branch 'master' into feature_eigen

parents eb1d6fe5 42932d88
......@@ -10,7 +10,6 @@ Best used with glm and glow.
* Algorithms
* Tests
* std::less and std::hash for _index (and maybe _handle)
* attribute transformations (also between different types)
* Debug: store compactify generation in handles to check for invalidation
* Debug: insert is_removed assertions into handle access
* Test self-adjacent faces
......@@ -19,14 +18,15 @@ Best used with glm and glow.
* opposite edges (from vertex)
* cotangents weights etc.
* smoothing
* make handle.<primitives>() contain only valid ones and provide an all_<primitives>() version
* _copy versions of topological operations that copy attributes
* vertex split?
* half-edge collapse
* normal, tangent, bitangent computation
* attribute iterator
* primitive sort functions, better remap function, cache optimization
* structure of arrays instead of AOS
* lowlevel API that allows direct half-edge manipulation and does not fix boundaries (but also mirrors high level one)
* primitive collection sort and sort_by functions
* paired_with function for smart range
* operator +-*/ for attributes (similar to valarray)
* dual mesh construction
* cast<>, reinterpret<> function
* surface tracing
\ No newline at end of file
......@@ -17,7 +17,7 @@
// Basic mesh operations, including:
// - elementary subdivision
// - intersections
#include "algorithms/operations.hh"
#include "algorithms/remeshing/triangulate.hh"
// Mesh statistics
#include "algorithms/components.hh"
......
#pragma once
#include "../Mesh.hh"
#include <glm/glm.hpp>
#include <polymesh/Mesh.hh>
// Basic mesh operations, including:
// - elementary subdivision
......@@ -10,26 +8,43 @@
namespace polymesh
{
/// Given a flat polymesh with convex faces, naively triangulates all faces
void triangulate_naive(Mesh& m);
/// Removes all faces of a given mesh
/// NOTE: does NOT compactify!
void remove_faces(Mesh& m);
/// Removes all edges and faces of a given mesh
/// NOTE: does NOT compactify!
void remove_edges_and_faces(Mesh& m);
/// ======== IMPLEMENTATION ========
inline void triangulate_naive(Mesh& m)
inline void remove_faces(Mesh& m)
{
std::vector<vertex_handle> vs;
auto ll = low_level_api(m);
// set faces to removed
for (auto f : m.faces())
{
vs = f.vertices().to_vector();
if (vs.size() <= 3)
continue;
// remove
m.faces().remove(f);
// triangulate
for (auto i = 2u; i < vs.size(); ++i)
m.faces().add(vs[0], vs[1], vs[i]);
}
ll.set_removed(f);
// remove all faces from half-edges
for (auto h : m.halfedges())
ll.face_of(h) = face_index::invalid();
}
inline void remove_edges_and_faces(Mesh& m)
{
auto ll = low_level_api(m);
// set faces to removed
for (auto f : m.faces())
ll.set_removed(f);
// set edges to removed
for (auto e : m.edges())
ll.set_removed(e);
// remove all halfedges from vertices
for (auto v : m.vertices())
ll.outgoing_halfedge_of(v) = halfedge_index::invalid();
}
}
......@@ -63,22 +63,116 @@ inline void optimize_for_rendering(Mesh& m)
inline std::vector<int> cache_coherent_face_layout(Mesh const& m)
{
// build binary tree
// - approx min cut
// - refine approx
// - split
if (m.faces().empty())
return {};
assert(m.faces().size() == m.all_faces().size() && "non-compact currently not supported");
std::vector<std::pair<int, int>> edges;
polymesh::detail::disjoint_set clusters(m.all_faces().size());
std::vector<std::pair<float, std::pair<int, int>>> edges;
for (auto e : m.edges())
edges.emplace_back((int)e.faceA(), (int)e.faceB());
edges.push_back({-1, {(int)e.faceA(), (int)e.faceB()}});
// TODO
struct node
{
int rep;
std::vector<node*> children;
std::vector<int> id;
for (auto i = 0u; i < m.faces().size(); ++i)
id.push_back(i);
// TODO
return id;
bool is_leaf() const { return children.empty(); }
void assign_idx(int& next_idx, std::vector<int>& indices) const
{
if (is_leaf())
{
indices[rep] = next_idx++;
}
else
{
for (auto n : children)
n->assign_idx(next_idx, indices);
}
}
~node()
{
for (auto n : children)
delete n;
}
};
std::map<int, node*> cluster_centers;
for (auto f : m.faces())
cluster_centers[(int)f] = new node{(int)f};
std::map<std::pair<int, int>, float> cluster_neighbors;
// bottom-up clustering
auto cluster_limit = 1;
while (!edges.empty())
{
cluster_limit *= 2;
// merge edges where appropriate
for (auto e : edges)
{
auto f0 = e.second.first;
auto f1 = e.second.second;
auto s0 = clusters.size_of(f0);
auto s1 = clusters.size_of(f1);
if (s0 + s1 <= cluster_limit)
clusters.do_union(f0, f1);
}
// collect new neighbors
cluster_neighbors.clear();
for (auto e : edges)
{
auto w = e.first;
auto f0 = clusters.find(e.second.first);
auto f1 = clusters.find(e.second.second);
if (f0 == f1)
continue;
if (f0 > f1)
std::swap(f0, f1);
cluster_neighbors[{f0, f1}] += w;
}
// create new edges
edges.clear();
for (auto const& kvp : cluster_neighbors)
edges.push_back({kvp.second, kvp.first});
sort(edges.begin(), edges.end());
// new cluster centers
std::map<int, node*> new_centers;
// .. create nodes
for (auto const& kvp : cluster_centers)
if (clusters.is_representative(kvp.first))
new_centers[kvp.first] = new node{kvp.first};
// .. add children
for (auto const& kvp : cluster_centers)
new_centers[clusters.find(kvp.first)]->children.push_back(kvp.second);
// .. replace old
cluster_centers = new_centers;
}
// distribute indices
std::vector<int> new_indices(m.all_faces().size());
int next_idx = 0;
for (auto const& kvp : cluster_centers)
kvp.second->assign_idx(next_idx, new_indices);
assert(next_idx == m.faces().size());
// cleanup
for (auto const& kvp : cluster_centers)
delete kvp.second;
return new_indices;
}
inline std::vector<int> cache_coherent_vertex_layout(Mesh const& m)
......@@ -97,7 +191,7 @@ inline void optimize_edges_for_faces(Mesh& m)
auto fA = e.faceA();
auto fB = e.faceB();
auto f = fA.is_invalid() ? fB : fB.is_invalid() ? fA : detail::xorshift64star(rng) % 2 ? fA : fB;
face_edge_indices.emplace_back(f.idx.value, e.idx.value);
face_edge_indices.emplace_back((int)f, (int)e);
}
// sort by face idx
......@@ -106,7 +200,7 @@ inline void optimize_edges_for_faces(Mesh& m)
// extract edge indices
std::vector<int> permutation(face_edge_indices.size());
for (auto i = 0u; i < face_edge_indices.size(); ++i)
permutation[i] = face_edge_indices[i].second;
permutation[face_edge_indices[i].second] = i;
// apply permutation
m.edges().permute(permutation);
......@@ -129,7 +223,7 @@ inline void optimize_edges_for_vertices(Mesh& m)
// extract edge indices
std::vector<int> permutation(vertex_edge_indices.size());
for (auto i = 0u; i < vertex_edge_indices.size(); ++i)
permutation[i] = vertex_edge_indices[i].second;
permutation[vertex_edge_indices[i].second] = i;
// apply permutation
m.edges().permute(permutation);
......@@ -159,7 +253,7 @@ inline void optimize_faces_for_vertices(Mesh& m)
// extract face indices
std::vector<int> permutation(vertex_face_indices.size());
for (auto i = 0u; i < vertex_face_indices.size(); ++i)
permutation[i] = vertex_face_indices[i].second;
permutation[vertex_face_indices[i].second] = i;
// apply permutation
m.faces().permute(permutation);
......@@ -192,7 +286,7 @@ inline void optimize_vertices_for_faces(Mesh& m)
// extract vertex indices
std::vector<int> permutation(face_vertex_indices.size());
for (auto i = 0u; i < face_vertex_indices.size(); ++i)
permutation[i] = face_vertex_indices[i].second;
permutation[face_vertex_indices[i].second] = i;
// apply permutation
m.vertices().permute(permutation);
......
#pragma once
#include <polymesh/Mesh.hh>
// Basic mesh operations, including:
// - elementary subdivision
// - intersections
namespace polymesh
{
/// Given a flat polymesh with convex faces, naively triangulates all faces
void triangulate_naive(Mesh& m);
/// ======== IMPLEMENTATION ========
inline void triangulate_naive(Mesh& m)
{
std::vector<vertex_handle> vs;
for (auto f : m.faces())
{
vs = f.vertices().to_vector();
if (vs.size() <= 3)
continue;
// remove
m.faces().remove(f);
// triangulate
for (auto i = 2u; i < vs.size(); ++i)
m.faces().add(vs[0], vs[1], vs[i]);
}
}
}
#pragma once
#include <unordered_map>
#include <polymesh/Mesh.hh>
#include "../operations.hh"
namespace polymesh
{
/// Merges vertices that report the same key
///
/// Example usage:
/// Mesh m;
/// vertex_attribute<glm::vec3> pos;
/// load_stl(file, m, pos);
/// deduplicate(m, pos);
///
/// Note:
/// preserves (first) vertex and face attributes ONLY!
/// edge/halfedge attributes are UNDEFINED (will probably contain uncorrelated old data)
///
/// returns number of removed vertices (-1 if deduplication failed (e.g. due to non-manifoldness))
template <class KeyF>
int deduplicate(Mesh& m, KeyF&& kf);
/// ======== IMPLEMENTATION ========
template <class KeyF>
int deduplicate(Mesh& m, KeyF&& kf)
{
using KeyT = typename std::decay<decltype(kf(m.vertices().first()))>::type;
std::unordered_map<KeyT, vertex_index> remap;
auto new_idx = m.vertices().make_attribute<vertex_index>();
// calculate remapped vertices
for (auto v : m.vertices())
{
auto const& k = kf(v);
if (!remap.count(k))
remap[k] = v;
new_idx[v] = remap[k];
}
// calc face remapping
std::vector<vertex_index> poly_verts;
struct poly
{
face_index f;
int start;
int count;
};
std::vector<poly> polys;
polys.reserve(m.faces().size());
for (auto f : m.faces())
{
auto s = (int)poly_verts.size();
for (auto v : f.vertices())
poly_verts.push_back(new_idx[v]);
auto e = (int)poly_verts.size();
polys.push_back({f, s, e - s});
}
auto ll = low_level_api(m);
// remove everything except vertices
remove_edges_and_faces(m);
// clear edge vector (new edges are allocated from idx 0)
ll.clear_removed_edge_vector();
// add remapped faces
auto manifold = true;
for (auto const& p : polys)
{
if (ll.can_add_face(poly_verts.data() + p.start, p.count))
ll.add_face(poly_verts.data() + p.start, p.count, p.f);
else
manifold = false;
}
// remove duplicated vertices
int removed = 0;
for (auto v : m.vertices())
if (new_idx[v] != v)
{
m.vertices().remove(v);
++removed;
}
return manifold ? removed : -1;
}
}
......@@ -91,6 +91,10 @@ public:
/// copies as much data as possible from the given attribute
void copy_from(attribute<AttrT> const& data);
/// copies all attribute data to another mesh
/// asserts that sizes are correct
attribute<AttrT> copy_to(Mesh const& m) const;
/// Saves ALL data into a vector (includes possibly removed ones)
std::vector<AttrT> to_vector() const;
// TODO: specialized implementation of to_vector(FuncT&&)
......
......@@ -26,6 +26,10 @@ struct primitive_index
bool is_invalid() const { return value < 0; }
static index_t invalid() { return {}; }
bool operator<(index_t const& rhs) const { return value < rhs.value; }
bool operator<=(index_t const& rhs) const { return value <= rhs.value; }
bool operator>(index_t const& rhs) const { return value > rhs.value; }
bool operator>=(index_t const& rhs) const { return value >= rhs.value; }
bool operator==(index_t const& rhs) const { return value == rhs.value; }
bool operator!=(index_t const& rhs) const { return value != rhs.value; }
bool operator==(handle_t const& rhs) const { return value == rhs.idx.value; }
......@@ -36,8 +40,18 @@ struct primitive_index
/// indexes this primitive by a functor
/// also works for attributes
/// - e.g. v[position] or f[area]
template <class FuncT>
auto operator[](FuncT&& f) const -> tmp::result_type_of<FuncT, index_t>;
template <class FuncT, class ResultT = tmp::result_type_of<FuncT, index_t>>
ResultT operator[](FuncT&& f) const;
/// indexes this primitive by an attribute pointer
/// if attr is null, returns writeable dummy location
/// CAUTION: always returns same writeable dummy location, this is intended as a write-only value!
template <class AttrT>
AttrT& operator[](attribute<AttrT>* attr) const;
/// indexes this primitive by an attribute pointer
/// if attr is null, returns default constructed value
template <class AttrT>
AttrT const& operator[](attribute<AttrT> const* attr) const;
};
template <class tag>
......@@ -65,8 +79,17 @@ struct primitive_handle
/// indexes this primitive by a functor
/// also works for attributes
/// - e.g. v[position] or f[area]
template <class FuncT>
auto operator[](FuncT&& f) const -> tmp::result_type_of<FuncT, handle_t>;
template <class FuncT, class ResultT = tmp::result_type_of<FuncT, index_t>>
ResultT operator[](FuncT&& f) const;
/// indexes this primitive by an attribute pointer
/// if attr is null, returns writeable dummy location
template <class AttrT>
AttrT& operator[](attribute<AttrT>* attr) const;
/// indexes this primitive by an attribute pointer
/// if attr is null, returns default constructed value
template <class AttrT>
AttrT const& operator[](attribute<AttrT> const* attr) const;
bool is_valid() const { return idx.is_valid(); } ///< valid idx (but could be deleted in some iterators)
bool is_invalid() const { return !idx.is_valid(); } ///< invalid idx
......