#pragma once #include #include #include #include #include #include #include "streaming_mesh.h" namespace cgv { namespace media { namespace mesh { template struct greater_equal { T reference_value; greater_equal(const T& _value) : reference_value(_value) {} bool operator () (const T& _value) const { return _value >= reference_value; } }; template struct equal { T reference_value; equal(const T& _value) : reference_value(_value) {} bool operator () (const T& _value) const { return _value == reference_value; } }; /** data structure for the information that is cached per volume slice bz the cuberille algorithm */ template struct c_slice_info { unsigned resx, resy; unsigned nr_vertices; std::vector flags; std::vector indices; /// c_slice_info(unsigned int _resx, unsigned int _resy) : resx(_resx), resy(_resy) { unsigned int n = resx*resy; flags.resize(n, false); indices.resize(n, -1); nr_vertices = 0; } /// void init() { std::fill(flags.begin(), flags.end(), false); std::fill(indices.begin(), indices.end(), -1); nr_vertices = 0; } int linear_index(int x, int y) const { return y*resx + x; } /// bool flag(int x, int y) const { return (x>=0) && (y>=0) && flags[linear_index(x, y)]; } /// void set_flag(int x, int y, bool flag) { flags[linear_index(x, y)] = flag; } /// const int& index(int x, int y) const { return indices[linear_index(x, y)]; } void set_index(int x, int y, int idx) { indices[linear_index(x,y)] = idx; ++nr_vertices; } }; /// class used to perform the marching cubes algorithm template class cuberille : public streaming_mesh { public: typedef streaming_mesh base_type; /// points must have three components typedef cgv::math::fvec pnt_type; /// vectors must have three components typedef cgv::math::fvec vec_type; private: pnt_type p, minp; unsigned int resx, resy, resz; vec_type d; const P& pred; protected: const cgv::math::v3_func& func; public: /// construct dual contouring object cuberille(const cgv::math::v3_func& _func, streaming_mesh_callback_handler* _smcbh, const P& _pred) : func(_func), pred(_pred) { base_type::set_callback_handler(_smcbh); } /// construct a quadrilateral void generate_edge_quad(int vi, int vj, int vk, int vl, bool reorient) { if (reorient) base_type::new_quad(vi, vl, vk, vj); else base_type::new_quad(vi, vj, vk, vl); } /// generate all vertices needed in the given slice I[0] with I[1] being the previous slice void process_slice(c_slice_info* I[2]) { // init slice info I[0]->init(); // iterate voxels of slice to create slice vertices unsigned i, j; for (j = 0, p(1) = minp(1); j <= resy; ++j, p(1) += d(1)) { for (i = 0, p(0) = minp(0); i <= resx; ++i, p(0) += d(0)) { // set voxel flag I[0]->set_flag(i, j, i < resx && j < resy && pred(func.evaluate(p.to_vec()))); // and check whether assigned vertex is needed bool need_vertex = false; need_vertex = need_vertex || (I[0]->flag(i, j) != I[1]->flag(i, j)); // z(x0,y0) need_vertex = need_vertex || (I[0]->flag(i, j) != I[0]->flag(i, j - 1)); // y(x0,z0) need_vertex = need_vertex || (I[1]->flag(i, j) != I[1]->flag(i, j - 1)); // y(x0,z1) need_vertex = need_vertex || (I[0]->flag(i, j) != I[0]->flag(i - 1, j)); // x(y0,z0) need_vertex = need_vertex || (I[1]->flag(i, j) != I[1]->flag(i - 1, j)); // x(y0,z1) if (i > 0) { need_vertex = need_vertex || (I[0]->flag(i - 1, j) != I[1]->flag(i - 1, j)); // z(x1,y0) need_vertex = need_vertex || (I[0]->flag(i - 1, j) != I[0]->flag(i - 1, j - 1)); // y(x1,z0) need_vertex = need_vertex || (I[1]->flag(i - 1, j) != I[1]->flag(i - 1, j - 1)); // y(x1,z1) if (j > 0) { need_vertex = need_vertex || (I[0]->flag(i - 1, j - 1) != I[1]->flag(i - 1, j - 1)); // z(x1,y1) } } if (j > 0) { need_vertex = need_vertex || (I[0]->flag(i, j - 1) != I[1]->flag(i - 1, j)); // z(x0,y1) need_vertex = need_vertex || (I[0]->flag(i, j - 1) != I[0]->flag(i - 1, j - 1)); // x(y1,z0) need_vertex = need_vertex || (I[1]->flag(i, j - 1) != I[1]->flag(i - 1, j - 1)); // x(y1,z1) } // create vertex if necessary if (need_vertex) I[0]->set_index(i, j, new_vertex(p - T(0.5)*d)); } } // iterate voxels again to create edge quads for (j = 0, p(1) = minp(1); j <= resy; ++j, p(1) += d(1)) { for (i = 0, p(0) = minp(0); i <= resx; ++i, p(0) += d(0)) { if ((j < resy) && (I[1]->flag(i - 1, j) != I[1]->flag(i, j))) generate_edge_quad(I[1]->index(i, j), I[1]->index(i, j + 1), I[0]->index(i, j + 1), I[0]->index(i, j), I[1]->flag(i, j)); if ((i < resx) && (I[1]->flag(i, j - 1) != I[1]->flag(i, j))) generate_edge_quad(I[1]->index(i, j), I[0]->index(i, j), I[0]->index(i + 1, j), I[1]->index(i + 1, j), I[1]->flag(i, j)); if ((i < resx) && (j < resy) && (I[1]->flag(i, j) != I[0]->flag(i, j))) generate_edge_quad(I[0]->index(i, j), I[0]->index(i + 1, j), I[0]->index(i + 1, j + 1), I[0]->index(i, j + 1), I[0]->flag(i, j)); } } } /// extract iso surface and send quads to streaming mesh handler void extract(const axis_aligned_box& box, unsigned int _resx, unsigned int _resy, unsigned int _resz, bool show_progress = false) { // prepare private members resx = _resx; resy = _resy; resz = _resz; minp = p = box.get_min_pnt(); d = box.get_extent(); d(0) /= (resx-1); d(1) /= (resy-1); d(2) /= (resz-1); // prepare progression cgv::utils::progression prog; if (show_progress) prog.init("extraction", resz+1, 10); // construct three slice infos c_slice_info slice_info_1(resx+1,resy+1), slice_info_2(resx+1,resy+1); c_slice_info *I[2] = { &slice_info_1, &slice_info_2 }; for (unsigned k=0; k<=resz; ++k, p(2) += d(2)) { process_slice(I); base_type::drop_vertices(I[1]->nr_vertices); // show progression if (show_progress) prog.step(); std::swap(I[0], I[1]); } } }; } } }