Rev 169 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include <cfloat>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <list>
#include "CGLA/Mat2x3f.h"
#include "CGLA/Mat2x2f.h"
#include "HMesh/Manifold.h"
#include "HMesh/VertexCirculator.h"
#include "HMesh/FaceCirculator.h"
#include "HMesh/build_manifold.h"
#include "Geometry/RGrid.h"
#include "Geometry/TrilinFilter.h"
#include "Geometry/GradientFilter.h"
#include "Geometry/Neighbours.h"
#include "Util/HashTable.h"
#include "Util/HashKey.h"
#include "fair_polygonize.h"
namespace HMesh
{
using namespace Geometry;
using namespace Util;
using namespace CGLA;
using namespace std;
namespace
{
void compute_dual(Manifold& m, const vector<Vec3f>& face_positions)
{
// make sure every face knows its number
m.enumerate_faces();
vector<Vec3f> vertices(m.no_faces());
vector<int> faces;
vector<int> indices;
// Create new vertices. Each face becomes a vertex whose positio
// is the centre of the face
int i=0;
for(FaceIter f=m.faces_begin(); f!=m.faces_end(); ++f,++i)
vertices[i] = face_positions[f->touched];
// Create new faces. Each vertex is a new face with N=valency of
// edges.
i=0;
for(VertexIter v=m.vertices_begin(); v!= m.vertices_end(); ++v,++i)
if(!is_boundary(v))
{
int no_edges = 0;
vector<int> index_tmp;
// Circulate around vertex
VertexCirculator vc(v);
for(; !vc.end(); ++vc)
{
FaceIter f = vc.get_face();
if(f != NULL_FACE_ITER)
{
index_tmp.push_back(f->touched);
++no_edges;
}
}
// Push vertex indices for this face onto indices vector
// The circulator moves around the face in a clockwise f
// so we just reverse the ordering.
indices.insert(indices.end(), index_tmp.rbegin(), index_tmp.rend());
faces.push_back(no_edges);
}
// Clear the manifold before new geometry is inserted.
m.clear();
// And build
build_manifold(m, vertices.size(), &vertices[0], faces.size(),
&faces[0],&indices[0]);
}
/*
Vectors along edges. For each of six cube faces there are four such vectors
since the faces of a cube are quads.
*/
const Vec3i edges[6][4] = {
{Vec3i(0,-1,0),Vec3i(0,0,1),Vec3i(0,1,0),Vec3i(0,0,-1)},
{Vec3i(0,1,0),Vec3i(0,0,1),Vec3i(0,-1,0),Vec3i(0,0,-1)},
{Vec3i(0,0,-1),Vec3i(1,0,0),Vec3i(0,0,1),Vec3i(-1,0,0)},
{Vec3i(0,0,1),Vec3i(1,0,0),Vec3i(0,0,-1),Vec3i(-1,0,0)},
{Vec3i(-1,0,0),Vec3i(0,1,0),Vec3i(1,0,0),Vec3i(0,-1,0)},
{Vec3i(1,0,0),Vec3i(0,1,0),Vec3i(-1,0,0),Vec3i(0,-1,0)}};
/* Convert a direction vector to a cube face index. */
int dir_to_face(const Vec3i& dir)
{
assert(dot(dir,dir)==1);
if(dir[0] != 0)
return (dir[0]<0) ? 0 : 1;
if(dir[1] != 0)
return (dir[1]<0) ? 2 : 3;
if(dir[2] != 0)
return (dir[2]<0) ? 4 : 5;
return -1;
}
/* Convert a face index and an edge vector to an edge index. */
int dir_to_edge(int face, const Vec3i& edge)
{
for(int i=0;i<4;++i)
if(edges[face][i] == edge) return i;
assert(0);
return -1;
}
Vec3f cube_corner(const Vec3i& pos, int i, int e)
{
Vec3i to_face = N6i[i];
Vec3i along_edge = edges[i][e];
Vec3i to_edge = cross(along_edge,to_face);
return Vec3f(pos) + 0.5*Vec3f(to_face+to_edge+along_edge);
}
/* Cube faces are created whenever a voxel that is inside is adjacent
to a voxel that is outside. Thus the voxel can be seen as a small
box that either contains material or not. A face is created where
full and empty boxes meet. A vertex is placed on every box face.
A halfedge is created for every edge of the (quadrilateral) face.
This halfedge connects the face to its neighbour. */
struct CubeFace
{
bool used;
HalfEdgeIter he[4];
CubeFace();
HalfEdgeIter& get_he(Manifold& m, int i)
{
if(he[i] == NULL_HALFEDGE_ITER)
he[i] = m.create_halfedge();
return he[i];
}
};
CubeFace::CubeFace(): used(false)
{
for(int i=0;i<4;++i)
he[i] = NULL_HALFEDGE_ITER;
}
/* A cube consists of six faces and a position. The cube centre is
an integer (voxel grid) position. */
struct Cube
{
Vec3i pos;
CubeFace faces[6];
Cube(const Vec3i& _pos): pos(_pos) {}
};
/* The mesher class does the actual work. It is not pretty and it
is not seen by the user. */
template<class T>
class CubeGrid
{
float iso;
HashTable<HashKey3usi,Cube*> cube_hash; // Hashtable of cube pointers
list<Cube> cube_table; // Linked list containing
// the actual cubes.
public:
CubeGrid(const RGrid<T>& _voxel_grid, // input voxel grid
float iso); // iso value
HalfEdgeIter edge_to_glue(const RGrid<T>& voxel_grid,
Cube& cube, int face_idx, int edge_idx);
void cuberille_mesh(const RGrid<T>&, Manifold& m, vector<Vec3f>& face_positions);
};
template<class T>
HalfEdgeIter CubeGrid<T>::edge_to_glue(const RGrid<T>& voxel_grid,
Cube& cube, int face_idx, int edge_idx)
{
/* Figure.
0|B
-+-
A|C
Explanation: ... pending
*/
Vec3i dir = N6i[face_idx];
Vec3i along_edge = edges[face_idx][edge_idx]; // Vector _along_ edge
Vec3i to_edge = cross(along_edge,dir); // vector to edge
Vec3i a_pos = cube.pos;
Vec3i o_pos = a_pos + dir;
Vec3i b_pos = o_pos + to_edge;
Vec3i c_pos = a_pos + to_edge;
if(!voxel_grid.in_domain(b_pos))
return NULL_HALFEDGE_ITER;
Cube* cube_n=0;
Vec3i dir_n; // direction from center to neighbour face
Vec3i along_edge_n; // Edge vector for neighbour face
if(!cube_hash.find_entry(HashKey3usi(b_pos), cube_n))
{
if(cube.faces[dir_to_face(to_edge)].used)
{
dir_n = to_edge;
along_edge_n = - along_edge;
cube_n = &cube;
}
else
{
dir_n = dir;
along_edge_n = - along_edge;
cube_hash.find_entry(HashKey3usi(c_pos), cube_n);
assert(cube_n->faces[dir_to_face(dir_n)].used);
}
}
else
{
float a_val = voxel_grid[a_pos];
float b_val = voxel_grid[b_pos];
float c_val = voxel_grid[c_pos];
float o_val = voxel_grid[o_pos];
bool connect = (a_val*b_val-c_val*o_val)/(a_val+b_val-c_val-o_val) >iso;
if(connect || !cube.faces[dir_to_face(to_edge)].used)
{
dir_n = - to_edge;
along_edge_n = - along_edge;
assert(cube_n->faces[dir_to_face(dir_n)].used);
}
else
{
dir_n = to_edge;
along_edge_n = - along_edge;
cube_n = &cube;
}
}
int i_n = dir_to_face(dir_n);
int e_n = dir_to_edge(i_n, along_edge_n);
return cube_n->faces[i_n].he[e_n];
}
template<class T>
inline bool comp(bool d, T a, T b)
{
return d? (a<b) : (b<a);
}
template<class T>
CubeGrid<T>::CubeGrid(const RGrid<T>& voxel_grid, float _iso): iso(_iso)
{
/* Loop over all voxels */
for(int k=0;k<voxel_grid.get_dims()[ 2 ];++k)
for(int j=0;j<voxel_grid.get_dims()[ 1 ];++j)
for(int i=0;i<voxel_grid.get_dims()[ 0 ];++i)
{
Vec3i pi0(i,j,k);
float val0 = voxel_grid[pi0];
/* If the voxel value is above the isovalue */
if(val0>iso)
{
bool interface_cell = false;
vector<VertexIter> vertices;
Cube* cube;
// For each neighbouring voxel.
for(int l=0;l<6;++l)
{
// First make sure the voxel is inside the voxel grid.
Vec3i pi1(pi0);
pi1 += N6i[l];
bool indom = voxel_grid.in_domain(pi1);
/* If the neighbour is **not** in the grid or it
is on the other side of the isovalue */
if(indom && voxel_grid[pi1]<=iso)
{
/* Store the cube in a hash table. This code is
executed the first time we create a face
for the cube. In other words only cubes that
actually have faces are stored in the hash */
if(!interface_cell)
{
interface_cell = true;
Cube** c_ptr;
cube_hash.create_entry(HashKey3usi(pi0), c_ptr);
Cube c(pi0);
cube_table.push_back(c);
cube = (*c_ptr) = &cube_table.back();
}
// Now add the face to the cube.
int face_idx = dir_to_face(N6i[l]);
CubeFace& face = cube->faces[face_idx];
face.used = true;
}
}
}
}
}
template<class T>
void CubeGrid<T>::cuberille_mesh(const RGrid<T>& voxel_grid, Manifold& m,
vector<Vec3f>& face_positions)
{
face_positions.clear();
Geometry::TrilinFilter<Geometry::RGrid<T> > inter(&voxel_grid);
typename list<Cube>::iterator cube_iter = cube_table.begin();
/* For each cube in the table of cubes */
cube_iter = cube_table.begin();
for(;cube_iter != cube_table.end(); ++cube_iter)
{
/* For each of the six faces, if the face is used */
Cube& cube = *cube_iter;
for(int i=0;i<6;++i)
if(cube.faces[i].used)
{
CubeFace& face = cube.faces[i]; // The face
FaceIter hmesh_face = m.create_face();
hmesh_face->touched = face_positions.size();
Vec3i p0(cube.pos);
Vec3i p1 = p0 + N6i[i];
float v0 = voxel_grid[p0];
float v1 = voxel_grid[p1];
float t = (iso-v0)/(v1-v0);
face_positions.push_back(Vec3f(p0)*(1.0f-t)+Vec3f(p1)*t);
// Let e be one of the four edges of the face
for(int e=0;e<4;++e)
{
HalfEdgeIter he = face.get_he(m,e);
link(he,face.get_he(m,(e+1)%4));
link(face.get_he(m,(e+3)%4),he);
he->face = hmesh_face;
HalfEdgeIter he_n = edge_to_glue(voxel_grid, cube, i, e);
if(he_n != NULL_HALFEDGE_ITER)
glue(he, he_n);
}
hmesh_face->last = face.get_he(m,0);
}
}
for(HalfEdgeIter h = m.halfedges_begin(); h != m.halfedges_end(); ++h)
{
if (h->opp == NULL_HALFEDGE_ITER)
{
HalfEdgeIter ho = m.create_halfedge();
glue(ho,h);
}
}
cube_iter = cube_table.begin();
for(;cube_iter != cube_table.end(); ++cube_iter)
{
//For each of the six faces, if the face is used
Cube& cube = *cube_iter;
for(int i=0;i<6;++i)
if(cube.faces[i].used)
{
CubeFace& face = cube.faces[i]; // The face
// Let e be one of the four edges of the face
for(int e=0;e<4;++e)
if(face.he[e]->vert == NULL_VERTEX_ITER)
{
HalfEdgeIter last = face.he[e];
HalfEdgeIter he = last;
while(he->opp->face != NULL_FACE_ITER)
{
he = he->opp->prev;
if(he == last)
break;
}
last = he;
Vec3f pos = cube_corner(cube.pos, i, e);
VertexIter vert = m.create_vertex(pos);
do
{
he->vert = vert;
he = he->next->opp;
}
while(he->face != NULL_FACE_ITER && he != last);
if(he->face == NULL_FACE_ITER)
{
he->vert = vert;
link(he,last->opp);
}
vert->he = last->opp;
}
}
}
}
}
/* A fairly complex algorithm which triangulates faces. It triangulates
by iteratively splitting faces. It always splits a face by picking the
edge whose midpoint is closest to the isovalue. */
template<class T>
void triangulate(const RGrid<T>& voxel_grid, Manifold& m, float iso)
{
#ifndef NDEBUG
cout << "Initial mesh valid : " << m.is_valid() << endl;
#endif
Geometry::TrilinFilter<Geometry::RGrid<T> > grid_interp(&voxel_grid);
int work;
do
{
// For every face.
work = 0;
for(FaceIter fi =m.faces_begin(); fi != m.faces_end(); ++fi)
{
// Create a vector of vertices.
vector<VertexIter> verts;
for(FaceCirculator fc(fi); !fc.end(); ++fc)
verts.push_back(fc.get_vertex());
// If there are just three we are done.
if(verts.size() == 3) continue;
// Find vertex pairs that may be connected.
vector<pair<int,int> > vpairs;
const int N = verts.size();
for(int i=0;i<N-2;++i)
for(int j=i+2;j<N; ++j)
if(!is_connected(verts[i], verts[j]))
vpairs.push_back(pair<int,int>(i,j));
if(vpairs.empty())
{
cout << "Warning: could not triangulate a face."
<< "Probably a vertex appears more than one time in other vertex's one-ring" << endl;
continue;
}
/* For all vertex pairs, find the absolute value of iso-x
where x is the interpolated value of the volume at the
midpoint of the line segment connecting the two vertices.*/
float min_val=FLT_MAX;
int min_k = -1;
for(size_t k=0;k<vpairs.size(); ++k)
{
int i = vpairs[k].first;
int j = vpairs[k].second;
Vec3f centre =
(verts[i]->pos +
verts[j]->pos)/2.0f;
float val;
if(grid_interp.in_domain(centre))
val = fabs(grid_interp(centre)-iso);
else
val = 0.0f;
if(val<min_val)
{
min_val = val;
min_k = k;
}
}
assert(min_k != -1);
{
// Split faces along edge whose midpoint is closest to isovalue
int i = vpairs[min_k].first;
int j = vpairs[min_k].second;
m.split_face(fi, verts[i], verts[j]);
}
++work;
}
}
while(work);
#ifndef NDEBUG
cout << "Valid after triangulation : " << m.is_valid() << endl;
#endif
}
template<typename T>
void fair_polygonize(const RGrid<T>& voxel_grid,
Manifold& mani,
float iso)
{
CubeGrid<T> cube_grid(voxel_grid, iso);
vector<Vec3f> face_positions;
cube_grid.cuberille_mesh(voxel_grid, mani, face_positions);
cout << "Initial mesh valid : " << mani.is_valid() << endl;
compute_dual(mani, face_positions);
}
template<typename T>
void cuberille_polygonize(const RGrid<T>& voxel_grid,
Manifold& mani,
float iso,
bool push)
{
CubeGrid<T> cube_grid(voxel_grid, iso);
vector<Vec3f> face_positions;
cube_grid.cuberille_mesh(voxel_grid, mani, face_positions);
if(push)
{
for(VertexIter v=mani.vertices_begin(); v != mani.vertices_end(); ++v)
{
VertexCirculator vc(v);
Vec3f p(0);
int n=0;
while(!vc.end())
{
if(vc.get_face() != NULL_FACE_ITER)
{
p += face_positions[vc.get_face()->touched];
++n;
}
++vc;
}
v->pos = p/float(n);
}
}
}
template void fair_polygonize<unsigned char>(const RGrid<unsigned char>&,
Manifold&, float);
template void fair_polygonize<float>(const RGrid<float>&,
Manifold&, float);
template void cuberille_polygonize<unsigned char>(const RGrid<unsigned char>&,
Manifold&, float, bool);
template void cuberille_polygonize<float>(const RGrid<float>&,
Manifold&, float, bool);
template void triangulate(const RGrid<unsigned char>& voxel_grid, Manifold& m, float iso);
template void triangulate(const RGrid<float>& voxel_grid, Manifold& m, float iso);
}