├── libs ├── Makefile ├── fssr │ ├── defines.h │ ├── Makefile │ ├── sample.h │ ├── mesh_clean.h │ ├── triangulation.h │ ├── basis_function.cc │ ├── pointset.h │ ├── triangulation.cc │ ├── iso_octree.h │ ├── pointset.cc │ ├── voxel.cc │ ├── voxel.h │ ├── basis_function.h │ ├── mesh_clean.cc │ ├── iso_octree.cc │ ├── octree.h │ └── octree.cc └── iso │ ├── SimonIsoOctree.h │ ├── BinaryNode.h │ ├── NeighborKey.h │ ├── Octree.h │ ├── SimonIsoOctree.inl │ ├── IsoOctree.h │ ├── MarchingCubes.h │ ├── NeighborKey.inl │ ├── MarchingCubes.inl │ └── Octree.inl ├── Makefile ├── apps ├── Makefile ├── fssr_octree │ ├── Makefile │ └── fssr_octree.cc └── fssr_surface │ ├── Makefile │ └── fssr_surface.cc ├── tests ├── Makefile ├── gtest_basis_function.cc ├── gtest_iso_octree.cc ├── gtest_mesh_clean.cc └── gtest_octree.cc └── README.txt /libs/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C fssr 3 | 4 | clean: 5 | $(MAKE) -C fssr clean 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C libs 3 | $(MAKE) -C apps 4 | 5 | clean: 6 | $(MAKE) -C libs clean 7 | $(MAKE) -C apps clean 8 | -------------------------------------------------------------------------------- /apps/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | $(MAKE) -C fssr_octree 3 | $(MAKE) -C fssr_surface 4 | 5 | clean: 6 | $(MAKE) -C fssr_octree clean 7 | $(MAKE) -C fssr_surface clean 8 | -------------------------------------------------------------------------------- /libs/fssr/defines.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_DEFINES_HEADER 7 | #define FSSR_DEFINES_HEADER 8 | 9 | #define FSSR_NAMESPACE_BEGIN namespace fssr { 10 | #define FSSR_NAMESPACE_END } 11 | 12 | #endif /* FSSR_DEFINES_HEADER */ 13 | -------------------------------------------------------------------------------- /libs/fssr/Makefile: -------------------------------------------------------------------------------- 1 | MVE_ROOT ?= ../../../mve 2 | TARGET := libfssr.a 3 | include ${MVE_ROOT}/Makefile.inc 4 | 5 | CXXFLAGS += -I.. -I${MVE_ROOT}/libs ${OPENMP} 6 | LDLIBS += -lpng -ltiff -ljpeg ${OPENMP} 7 | 8 | SOURCES := $(wildcard [^_]*.cc) 9 | ${TARGET}: ${SOURCES:.cc=.o} 10 | $(AR) rcs $@ $^ 11 | 12 | clean: 13 | ${RM} $(TARGET) *.o Makefile.dep 14 | 15 | .PHONY: clean 16 | -------------------------------------------------------------------------------- /apps/fssr_octree/Makefile: -------------------------------------------------------------------------------- 1 | MVE_ROOT := ../../../mve 2 | TARGET := $(shell basename `pwd`) 3 | include ${MVE_ROOT}/Makefile.inc 4 | 5 | FSSR_ROOT := ../.. 6 | vpath libfssr.a ${FSSR_ROOT}/libs/fssr/ 7 | 8 | CXXFLAGS += -I${FSSR_ROOT}/libs -I${MVE_ROOT}/libs ${OPENMP} 9 | LDLIBS += -lpng -ltiff -ljpeg ${OPENMP} 10 | 11 | SOURCES := $(wildcard [^_]*.cc) 12 | ${TARGET}: ${SOURCES:.cc=.o} libfssr.a libmve.a libmve_util.a 13 | 14 | clean: 15 | ${RM} ${TARGET} *.o Makefile.dep 16 | 17 | .PHONY: clean 18 | -------------------------------------------------------------------------------- /apps/fssr_surface/Makefile: -------------------------------------------------------------------------------- 1 | MVE_ROOT := ../../../mve 2 | TARGET := $(shell basename `pwd`) 3 | include ${MVE_ROOT}/Makefile.inc 4 | 5 | FSSR_ROOT := ../.. 6 | vpath libfssr.a ${FSSR_ROOT}/libs/fssr/ 7 | 8 | CXXFLAGS += -I${FSSR_ROOT}/libs -I${MVE_ROOT}/libs ${OPENMP} 9 | LDLIBS += -lpng -ltiff -ljpeg ${OPENMP} 10 | 11 | SOURCES := $(wildcard [^_]*.cc) 12 | ${TARGET}: ${SOURCES:.cc=.o} libfssr.a libmve.a libmve_util.a 13 | 14 | clean: 15 | ${RM} ${TARGET} *.o Makefile.dep 16 | 17 | .PHONY: clean 18 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | MVE_ROOT := ../../mve 2 | TARGET := test 3 | include ${MVE_ROOT}/Makefile.inc 4 | vpath gtest_main.a ${GTEST_PATH}/make/ 5 | 6 | SOURCES = $(wildcard gtest_*.cc) 7 | CXXFLAGS = -g -O3 -I../libs -I${MVE_ROOT}/libs -I${GTEST_PATH}/include 8 | LDLIBS += -lpng -ltiff -ljpeg 9 | 10 | vpath libfssr.a ../libs/fssr/ 11 | test: ${SOURCES:.cc=.o} gtest_main.a libmve.a libmve_util.a libfssr.a 12 | ${LINK.cc} -o $@ $^ ${LDLIBS} 13 | 14 | clean: 15 | ${MAKE} -C ../libs clean 16 | ${RM} ${TARGET} *.o Makefile.dep 17 | -------------------------------------------------------------------------------- /libs/fssr/sample.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_SAMPLE_HEADER 7 | #define FSSR_SAMPLE_HEADER 8 | 9 | #include "math/vector.h" 10 | #include "fssr/defines.h" 11 | 12 | FSSR_NAMESPACE_BEGIN 13 | 14 | /** Representation of a point sample. */ 15 | struct Sample 16 | { 17 | math::Vec3f pos; 18 | math::Vec3f normal; 19 | math::Vec3f color; 20 | float scale; 21 | float confidence; 22 | }; 23 | 24 | /** Comparator that orders samples according to scale. */ 25 | bool 26 | sample_scale_compare (Sample const* s1, Sample const* s2); 27 | 28 | FSSR_NAMESPACE_END 29 | 30 | /* ------------------------- Implementation ---------------------------- */ 31 | 32 | FSSR_NAMESPACE_BEGIN 33 | 34 | inline bool 35 | sample_scale_compare (Sample const* s1, Sample const* s2) 36 | { 37 | return s1->scale < s2->scale; 38 | } 39 | 40 | FSSR_NAMESPACE_END 41 | 42 | #endif // FSSR_SAMPLE_HEADER 43 | -------------------------------------------------------------------------------- /libs/fssr/mesh_clean.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_MESH_CLEAN_HEADER 7 | #define FSSR_MESH_CLEAN_HEADER 8 | 9 | #include "fssr/defines.h" 10 | #include "mve/mesh.h" 11 | 12 | FSSR_NAMESPACE_BEGIN 13 | 14 | /** 15 | * Cleans needles from the mesh by collapsing short edges of degenerated 16 | * triangles. The number of successful edge collapses is returned. 17 | */ 18 | std::size_t 19 | clean_needles (mve::TriangleMesh::Ptr mesh, float needle_ratio_thres); 20 | 21 | /** 22 | * Cleans caps from the mesh by removing vertices with only three 23 | * adjacent triangles. The number of successful edge collapses is returned. 24 | */ 25 | std::size_t 26 | clean_caps (mve::TriangleMesh::Ptr mesh); 27 | 28 | /** 29 | * Removes degenerated triangles from the mesh typical for Marching Cubes. 30 | * The routine first cleans needles, then caps, then remaining needles. 31 | */ 32 | std::size_t 33 | clean_mc_mesh (mve::TriangleMesh::Ptr mesh, float needle_ratio_thres = 0.4f); 34 | 35 | FSSR_NAMESPACE_END 36 | 37 | #endif /* FSSR_MESH_CLEAN_HEADER */ 38 | -------------------------------------------------------------------------------- /tests/gtest_basis_function.cc: -------------------------------------------------------------------------------- 1 | // Test cases for basis function. 2 | // Written by Simon Fuhrmann. 3 | 4 | #include //tmp 5 | 6 | #include 7 | 8 | #include "util/timer.h" 9 | #include "fssr/basis_function.h" 10 | 11 | TEST(BasisFunctionTest, TestWeightingFunction) 12 | { 13 | for (double x = -3.0; x < 3.0; x += 0.1) 14 | EXPECT_GE(fssr::weighting_function_x(x), 0.0); 15 | EXPECT_EQ(0.0, fssr::weighting_function_x(-3.0)); 16 | EXPECT_EQ(0.0, fssr::weighting_function_x(3.0)); 17 | 18 | for (double y = -3.0; y < 3.0; y += 0.3) 19 | for (double x = -3.0; x < 3.0; x += 0.3) 20 | { 21 | EXPECT_EQ(fssr::weighting_function_yz(0.0, x), 22 | fssr::weighting_function_yz(x, 0.0)); 23 | EXPECT_EQ(fssr::weighting_function_yz(y, x), 24 | fssr::weighting_function_yz(x, y)); 25 | } 26 | } 27 | 28 | TEST(BasisFunctionTest, TestMPUWeightingFunction) 29 | { 30 | std::ofstream out("/tmp/data.txt"); 31 | for (double x = -3.5; x <= 3.5; x += 0.1) 32 | out << x << " " << fssr::weighting_function_mpu(x) << std::endl; 33 | out.close(); 34 | } 35 | -------------------------------------------------------------------------------- /libs/iso/SimonIsoOctree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Written by Simon Fuhrmann, 2013. 3 | */ 4 | 5 | #ifndef SIMON_ISO_OCTREE_H 6 | #define SIMON_ISO_OCTREE_H 7 | 8 | #include "math/vector.h" 9 | #include "mve/mesh.h" 10 | #include "fssr/iso_octree.h" 11 | 12 | #include "IsoOctree.h" 13 | 14 | template 15 | struct MyNodeData 16 | { 17 | int mcIndex; 18 | }; 19 | 20 | typedef fssr::VoxelData SimonVertexData; 21 | typedef MyNodeData SimonNodeData; 22 | typedef OctNode SimonOctNode; 23 | 24 | class SimonIsoOctree : public IsoOctree 25 | { 26 | public: 27 | void set_octree (fssr::IsoOctree const& octree); 28 | mve::TriangleMesh::Ptr extract_mesh (void); 29 | void clear (void); 30 | 31 | private: 32 | void transfer_octree (fssr::IsoOctree::Iterator const& in_iter, 33 | SimonOctNode* out_node, 34 | SimonOctNode::NodeIndex out_node_index, 35 | int max_level); 36 | 37 | void copy_voxel_data (fssr::IsoOctree const& octree); 38 | long long index_convert (fssr::VoxelIndex const& index) const; 39 | 40 | private: 41 | math::Vec3f translate; 42 | float scale; 43 | }; 44 | 45 | #include "SimonIsoOctree.inl" 46 | 47 | #endif // SIMON_ISO_OCTREE_H 48 | -------------------------------------------------------------------------------- /libs/fssr/triangulation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_TRIANGULATION_HEADER 7 | #define FSSR_TRIANGULATION_HEADER 8 | 9 | #include 10 | 11 | #include "math/vector.h" 12 | #include "fssr/defines.h" 13 | 14 | FSSR_NAMESPACE_BEGIN 15 | 16 | /** 17 | * Computes the minimum area triangulation of a polygon. 18 | * The algorithm is described in the paper: 19 | * 20 | * Unconstrained Isosurface Extraction on Arbitrary Octrees 21 | * Michael Kazhdan, Allison Klein, Ketan Dalal, Hugues Hoppe 22 | */ 23 | class MinAreaTriangulation 24 | { 25 | public: 26 | void triangulate (std::vector const& verts, 27 | std::vector* indices); 28 | 29 | private: 30 | float compute_table (std::vector const& verts, 31 | int start_id, int end_id); 32 | void compute_triangulation (std::vector* indices, 33 | int start_id, int end_id, int num_verts); 34 | 35 | private: 36 | std::vector min_area_table; 37 | std::vector mid_point_table; 38 | }; 39 | 40 | /* 41 | * TODO: Triangulation that creates a center vertex. 42 | */ 43 | 44 | FSSR_NAMESPACE_END 45 | 46 | #endif /* FSSR_TRIANGULATION_HEADER */ 47 | -------------------------------------------------------------------------------- /tests/gtest_iso_octree.cc: -------------------------------------------------------------------------------- 1 | // Test cases for ISO octree. 2 | // Written by Simon Fuhrmann. 3 | 4 | #include 5 | #include 6 | 7 | #include "fssr/iso_octree.h" 8 | #include "fssr/sample.h" 9 | 10 | #if 0 11 | TEST(IsoOctreeTest, TempTest) 12 | { 13 | fssr::Octree::NodePath path; 14 | fssr::VoxelIndex index; 15 | 16 | path.level = 0; 17 | path.path = 0; 18 | index.from_path_and_corner(path, 1); 19 | std::cout << index.index << std::endl; 20 | 21 | path.level = 1; 22 | path.path = 1; 23 | index.from_path_and_corner(path, 1); 24 | std::cout << index.index << std::endl; 25 | 26 | path.level = 2; 27 | path.path = 9; 28 | index.from_path_and_corner(path, 1); 29 | std::cout << index.index << std::endl; 30 | 31 | 32 | path.level = 0; 33 | path.path = 0; 34 | index.from_path_and_corner(path, 2); 35 | std::cout << index.index << std::endl; 36 | 37 | path.level = 1; 38 | path.path = 2; 39 | index.from_path_and_corner(path, 2); 40 | std::cout << index.index << std::endl; 41 | 42 | path.level = 2; 43 | path.path = 18; 44 | index.from_path_and_corner(path, 2); 45 | std::cout << index.index << std::endl; 46 | } 47 | 48 | TEST(IsoOctreeTest, Temp2Test) 49 | { 50 | fssr::Octree::NodePath path; 51 | 52 | path.level = 1; 53 | path.path = 0; 54 | fssr::VoxelIndex index1; 55 | index1.from_path_and_corner(path, 7); 56 | std::cout << index1.index << std::endl; 57 | 58 | path.level = 2; 59 | path.path = 14; 60 | fssr::VoxelIndex index2; 61 | index2.from_path_and_corner(path, 6); 62 | std::cout << index2.index << std::endl; 63 | } 64 | #endif 65 | -------------------------------------------------------------------------------- /libs/fssr/basis_function.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #include "math/vector.h" 7 | #include "math/matrix.h" 8 | #include "math/matrix_tools.h" 9 | #include "math/functions.h" 10 | #include "fssr/basis_function.h" 11 | 12 | FSSR_NAMESPACE_BEGIN 13 | 14 | /* 15 | * Rotation from normal in 3D. 16 | * The rotation axis is determined using cross product between the reference 17 | * normal and the given normal. The rotation angle is obtained using the 18 | * dot product. MVE is used to obtain the 3x3 rotation matrix. 19 | */ 20 | void 21 | rotation_from_normal (math::Vec3f const& normal, math::Matrix3f* rot) 22 | { 23 | math::Vec3f const ref(1.0f, 0.0f, 0.0f); 24 | if (normal.is_similar(ref, 0.001f)) 25 | { 26 | math::matrix_set_identity(rot); 27 | return; 28 | } 29 | 30 | math::Vec3f const mirror(-1.0f, 0.0f, 0.0f); 31 | if (normal.is_similar(mirror, 0.001f)) 32 | { 33 | /* 180 degree rotation around the z-axis. */ 34 | (*rot)[0] = -1.0f; (*rot)[1] = 0.0f; (*rot)[2] = 0.0f; 35 | (*rot)[3] = 0.0f; (*rot)[4] = -1.0f; (*rot)[5] = 0.0f; 36 | (*rot)[6] = 0.0f; (*rot)[7] = 0.0f; (*rot)[8] = 1.0f; 37 | return; 38 | } 39 | 40 | math::Vec3f const axis = normal.cross(ref).normalized(); 41 | float const cos_alpha = math::clamp(ref.dot(normal), -1.0f, 1.0f); 42 | float const angle = std::acos(cos_alpha); 43 | *rot = math::matrix_rotation_from_axis_angle(axis, angle); 44 | } 45 | 46 | /* 47 | * The 2D rotation matrix where cos(angle) and sin(angle) is directly 48 | * taken from the normal. The reference normal is oriented toward the 49 | * positive x-axis. 50 | */ 51 | void 52 | rotation_from_normal (math::Vec2f const& normal, math::Matrix2f* rot) 53 | { 54 | (*rot)[0] = normal[0]; 55 | (*rot)[1] = normal[1]; 56 | (*rot)[2] = -normal[1]; 57 | (*rot)[3] = normal[0]; 58 | } 59 | 60 | FSSR_NAMESPACE_END 61 | -------------------------------------------------------------------------------- /tests/gtest_mesh_clean.cc: -------------------------------------------------------------------------------- 1 | // Test cases for mesh decimator. 2 | // Written by Simon Fuhrmann. 3 | 4 | #include 5 | 6 | #include "mve/mesh.h" 7 | #include "mve/mesh_tools.h" 8 | #include "fssr/mesh_clean.h" 9 | 10 | #if 0 11 | TEST(MeshCleanTest, CleanTest1) 12 | { 13 | mve::TriangleMesh::Ptr mesh = mve::TriangleMesh::create(); 14 | mve::TriangleMesh::FaceList& faces = mesh->get_faces(); 15 | mve::TriangleMesh::VertexList& verts = mesh->get_vertices(); 16 | 17 | verts.push_back(math::Vec3f(0,0,0)); 18 | verts.push_back(math::Vec3f(1,0,0)); 19 | verts.push_back(math::Vec3f(2,0,0)); 20 | verts.push_back(math::Vec3f(2,4,0)); 21 | verts.push_back(math::Vec3f(1,4,0)); 22 | verts.push_back(math::Vec3f(0,4,0)); 23 | verts.push_back(math::Vec3f(0.9,2,0)); 24 | verts.push_back(math::Vec3f(1.1,2,0)); 25 | 26 | faces.push_back(0); faces.push_back(1); faces.push_back(6); 27 | faces.push_back(1); faces.push_back(2); faces.push_back(7); 28 | faces.push_back(2); faces.push_back(3); faces.push_back(7); 29 | faces.push_back(3); faces.push_back(4); faces.push_back(7); 30 | faces.push_back(4); faces.push_back(5); faces.push_back(6); 31 | faces.push_back(5); faces.push_back(0); faces.push_back(6); 32 | faces.push_back(1); faces.push_back(7); faces.push_back(6); 33 | faces.push_back(4); faces.push_back(6); faces.push_back(7); 34 | 35 | mve::geom::save_mesh(mesh, "/tmp/testmesh.off"); 36 | fssr::clean_slivers(mesh, 0.1f); 37 | mve::geom::save_mesh(mesh, "/tmp/testmesh_cleaned.off"); 38 | 39 | // TODO: Write some EXPECT tests! 40 | } 41 | #endif 42 | 43 | #if 0 44 | TEST(MeshCleanTest, CleanTest2) 45 | { 46 | mve::TriangleMesh::Ptr mesh = mve::geom::load_mesh("/tmp/camel_mc.off"); 47 | std::size_t num_collapsed = 0; 48 | num_collapsed += fssr::clean_needles(mesh, 0.4f); 49 | mve::geom::save_mesh(mesh, "/tmp/camel_cleaned_1.ply"); 50 | num_collapsed += fssr::clean_caps(mesh); 51 | mve::geom::save_mesh(mesh, "/tmp/camel_cleaned_2.ply"); 52 | num_collapsed += fssr::clean_needles(mesh, 0.4f); 53 | mve::geom::save_mesh(mesh, "/tmp/camel_cleaned_3.ply"); 54 | std::cout << "Collapsed " << num_collapsed << " edges." << std::endl; 55 | } 56 | #endif 57 | -------------------------------------------------------------------------------- /libs/fssr/pointset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_POINTSET_HEADER 7 | #define FSSR_POINTSET_HEADER 8 | 9 | #include 10 | #include 11 | 12 | #include "fssr/defines.h" 13 | #include "fssr/sample.h" 14 | 15 | FSSR_NAMESPACE_BEGIN 16 | 17 | /** 18 | * Reads a point set from file and converts it to samples. 19 | */ 20 | class PointSet 21 | { 22 | public: 23 | typedef std::vector SampleList; 24 | 25 | public: 26 | /** Default constructor doing nothing. */ 27 | PointSet (void); 28 | /** Reads the input file with default settings. */ 29 | PointSet (std::string const& filename); 30 | 31 | /** Sets the factor multiplied with sample scale. */ 32 | void set_scale_factor (float factor); 33 | /** Sets how many samples are skipped, defaults to 0. */ 34 | void set_skip_samples (int num_skip); 35 | 36 | /** Reads the input file. Options need to be set before calling this. */ 37 | void read_from_file (std::string const& filename); 38 | 39 | /* Returns the list of samples. */ 40 | SampleList const& get_samples (void) const; 41 | /* Returns the list of samples. */ 42 | SampleList& get_samples (void); 43 | 44 | /** Reset to defaults and release points. */ 45 | void clear (void); 46 | 47 | private: 48 | float scale_factor; 49 | int num_skip; 50 | SampleList samples; 51 | }; 52 | 53 | /* ---------------------------------------------------------------- */ 54 | 55 | inline 56 | PointSet::PointSet (void) 57 | { 58 | this->clear(); 59 | } 60 | 61 | inline 62 | PointSet::PointSet (std::string const& filename) 63 | { 64 | this->clear(); 65 | this->read_from_file(filename); 66 | } 67 | 68 | inline void 69 | PointSet::set_scale_factor (float factor) 70 | { 71 | this->scale_factor = factor; 72 | } 73 | 74 | inline void 75 | PointSet::set_skip_samples (int num_skip) 76 | { 77 | this->num_skip = num_skip; 78 | } 79 | 80 | inline PointSet::SampleList const& 81 | PointSet::get_samples (void) const 82 | { 83 | return this->samples; 84 | } 85 | 86 | inline PointSet::SampleList& 87 | PointSet::get_samples (void) 88 | { 89 | return this->samples; 90 | } 91 | 92 | inline void 93 | PointSet::clear (void) 94 | { 95 | this->scale_factor = 1.0f; 96 | this->num_skip = 0; 97 | this->samples.clear(); 98 | } 99 | 100 | FSSR_NAMESPACE_END 101 | 102 | #endif /* FSSR_POINTSET_HEADER */ 103 | -------------------------------------------------------------------------------- /libs/fssr/triangulation.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #include 7 | 8 | #include "math/geometry.h" 9 | #include "fssr/defines.h" 10 | #include "fssr/triangulation.h" 11 | 12 | FSSR_NAMESPACE_BEGIN 13 | 14 | void 15 | MinAreaTriangulation::triangulate (std::vector const& verts, 16 | std::vector* indices) 17 | { 18 | if (verts.size() < 3) 19 | throw std::invalid_argument("Invalid polygon with <3 vertices"); 20 | 21 | if (verts.size() == 3) 22 | { 23 | indices->push_back(0); 24 | indices->push_back(1); 25 | indices->push_back(2); 26 | return; 27 | } 28 | 29 | this->min_area_table.clear(); 30 | this->mid_point_table.clear(); 31 | this->min_area_table.resize(verts.size() * verts.size(), -1.0f); 32 | this->mid_point_table.resize(verts.size() * verts.size(), -1); 33 | this->compute_table(verts, 0, 1); 34 | indices->clear(); 35 | this->compute_triangulation(indices, 0, 1, verts.size()); 36 | } 37 | 38 | float 39 | MinAreaTriangulation::compute_table (std::vector const& verts, 40 | int start_id, int end_id) 41 | { 42 | if (start_id == end_id) 43 | return 0.0f; 44 | if (start_id == (end_id + 1) % (int)verts.size()) 45 | return 0.0f; 46 | 47 | int const index = start_id * verts.size() + end_id; 48 | float& min_area = this->min_area_table[index]; 49 | if (min_area < 0.0f) 50 | { 51 | for (int mid_point = (end_id + 1) % (int)verts.size(); 52 | mid_point != start_id; 53 | mid_point = (mid_point + 1) % (int)verts.size()) 54 | { 55 | float temp = this->compute_table(verts, start_id, mid_point) 56 | + this->compute_table(verts, mid_point, end_id) 57 | + math::geom::triangle_area(verts[start_id], 58 | verts[end_id], verts[mid_point]); 59 | 60 | if (temp < min_area || min_area < 0.0f) 61 | { 62 | min_area = temp; 63 | this->mid_point_table[index] = mid_point; 64 | } 65 | } 66 | } 67 | 68 | return min_area; 69 | } 70 | 71 | void 72 | MinAreaTriangulation::compute_triangulation (std::vector* indices, 73 | int start_id, int end_id, int num_verts) 74 | { 75 | int const index = start_id * num_verts + end_id; 76 | int mid_point = this->mid_point_table[index]; 77 | if (mid_point < 0) 78 | return; 79 | indices->push_back(start_id); 80 | indices->push_back(end_id); 81 | indices->push_back(mid_point); 82 | this->compute_triangulation(indices, start_id, mid_point, num_verts); 83 | this->compute_triangulation(indices, mid_point, end_id, num_verts); 84 | } 85 | 86 | FSSR_NAMESPACE_END 87 | -------------------------------------------------------------------------------- /libs/fssr/iso_octree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_ISO_OCTREE_HEADER 7 | #define FSSR_ISO_OCTREE_HEADER 8 | 9 | #include 10 | 11 | #include "fssr/defines.h" 12 | #include "fssr/voxel.h" 13 | #include "fssr/octree.h" 14 | 15 | FSSR_NAMESPACE_BEGIN 16 | 17 | /** 18 | * Given an octree with samples, this class generates the actual implicit 19 | * function by querying function values at the octree primal vertices of 20 | * the leaf nodes. The voxels are stored in a map, which is convenient and 21 | * simplifies the data structure (because neighboring octree nodes share 22 | * common voxels). 23 | */ 24 | class IsoOctree : public Octree 25 | { 26 | public: 27 | typedef std::vector > VoxelVector; 28 | 29 | public: 30 | IsoOctree (void); 31 | 32 | /** Evaluate the implicit function for all voxels on all leaf nodes. */ 33 | void compute_voxels (void); 34 | 35 | /** 36 | * Sets the maximum level on which voxels are generated. See voxel.h. 37 | * The default is 19, deeper levels are not supported by Kazhdans code. 38 | */ 39 | void set_max_level (int max_level); 40 | 41 | /** 42 | * Returns the maximum level on which voxels are generated. 43 | * The root level is 0, children are at level 1, and so on. 44 | */ 45 | int get_max_level (void) const; 46 | 47 | /** Returns the map of computed voxels. */ 48 | VoxelVector const& get_voxels (void) const; 49 | /** Cleas the octree and voxels. */ 50 | void clear (void); 51 | 52 | /** Writes the voxel data and octree hierarchy to file. */ 53 | void write_to_file (std::string const& filename) const; 54 | /** Reads the voxel data and octree hierarchy from file. */ 55 | void read_from_file (std::string const& filename); 56 | 57 | private: 58 | void compute_all_voxels (void); 59 | VoxelData sample_ifn (math::Vec3d const& voxel_pos); 60 | void print_progress (std::size_t voxels_done, std::size_t voxels_total); 61 | 62 | private: 63 | int max_level; 64 | VoxelVector voxels; 65 | std::size_t num_processed; 66 | }; 67 | 68 | FSSR_NAMESPACE_END 69 | 70 | /* ------------------------- Implementation ---------------------------- */ 71 | 72 | FSSR_NAMESPACE_BEGIN 73 | 74 | inline 75 | IsoOctree::IsoOctree (void) 76 | { 77 | this->clear(); 78 | } 79 | 80 | inline IsoOctree::VoxelVector const& 81 | IsoOctree::get_voxels (void) const 82 | { 83 | return this->voxels; 84 | } 85 | 86 | inline void 87 | IsoOctree::clear (void) 88 | { 89 | this->Octree::clear(); 90 | this->voxels.clear(); 91 | this->max_level = 19; 92 | } 93 | 94 | inline void 95 | IsoOctree::set_max_level (int max_level) 96 | { 97 | this->max_level = max_level; 98 | } 99 | 100 | inline int 101 | IsoOctree::get_max_level (void) const 102 | { 103 | return this->max_level; 104 | } 105 | 106 | FSSR_NAMESPACE_END 107 | 108 | #endif /* FSSR_ISO_OCTREE_HEADER */ 109 | -------------------------------------------------------------------------------- /libs/iso/BinaryNode.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho 3 | All rights reserved. 4 | 5 | Contains modifications by Simon Fuhrmann, 2013. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of 11 | conditions and the following disclaimer. Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of the Johns Hopkins University nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | DAMAGE. 29 | */ 30 | 31 | #ifndef BINARY_NODE_INCLUDED 32 | #define BINARY_NODE_INCLUDED 33 | 34 | template 35 | class BinaryNode 36 | { 37 | public: 38 | static inline int CenterCount(int depth){return 1<>=1; 64 | depth++; 65 | } 66 | offset=(idx+1)-(1< 7 | 8 | #include "mve/mesh_io_ply.h" 9 | #include "fssr/pointset.h" 10 | 11 | FSSR_NAMESPACE_BEGIN 12 | 13 | void 14 | PointSet::read_from_file (std::string const& filename) 15 | { 16 | /* Load or generate point set. */ 17 | mve::TriangleMesh::Ptr mesh = mve::geom::load_ply_mesh(filename); 18 | 19 | mve::TriangleMesh::VertexList const& verts = mesh->get_vertices(); 20 | if (verts.empty()) 21 | throw std::invalid_argument("Point set is empty!"); 22 | 23 | mve::TriangleMesh::NormalList const& vnormals = mesh->get_vertex_normals(); 24 | if (!mesh->has_vertex_normals()) 25 | throw std::invalid_argument("Vertex normals missing!"); 26 | 27 | mve::TriangleMesh::ValueList const& vvalues = mesh->get_vertex_values(); 28 | if (!mesh->has_vertex_values()) 29 | throw std::invalid_argument("Vertex scale missing!"); 30 | 31 | mve::TriangleMesh::ConfidenceList& vconfs = mesh->get_vertex_confidences(); 32 | if (!mesh->has_vertex_confidences()) 33 | vconfs.resize(verts.size(), 1.0f); 34 | 35 | mve::TriangleMesh::ColorList& vcolors = mesh->get_vertex_colors(); 36 | if (!mesh->has_vertex_colors()) 37 | vcolors.resize(verts.size(), math::Vec4f(-1.0f)); 38 | 39 | std::size_t num_skipped_zero_normal = 0; 40 | std::size_t num_unnormalized_normals = 0; 41 | std::size_t num_skipped_invalid_confidence = 0; 42 | std::size_t num_skipped_invalid_scale = 0; 43 | this->samples.reserve(verts.size()); 44 | for (std::size_t i = 0; i < verts.size(); i += (1 + this->num_skip)) 45 | { 46 | Sample s; 47 | s.pos = verts[i]; 48 | s.normal = vnormals[i]; 49 | s.scale = vvalues[i] * this->scale_factor; 50 | s.confidence = vconfs[i]; 51 | s.color = math::Vec3f(*vcolors[i]); 52 | 53 | if (s.scale <= 0.0f) 54 | { 55 | num_skipped_invalid_scale += 1; 56 | continue; 57 | } 58 | 59 | if (s.confidence <= 0.0f) 60 | { 61 | num_skipped_invalid_confidence += 1; 62 | continue; 63 | } 64 | 65 | if (s.normal.square_norm() == 0.0f) 66 | { 67 | num_skipped_zero_normal += 1; 68 | continue; 69 | } 70 | 71 | if (!MATH_EPSILON_EQ(1.0f, s.normal.square_norm(), 1e-5f)) 72 | { 73 | s.normal.normalize(); 74 | num_unnormalized_normals += 1; 75 | } 76 | 77 | this->samples.push_back(s); 78 | } 79 | 80 | if (num_skipped_invalid_scale > 0) 81 | { 82 | std::cout << "WARNING: Skipped " << num_skipped_invalid_scale 83 | << " samples with invalid scale." << std::endl; 84 | } 85 | if (num_skipped_invalid_confidence > 0) 86 | { 87 | std::cout << "WARNING: Skipped " << num_skipped_invalid_confidence 88 | << " samples with invalid confidence." << std::endl; 89 | } 90 | if (num_skipped_zero_normal > 0) 91 | { 92 | std::cout << "WARNING: Skipped " << num_skipped_zero_normal 93 | << " samples with zero-length normal." << std::endl; 94 | } 95 | if (num_unnormalized_normals > 0) 96 | { 97 | std::cout << "WARNING: Normalized " << num_unnormalized_normals 98 | << " normals with non-unit length." << std::endl; 99 | } 100 | } 101 | 102 | FSSR_NAMESPACE_END 103 | -------------------------------------------------------------------------------- /libs/iso/NeighborKey.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Contains modifications by Simon Fuhrmann, 2013. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of 11 | conditions and the following disclaimer. Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of the Johns Hopkins University nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | DAMAGE. 29 | */ 30 | 31 | #ifndef NEIGHBOR_KEY_INCLUDED 32 | #define NEIGHBOR_KEY_INCLUDED 33 | 34 | #include "Octree.h" 35 | 36 | template 37 | struct Neighbors 38 | { 39 | Neighbors(void); 40 | void clear(void); 41 | 42 | OctNode* neighbors[3][3][3]; 43 | typename OctNode::NodeIndex nIndex; 44 | }; 45 | 46 | template 47 | class NeighborKey 48 | { 49 | public: 50 | Neighbors* neighbors; 51 | int depth; 52 | 53 | NeighborKey(void); 54 | ~NeighborKey(void); 55 | 56 | void set(const int& depth); 57 | Neighbors& setNeighbors(OctNode* node); 58 | Neighbors& getNeighbors(OctNode* node); 59 | 60 | private: 61 | int _depth; 62 | Neighbors& _setNeighbors(OctNode* node,const int& d); 63 | Neighbors& _getNeighbors(OctNode* node,const int& d); 64 | }; 65 | 66 | template 67 | class IsoNeighborKey : public NeighborKey 68 | { 69 | public: 70 | void GetCornerNeighbors(OctNode* node,const int& c,OctNode* neighbors[Cube::CORNERS]); 71 | OctNode* FaceNeighbor(OctNode* node,int dir,int off); 72 | OctNode* EdgeNeighbor(OctNode* node,int o,int i1,int i2); 73 | OctNode* CornerNeighbor(OctNode* node,int x,int y,int z); 74 | 75 | static void CornerIndex(const int& c,int idx[3]); 76 | static void EdgeIndex(const int& c,int idx[3]); 77 | static void FaceIndex(const int& c,int idx[3]); 78 | 79 | private: 80 | void __GetCornerNeighbors(OctNode* node,const int& depth,const int& c,OctNode* neighbors[Cube::CORNERS]); 81 | OctNode* __FaceNeighbor(OctNode* node,const int& depth,int dir,int off); 82 | OctNode* __EdgeNeighbor(OctNode* node,const int& depth,int o,int i1,int i2); 83 | OctNode* __CornerNeighbor(OctNode* node,const int& depth,int x,int y,int z); 84 | }; 85 | 86 | 87 | #include "NeighborKey.inl" 88 | 89 | #endif // NEIGHBOR_KEY_INCLUDED 90 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Introduction 2 | ====================================================================== 3 | 4 | This is a guideline how to compile and use FSSR, the Floating Scale 5 | Surface Reconstruction software accompanying the following paper: 6 | 7 | Floating Scale Surface Reconstruction 8 | Simon Fuhrmann and Michael Goesele 9 | In: ACM ToG (Proceedings of ACM SIGGRAPH 2014). 10 | http://tinyurl.com/floating-scale-surface-recon 11 | 12 | This software is based on MVE, the Multi-View Environment. See the link below 13 | for details on MVE, and the next section for downloading and building MVE. 14 | 15 | http://www.gris.tu-darmstadt.de/projects/multiview-environment/ 16 | 17 | 18 | Downlaoding and Building MVE and FSSR 19 | ====================================================================== 20 | 21 | MVE requires libjpeg, libpng and libtiff as dependencies, which can be obtained 22 | using the package system of your distribution. 23 | 24 | The following commands should get you started: 25 | 26 | # Download and compile MVE 27 | git clone https://github.com/simonfuhrmann/mve.git 28 | make -j8 -C mve 29 | 30 | # Download and compile FSSR 31 | git clone https://github.com/simonfuhrmann/fssr.git 32 | make -j8 -C fssr 33 | 34 | The binaries will be located and ready for execution here: 35 | 36 | fssr/apps/fssr_octree 37 | fssr/apps/fssr_surface 38 | 39 | 40 | FSSR Input Data 41 | ====================================================================== 42 | 43 | FSSR requires as input a point set which contains several required and 44 | some optional attributes per sample. Currently, only PLY is supported as 45 | input format. All required and optional attributes are listed below: 46 | 47 | Name PLY Attribute(s) Type Required? 48 | ------------------------------------------------------------------ 49 | 3D positions x, y, z float 3-vector required 50 | normals nx, ny, nz float 3-vector required 51 | scale values value single float value required 52 | confidence confidence single float value optional 53 | color red, green, blue uchar 3-vector optional 54 | 55 | A typical PLY header (without color) then looks like this: 56 | 57 | format binary_little_endian 1.0 58 | element vertex 36228 59 | property float x 60 | property float y 61 | property float z 62 | property float nx 63 | property float ny 64 | property float nz 65 | property float confidence 66 | property float value 67 | end_header 68 | 69 | If you are using MVE and want to create a surface from a set of depth maps, 70 | you can use the 'scene2pset' tools, which is included in the MVE distribution. 71 | 72 | Usage: scene2pset [ OPTS ] SCENE_DIR MESH_OUT 73 | 74 | Make sure the parameters --depth and --image specify the correct depth map 75 | and corresponding image. Also enable normals, scale and confidence with the 76 | parameters --with-normals, --with-scale and --with-conf. 77 | 78 | 79 | Running FSSR 80 | ====================================================================== 81 | 82 | First, the implicit function defined by the input points is sampled and 83 | stored in an octree. This is done with the 'fssr_octree' tool. 84 | 85 | Usage: fssr_octree [ OPTS ] IN_PLY [ IN_PLY ... ] OUT_OCTREE 86 | 87 | It takes one or more PLY files as input (connectivity information is ignored) 88 | and samples the implicit function over an octree hierarchy. The result is 89 | stored in a binary format. (Check the code for details.) 90 | 91 | Second, the sampling of the implicit function must be converted to a 92 | surface mesh. This is done with a second 'fssr_surface' tool. 93 | 94 | Usage: fssr_surface [ OPTS ] IN_OCTREE OUT_PLY_MESH 95 | 96 | The tool takes as input the octree and produces a mesh. Several options 97 | can control the resulting mesh. See the tools output for details. 98 | 99 | 100 | Trouble? Contact! 101 | ====================================================================== 102 | 103 | Contact information can be found on the FSSR and MVE websites. 104 | -------------------------------------------------------------------------------- /libs/fssr/voxel.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #include 7 | 8 | #include "math/vector.h" 9 | #include "fssr/voxel.h" 10 | 11 | FSSR_NAMESPACE_BEGIN 12 | 13 | void 14 | VoxelIndex::from_path_and_corner (Octree::NodePath const& path, int corner) 15 | { 16 | /* Compute node index from the node path. */ 17 | math::Vector coords(0, 0, 0); 18 | for (int l = path.level - 1; l >= 0; --l) 19 | { 20 | uint64_t index = (path.path >> (3 * l)) & 7; 21 | for (int i = 0; i < 3; ++i) 22 | { 23 | coords[i] = coords[i] << 1; 24 | coords[i] += (index & (1 << i)) >> i; 25 | } 26 | } 27 | 28 | /* Convert node index to voxel index. */ 29 | for (int i = 0; i < 3; ++i) 30 | { 31 | coords[i] += (corner >> i) & 1; 32 | coords[i] = coords[i] << (20 - path.level); 33 | } 34 | 35 | /* Compute voxel ID. */ 36 | this->index = coords[0] | coords[1] << 21 | coords[2] << 42; 37 | } 38 | 39 | math::Vec3d 40 | VoxelIndex::compute_position (math::Vec3d const& center, double size) 41 | { 42 | double const dim_max = static_cast(1 << 20); 43 | double const fx = static_cast(this->get_offset_x()) / dim_max; 44 | double const fy = static_cast(this->get_offset_y()) / dim_max; 45 | double const fz = static_cast(this->get_offset_z()) / dim_max; 46 | return center - size / 2.0 + math::Vec3d(fx, fy, fz) * size; 47 | } 48 | 49 | uint32_t 50 | VoxelIndex::get_offset_x (void) const 51 | { 52 | return static_cast((this->index >> 0) & 0x1fffff); 53 | } 54 | 55 | uint32_t 56 | VoxelIndex::get_offset_y (void) const 57 | { 58 | return static_cast((this->index >> 21) & 0x1fffff); 59 | } 60 | 61 | uint32_t 62 | VoxelIndex::get_offset_z (void) const 63 | { 64 | return static_cast((this->index >> 42) & 0x1fffff); 65 | } 66 | 67 | /* ---------------------------------------------------------------- */ 68 | 69 | void 70 | VoxelIndexWithLevel::from_voxel_id_and_level (uint64_t voxel_id, uint8_t level) 71 | { 72 | /* 5 bits for the level, the rest for the voxel ID. */ 73 | this->index = (static_cast(level) << 59 | voxel_id); 74 | } 75 | 76 | void 77 | VoxelIndexWithLevel::from_path_and_corner (Octree::NodePath const& path, 78 | int corner) 79 | { 80 | /* Compute node index from the node path. */ 81 | math::Vector coords(0, 0, 0); 82 | for (int l = path.level - 1; l >= 0; --l) 83 | { 84 | uint64_t index = (path.path >> (3 * l)) & 7; 85 | for (int i = 0; i < 3; ++i) 86 | { 87 | coords[i] = coords[i] << 1; 88 | coords[i] += (index & (1 << i)) >> i; 89 | } 90 | } 91 | 92 | /* Convert node index to voxel index. */ 93 | for (int i = 0; i < 3; ++i) 94 | coords[i] += (corner >> i) & 1; 95 | 96 | /* Compute voxel ID. */ 97 | uint64_t voxel_id = coords[0] | coords[1] << 19 | coords[2] << 38; 98 | this->from_voxel_id_and_level(voxel_id, path.level); 99 | } 100 | 101 | uint8_t 102 | VoxelIndexWithLevel::get_level (void) const 103 | { 104 | return static_cast(this->index >> 59); 105 | } 106 | 107 | uint32_t 108 | VoxelIndexWithLevel::get_offset_x (void) const 109 | { 110 | return static_cast((this->index >> 0) & 0x7ffff); 111 | } 112 | 113 | uint32_t 114 | VoxelIndexWithLevel::get_offset_y (void) const 115 | { 116 | return static_cast((this->index >> 19) & 0x7ffff); 117 | } 118 | 119 | uint32_t 120 | VoxelIndexWithLevel::get_offset_z (void) const 121 | { 122 | return static_cast((this->index >> 38) & 0x7ffff); 123 | } 124 | 125 | /* ---------------------------------------------------------------- */ 126 | 127 | VoxelData 128 | interpolate (VoxelData const& d1, float w1, VoxelData const& d2, float w2) 129 | { 130 | VoxelData ret; 131 | ret.value = w1 * d1.value + w2 * d2.value; 132 | ret.conf = std::min(d1.conf, d2.conf); 133 | ret.scale = w1 * d1.scale + w2 * d2.scale; 134 | ret.color = w1 * d1.color + w2 * d2.color; 135 | return ret; 136 | } 137 | 138 | FSSR_NAMESPACE_END 139 | -------------------------------------------------------------------------------- /tests/gtest_octree.cc: -------------------------------------------------------------------------------- 1 | // Test cases for octree. 2 | // Written by Simon Fuhrmann. 3 | 4 | #include 5 | #include 6 | 7 | #include "fssr/octree.h" 8 | #include "fssr/sample.h" 9 | 10 | TEST(OctreeTest, TestEmptyOctreeOperations) 11 | { 12 | fssr::Octree octree; 13 | EXPECT_EQ(0, octree.get_num_levels()); 14 | EXPECT_EQ(0, octree.get_num_samples()); 15 | EXPECT_EQ(0, octree.get_num_nodes()); 16 | 17 | std::vector stats; 18 | octree.get_points_per_level(&stats); 19 | EXPECT_TRUE(stats.empty()); 20 | } 21 | 22 | TEST(OctreeTest, TestOneSampleOctreeOperations) 23 | { 24 | fssr::Sample s; 25 | s.pos = math::Vec3f(0.0f); 26 | s.scale = 1.0f; 27 | 28 | fssr::Octree octree; 29 | octree.insert_sample(s); 30 | EXPECT_EQ(1, octree.get_num_levels()); 31 | EXPECT_EQ(1, octree.get_num_samples()); 32 | EXPECT_EQ(1, octree.get_num_nodes()); 33 | 34 | std::vector stats; 35 | octree.get_points_per_level(&stats); 36 | ASSERT_EQ(1, stats.size()); 37 | EXPECT_EQ(1, stats[0]); 38 | } 39 | 40 | TEST(OctreeTest, TestTwoSamplesDescend) 41 | { 42 | fssr::Sample s1, s2; 43 | s1.pos = math::Vec3f(0.0f); 44 | s1.scale = 1.0f; 45 | s2.pos = math::Vec3f(0.0f); 46 | s2.scale = 0.5f; 47 | 48 | fssr::Octree octree; 49 | octree.insert_sample(s1); 50 | octree.insert_sample(s2); 51 | 52 | EXPECT_EQ(2, octree.get_num_levels()); 53 | EXPECT_EQ(2, octree.get_num_samples()); 54 | EXPECT_EQ(2, octree.get_num_nodes()); 55 | } 56 | 57 | TEST(OctreeTest, TestTwoSamplesExpand) 58 | { 59 | fssr::Sample s1, s2; 60 | s1.pos = math::Vec3f(0.0f); 61 | s1.scale = 1.0f; 62 | s2.pos = math::Vec3f(0.0f); 63 | s2.scale = 2.0f; 64 | 65 | fssr::Octree octree; 66 | octree.insert_sample(s1); 67 | octree.insert_sample(s2); 68 | 69 | EXPECT_EQ(2, octree.get_num_levels()); 70 | EXPECT_EQ(2, octree.get_num_samples()); 71 | EXPECT_EQ(2, octree.get_num_nodes()); 72 | } 73 | 74 | TEST(OctreeTest, TestTwoSamplesSameScale) 75 | { 76 | fssr::Sample s1, s2; 77 | s1.pos = math::Vec3f(0.0f); 78 | s1.scale = 1.0f; 79 | s2.pos = math::Vec3f(0.0f); 80 | s2.scale = 1.0f; 81 | 82 | fssr::Octree octree; 83 | octree.insert_sample(s1); 84 | octree.insert_sample(s2); 85 | 86 | EXPECT_EQ(1, octree.get_num_levels()); 87 | EXPECT_EQ(2, octree.get_num_samples()); 88 | EXPECT_EQ(1, octree.get_num_nodes()); 89 | } 90 | 91 | TEST(OctreeTest, TestInsertIntoOctants) 92 | { 93 | fssr::Sample root; 94 | root.pos = math::Vec3f(0.0f); 95 | root.scale = 1.0f; 96 | 97 | fssr::Sample oct[8]; 98 | for (int i = 0; i < 8; ++i) 99 | { 100 | oct[i].scale = 0.5f; 101 | oct[i].pos = math::Vec3f( 102 | (i & 1 ? -0.1f : 0.1f), 103 | (i & 2 ? -0.1f : 0.1f), 104 | (i & 4 ? -0.1f : 0.1f)); 105 | } 106 | 107 | fssr::Octree octree; 108 | octree.insert_sample(root); 109 | for (int i = 0; i < 8; ++i) 110 | octree.insert_sample(oct[i]); 111 | 112 | EXPECT_EQ(2, octree.get_num_levels()); 113 | EXPECT_EQ(9, octree.get_num_samples()); 114 | EXPECT_EQ(9, octree.get_num_nodes()); 115 | } 116 | 117 | TEST(OctreeTest, OctreeReadWriteEmpty) 118 | { 119 | // Just an empty octree. 120 | std::stringstream ss_in("0"); 121 | std::stringstream ss_out; 122 | 123 | fssr::Octree octree; 124 | octree.read_hierarchy(ss_in, false); 125 | octree.write_hierarchy(ss_out, false); 126 | EXPECT_EQ(ss_out.str(), ss_in.str()); 127 | } 128 | 129 | TEST(OctreeTest, OctreeReadWriteRootOnly) 130 | { 131 | // Root node with no children. 132 | std::stringstream ss_in("100000000"); 133 | std::stringstream ss_out; 134 | 135 | fssr::Octree octree; 136 | octree.read_hierarchy(ss_in, false); 137 | octree.write_hierarchy(ss_out, false); 138 | EXPECT_EQ(ss_out.str(), ss_in.str()); 139 | } 140 | 141 | TEST(OctreeTest, OctreeReadWriteHierarcy1) 142 | { 143 | // More complicated hierarchy. Root node with two children. 144 | // Each child has another child. 145 | std::stringstream ss_in( 146 | // root child 1 child 2 child 1.1 child 2.1 147 | "1" "00100100" "00010000" "00000010" "00000000" "00000000"); 148 | std::stringstream ss_out; 149 | 150 | fssr::Octree octree; 151 | octree.read_hierarchy(ss_in, false); 152 | octree.write_hierarchy(ss_out, false); 153 | EXPECT_EQ(ss_out.str(), ss_in.str()); 154 | } 155 | -------------------------------------------------------------------------------- /libs/fssr/voxel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_VOXEL_HEADER 7 | #define FSSR_VOXEL_HEADER 8 | 9 | #include // TODO: Use once C++11 is standard. 10 | 11 | #include "fssr/defines.h" 12 | #include "fssr/octree.h" 13 | 14 | FSSR_NAMESPACE_BEGIN 15 | 16 | /** 17 | * The voxel index is a unique 64 bit ID for each voxel in the octree. 18 | * The index is designed in such a way that the voxel ID is independent 19 | * of the level, i.e. neighboring nodes on different levels share voxels 20 | * with the same ID. The bits are assigned as follows: 21 | * 22 | * 0 000...000 000...000 000...000 23 | * -------- --------- --------- --------- 24 | * 1 unused 21 bits 21 bits 21 bits 25 | * bit z-coord y-coord x-coord 26 | * 27 | * Since the maximum voxel index for level L is 2^L, this limits 28 | * the maximum level to 20 with 21 bits, i.e. 2^20-1 < 2^20 < 2^21-1. 29 | * Voxels at a lower level are shifted to the hightest bit to obtain the 30 | * same index over different levels. For example, index I on level L is 31 | * shifted I << (20 - L). 32 | */ 33 | struct VoxelIndex 34 | { 35 | public: 36 | void from_path_and_corner (Octree::NodePath const& path, int corner); 37 | math::Vec3d compute_position (math::Vec3d const& center, double size); 38 | uint32_t get_offset_x (void) const; 39 | uint32_t get_offset_y (void) const; 40 | uint32_t get_offset_z (void) const; 41 | bool operator< (VoxelIndex const& other) const; 42 | 43 | public: 44 | uint64_t index; 45 | }; 46 | 47 | /* --------------------------------------------------------------------- */ 48 | 49 | /** 50 | * The voxel index links the voxel ID together with the level of the voxel. 51 | * The index is stored as 64 bit value and the bits are assigned as follows: 52 | * 53 | * 00000 00 000...000 000...000 000...000 54 | * ----- -- --------- --------- --------- 55 | * 5 bit 2 unused 19 bits 19 bits 19 bits 56 | * level bits z-coord y-coord x-coord 57 | * 58 | * Since the maximum voxel index per axis for level L is 2^L, this limits 59 | * the maximum level to 18 with 19 bits, i.e. 2^18-1 < 2^18 < 2^19-1. 60 | * 61 | * Voxels are stored in the corners of octree nodes. Thus an octree has a 62 | * maximum of 2^L nodes per level per dimension, but 2^L+1 voxels. 63 | * Neighboring octree nodes share voxels. This is usually implemented using 64 | * maps or hash tables for efficient access. This class offers support for 65 | * conversion from octree node index plus corner index to voxel index. 66 | */ 67 | struct VoxelIndexWithLevel 68 | { 69 | public: 70 | void from_voxel_id_and_level (uint64_t voxel_id, uint8_t level); 71 | void from_path_and_corner (Octree::NodePath const& path, int corner); 72 | uint8_t get_level (void) const; 73 | uint32_t get_offset_x (void) const; 74 | uint32_t get_offset_y (void) const; 75 | uint32_t get_offset_z (void) const; 76 | bool operator< (VoxelIndexWithLevel const& other) const; 77 | 78 | public: 79 | uint64_t index; 80 | }; 81 | 82 | /* --------------------------------------------------------------------- */ 83 | 84 | /** 85 | * Stores per voxel data. The is the actual SDF/implicit function value, 86 | * a confidence value and the cumulative color. 87 | */ 88 | struct VoxelData 89 | { 90 | public: 91 | VoxelData (void); 92 | 93 | public: 94 | float value; 95 | float conf; 96 | float scale; 97 | math::Vec3f color; 98 | }; 99 | 100 | /* --------------------------------------------------------------------- */ 101 | 102 | /** 103 | * Interpolates between two VoxelData objects for Marching Cubes. 104 | * The interpolation linearly interpolates value and color, and uses 105 | * the minimum of the confidence. 106 | */ 107 | VoxelData 108 | interpolate (VoxelData const& d1, float w1, VoxelData const& d2, float w2); 109 | 110 | FSSR_NAMESPACE_END 111 | 112 | /* ------------------------- Implementation ---------------------------- */ 113 | 114 | FSSR_NAMESPACE_BEGIN 115 | 116 | inline bool 117 | VoxelIndex::operator< (VoxelIndex const& other) const 118 | { 119 | return this->index < other.index; 120 | } 121 | 122 | inline bool 123 | VoxelIndexWithLevel::operator< (VoxelIndexWithLevel const& other) const 124 | { 125 | return this->index < other.index; 126 | } 127 | 128 | inline 129 | VoxelData::VoxelData (void) 130 | : value(0.0f) 131 | , conf(0.0f) 132 | , scale(0.0f) 133 | , color(0.0f) 134 | { 135 | } 136 | 137 | FSSR_NAMESPACE_END 138 | 139 | #endif /* FSSR_VOXEL_HEADER */ 140 | -------------------------------------------------------------------------------- /libs/iso/Octree.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho 3 | All rights reserved. 4 | 5 | Contains modifications by Simon Fuhrmann, 2013. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of 11 | conditions and the following disclaimer. Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of the Johns Hopkins University nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | DAMAGE. 29 | */ 30 | 31 | #ifndef OCT_NODE_INCLUDED 32 | #define OCT_NODE_INCLUDED 33 | 34 | #include 35 | 36 | #include "math/vector.h" 37 | #include "BinaryNode.h" 38 | #include "MarchingCubes.h" 39 | 40 | template 41 | class OctNode 42 | { 43 | public: // Types 44 | typedef math::Vector VertexType; 45 | 46 | class NodeIndex 47 | { 48 | public: 49 | NodeIndex(void); 50 | int depth,offset[3]; 51 | NodeIndex child(const int& cIndex) const; 52 | NodeIndex parent(void) const; 53 | NodeIndex& operator += (const int& cIndex); 54 | NodeIndex& operator -- (void); 55 | }; 56 | 57 | public: // Variables 58 | OctNode* parent; 59 | OctNode* children; 60 | NodeData nodeData; 61 | 62 | public: // Methods 63 | OctNode(void); 64 | ~OctNode(void); 65 | int initChildren(void); 66 | void deleteChildren(void); 67 | 68 | static inline void CenterAndWidth(const NodeIndex& nIndex,VertexType& center,Real& width); 69 | 70 | int maxDepth(void) const; 71 | int nodes(void) const; 72 | int leaves(void) const; 73 | int maxDepthLeaves(const int& maxDepth) const; 74 | 75 | const OctNode* root(void) const; 76 | 77 | const OctNode* nextLeaf(const OctNode* currentLeaf) const; 78 | OctNode* nextLeaf(OctNode* currentLeaf); 79 | const OctNode* nextNode(const OctNode* currentNode) const; 80 | OctNode* nextNode(OctNode* currentNode); 81 | const OctNode* nextBranch(const OctNode* current) const; 82 | OctNode* nextBranch(OctNode* current); 83 | 84 | const OctNode* nextLeaf(const OctNode* currentLeaf,NodeIndex &nIndex) const; 85 | OctNode* nextLeaf(OctNode* currentLeaf,NodeIndex &nIndex); 86 | const OctNode* nextNode(const OctNode* currentNode,NodeIndex &nIndex) const; 87 | OctNode* nextNode(OctNode* currentNode,NodeIndex &nIndex); 88 | const OctNode* nextBranch(const OctNode* current,NodeIndex &nIndex) const; 89 | OctNode* nextBranch(OctNode* current,NodeIndex &nIndex); 90 | 91 | void setFullDepth(const int& maxDepth); 92 | 93 | OctNode* faceNeighbor(const int& faceIndex,const int& forceChildren=0); 94 | const OctNode* faceNeighbor(const int& faceIndex) const; 95 | OctNode* edgeNeighbor(const int& edgeIndex,const int& forceChildren=0); 96 | const OctNode* edgeNeighbor(const int& edgeIndex) const; 97 | OctNode* cornerNeighbor(const int& cornerIndex,const int& forceChildren=0); 98 | const OctNode* cornerNeighbor(const int& cornerIndex) const; 99 | 100 | static long long CornerIndex(const NodeIndex &nIndex,const int& cIndex,const int& maxDepth); 101 | static long long CornerIndex(const NodeIndex &nIndex,const int& cIndex,const int& maxDepth,int index[3]); 102 | 103 | private: 104 | const OctNode* __faceNeighbor(const int& dir,const int& off) const; 105 | const OctNode* __edgeNeighbor(const int& o,const int i[2],const int idx[2]) const; 106 | OctNode* __faceNeighbor(const int& dir,const int& off,const int& forceChildren); 107 | OctNode* __edgeNeighbor(const int& o,const int i[2],const int idx[2],const int& forceChildren); 108 | }; 109 | 110 | 111 | #include "Octree.inl" 112 | 113 | #endif // OCT_NODE 114 | -------------------------------------------------------------------------------- /libs/iso/SimonIsoOctree.inl: -------------------------------------------------------------------------------- 1 | /* 2 | * Written by Simon Fuhrmann, 2013. 3 | */ 4 | 5 | #include "math/vector.h" 6 | #include "util/timer.h" 7 | #include "fssr/iso_octree.h" 8 | #include "fssr/basis_function.h" 9 | #include "fssr/triangulation.h" 10 | #include "SimonIsoOctree.h" 11 | 12 | #define MAX_DEPTH 19 13 | 14 | template 15 | void PolygonToTriangleMesh( 16 | std::vector > const& vertices, 17 | std::vector > const& polygons, 18 | std::vector* triangles) 19 | { 20 | fssr::MinAreaTriangulation tri; 21 | for (size_t i = 0; i < polygons.size(); i++) 22 | { 23 | std::vector > loop; 24 | loop.resize(polygons[i].size()); 25 | for (size_t j=0;j result; 28 | tri.triangulate(loop, &result); 29 | for (std::size_t j = 0; j < result.size(); ++j) 30 | triangles->push_back(polygons[i][result[j]]); 31 | } 32 | } 33 | 34 | inline void 35 | SimonIsoOctree::clear (void) 36 | { 37 | this->cornerValues.clear(); 38 | this->tree.deleteChildren(); 39 | } 40 | 41 | inline void 42 | SimonIsoOctree::set_octree (fssr::IsoOctree const& octree) 43 | { 44 | this->maxDepth = MAX_DEPTH; 45 | 46 | /* Compute translation/scaling. */ 47 | // To transform a point p in the octree: 48 | // p' = (translate + p) * scale 49 | // To transfrom it back 50 | // p = p' / scale - translate 51 | math::Vec3f aabb_min = octree.get_node_geom_for_root().aabb_min(); 52 | math::Vec3f aabb_max = octree.get_node_geom_for_root().aabb_max(); 53 | this->scale = 1.0f / (aabb_max - aabb_min).maximum(); 54 | this->translate = math::Vec3f(0.5f) / this->scale 55 | - (aabb_min + aabb_max) / 2.0f; 56 | 57 | // Transfer the octree structure to the Kazhdan octree. 58 | SimonOctNode::NodeIndex out_index; 59 | this->transfer_octree(octree.get_iterator_for_root(), 60 | &this->tree, out_index, octree.get_max_level()); 61 | 62 | // Copy the voxel data to the Kazhdan octree. 63 | this->copy_voxel_data(octree); 64 | } 65 | 66 | inline mve::TriangleMesh::Ptr 67 | SimonIsoOctree::extract_mesh (void) 68 | { 69 | int const fullCaseTable = 0; 70 | 71 | mve::TriangleMesh::Ptr mesh = mve::TriangleMesh::create(); 72 | mve::TriangleMesh::VertexList& verts = mesh->get_vertices(); 73 | mve::TriangleMesh::FaceList& faces = mesh->get_faces(); 74 | mve::TriangleMesh::ConfidenceList& confs = mesh->get_vertex_confidences(); 75 | mve::TriangleMesh::ColorList& colors = mesh->get_vertex_colors(); 76 | mve::TriangleMesh::ValueList& scales = mesh->get_vertex_values(); 77 | 78 | std::cout << "Getting ISO surface..." << std::flush; 79 | util::WallTimer timer; 80 | std::vector > polygons; 81 | std::vector vertex_data; 82 | this->getIsoSurface(0.0f, verts, vertex_data, polygons, fullCaseTable); 83 | std::cout << " took " << timer.get_elapsed() << "ms." << std::endl; 84 | 85 | /* De-Normalize the output vertices. */ 86 | for (std::size_t i = 0; i < verts.size(); ++i) 87 | verts[i] = verts[i] / this->scale - this->translate; 88 | 89 | /* Colorize mesh and copy confidence values. */ 90 | confs.resize(verts.size()); 91 | colors.resize(verts.size()); 92 | scales.resize(verts.size()); 93 | for (std::size_t i = 0; i < verts.size(); ++i) 94 | { 95 | confs[i] = vertex_data[i].conf; 96 | colors[i] = math::Vec4f(vertex_data[i].color, 1.0f); 97 | scales[i] = vertex_data[i].scale; 98 | } 99 | 100 | std::cout << "Converting polygons to triangles..." << std::flush; 101 | timer.reset(); 102 | PolygonToTriangleMesh(verts, polygons, &faces); 103 | std::cout << " took " << timer.get_elapsed() << "ms." << std::endl; 104 | 105 | return mesh; 106 | } 107 | 108 | inline void 109 | SimonIsoOctree::transfer_octree ( 110 | fssr::IsoOctree::Iterator const& in_iter, 111 | SimonOctNode* out_node, 112 | SimonOctNode::NodeIndex out_node_index, 113 | int max_level) 114 | { 115 | /* 116 | * Have to build octree at one level less (?!?) for whatever reasons. 117 | * Otherwise code tries to locate samples at one level too deep. 118 | */ 119 | if (in_iter.node_path.level >= max_level) 120 | return; 121 | 122 | /* Determine whether to descend into octree. */ 123 | if (in_iter.node->children[0] == NULL) 124 | return; 125 | 126 | out_node->initChildren(); 127 | for (int i = 0; i < 8; ++i) 128 | this->transfer_octree(in_iter.descend(i), 129 | &out_node->children[i], out_node_index.child(i), max_level); 130 | } 131 | 132 | inline void 133 | SimonIsoOctree::copy_voxel_data (fssr::IsoOctree const& isooctree) 134 | { 135 | fssr::IsoOctree::VoxelVector const& voxels = isooctree.get_voxels(); 136 | for (std::size_t i = 0; i < voxels.size(); i++) 137 | { 138 | this->cornerValues[voxels[i].first.index] = voxels[i].second; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /apps/fssr_octree/fssr_octree.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * App to create and sample an implicit function defined by the input samples. 3 | * Written by Simon Fuhrmann. 4 | * 5 | * The surface reconstruction approach implemented here is described in: 6 | * 7 | * Floating Scale Surface Reconstruction 8 | * Simon Fuhrmann and Michael Goesele 9 | * In: ACM ToG (Proceedings of ACM SIGGRAPH 2014). 10 | * http://tinyurl.com/floating-scale-surface-recon 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #include "util/timer.h" 17 | #include "util/arguments.h" 18 | #include "fssr/pointset.h" 19 | #include "fssr/iso_octree.h" 20 | 21 | struct AppSettings 22 | { 23 | std::vector in_files; 24 | std::string out_octree; 25 | int skip_samples; 26 | float scale_factor; 27 | int refine_octree; 28 | }; 29 | 30 | int 31 | main (int argc, char** argv) 32 | { 33 | /* Setup argument parser. */ 34 | util::Arguments args; 35 | args.set_exit_on_error(true); 36 | args.set_nonopt_minnum(2); 37 | args.set_helptext_indent(25); 38 | args.set_usage(argv[0], "[ OPTS ] IN_PLY [ IN_PLY ... ] OUT_OCTREE"); 39 | args.add_option('s', "scale-factor", true, "Multiply sample scale with factor [1.0]"); 40 | args.add_option('r', "refine-octree", true, "Refines octree with N levels [0]"); 41 | args.add_option('k', "skip-samples", true, "Skip input samples [0]"); 42 | args.set_description("Builds an octree from a set of input samples. " 43 | "The samples must have normals and the \"values\" PLY attribute " 44 | "(the scale of the samples). Both confidence values and vertex colors " 45 | "are optional. The output octree is the sampled implicit function " 46 | "ready for isosurface extraction."); 47 | args.parse(argc, argv); 48 | 49 | /* Init default settings. */ 50 | AppSettings conf; 51 | conf.skip_samples = 0; 52 | conf.scale_factor = 1.0f; 53 | conf.refine_octree = 0; 54 | 55 | /* Scan arguments. */ 56 | while (util::ArgResult const* arg = args.next_result()) 57 | { 58 | if (arg->opt == NULL) 59 | { 60 | conf.in_files.push_back(arg->arg); 61 | continue; 62 | } 63 | 64 | switch (arg->opt->sopt) 65 | { 66 | case 's': conf.scale_factor = arg->get_arg(); break; 67 | case 'k': conf.skip_samples = arg->get_arg(); break; 68 | case 'r': conf.refine_octree = arg->get_arg(); break; 69 | default: 70 | std::cerr << "Invalid option: " << arg->opt->sopt << std::endl; 71 | return 1; 72 | } 73 | } 74 | 75 | if (conf.in_files.size() < 2) 76 | { 77 | args.generate_helptext(std::cerr); 78 | return 1; 79 | } 80 | conf.out_octree = conf.in_files.back(); 81 | conf.in_files.pop_back(); 82 | 83 | if (conf.refine_octree < 0 || conf.refine_octree > 3) 84 | { 85 | std::cerr << "Unreasonable refine level of " << conf.refine_octree 86 | << ", exiting." << std::endl; 87 | return 1; 88 | } 89 | 90 | /* Load input point set and insert samples in the octree. */ 91 | util::WallTimer timer; 92 | fssr::IsoOctree octree; 93 | for (std::size_t i = 0; i < conf.in_files.size(); ++i) 94 | { 95 | std::cout << "Loading: " << conf.in_files[i] << "..." << std::endl; 96 | fssr::PointSet pset; 97 | pset.set_scale_factor(conf.scale_factor); 98 | pset.set_skip_samples(conf.skip_samples); 99 | try 100 | { 101 | pset.read_from_file(conf.in_files[i]); 102 | } 103 | catch (std::exception& e) 104 | { 105 | std::cerr << "Error: " << e.what() << std::endl; 106 | return 1; 107 | } 108 | 109 | std::cout << "Inserting samples into the octree..." << std::flush; 110 | timer.reset(); 111 | octree.insert_samples(pset); 112 | std::cout << " took " << timer.get_elapsed() << "ms" << std::endl; 113 | } 114 | 115 | /* Refine octree if requested. Each iteration adds one level voxels. */ 116 | if (conf.refine_octree > 0) 117 | { 118 | timer.reset(); 119 | std::cout << "Refining octree..." << std::flush; 120 | for (int i = 0; i < conf.refine_octree; ++i) 121 | octree.refine_octree(); 122 | std::cout << " took " << timer.get_elapsed() << "ms" << std::endl; 123 | } 124 | 125 | /* Make octree regular such that inner nodes have exactly 8 children. */ 126 | { 127 | timer.reset(); 128 | std::cout << "Making octree regular..." << std::flush; 129 | octree.make_regular_octree(); 130 | std::cout << " took " << timer.get_elapsed() << "ms" << std::endl; 131 | } 132 | 133 | /* Compute voxels. */ 134 | octree.print_stats(std::cout); 135 | octree.compute_voxels(); 136 | 137 | /* Save octree to file. */ 138 | std::cout << "Octree output file: " << conf.out_octree << std::endl; 139 | std::cout << "Saving octree to file..." << std::flush; 140 | octree.write_to_file(conf.out_octree); 141 | std::cout << " done." << std::endl; 142 | 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /libs/iso/IsoOctree.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Contains modifications by Simon Fuhrmann, 2013. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of 11 | conditions and the following disclaimer. Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of the Johns Hopkins University nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | DAMAGE. 29 | */ 30 | 31 | #ifndef ISO_OCTREE_INCLUDED 32 | #define ISO_OCTREE_INCLUDED 33 | 34 | #ifdef USE_HASHMAP 35 | # include 36 | #else 37 | # include 38 | # define hash_map map 39 | # define stdext std 40 | #endif 41 | 42 | #include 43 | 44 | #include "math/vector.h" 45 | #include "MarchingCubes.h" 46 | #include "Octree.h" 47 | #include "NeighborKey.h" 48 | 49 | template 50 | class IsoOctree 51 | { 52 | public: // Types 53 | // SIMON The vertex type used. 54 | typedef math::Vector VertexType; 55 | 56 | public: // Variables 57 | // The maximum depth of the tree. This value must be at least as large as the true depth of the tree 58 | // as its used for assigning unique ids. (It can, however, be larger than the depth for uniqueness to still hold.) 59 | int maxDepth; 60 | 61 | /* 62 | * SIMON: 63 | * The NodeData contains a mcIndex, a 3D position and VertexData. 64 | */ 65 | 66 | // The octree itself 67 | OctNode tree; 68 | 69 | // A hash-table of data associated to the corners of the octree nodes 70 | stdext::hash_map cornerValues; 71 | 72 | public: // Methods 73 | // A clean-up method to remove un-needed entries in the cornerValues hash-table 74 | void resetValues(void); 75 | 76 | // Extracts an iso-surface from the octree 77 | void getIsoSurface( 78 | const Real& isoValue, 79 | std::vector& vertices, 80 | std::vector& vertex_data, 81 | std::vector >& polygons, 82 | const int& useFull); 83 | 84 | private: // Types 85 | class RootInfo 86 | { 87 | public: 88 | const OctNode* node; 89 | int edgeIndex; 90 | long long key; 91 | typename OctNode::NodeIndex nIdx; 92 | }; 93 | 94 | private: // Variables 95 | IsoNeighborKey nKey; 96 | 97 | private: // Methods 98 | void getRoots(OctNode* node,const typename OctNode::NodeIndex& nIdx,const Real& isoValue,stdext::hash_map& roots,std::vector& vertices,std::vector& vertex_data); 99 | int getRootIndex(OctNode* node,const typename OctNode::NodeIndex& nIdx,const int& edgeIndex,RootInfo& ri); 100 | int getRootPosition(const OctNode* node,const typename OctNode::NodeIndex& nIdx,const int& eIndex,const Real& isoValue, VertexType& position, VertexData& vertex_data); 101 | long long getRootKey(const typename OctNode::NodeIndex& nIdx,const int& edgeIndex); 102 | 103 | int getRootPair(const RootInfo& root,const int& maxDepth,RootInfo& pair); 104 | 105 | void getIsoFaceEdges(OctNode* node, 106 | const typename OctNode::NodeIndex& nIdx, 107 | const int& faceIndex, 108 | std::vector >& edges, 109 | const int& flip, 110 | const int& useFull); 111 | 112 | void getIsoPolygons(OctNode* node,const typename OctNode::NodeIndex& nIdx,stdext::hash_map& roots,std::vector >& polygons,const int& useFull); 113 | 114 | template 115 | void getEdgeLoops(std::vector >& edges,stdext::hash_map& roots,std::vector >& polygons); 116 | 117 | template 118 | void getEdgeLoops(std::vector >& edges,std::vector >& loops); 119 | 120 | // Assumes NodeData::mcIndex 121 | void setMCIndex(const Real& isoValue,const int& useFull); 122 | }; 123 | 124 | #include "IsoOctree.inl" 125 | 126 | #endif // ISO_OCTREE_INCLUDED 127 | -------------------------------------------------------------------------------- /libs/iso/MarchingCubes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Contains modifications by Simon Fuhrmann, 2013. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of 11 | conditions and the following disclaimer. Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of the Johns Hopkins University nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | DAMAGE. 29 | */ 30 | 31 | #ifndef MARCHING_CUBES_INCLUDED 32 | #define MARCHING_CUBES_INCLUDED 33 | 34 | #include 35 | #include 36 | 37 | class Square 38 | { 39 | public: 40 | const static int CORNERS=4,EDGES=4; 41 | static int CornerIndex (const int& x,const int& y); 42 | static void FactorCornerIndex (const int& idx,int& x,int& y); 43 | static int EdgeIndex (const int& orientation,const int& i); 44 | static void FactorEdgeIndex (const int& idx,int& orientation,int& i); 45 | 46 | static int ReflectCornerIndex (const int& idx,const int& edgeIndex); 47 | static int ReflectEdgeIndex (const int& idx,const int& edgeIndex); 48 | 49 | static void EdgeCorners(const int& idx,int& c1,int &c2); 50 | static void OrientedEdgeCorners(const int& idx,int& c1,int &c2); 51 | }; 52 | 53 | class Cube 54 | { 55 | public: 56 | const static int CORNERS=8,EDGES=12,FACES=6; 57 | 58 | static int CornerIndex (const int& x,const int& y,const int& z); 59 | static void FactorCornerIndex (const int& idx,int& x,int& y,int& z); 60 | static int EdgeIndex (const int& orientation,const int& i,const int& j); 61 | static void FactorEdgeIndex (const int& idx,int& orientation,int& i,int &j); 62 | static int FaceIndex (const int& dir,const int& offSet); 63 | static int FaceIndex (const int& x,const int& y,const int& z); 64 | static void FactorFaceIndex (const int& idx,int& x,int &y,int& z); 65 | static void FactorFaceIndex (const int& idx,int& dir,int& offSet); 66 | 67 | static int AntipodalCornerIndex (const int& idx); 68 | static int FaceReflectCornerIndex (const int& idx,const int& faceIndex); 69 | static int FaceReflectEdgeIndex (const int& idx,const int& faceIndex); 70 | static int FaceReflectFaceIndex (const int& idx,const int& faceIndex); 71 | static int EdgeReflectCornerIndex (const int& idx,const int& edgeIndex); 72 | static int EdgeReflectEdgeIndex (const int& edgeIndex); 73 | 74 | static int FaceAdjacentToEdges (const int& eIndex1,const int& eIndex2); 75 | static void FacesAdjacentToEdge (const int& eIndex,int& f1Index,int& f2Index); 76 | 77 | static void EdgeCorners(const int& idx,int& c1,int &c2); 78 | static void FaceCorners(const int& idx,int& c1,int &c2,int& c3,int& c4); 79 | 80 | static int SquareToCubeCorner(const int& fIndex,const int& cIndex); 81 | static int SquareToCubeEdge(const int& fIndex,const int& eIndex); 82 | }; 83 | 84 | class MarchingSquares 85 | { 86 | public: 87 | class FaceEdges 88 | { 89 | public: 90 | int count; 91 | std::pair edge[2]; 92 | }; 93 | private: 94 | static FaceEdges __caseTable [1<<(Square::CORNERS )]; 95 | static FaceEdges __fullCaseTable[1<<(Square::CORNERS+1)]; 96 | public: 97 | static void SetCaseTable(void); 98 | static void SetFullCaseTable(void); 99 | 100 | static const FaceEdges& caseTable(const int& idx); 101 | static const FaceEdges& fullCaseTable(const int& idx); 102 | template 103 | static int GetFullIndex(const Real values[Square::CORNERS],const Real& iso); 104 | template 105 | static int GetIndex(const Real values[Square::CORNERS],const Real& iso); 106 | }; 107 | 108 | class MarchingCubes 109 | { 110 | static void GetEdgeLoops(std::vector >& edges,std::vector >& loops); 111 | static std::vector< std::vector > __caseTable[1< > > __fullCaseTable; 114 | public: 115 | static void SetCaseTable(void); 116 | static void SetFullCaseTable(void); 117 | 118 | template 119 | static int GetFullIndex(const Real values[Cube::CORNERS],const Real& iso); 120 | template 121 | static int GetIndex(const Real values[Cube::CORNERS],const Real& iso); 122 | static const std::vector< std::vector >& caseTable(const int& idx); 123 | static const std::vector< std::vector >& fullCaseTable(const int& idx); 124 | static const std::vector< std::vector >& caseTable(const int& idx,const int& useFull); 125 | 126 | static int IsAmbiguous(const int& idx); 127 | static int IsAmbiguous(const int& idx,const int& f); 128 | static int HasRoots(const int& mcIndex); 129 | static int HasEdgeRoots(const int& mcIndex,const int& edgeIndex); 130 | }; 131 | #include "MarchingCubes.inl" 132 | #endif //MARCHING_CUBES_INCLUDED 133 | -------------------------------------------------------------------------------- /apps/fssr_surface/fssr_surface.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * App to extract an isosurface from the sampled implicit function. 3 | * Written by Simon Fuhrmann. 4 | * 5 | * The surface reconstruction approach implemented here is described in: 6 | * 7 | * Floating Scale Surface Reconstruction 8 | * Simon Fuhrmann and Michael Goesele 9 | * In: ACM ToG (Proceedings of ACM SIGGRAPH 2014). 10 | * http://tinyurl.com/floating-scale-surface-recon 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #include "util/timer.h" 17 | #include "util/arguments.h" 18 | #include "mve/mesh.h" 19 | #include "mve/mesh_tools.h" 20 | #include "mve/mesh_io_ply.h" 21 | #include "fssr/iso_octree.h" 22 | #include "fssr/mesh_clean.h" 23 | #include "iso/SimonIsoOctree.h" 24 | #include "iso/MarchingCubes.h" 25 | 26 | struct AppSettings 27 | { 28 | std::string in_octree; 29 | std::string out_mesh; 30 | float conf_threshold; 31 | int component_size; 32 | bool clean_degenerated; 33 | }; 34 | 35 | void 36 | remove_low_conf_geometry (mve::TriangleMesh::Ptr mesh, float const thres) 37 | { 38 | mve::TriangleMesh::ConfidenceList const& confs = mesh->get_vertex_confidences(); 39 | std::vector delete_list(confs.size(), false); 40 | std::size_t num_deleted = 0; 41 | for (std::size_t i = 0; i < confs.size(); ++i) 42 | { 43 | if (confs[i] > thres) 44 | continue; 45 | num_deleted += 1; 46 | delete_list[i] = true; 47 | } 48 | mesh->delete_vertices_fix_faces(delete_list); 49 | std::cout << "Deleted " << num_deleted 50 | << " low-confidence vertices." << std::endl; 51 | } 52 | 53 | int 54 | main (int argc, char** argv) 55 | { 56 | /* Setup argument parser. */ 57 | util::Arguments args; 58 | args.set_exit_on_error(true); 59 | args.set_nonopt_minnum(2); 60 | args.set_nonopt_maxnum(2); 61 | args.set_helptext_indent(25); 62 | args.set_usage(argv[0], "[ OPTS ] IN_OCTREE OUT_PLY_MESH"); 63 | args.add_option('t', "threshold", true, "Threshold on the geometry confidence [1.0]"); 64 | args.add_option('c', "component-size", true, "Minimum number of vertices per component [1000]"); 65 | args.add_option('n', "no-clean", false, "Prevents cleanup of degenerated faces"); 66 | args.set_description("Extracts the isosurface from the sampled implicit " 67 | "function from an input octree. The accumulated weights in the octree " 68 | "can be thresholded to extract reliable parts of the geometry only. " 69 | "Small isolated components may be removed using a threshold on the " 70 | "vertex amount per component. A cleanup procedure for Marching Cubes " 71 | "artifacts is executed, but can be disabled."); 72 | args.parse(argc, argv); 73 | 74 | /* Init default settings. */ 75 | AppSettings conf; 76 | conf.in_octree = args.get_nth_nonopt(0); 77 | conf.out_mesh = args.get_nth_nonopt(1); 78 | conf.conf_threshold = 1.0f; 79 | conf.component_size = 1000; 80 | conf.clean_degenerated = true; 81 | 82 | /* Scan arguments. */ 83 | while (util::ArgResult const* arg = args.next_result()) 84 | { 85 | if (arg->opt == NULL) 86 | continue; 87 | 88 | switch (arg->opt->sopt) 89 | { 90 | case 't': conf.conf_threshold = arg->get_arg(); break; 91 | case 'c': conf.component_size = arg->get_arg(); break; 92 | case 'n': conf.clean_degenerated = false; break; 93 | default: 94 | std::cerr << "Invalid option: " << arg->opt->sopt << std::endl; 95 | return 1; 96 | } 97 | } 98 | 99 | /* Load octree. */ 100 | std::cout << "Octree input file: " << conf.in_octree << std::endl; 101 | std::cout << "Loading octree from file..." << std::flush; 102 | util::WallTimer timer; 103 | fssr::IsoOctree octree; 104 | octree.read_from_file(conf.in_octree); 105 | std::cout << " took " << timer.get_elapsed() << "ms." << std::endl; 106 | std::cout << "Octree contains " << octree.get_voxels().size() 107 | << " voxels in " << octree.get_num_nodes() << " nodes." << std::endl; 108 | 109 | /* Transfer octree. */ 110 | std::cout << "Transfering octree and voxel data..." << std::flush; 111 | timer.reset(); 112 | SimonIsoOctree iso_tree; 113 | iso_tree.set_octree(octree); 114 | octree.clear(); 115 | std::cout << " took " << timer.get_elapsed() << "ms." << std::endl; 116 | 117 | /* Extract mesh from octree. */ 118 | MarchingCubes::SetCaseTable(); 119 | MarchingCubes::SetFullCaseTable(); 120 | mve::TriangleMesh::Ptr mesh = iso_tree.extract_mesh(); 121 | iso_tree.clear(); 122 | 123 | /* Check if anything has been extracted. */ 124 | if (mesh->get_vertices().empty()) 125 | { 126 | std::cerr << "Isosurface does not contain any vertices." << std::endl; 127 | return 1; 128 | } 129 | 130 | /* Remove low-confidence geometry. */ 131 | std::cout << "Removing low-confidence geometry (threshold " 132 | << conf.conf_threshold << ")..." << std::endl; 133 | remove_low_conf_geometry(mesh, conf.conf_threshold); 134 | 135 | /* Check for color and delete if not existing. */ 136 | mve::TriangleMesh::ColorList& colors = mesh->get_vertex_colors(); 137 | if (!colors.empty() && colors[0].minimum() < 0.0f) 138 | { 139 | std::cout << "Removing dummy mesh coloring..." << std::endl; 140 | colors.clear(); 141 | } 142 | 143 | /* Remove isolated components if requested. */ 144 | if (conf.component_size > 0) 145 | { 146 | std::cout << "Removing isolated components with <" 147 | << conf.component_size << " vertices..." << std::endl; 148 | std::size_t num_verts = mesh->get_vertices().size(); 149 | mve::geom::mesh_components(mesh, conf.component_size); 150 | std::size_t new_num_verts = mesh->get_vertices().size(); 151 | std::cout << "Deleted " << (num_verts - new_num_verts) 152 | << " vertices in isolated regions." << std::endl; 153 | } 154 | 155 | /* Remove degenerated faces from the mesh. */ 156 | if (conf.clean_degenerated) 157 | { 158 | std::cout << "Removing degenerated faces..." << std::flush; 159 | std::size_t num_collapsed = fssr::clean_mc_mesh(mesh); 160 | std::cout << " collapsed " << num_collapsed << " edges." << std::endl; 161 | } 162 | 163 | mve::geom::SavePLYOptions ply_opts; 164 | ply_opts.write_vertex_colors = true; 165 | ply_opts.write_vertex_confidences = true; 166 | ply_opts.write_vertex_values = true; 167 | std::cout << "Mesh output file: " << conf.out_mesh << std::endl; 168 | mve::geom::save_ply_mesh(mesh, conf.out_mesh, ply_opts); 169 | 170 | return 0; 171 | } 172 | -------------------------------------------------------------------------------- /libs/fssr/basis_function.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_BASIS_FUNCTION_HEADER 7 | #define FSSR_BASIS_FUNCTION_HEADER 8 | 9 | #include "math/vector.h" 10 | #include "math/matrix.h" 11 | #include "fssr/defines.h" 12 | #include "fssr/sample.h" 13 | 14 | FSSR_NAMESPACE_BEGIN 15 | 16 | /* ------------------------- Gaussian funcitons -------------------------- */ 17 | 18 | /** 19 | * The Gaussian function in 3D. This basis function expects 'pos' to be 20 | * shifted properly but is rotation invariant. 21 | */ 22 | template 23 | T 24 | gaussian (T const& sigma, math::Vector const& pos); 25 | 26 | /** 27 | * Evaluates the 3D Gaussian with respect to the sample's position and scale. 28 | */ 29 | template 30 | T 31 | gaussian (Sample const& sample, math::Vector const& pos); 32 | 33 | /** 34 | * The normalized Gaussian function in 3D. This basis function expects 35 | * 'pos' to be shifted properly but is rotation invariant. 36 | */ 37 | template 38 | T 39 | gaussian_normalized (T const& sigma, math::Vector const& pos); 40 | 41 | /** 42 | * Evaluates the normalized 3D Gaussian with respect to the sample's 43 | * position and scale. 44 | */ 45 | template 46 | T 47 | gaussian_normalized (Sample const& sample, math::Vector const& pos); 48 | 49 | /* 50 | * The Gaussian derivative function in 3D. The is actually the function 51 | * where the derivative is only taken in x-direction, the y and z direction 52 | * are usual Gaussians. This basis function expects 'pos' to be shifted and 53 | * rotated properly. 54 | */ 55 | template 56 | T 57 | gaussian_derivative (T const& sigma, math::Vector const& pos); 58 | 59 | /** 60 | * Evaluates the 3D derivative of the Gaussian with respect to the sample's 61 | * position and orientation for the given query position. The derivative 62 | * is rotated in such a way that along the normal direction (i.e. in front 63 | * of the surface) the function takes positive values. 64 | */ 65 | template 66 | T 67 | gaussian_derivative (Sample const& sample, math::Vector const& pos); 68 | 69 | /* ----------------------------- Linear Ramp ----------------------------- */ 70 | 71 | /** 72 | * Implementation of a linear ramp signed distance function. Expects the 73 | * sample and the NON-TRANSFORMED sample position. The SDF is computed 74 | * using the dot product sdf = < pos - sample.pos | sample.normal >. 75 | */ 76 | template 77 | T 78 | linear_ramp (Sample const& sample, math::Vector const& pos); 79 | 80 | /* ------------------------- Weighting function -------------------------- */ 81 | 82 | /** 83 | * Weighting function in x-direction in [-3, 3]. The function falls of 84 | * less quickly in front of the surface, i.e. in normal direction (positive x) 85 | * and more quickly behind the surface (negative x). 86 | */ 87 | template 88 | T 89 | weighting_function_x (T const& x); 90 | 91 | /** 92 | * Weighting function in y and z direction. 93 | */ 94 | template 95 | T 96 | weighting_function_yz (T const& y, T const& z); 97 | 98 | /** 99 | * Evaluates the weighting function for a variable sigma. 100 | */ 101 | template 102 | inline T 103 | weighting_function (T const& sample_scale, math::Vector const& pos); 104 | 105 | /** 106 | * Evaluates the weighting function of a sample for a given position. 107 | */ 108 | template 109 | inline T 110 | weighting_function (Sample const& sample, math::Vector const& pos); 111 | 112 | /* ----------------------- MPU Weighting function ------------------------ */ 113 | 114 | /** 115 | * Radially symmetric weighting function in [-3, 3] from the MPU paper (3) 116 | * which uses a quadratic B-spline. 117 | */ 118 | template 119 | T 120 | weighting_function_mpu (T const& x); 121 | 122 | template 123 | inline T 124 | weighting_function_mpu (T const& sample_scale, math::Vector const& pos); 125 | 126 | template 127 | inline T 128 | weighting_function_mpu (Sample const& sample, math::Vector const& pos); 129 | 130 | /* -------------------------- Helper functions --------------------------- */ 131 | 132 | /** 133 | * Transforms the given position according to the samples position and normal. 134 | */ 135 | math::Vec3f 136 | transform_position (math::Vec3f const& pos, Sample const& sample); 137 | 138 | void 139 | rotation_from_normal (math::Vec3f const& normal, math::Matrix3f* rot); 140 | 141 | void 142 | rotation_from_normal (math::Vec2f const& normal, math::Matrix2f* rot); 143 | 144 | FSSR_NAMESPACE_END 145 | 146 | /* 147 | * ========================= Implementation ============================ 148 | */ 149 | 150 | FSSR_NAMESPACE_BEGIN 151 | 152 | template 153 | inline T 154 | gaussian (T const& sigma, math::Vector const& pos) 155 | { 156 | return std::exp(-pos.dot(pos) / (T(2) * MATH_POW2(sigma))); 157 | } 158 | 159 | template 160 | inline T 161 | gaussian (Sample const& sample, math::Vector const& pos) 162 | { 163 | return gaussian(sample.scale, pos - sample.pos); 164 | } 165 | 166 | template 167 | inline T 168 | gaussian_normalized (T const& sigma, math::Vector const& pos) 169 | { 170 | return gaussian(sigma, pos) / (sigma * MATH_SQRT_2PI); 171 | } 172 | 173 | template 174 | inline T 175 | gaussian_normalized (Sample const& sample, math::Vector const& pos) 176 | { 177 | return gaussian(sample, pos) / (sample.scale * MATH_SQRT_2PI); 178 | } 179 | 180 | template 181 | inline T 182 | gaussian_derivative (T const& sigma, math::Vector const& pos) 183 | { 184 | return pos[0] * gaussian(sigma, pos) / (MATH_POW4(sigma) * MATH_2_PI); 185 | } 186 | 187 | template 188 | T 189 | gaussian_derivative (Sample const& sample, math::Vector const& pos) 190 | { 191 | math::Vector const tpos = transform_position(pos, sample); 192 | return gaussian_derivative(sample.scale, tpos); 193 | } 194 | 195 | /* -------------------------------------------------------------------- */ 196 | 197 | template 198 | T 199 | linear_ramp (Sample const& sample, math::Vector const& pos) 200 | { 201 | return (pos - sample.pos).dot(sample.normal); 202 | } 203 | 204 | /* -------------------------------------------------------------------- */ 205 | 206 | template 207 | T 208 | weighting_function_x (T const& x) 209 | { 210 | if (x <= -T(3) || x >= T(3)) 211 | return T(0); 212 | 213 | if (x > T(0)) 214 | { 215 | T const a_o = T(2) / T(27); 216 | T const b_o = -T(1) / T(3); 217 | T const d_o = T(1); 218 | T const value = a_o * MATH_POW3(x) + b_o * MATH_POW2(x) + d_o; 219 | return value; 220 | } 221 | 222 | T const a_i = T(1) / T(9); 223 | T const b_i = T(2) / T(3); 224 | T const c_i = T(1); 225 | T const value = a_i * MATH_POW2(x) + b_i * x + c_i; 226 | return value; 227 | } 228 | 229 | template 230 | T 231 | weighting_function_yz (T const& y, T const& z) 232 | { 233 | if (y * y + z * z > T(9)) 234 | return T(0); 235 | 236 | T const a_o = T(2) / T(27); 237 | T const b_o = -T(1) / T(3); 238 | T const d_o = T(1); 239 | T const value = a_o * std::pow(MATH_POW2(y) + MATH_POW2(z), T(1.5)) 240 | + b_o * (MATH_POW2(y) + MATH_POW2(z)) + d_o; 241 | return value; 242 | } 243 | 244 | template 245 | inline T 246 | weighting_function (T const& sample_scale, math::Vector const& pos) 247 | { 248 | return weighting_function_x(pos[0] / sample_scale) 249 | * weighting_function_yz(pos[1] / sample_scale, pos[2] / sample_scale); 250 | } 251 | 252 | template 253 | inline T 254 | weighting_function (Sample const& sample, math::Vector const& pos) 255 | { 256 | math::Vec3f const tpos = transform_position(pos, sample); 257 | return weighting_function(sample.scale, tpos); 258 | } 259 | 260 | /* -------------------------------------------------------------------- */ 261 | 262 | template 263 | T 264 | weighting_function_mpu (T const& x) 265 | { 266 | if (x <= T(-3) || x >= T(3)) 267 | return T(0); 268 | 269 | T xf = (x + T(3)) / T(2); 270 | if (xf <= T(1)) 271 | return MATH_POW2(xf) / T(2); 272 | if (xf <= T(2)) 273 | return (T(-2) * MATH_POW2(xf) + T(6) * xf - T(3)) / T(2); 274 | return MATH_POW2(T(3) - xf) / T(2); 275 | } 276 | 277 | template 278 | inline T 279 | weighting_function_mpu (T const& sample_scale, math::Vector const& pos) 280 | { 281 | return weighting_function_mpu(pos.norm() / sample_scale); 282 | } 283 | 284 | template 285 | inline T 286 | weighting_function_mpu (Sample const& sample, math::Vector const& pos) 287 | { 288 | return weighting_function_mpu(sample.scale, pos - sample.pos); 289 | } 290 | 291 | /* -------------------------------------------------------------------- */ 292 | 293 | inline math::Vec3f 294 | transform_position (math::Vec3f const& pos, Sample const& sample) 295 | { 296 | math::Matrix3f rot; 297 | rotation_from_normal(sample.normal, &rot); 298 | return rot * (pos - sample.pos); 299 | } 300 | 301 | FSSR_NAMESPACE_END 302 | 303 | #endif // FSSR_BASIS_FUNCTION_HEADER 304 | -------------------------------------------------------------------------------- /libs/fssr/mesh_clean.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #include "math/defines.h" 7 | #include "mve/mesh.h" 8 | #include "mve/mesh_tools.h" 9 | #include "mve/mesh_info.h" 10 | #include "fssr/mesh_clean.h" 11 | 12 | FSSR_NAMESPACE_BEGIN 13 | 14 | bool 15 | edge_collapse (mve::TriangleMesh::Ptr mesh, mve::VertexInfoList& vinfos, 16 | std::size_t v1, std::size_t v2, math::Vec3f const& new_vert, 17 | std::vector const& afaces) 18 | { 19 | mve::TriangleMesh::FaceList& faces = mesh->get_faces(); 20 | mve::TriangleMesh::VertexList& verts = mesh->get_vertices(); 21 | 22 | /* Test if the hypothetical vertex destroys geometry. */ 23 | mve::MeshVertexInfo& vinfo1 = vinfos[v1]; 24 | for (std::size_t i = 0; i < vinfo1.verts.size(); ++i) 25 | { 26 | std::size_t ip1 = (i + 1) % vinfo1.verts.size(); 27 | if (vinfo1.verts[i] == v2 || vinfo1.verts[ip1] == v2) 28 | continue; 29 | 30 | math::Vec3f const& av1 = verts[vinfo1.verts[i]]; 31 | math::Vec3f const& av2 = verts[vinfo1.verts[ip1]]; 32 | math::Vec3f n1 = (av1 - verts[v1]).cross(av2 - verts[v1]).normalized(); 33 | math::Vec3f n2 = (av1 - new_vert).cross(av2 - new_vert).normalized(); 34 | 35 | float dot = n1.dot(n2); 36 | if (MATH_ISNAN(dot) || dot < 0.95f) 37 | return false; 38 | } 39 | 40 | mve::MeshVertexInfo& vinfo2 = vinfos[v2]; 41 | for (std::size_t i = 0; i < vinfo2.verts.size(); ++i) 42 | { 43 | std::size_t ip1 = (i + 1) % vinfo2.verts.size(); 44 | if (vinfo2.verts[i] == v1 || vinfo2.verts[ip1] == v1) 45 | continue; 46 | math::Vec3f const& av1 = verts[vinfo2.verts[i]]; 47 | math::Vec3f const& av2 = verts[vinfo2.verts[ip1]]; 48 | math::Vec3f n1 = (av1 - verts[v2]).cross(av2 - verts[v2]).normalized(); 49 | math::Vec3f n2 = (av1 - new_vert).cross(av2 - new_vert).normalized(); 50 | 51 | float dot = n1.dot(n2); 52 | if (MATH_ISNAN(dot) || dot < 0.95f) 53 | return false; 54 | } 55 | 56 | /* Test succeeded. Assign new vertex position to v1. */ 57 | verts[v1] = new_vert; 58 | 59 | /* Update faces adjacent to v2 replacing v2 with v1. */ 60 | for (std::size_t i = 0; i < vinfo2.faces.size(); ++i) 61 | for (std::size_t j = 0; j < 3; ++j) 62 | if (faces[vinfo2.faces[i] * 3 + j] == v2) 63 | faces[vinfo2.faces[i] * 3 + j] = v1; 64 | 65 | /* Delete the two faces adjacent to the collapsed edge. */ 66 | std::size_t v3 = 0, v4 = 0; 67 | for (std::size_t i = 0; i < 3; ++i) 68 | { 69 | std::size_t fid1 = afaces[0] * 3 + i; 70 | std::size_t fid2 = afaces[1] * 3 + i; 71 | if (faces[fid1] != v1 && faces[fid1] != v2) 72 | v3 = faces[fid1]; 73 | if (faces[fid2] != v1 && faces[fid2] != v2) 74 | v4 = faces[fid2]; 75 | faces[fid1] = 0; 76 | faces[fid2] = 0; 77 | } 78 | 79 | /* Update vertex info for vertices adjcent to v2, replacing v2 with v1. */ 80 | for (std::size_t i = 0; i < vinfo2.verts.size(); ++i) 81 | { 82 | std::size_t const vert_id = vinfo2.verts[i]; 83 | if (vert_id != v1 && vert_id != v3 && vert_id != v4) 84 | vinfos[vert_id].replace_adjacent_vertex(v2, v1); 85 | } 86 | 87 | /* Update vertex info for v3 and v4: remove v2, remove deleted faces. */ 88 | mve::MeshVertexInfo& vinfo3 = vinfos[v3]; 89 | vinfo3.remove_adjacent_face(afaces[0]); 90 | vinfo3.remove_adjacent_vertex(v2); 91 | mve::MeshVertexInfo& vinfo4 = vinfos[v4]; 92 | vinfo4.remove_adjacent_face(afaces[1]); 93 | vinfo4.remove_adjacent_vertex(v2); 94 | 95 | /* Update vinfo for v1: Remove v2, remove collapsed faces, add v2 faces. */ 96 | vinfo1.remove_adjacent_face(afaces[0]); 97 | vinfo1.remove_adjacent_face(afaces[1]); 98 | for (std::size_t i = 0; i < vinfo2.faces.size(); ++i) 99 | if (vinfo2.faces[i] != afaces[0] && vinfo2.faces[i] != afaces[1]) 100 | vinfo1.faces.push_back(vinfo2.faces[i]); 101 | vinfos.order_and_classify(*mesh, v1); 102 | 103 | /* Update vertex info for v2. */ 104 | vinfo2.faces.clear(); 105 | vinfo2.verts.clear(); 106 | vinfo2.vclass = mve::VERTEX_CLASS_UNREF; 107 | 108 | return true; 109 | } 110 | 111 | /* ---------------------------------------------------------------- */ 112 | 113 | namespace 114 | { 115 | float 116 | get_needle_ratio_squared (mve::TriangleMesh::VertexList const& verts, 117 | unsigned int const* vid, 118 | std::size_t* shortest_edge_v1, std::size_t* shortest_edge_v2) 119 | { 120 | typedef std::pair Edge; 121 | Edge edges[3]; 122 | for (int j = 0; j < 3; ++j) 123 | { 124 | int const jp1 = (j + 1) % 3; 125 | edges[j].first = (verts[vid[j]] - verts[vid[jp1]]).square_norm(); 126 | edges[j].second = j; 127 | } 128 | math::algo::sort_values(edges + 0, edges + 1, edges + 2); 129 | 130 | /* Test shortest to second-shortest edge ratio. */ 131 | float const square_ratio = edges[0].first / edges[1].first; 132 | if (shortest_edge_v1 != NULL && shortest_edge_v2 != NULL) 133 | { 134 | *shortest_edge_v1 = vid[edges[0].second]; 135 | *shortest_edge_v2 = vid[(edges[0].second + 1) % 3]; 136 | } 137 | 138 | return square_ratio; 139 | } 140 | } 141 | 142 | std::size_t 143 | clean_needles (mve::TriangleMesh::Ptr mesh, float needle_ratio_thres) 144 | { 145 | float const needle_ratio_thres_squared = MATH_POW2(needle_ratio_thres); 146 | mve::VertexInfoList vinfos(mesh); 147 | 148 | /* 149 | * Algorithm to remove slivers with a two long and a very short edge. 150 | * The sliver is identified using the ratio of the shortest by the second 151 | * shortest edge. An edge collapse of the short edge is performed if it 152 | * does not modify the geometry in a negative way, e.g. flips triangles. 153 | */ 154 | mve::TriangleMesh::FaceList& faces = mesh->get_faces(); 155 | mve::TriangleMesh::VertexList& verts = mesh->get_vertices(); 156 | std::size_t num_collapses = 0; 157 | 158 | for (std::size_t i = 0; i < faces.size(); i += 3) 159 | { 160 | /* Skip invalid faces. */ 161 | if (faces[i] == faces[i + 1] && faces[i] == faces[i + 2]) 162 | continue; 163 | 164 | /* Skip faces that are no needles. */ 165 | std::size_t v1, v2; 166 | if (get_needle_ratio_squared(verts, &faces[i], &v1, &v2) 167 | > needle_ratio_thres_squared) 168 | continue; 169 | 170 | /* Skip edges between non-simple vertices. */ 171 | if (vinfos[v1].vclass != mve::VERTEX_CLASS_SIMPLE 172 | || vinfos[v2].vclass != mve::VERTEX_CLASS_SIMPLE) 173 | continue; 174 | 175 | /* Find triangle adjecent to the edge, skip non-simple edges. */ 176 | std::vector afaces; 177 | vinfos.get_faces_for_edge(v1, v2, &afaces); 178 | if (afaces.size() != 2) 179 | continue; 180 | 181 | /* Collapse the edge. */ 182 | math::Vec3f new_v = (verts[v1] + verts[v2]) / 2.0f; 183 | if (edge_collapse(mesh, vinfos, v1, v2, new_v, afaces)) 184 | num_collapses += 1; 185 | } 186 | 187 | /* Cleanup invalid triangles and unreferenced vertices. */ 188 | mve::geom::mesh_delete_unreferenced(mesh); 189 | 190 | return num_collapses; 191 | } 192 | 193 | /* ---------------------------------------------------------------- */ 194 | 195 | std::size_t 196 | clean_caps (mve::TriangleMesh::Ptr mesh) 197 | { 198 | mve::VertexInfoList vinfos(mesh); 199 | mve::TriangleMesh::VertexList& verts = mesh->get_vertices(); 200 | std::size_t num_collapses = 0; 201 | for (std::size_t v1 = 0; v1 < verts.size(); ++v1) 202 | { 203 | mve::MeshVertexInfo& vinfo = vinfos[v1]; 204 | 205 | if (vinfo.vclass != mve::VERTEX_CLASS_SIMPLE) 206 | continue; 207 | 208 | if (vinfo.verts.size() != 3) 209 | continue; 210 | 211 | std::pair edge_len[3]; 212 | for (std::size_t j = 0; j < vinfo.verts.size(); ++j) 213 | edge_len[j] = std::make_pair( 214 | (verts[vinfo.verts[j]] - verts[v1]).square_norm(), 215 | vinfo.verts[j]); 216 | math::algo::sort_values(edge_len + 0, edge_len + 1, edge_len + 2); 217 | std::size_t v2 = edge_len[0].second; 218 | 219 | std::vector afaces; 220 | vinfos.get_faces_for_edge(v1, v2, &afaces); 221 | if (afaces.size() != 2) 222 | continue; 223 | 224 | /* Edge collapse fails if (v2 - v1) is not coplanar to triangle. */ 225 | if (edge_collapse(mesh, vinfos, v1, v2, verts[v2], afaces)) 226 | num_collapses += 1; 227 | } 228 | 229 | /* Cleanup invalid triangles and unreferenced vertices. */ 230 | mve::geom::mesh_delete_unreferenced(mesh); 231 | 232 | return num_collapses; 233 | } 234 | 235 | /* ---------------------------------------------------------------- */ 236 | 237 | std::size_t 238 | clean_mc_mesh (mve::TriangleMesh::Ptr mesh, float needle_ratio_thres) 239 | { 240 | std::size_t num_collapsed = 0; 241 | num_collapsed += clean_needles(mesh, needle_ratio_thres); 242 | num_collapsed += clean_caps(mesh); 243 | num_collapsed += clean_needles(mesh, needle_ratio_thres); 244 | return num_collapsed; 245 | } 246 | 247 | FSSR_NAMESPACE_END 248 | -------------------------------------------------------------------------------- /libs/fssr/iso_octree.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "util/timer.h" 17 | #include "util/string.h" 18 | #include "fssr/basis_function.h" 19 | #include "fssr/sample.h" 20 | #include "fssr/iso_octree.h" 21 | 22 | FSSR_NAMESPACE_BEGIN 23 | 24 | void 25 | IsoOctree::compute_voxels (void) 26 | { 27 | util::WallTimer timer; 28 | this->voxels.clear(); 29 | this->compute_all_voxels(); 30 | std::cout << "Generated " << this->voxels.size() 31 | << " voxels, took " << timer.get_elapsed() << "ms." << std::endl; 32 | } 33 | 34 | void 35 | IsoOctree::compute_all_voxels (void) 36 | { 37 | /* Locate all leafs and store voxels in a vector. */ 38 | std::cout << "Computing sampling of the implicit function..." << std::endl; 39 | { 40 | /* Make voxels unique by storing them in a set first. */ 41 | typedef std::set VoxelIndexSet; 42 | VoxelIndexSet voxel_set; 43 | std::list queue; 44 | queue.push_back(this->get_iterator_for_root()); 45 | while (!queue.empty()) 46 | { 47 | Octree::Iterator iter = queue.front(); 48 | queue.pop_front(); 49 | 50 | bool is_leaf = true; 51 | for (int i = 0; i < 8; ++i) 52 | if (iter.node_path.level < this->max_level 53 | && iter.node->children[i] != NULL) 54 | { 55 | queue.push_back(iter.descend(i)); 56 | is_leaf = false; 57 | } 58 | 59 | if (!is_leaf) 60 | continue; 61 | 62 | for (int i = 0; i < 8; ++i) 63 | { 64 | VoxelIndex index; 65 | index.from_path_and_corner(iter.node_path, i); 66 | voxel_set.insert(index); 67 | } 68 | } 69 | 70 | /* Copy voxels over to a vector. */ 71 | this->voxels.clear(); 72 | this->voxels.reserve(voxel_set.size()); 73 | for (VoxelIndexSet::const_iterator i = voxel_set.begin(); 74 | i != voxel_set.end(); ++i) 75 | this->voxels.push_back(std::make_pair(*i, VoxelData())); 76 | } 77 | 78 | std::cout << "Sampling the implicit function at " << this->voxels.size() 79 | << " positions, fetch a beer..." << std::endl; 80 | 81 | /* Sample the implicit function for every voxel. */ 82 | this->num_processed = 0; 83 | #pragma omp parallel for schedule(dynamic) 84 | for (std::size_t i = 0; i < voxels.size(); ++i) 85 | { 86 | VoxelIndex index = this->voxels[i].first; 87 | math::Vec3d voxel_pos = index.compute_position( 88 | this->get_root_node_center(), this->get_root_node_size()); 89 | this->voxels[i].second = this->sample_ifn(voxel_pos); 90 | 91 | #pragma omp critical 92 | { 93 | this->num_processed += 1; 94 | this->print_progress(this->num_processed, this->voxels.size()); 95 | } 96 | } 97 | 98 | /* Print progress one last time to get the 100% progress output. */ 99 | this->print_progress(this->voxels.size(), this->voxels.size()); 100 | std::cout << std::endl; 101 | } 102 | 103 | VoxelData 104 | IsoOctree::sample_ifn (math::Vec3d const& voxel_pos) 105 | { 106 | /* Query samples that influence the voxel. */ 107 | std::vector samples; 108 | samples.reserve(2048); 109 | this->influence_query(voxel_pos, 3.0, &samples); 110 | 111 | if (samples.empty()) 112 | return VoxelData(); 113 | 114 | /* 115 | * Handling of scale: Sort the samples according to scale, high-res 116 | * samples first. If the confidence of the voxel is high enough, no 117 | * more samples are necessary. 118 | */ 119 | std::size_t num_samples = samples.size() / 10; 120 | std::nth_element(samples.begin(), samples.begin() + num_samples, 121 | samples.end(), sample_scale_compare); 122 | float const sample_max_scale = samples[num_samples]->scale * 2.0f; 123 | //float const sample_max_scale = std::numeric_limits::max(); 124 | 125 | /* Evaluate implicit function as the sum of basis functions. */ 126 | double total_ifn = 0.0; 127 | double total_weight = 0.0; 128 | double total_scale = 0.0f; 129 | math::Vec3d total_color(0.0f); 130 | double total_color_weight = 0.0; 131 | 132 | for (std::size_t i = 0; i < samples.size(); ++i) 133 | { 134 | Sample const& sample = *samples[i]; 135 | if (sample.scale > sample_max_scale) 136 | continue; 137 | 138 | math::Vec3f const tpos = transform_position(voxel_pos, sample); 139 | 140 | /* Evaluate basis fucntion. */ 141 | double const value = gaussian_derivative(sample.scale, tpos); 142 | //double const value = linear_ramp(sample, voxel_pos); 143 | 144 | /* Evaluate weight function. */ 145 | double const weight = weighting_function(sample.scale, tpos) * sample.confidence; 146 | //double const weight = weighting_function_mpu(sample.scale, tpos) * sample.confidence; 147 | 148 | /* Incrementally update. */ 149 | total_ifn += value * weight; 150 | total_weight += weight; 151 | 152 | double const color_weight = gaussian_normalized(sample.scale / 5.0f, tpos) * sample.confidence; 153 | total_scale += sample.scale * color_weight; 154 | total_color += sample.color * color_weight; 155 | total_color_weight += color_weight; 156 | } 157 | 158 | /* Store voxel in the map. */ 159 | VoxelData data; 160 | data.value = total_ifn / total_weight; 161 | data.conf = total_weight; 162 | data.scale = total_scale / total_color_weight; 163 | data.color = total_color / total_color_weight; 164 | return data; 165 | } 166 | 167 | void 168 | IsoOctree::print_progress (std::size_t voxels_done, std::size_t voxels_total) 169 | { 170 | static std::size_t last_voxels_done = 0; 171 | static util::WallTimer timer; 172 | static unsigned int last_elapsed = 0; 173 | 174 | /* Make sure we don't call timer.get_elapsed() too often. */ 175 | if (voxels_done != voxels_total) 176 | { 177 | if (voxels_done - last_voxels_done < 500) 178 | return; 179 | } 180 | last_voxels_done = voxels_done; 181 | 182 | /* Make sure we don't print the progress too often, every 100ms. */ 183 | unsigned int elapsed = timer.get_elapsed(); 184 | if (voxels_done != voxels_total) 185 | { 186 | if (elapsed - last_elapsed < 100) 187 | return; 188 | } 189 | last_elapsed = elapsed; 190 | 191 | /* Compute percentage and nice elapsed and ETA strings. */ 192 | unsigned int elapsed_mins = elapsed / (1000 * 60); 193 | unsigned int elapsed_secs = (elapsed / 1000) % 60; 194 | float percentage = static_cast(voxels_done) 195 | / static_cast(voxels_total) ; 196 | unsigned int total = static_cast(elapsed / percentage); 197 | unsigned int remaining = total - elapsed; 198 | unsigned int remaining_mins = remaining / (1000 * 60); 199 | unsigned int remaining_secs = (remaining / 1000) % 60; 200 | 201 | std::cout << "\rProcessing voxel " //<< this->voxels.size() 202 | << this->num_processed 203 | << " (" << util::string::get_fixed(percentage * 100.0f, 2) << "%, " 204 | << elapsed_mins << ":" 205 | << util::string::get_filled(elapsed_secs, 2, '0') << ", ETA " 206 | << remaining_mins << ":" 207 | << util::string::get_filled(remaining_secs, 2, '0') << ")..." 208 | << std::flush; 209 | } 210 | 211 | #define FSSR_OCTREE_FILE_ID "FSSR_OCTREE\n" 212 | 213 | void 214 | IsoOctree::write_to_file (std::string const& filename) const 215 | { 216 | std::ofstream out(filename.c_str(), std::ios::binary); 217 | if (!out) 218 | throw std::runtime_error(::strerror(errno)); 219 | 220 | /* Write file identifier. */ 221 | out << FSSR_OCTREE_FILE_ID; 222 | 223 | /* Write the octree hierarchy. */ 224 | this->write_hierarchy(out); 225 | 226 | /* Write header: Number of voxels. */ 227 | out << "\n" << this->voxels.size() << "\n"; 228 | 229 | /* Write all voxels. */ 230 | for (std::size_t i = 0; i < this->voxels.size(); ++i) 231 | { 232 | VoxelIndex const& index = this->voxels[i].first; 233 | VoxelData const& data = this->voxels[i].second; 234 | out.write(reinterpret_cast(&index.index), 235 | sizeof(index.index)); 236 | out.write(reinterpret_cast(&data.value), 237 | sizeof(data.value)); 238 | out.write(reinterpret_cast(&data.conf), 239 | sizeof(data.conf)); 240 | out.write(reinterpret_cast(&data.scale), 241 | sizeof(data.scale)); 242 | out.write(reinterpret_cast(*data.color), 243 | 3 * sizeof(float)); 244 | } 245 | out.close(); 246 | } 247 | 248 | void 249 | IsoOctree::read_from_file (std::string const& filename) 250 | { 251 | std::ifstream in(filename.c_str(), std::ios::binary); 252 | if (!in) 253 | throw std::runtime_error(::strerror(errno)); 254 | 255 | /* Read file identifier. */ 256 | std::string file_id; 257 | std::size_t file_id_size = std::strlen(FSSR_OCTREE_FILE_ID); 258 | file_id.resize(file_id_size); 259 | in.read(&file_id[0], file_id_size); 260 | if (file_id != FSSR_OCTREE_FILE_ID) 261 | throw std::runtime_error("Invalid file indentifier"); 262 | 263 | /* Read octree hierarchy. */ 264 | this->read_hierarchy(in); 265 | 266 | /* Read number of voxels. */ 267 | std::size_t num_voxels; 268 | in >> num_voxels; 269 | in.ignore(1); 270 | 271 | this->voxels.clear(); 272 | this->voxels.resize(num_voxels); 273 | for (std::size_t i = 0; i < num_voxels; ++i) 274 | { 275 | VoxelIndex index; 276 | VoxelData data; 277 | in.read(reinterpret_cast(&index.index), sizeof(index.index)); 278 | in.read(reinterpret_cast(&data.value), sizeof(data.value)); 279 | in.read(reinterpret_cast(&data.conf), sizeof(data.conf)); 280 | in.read(reinterpret_cast(&data.scale), sizeof(data.scale)); 281 | in.read(reinterpret_cast(*data.color), 3 * sizeof(float)); 282 | this->voxels[i].first = index; 283 | this->voxels[i].second = data; 284 | } 285 | in.close(); 286 | } 287 | 288 | FSSR_NAMESPACE_END 289 | -------------------------------------------------------------------------------- /libs/fssr/octree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #ifndef FSSR_OCTREE_HEADER 7 | #define FSSR_OCTREE_HEADER 8 | 9 | #include // TODO: Use once C++11 is standard. 10 | #include 11 | #include 12 | 13 | #include "math/vector.h" 14 | #include "mve/mesh.h" 15 | #include "fssr/defines.h" 16 | #include "fssr/sample.h" 17 | #include "fssr/pointset.h" 18 | 19 | FSSR_NAMESPACE_BEGIN 20 | 21 | class Octree 22 | { 23 | public: 24 | /** 25 | * Simple recursive octree node that stores samples in a vector. 26 | */ 27 | struct Node 28 | { 29 | public: 30 | Node (void); 31 | virtual ~Node (void); 32 | 33 | public: 34 | Node* children[8]; 35 | std::vector samples; 36 | }; 37 | 38 | /** 39 | * Keeps track of the current node path during descend and ascend. 40 | * The complete path is a series of 3 bits each indicating the octant 41 | * from the root towards the target node. 42 | */ 43 | struct NodePath 44 | { 45 | public: 46 | NodePath (void); 47 | NodePath descend (int const octant) const; 48 | NodePath ascend (void) const; 49 | 50 | public: 51 | uint64_t path; 52 | uint8_t level; 53 | }; 54 | 55 | /** 56 | * Keeps track of the node geometry, i.e. the node's center and size, 57 | * during octree descend. Support for computing the AABB of the node. 58 | */ 59 | struct NodeGeom 60 | { 61 | public: 62 | NodeGeom (void); 63 | NodeGeom descend (int const octant) const; 64 | 65 | math::Vec3d aabb_min (void) const; 66 | math::Vec3d aabb_max (void) const; 67 | math::Vec3d corner_pos (int const corner) const; 68 | 69 | public: 70 | math::Vec3d center; 71 | double size; 72 | }; 73 | 74 | /** 75 | * Comfortable iterator for the octree. 76 | */ 77 | struct Iterator 78 | { 79 | Octree::Node const* node; 80 | Octree::NodeGeom node_geom; 81 | Octree::NodePath node_path; 82 | 83 | Iterator (void); 84 | Iterator (Octree const* octree); 85 | void init (Octree const* octree); 86 | Iterator descend (int const octant) const; 87 | }; 88 | 89 | public: 90 | Octree (void); 91 | virtual ~Octree (void); 92 | void clear (void); 93 | 94 | /** 95 | * Inserts a single sample into the Octree. 96 | * The scale information is used to determine the level based on the 97 | * size of the root node. If the sample is outside the octree root node, 98 | * the octree is expanded. 99 | */ 100 | void insert_sample (Sample const& s); 101 | 102 | /** 103 | * Inserts all samples from the point set into the octree. 104 | */ 105 | void insert_samples (PointSet const& pset); 106 | 107 | /** Returns the number of samples in the octree. */ 108 | std::size_t get_num_samples (void) const; 109 | 110 | /** 111 | * Returns the number of nodes in the octree. 112 | */ 113 | std::size_t get_num_nodes (void) const; 114 | 115 | /** 116 | * Returns the number of levels (WARNING: traverses whole tree). 117 | * For an empty octree (without any nodes), this returns 0. For 118 | * one root node only, this returns 1, and so on. 119 | */ 120 | int get_num_levels (void) const; 121 | 122 | /** 123 | * Returns octree level statistics (WARNING: traverses whole tree). 124 | * For an empty octree (without any nodes), the result vector is empty. 125 | * For an octree with root node only, the vector contains one element. 126 | */ 127 | void get_points_per_level (std::vector* stats) const; 128 | 129 | /** Returns the root node (read-only). */ 130 | Node const* get_root_node (void) const; 131 | 132 | /** Returns the center of the root node. */ 133 | math::Vec3d const& get_root_node_center (void) const; 134 | 135 | /** Returns the size of the root node. */ 136 | double get_root_node_size (void) const; 137 | 138 | /** Returns a NodeGeom object for the root. */ 139 | NodeGeom get_node_geom_for_root (void) const; 140 | 141 | /** Returns a NodePath object for the root. */ 142 | NodePath get_node_path_for_root (void) const; 143 | 144 | /** Returns an octree iterator for the root. */ 145 | Iterator get_iterator_for_root (void) const; 146 | 147 | /** 148 | * Queries all samples that influence the given point. The actual 149 | * influence distance is given as factor of the sample's scale value. 150 | */ 151 | void influence_query (math::Vec3d const& pos, double factor, 152 | std::vector* result) const; 153 | 154 | /** 155 | * Queries all nodes that are influenced by the given sample. 156 | * The result is an approximation, i.e. some nodes may not actually 157 | * be in the support range of the sample. 158 | */ 159 | void influenced_query (Sample const& sample, double factor, 160 | std::vector* result); 161 | 162 | /** 163 | * Makes the octree regular such that that every node has either 164 | * exactly eight (inner node) or none (leaf node) children allocated. 165 | */ 166 | void make_regular_octree (void); 167 | 168 | /** 169 | * Refines the octree by subdividing all leaves. 170 | */ 171 | void refine_octree (void); 172 | 173 | /** 174 | * Removes low-res samples up to a specific level. 175 | */ 176 | void remove_low_res_samples (int min_level); 177 | 178 | /** 179 | * Writes the octree hierarchy to stream. 180 | * This does NOT write the sample data, just the hierarchy. 181 | */ 182 | void write_hierarchy (std::ostream& out, bool with_meta = true) const; 183 | 184 | /** 185 | * Reads the hierarchy from stream and builds the octree. 186 | * This des NOT read the sample data, just the hierarchy. 187 | */ 188 | void read_hierarchy (std::istream& in, bool with_meta = true); 189 | 190 | /** Write an octree visualization to mesh. */ 191 | void octree_to_mesh (std::string const& filename); 192 | 193 | /** Computes a new center for a child specified by octant. */ 194 | static math::Vec3d child_center_for_octant (math::Vec3d const& old_center, 195 | double old_size, int octant); 196 | 197 | /** Prints some octree statitics to the stream. */ 198 | void print_stats (std::ostream& out); 199 | 200 | private: 201 | /* Octree functions. */ 202 | bool is_inside_octree (math::Vec3d const& pos); 203 | void expand_root_for_point (math::Vec3d const& pos); 204 | Node* find_node_for_sample (Sample const& sample); 205 | 206 | /* Octree recursive functions. */ 207 | Node* find_node_descend (Sample const& sample, Node* node, 208 | NodeGeom const& node_geom); 209 | Node* find_node_expand (Sample const& sample); 210 | int get_num_levels (Node const* node) const; 211 | void get_points_per_level (std::vector* stats, 212 | Node const* node, std::size_t level) const; 213 | void influence_query (math::Vec3d const& pos, double factor, 214 | std::vector* result, 215 | Node const* node, NodeGeom const& node_geom) const; 216 | void influenced_query (Sample const& sample, double factor, 217 | std::vector* result, Iterator const& iter); 218 | void make_regular_octree (Node* node); 219 | 220 | /* Debugging functions. */ 221 | void octree_to_mesh (mve::TriangleMesh::Ptr mesh, 222 | Octree::Node const* node, NodeGeom const& node_geom); 223 | 224 | private: 225 | /* The number of samples in the octree. */ 226 | std::size_t num_samples; 227 | /* The number of nodes in the octree (inner nodes plus leafs). */ 228 | std::size_t num_nodes; 229 | 230 | /* The root node with its center and side length. */ 231 | Node* root; 232 | math::Vec3d root_center; 233 | double root_size; 234 | }; 235 | 236 | /* ------------------------- Implementation ---------------------------- */ 237 | 238 | inline 239 | Octree::Node::Node (void) 240 | { 241 | std::fill(this->children, this->children + 8, (Octree::Node*)NULL); 242 | } 243 | 244 | inline 245 | Octree::Node::~Node (void) 246 | { 247 | for (int i = 0; i < 8; ++i) 248 | delete this->children[i]; 249 | } 250 | 251 | /* ---------------------------------------------------------------- */ 252 | 253 | inline 254 | Octree::NodePath::NodePath (void) 255 | : path(0), level(0) 256 | { 257 | } 258 | 259 | /* ---------------------------------------------------------------- */ 260 | 261 | inline 262 | Octree::NodeGeom::NodeGeom (void) 263 | : center(0.0), size(0.0) 264 | { 265 | } 266 | 267 | /* ---------------------------------------------------------------- */ 268 | 269 | inline 270 | Octree::Iterator::Iterator (void) 271 | { 272 | } 273 | 274 | inline 275 | Octree::Iterator::Iterator (Octree const* octree) 276 | { 277 | this->init(octree); 278 | } 279 | 280 | /* ---------------------------------------------------------------- */ 281 | 282 | inline 283 | Octree::Octree (void) 284 | { 285 | this->clear(); 286 | } 287 | 288 | inline 289 | Octree::~Octree (void) 290 | { 291 | delete this->root; 292 | } 293 | 294 | inline std::size_t 295 | Octree::get_num_samples (void) const 296 | { 297 | return this->num_samples; 298 | } 299 | 300 | inline std::size_t 301 | Octree::get_num_nodes (void) const 302 | { 303 | return this->num_nodes; 304 | } 305 | 306 | inline int 307 | Octree::get_num_levels (void) const 308 | { 309 | return this->get_num_levels(this->root); 310 | } 311 | 312 | inline void 313 | Octree::get_points_per_level (std::vector* stats) const 314 | { 315 | stats->clear(); 316 | this->get_points_per_level(stats, this->root, 0); 317 | } 318 | 319 | inline Octree::Node const* 320 | Octree::get_root_node (void) const 321 | { 322 | return this->root; 323 | } 324 | 325 | inline math::Vec3d const& 326 | Octree::get_root_node_center (void) const 327 | { 328 | return this->root_center; 329 | } 330 | 331 | inline double 332 | Octree::get_root_node_size (void) const 333 | { 334 | return this->root_size; 335 | } 336 | 337 | inline void 338 | Octree::influence_query (math::Vec3d const& pos, double factor, 339 | std::vector* result) const 340 | { 341 | result->resize(0); 342 | this->influence_query(pos, factor, result, this->root, 343 | this->get_node_geom_for_root()); 344 | } 345 | 346 | inline void 347 | Octree::influenced_query (Sample const& sample, double factor, 348 | std::vector* result) 349 | { 350 | result->resize(0); 351 | this->influenced_query(sample, factor, result, 352 | this->get_iterator_for_root()); 353 | } 354 | 355 | inline void 356 | Octree::make_regular_octree (void) 357 | { 358 | this->make_regular_octree(this->root); 359 | } 360 | 361 | inline Octree::NodeGeom 362 | Octree::get_node_geom_for_root (void) const 363 | { 364 | NodeGeom geom; 365 | geom.size = this->get_root_node_size(); 366 | geom.center = this->get_root_node_center(); 367 | return geom; 368 | } 369 | 370 | inline Octree::NodePath 371 | Octree::get_node_path_for_root (void) const 372 | { 373 | return NodePath(); 374 | } 375 | 376 | inline Octree::Iterator 377 | Octree::get_iterator_for_root (void) const 378 | { 379 | return Iterator(this); 380 | } 381 | 382 | FSSR_NAMESPACE_END 383 | 384 | #endif // FSSR_OCTREE_HEADER 385 | -------------------------------------------------------------------------------- /libs/iso/NeighborKey.inl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Contains modifications by Simon Fuhrmann, 2013. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of 11 | conditions and the following disclaimer. Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of the Johns Hopkins University nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | DAMAGE. 29 | */ 30 | 31 | #ifndef NEIGHBORKEY_INL 32 | #define NEIGHBORKEY_INL 33 | 34 | //////////////////////// 35 | // OctNodeNeighborKey // 36 | //////////////////////// 37 | template 38 | Neighbors::Neighbors(void) 39 | { 40 | clear(); 41 | } 42 | 43 | template 44 | void 45 | Neighbors::clear(void) 46 | { 47 | for(int i=0;i<3;i++){for(int j=0;j<3;j++){for(int k=0;k<3;k++){neighbors[i][j][k]=NULL;}}} 48 | } 49 | 50 | template 51 | NeighborKey::NeighborKey(void) 52 | { 53 | neighbors=NULL; 54 | } 55 | 56 | template 57 | NeighborKey::~NeighborKey(void) 58 | { 59 | if(neighbors){delete[] neighbors;} 60 | neighbors=NULL; 61 | } 62 | 63 | template 64 | void 65 | NeighborKey::set(const int& d) 66 | { 67 | if(neighbors){delete[] neighbors;} 68 | neighbors=NULL; 69 | if(d<0){return;} 70 | _depth=d; 71 | neighbors=new Neighbors[d+1]; 72 | } 73 | 74 | template 75 | Neighbors& 76 | NeighborKey::setNeighbors(OctNode* node) 77 | { 78 | OctNode* temp=node; 79 | depth=0; 80 | while(temp->parent) 81 | { 82 | depth++; 83 | temp=temp->parent; 84 | } 85 | if(node!=neighbors[depth].neighbors[1][1][1]) 86 | for(int i=depth;i<=_depth;i++) 87 | neighbors[i].clear(); 88 | 89 | return _setNeighbors(node,depth); 90 | } 91 | 92 | template 93 | Neighbors& 94 | NeighborKey::_setNeighbors(OctNode* node,const int& d) 95 | { 96 | if(node!=neighbors[d].neighbors[1][1][1]){ 97 | neighbors[d].clear(); 98 | 99 | if(!node->parent) 100 | { 101 | neighbors[d].nIndex=OctNode::NodeIndex(); 102 | neighbors[d].neighbors[1][1][1]=node; 103 | } 104 | else 105 | { 106 | Neighbors& temp=_setNeighbors(node->parent,d-1); 107 | int i,j,k,x1,y1,z1,x2,y2,z2; 108 | int idx=int(node-node->parent->children); 109 | neighbors[d].nIndex=neighbors[d-1].nIndex.child(idx); 110 | Cube::FactorCornerIndex( idx ,x1,y1,z1); 111 | Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); 112 | for(i=0;i<2;i++){ 113 | for(j=0;j<2;j++){ 114 | for(k=0;k<2;k++){ 115 | neighbors[d].neighbors[x2+i][y2+j][z2+k]=&node->parent->children[Cube::CornerIndex(i,j,k)]; 116 | } 117 | } 118 | } 119 | 120 | // Set the neighbors from across the faces 121 | i=x1<<1; 122 | if(temp.neighbors[i][1][1]){ 123 | if(!temp.neighbors[i][1][1]->children){temp.neighbors[i][1][1]->initChildren();} 124 | for(j=0;j<2;j++){for(k=0;k<2;k++){neighbors[d].neighbors[i][y2+j][z2+k]=&temp.neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)];}} 125 | } 126 | j=y1<<1; 127 | if(temp.neighbors[1][j][1]){ 128 | if(!temp.neighbors[1][j][1]->children){temp.neighbors[1][j][1]->initChildren();} 129 | for(i=0;i<2;i++){for(k=0;k<2;k++){neighbors[d].neighbors[x2+i][j][z2+k]=&temp.neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)];}} 130 | } 131 | k=z1<<1; 132 | if(temp.neighbors[1][1][k]){ 133 | if(!temp.neighbors[1][1][k]->children){temp.neighbors[1][1][k]->initChildren();} 134 | for(i=0;i<2;i++){for(j=0;j<2;j++){neighbors[d].neighbors[x2+i][y2+j][k]=&temp.neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)];}} 135 | } 136 | 137 | // Set the neighbors from across the edges 138 | i=x1<<1; j=y1<<1; 139 | if(temp.neighbors[i][j][1]){ 140 | if(!temp.neighbors[i][j][1]->children){temp.neighbors[i][j][1]->initChildren();} 141 | for(k=0;k<2;k++){neighbors[d].neighbors[i][j][z2+k]=&temp.neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)];} 142 | } 143 | i=x1<<1; k=z1<<1; 144 | if(temp.neighbors[i][1][k]){ 145 | if(!temp.neighbors[i][1][k]->children){temp.neighbors[i][1][k]->initChildren();} 146 | for(j=0;j<2;j++){neighbors[d].neighbors[i][y2+j][k]=&temp.neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)];} 147 | } 148 | j=y1<<1; k=z1<<1; 149 | if(temp.neighbors[1][j][k]){ 150 | if(!temp.neighbors[1][j][k]->children){temp.neighbors[1][j][k]->initChildren();} 151 | for(i=0;i<2;i++){neighbors[d].neighbors[x2+i][j][k]=&temp.neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)];} 152 | } 153 | 154 | // Set the neighbor from across the corner 155 | i=x1<<1; j=y1<<1; k=z1<<1; 156 | if(temp.neighbors[i][j][k]){ 157 | if(!temp.neighbors[i][j][k]->children){temp.neighbors[i][j][k]->initChildren();} 158 | neighbors[d].neighbors[i][j][k]=&temp.neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; 159 | } 160 | } 161 | } 162 | return neighbors[d]; 163 | } 164 | template 165 | Neighbors& 166 | NeighborKey::getNeighbors(OctNode* node) 167 | { 168 | depth=0; 169 | OctNode* temp=node; 170 | while(temp->parent) 171 | { 172 | depth++; 173 | temp=temp->parent; 174 | } 175 | if(node!=neighbors[depth].neighbors[1][1][1]) 176 | for(int i=depth;i<=_depth;i++) 177 | neighbors[i].clear(); 178 | 179 | return _getNeighbors(node,depth); 180 | } 181 | 182 | template 183 | Neighbors& 184 | NeighborKey::_getNeighbors(OctNode* node,const int& d) 185 | { 186 | if(node!=neighbors[d].neighbors[1][1][1]){ 187 | neighbors[d].clear(); 188 | 189 | if(!node->parent) 190 | { 191 | neighbors[d].neighbors[1][1][1]=node; 192 | neighbors[d].nIndex = typename OctNode::NodeIndex(); 193 | } 194 | else 195 | { 196 | Neighbors& temp=_getNeighbors(node->parent,d-1); 197 | int i,j,k,x1,y1,z1,x2,y2,z2; 198 | int idx=int(node-node->parent->children); 199 | neighbors[d].nIndex=neighbors[d-1].nIndex.child(idx); 200 | Cube::FactorCornerIndex( idx ,x1,y1,z1); 201 | Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); 202 | for(i=0;i<2;i++){ 203 | for(j=0;j<2;j++){ 204 | for(k=0;k<2;k++){ 205 | neighbors[d].neighbors[x2+i][y2+j][z2+k]=&node->parent->children[Cube::CornerIndex(i,j,k)]; 206 | } 207 | } 208 | } 209 | 210 | // Set the neighbors from across the faces 211 | i=x1<<1; 212 | if(temp.neighbors[i][1][1] && temp.neighbors[i][1][1]->children){ 213 | for(j=0;j<2;j++){for(k=0;k<2;k++){neighbors[d].neighbors[i][y2+j][z2+k]=&temp.neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)];}} 214 | } 215 | j=y1<<1; 216 | if(temp.neighbors[1][j][1] && temp.neighbors[1][j][1]->children){ 217 | for(i=0;i<2;i++){for(k=0;k<2;k++){neighbors[d].neighbors[x2+i][j][z2+k]=&temp.neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)];}} 218 | } 219 | k=z1<<1; 220 | if(temp.neighbors[1][1][k] && temp.neighbors[1][1][k]->children){ 221 | for(i=0;i<2;i++){for(j=0;j<2;j++){neighbors[d].neighbors[x2+i][y2+j][k]=&temp.neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)];}} 222 | } 223 | 224 | // Set the neighbors from across the edges 225 | i=x1<<1; j=y1<<1; 226 | if(temp.neighbors[i][j][1] && temp.neighbors[i][j][1]->children){ 227 | for(k=0;k<2;k++){neighbors[d].neighbors[i][j][z2+k]=&temp.neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)];} 228 | } 229 | i=x1<<1; k=z1<<1; 230 | if(temp.neighbors[i][1][k] && temp.neighbors[i][1][k]->children){ 231 | for(j=0;j<2;j++){neighbors[d].neighbors[i][y2+j][k]=&temp.neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)];} 232 | } 233 | j=y1<<1; k=z1<<1; 234 | if(temp.neighbors[1][j][k] && temp.neighbors[1][j][k]->children){ 235 | for(i=0;i<2;i++){neighbors[d].neighbors[x2+i][j][k]=&temp.neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)];} 236 | } 237 | 238 | // Set the neighbor from across the corner 239 | i=x1<<1; j=y1<<1; k=z1<<1; 240 | if(temp.neighbors[i][j][k] && temp.neighbors[i][j][k]->children){ 241 | neighbors[d].neighbors[i][j][k]=&temp.neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; 242 | } 243 | } 244 | } 245 | return neighbors[d]; 246 | } 247 | 248 | ///////////////// 249 | // IsoNeighborKey // 250 | ///////////////// 251 | template 252 | OctNode* 253 | IsoNeighborKey::__FaceNeighbor(OctNode* node,const int& depth,int dir,int off) 254 | { 255 | if(!depth) return NULL; 256 | int x,y,z; 257 | x=y=z=1; 258 | switch(dir) 259 | { 260 | case 0: 261 | x=off<<1; 262 | break; 263 | case 1: 264 | y=off<<1; 265 | break; 266 | case 2: 267 | z=off<<1; 268 | break; 269 | } 270 | for(int d=depth;d>=0;d--) 271 | if(NeighborKey::neighbors[d].neighbors[x][y][z]) 272 | return NeighborKey::neighbors[d].neighbors[x][y][z]; 273 | 274 | return NULL; 275 | } 276 | 277 | template 278 | OctNode* 279 | IsoNeighborKey::__EdgeNeighbor(OctNode* node,const int& depth,int o,int i1,int i2) 280 | { 281 | if(!depth) return NULL; 282 | int x,y,z,cIndex,xx,yy,zz; 283 | x=y=z=1; 284 | 285 | // Check if the edge-adjacent neighbor exists at the current depth 286 | switch(o) 287 | { 288 | case 0: 289 | y=i1<<1; 290 | z=i2<<1; 291 | break; 292 | case 1: 293 | x=i1<<1; 294 | z=i2<<1; 295 | break; 296 | case 2: 297 | x=i1<<1; 298 | y=i2<<1; 299 | break; 300 | } 301 | if(NeighborKey::neighbors[depth].neighbors[x][y][z]) 302 | return NeighborKey::neighbors[depth].neighbors[x][y][z]; 303 | 304 | cIndex=int(node-node->parent->children); 305 | Cube::FactorCornerIndex(cIndex,xx,yy,zz); 306 | 307 | // Check if the node is on the corresponding edge of the parent 308 | switch(o) 309 | { 310 | case 0: 311 | if(yy==i1 && zz==i2) return __EdgeNeighbor(node->parent,depth-1,o,i1,i2); 312 | break; 313 | case 1: 314 | if(xx==i1 && zz==i2) return __EdgeNeighbor(node->parent,depth-1,o,i1,i2); 315 | break; 316 | case 2: 317 | if(xx==i1 && yy==i2) return __EdgeNeighbor(node->parent,depth-1,o,i1,i2); 318 | break; 319 | } 320 | 321 | // Check if the node is on a face of the parent containing the edge 322 | switch(o) 323 | { 324 | case 0: 325 | if(yy==i1) return __FaceNeighbor(node->parent,depth-1,1,yy); 326 | if(zz==i2) return __FaceNeighbor(node->parent,depth-1,2,zz); 327 | break; 328 | case 1: 329 | if(xx==i1) return __FaceNeighbor(node->parent,depth-1,0,xx); 330 | if(zz==i2) return __FaceNeighbor(node->parent,depth-1,2,zz); 331 | break; 332 | case 2: 333 | if(xx==i1) return __FaceNeighbor(node->parent,depth-1,0,xx); 334 | if(yy==i2) return __FaceNeighbor(node->parent,depth-1,1,yy); 335 | break; 336 | } 337 | fprintf(stderr,"We shouldn't be here: Edges\n"); 338 | return NULL; 339 | } 340 | 341 | template 342 | OctNode* 343 | IsoNeighborKey::__CornerNeighbor(OctNode* node,const int& depth,int x,int y,int z) 344 | { 345 | if(!depth) return NULL; 346 | int cIndex,xx,yy,zz; 347 | 348 | // Check if the edge-adjacent neighbor exists at the current depth 349 | if(NeighborKey::neighbors[depth].neighbors[x<<1][y<<1][z<<1]) 350 | return NeighborKey::neighbors[depth].neighbors[x<<1][y<<1][z<<1]; 351 | 352 | cIndex=int(node-node->parent->children); 353 | Cube::FactorCornerIndex(cIndex,xx,yy,zz); 354 | 355 | // Check if the node is on the corresponding corner of the parent 356 | if(xx==x && yy==y && zz==z) return __CornerNeighbor(node->parent,depth-1,x,y,z); 357 | 358 | // Check if the node is on an edge of the parent containing the corner 359 | if(xx==x && yy==y) return __EdgeNeighbor(node->parent,depth-1,2,x,y); 360 | if(xx==x && zz==z) return __EdgeNeighbor(node->parent,depth-1,1,x,z); 361 | if(yy==y && zz==z) return __EdgeNeighbor(node->parent,depth-1,0,y,z); 362 | 363 | // Check if the node is on a face of the parent containing the edge 364 | if(xx==x) return __FaceNeighbor(node->parent,depth-1,0,x); 365 | if(yy==y) return __FaceNeighbor(node->parent,depth-1,1,y); 366 | if(zz==z) return __FaceNeighbor(node->parent,depth-1,2,z); 367 | 368 | fprintf(stderr,"We shouldn't be here: Corners\n"); 369 | return NULL; 370 | } 371 | 372 | template 373 | void 374 | IsoNeighborKey::__GetCornerNeighbors(OctNode* node,const int& d,const int& c,OctNode* neighbors[Cube::CORNERS]) 375 | { 376 | int x,y,z,xx,yy,zz,ax,ay,az; 377 | Cube::FactorCornerIndex(c,x,y,z); 378 | ax=x^1; 379 | ay=y^1; 380 | az=z^1; 381 | xx=x<<0; 382 | yy=y<<1; 383 | zz=z<<2; 384 | ax<<=0; 385 | ay<<=1; 386 | az<<=2; 387 | 388 | // Set the current node 389 | neighbors[ax|ay|az]=node; 390 | 391 | // Set the face adjacent neighbors 392 | neighbors[xx|ay|az]=__FaceNeighbor(node,d,0,x); 393 | neighbors[ax|yy|az]=__FaceNeighbor(node,d,1,y); 394 | neighbors[ax|ay|zz]=__FaceNeighbor(node,d,2,z); 395 | 396 | // Set the edge adjacent neighbors 397 | neighbors[ax|yy|zz]=__EdgeNeighbor(node,d,0,y,z); 398 | neighbors[xx|ay|zz]=__EdgeNeighbor(node,d,1,x,z); 399 | neighbors[xx|yy|az]=__EdgeNeighbor(node,d,2,x,y); 400 | 401 | // Set the corner adjacent neighbor 402 | neighbors[xx|yy|zz]=__CornerNeighbor(node,d,x,y,z); 403 | } 404 | 405 | template 406 | void 407 | IsoNeighborKey::GetCornerNeighbors(OctNode* node,const int& c,OctNode* neighbors[Cube::CORNERS]) 408 | { 409 | getNeighbors(node); 410 | memset(neighbors,NULL,sizeof(OctNode*)*Cube::CORNERS); 411 | __GetCornerNeighbors(node,NeighborKey::depth,c,neighbors); 412 | } 413 | 414 | template 415 | void 416 | IsoNeighborKey::CornerIndex(const int& c,int idx[3]) 417 | { 418 | int x,y,z; 419 | Cube::FactorCornerIndex(c,x,y,z); 420 | idx[0]=x<<1; 421 | idx[1]=y<<1; 422 | idx[2]=z<<1; 423 | } 424 | 425 | template 426 | void 427 | IsoNeighborKey::EdgeIndex(const int& e,int idx[3]) 428 | { 429 | int o,i1,i2; 430 | Cube::FactorEdgeIndex(e,o,i1,i2); 431 | idx[0]=idx[1]=idx[2]=1; 432 | switch(o) 433 | { 434 | case 0: 435 | idx[1]=i1<<1; 436 | idx[2]=i2<<1; 437 | break; 438 | case 1: 439 | idx[0]=i1<<1; 440 | idx[2]=i2<<1; 441 | break; 442 | case 2: 443 | idx[0]=i1<<1; 444 | idx[1]=i2<<1; 445 | break; 446 | } 447 | } 448 | 449 | template 450 | void 451 | IsoNeighborKey::FaceIndex(const int& f,int idx[3]) 452 | { 453 | int dir,off; 454 | Cube::FactorFaceIndex(f,dir,off); 455 | idx[0]=idx[1]=idx[2]=1; 456 | idx[dir]=off<<1; 457 | } 458 | 459 | #endif // NEIGHBORKEY_INL 460 | -------------------------------------------------------------------------------- /libs/fssr/octree.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the Floating Scale Surface Reconstruction software. 3 | * Written by Simon Fuhrmann. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "util/timer.h" 11 | #include "mve/mesh_io.h" 12 | #include "fssr/octree.h" 13 | 14 | FSSR_NAMESPACE_BEGIN 15 | 16 | Octree::NodePath 17 | Octree::NodePath::descend (int const octant) const 18 | { 19 | NodePath path; 20 | path.level = this->level + 1; 21 | path.path = (this->path << 3) | octant; 22 | return path; 23 | } 24 | 25 | Octree::NodePath 26 | Octree::NodePath::ascend (void) const 27 | { 28 | NodePath path; 29 | path.level = this->level +- 1; 30 | path.path = this->path >> 3; 31 | return path; 32 | } 33 | 34 | /* -------------------------------------------------------------------- */ 35 | 36 | Octree::NodeGeom 37 | Octree::NodeGeom::descend (int const octant) const 38 | { 39 | NodeGeom node; 40 | double const offset = this->size / 4.0; 41 | for (int i = 0; i < 3; ++i) 42 | node.center[i] = this->center[i] 43 | + ((octant & (1 << i)) ? offset : -offset); 44 | node.size = this->size / 2.0; 45 | return node; 46 | } 47 | 48 | math::Vec3d 49 | Octree::NodeGeom::aabb_min (void) const 50 | { 51 | return this->center - this->size / 2.0; 52 | } 53 | 54 | math::Vec3d 55 | Octree::NodeGeom::aabb_max (void) const 56 | { 57 | return this->center + this->size / 2.0; 58 | } 59 | 60 | math::Vec3d 61 | Octree::NodeGeom::corner_pos (int const corner) const 62 | { 63 | math::Vec3d pos; 64 | double const hs = this->size / 2.0; 65 | for (int i = 0; i < 3; ++i) 66 | pos[i] = this->center[i] + ((corner & (1 << i)) ? hs : -hs); 67 | return pos; 68 | } 69 | 70 | /* ---------------------------------------------------------------- */ 71 | 72 | void 73 | Octree::Iterator::init (Octree const* octree) 74 | { 75 | this->node = octree->get_root_node(); 76 | this->node_geom = octree->get_node_geom_for_root(); 77 | this->node_path = octree->get_node_path_for_root(); 78 | } 79 | 80 | Octree::Iterator 81 | Octree::Iterator::descend (int const octant) const 82 | { 83 | Iterator iter; 84 | iter.node = this->node->children[octant]; 85 | iter.node_geom = this->node_geom.descend(octant); 86 | iter.node_path = this->node_path.descend(octant); 87 | return iter; 88 | } 89 | 90 | /* -------------------------------------------------------------------- */ 91 | 92 | void 93 | Octree::clear (void) 94 | { 95 | this->num_samples = 0; 96 | this->num_nodes = 0; 97 | this->root = NULL; 98 | this->root_size = 0.0; 99 | this->root_center = math::Vec3d(0.0); 100 | } 101 | 102 | void 103 | Octree::insert_sample (Sample const& s) 104 | { 105 | if (this->root == NULL) 106 | { 107 | //std::cout << "INFO: Creating octree root node." << std::endl; 108 | this->root = new Node(); 109 | this->root_center = s.pos; 110 | this->root_size = s.scale; 111 | this->num_nodes = 1; 112 | } 113 | 114 | while (!this->is_inside_octree(s.pos)) 115 | this->expand_root_for_point(s.pos); 116 | 117 | Node* node = this->find_node_for_sample(s); 118 | if (node == NULL) 119 | { 120 | std::cerr << "Error finding node for sample " 121 | << this->num_samples << "!" << std::endl; 122 | return; 123 | } 124 | 125 | node->samples.push_back(s); 126 | this->num_samples += 1; 127 | } 128 | 129 | void 130 | Octree::insert_samples (PointSet const& pset) 131 | { 132 | PointSet::SampleList const& samples = pset.get_samples(); 133 | for (std::size_t i = 0; i < samples.size(); i++) 134 | this->insert_sample(samples[i]); 135 | } 136 | 137 | bool 138 | Octree::is_inside_octree (math::Vec3d const& pos) 139 | { 140 | double const len2 = this->root_size / 2.0; 141 | for (int i = 0; i < 3; ++i) 142 | if (pos[i] < this->root_center[i] - len2 143 | || pos[i] > this->root_center[i] + len2) 144 | return false; 145 | return true; 146 | } 147 | 148 | void 149 | Octree::expand_root_for_point (math::Vec3d const& pos) 150 | { 151 | //std::cout << "INFO: Expanding root node." << std::endl; 152 | int root_octant = 0; 153 | for (int i = 0; i < 3; ++i) 154 | if (pos[i] > this->root_center[i]) 155 | { 156 | this->root_center[i] += this->root_size / 2.0; 157 | } 158 | else 159 | { 160 | root_octant |= (1 << i); 161 | this->root_center[i] -= this->root_size / 2.0; 162 | } 163 | this->root_size *= 2.0; 164 | 165 | Node* new_root = new Node(); 166 | new_root->children[root_octant] = this->root; 167 | this->root = new_root; 168 | this->num_nodes += 1; 169 | } 170 | 171 | Octree::Node* 172 | Octree::find_node_for_sample (Sample const& sample) 173 | { 174 | /* 175 | * Determine whether to expand the root or descend the tree. 176 | * A fitting level for the sample with scale s is level l with 177 | * 178 | * scale(l) <= s < scale(l - 1) <==> scale(l) <= s < scale(l) * 2 179 | * 180 | * Thus, the root needs to be expanded if s >= scale(l) * 2. 181 | */ 182 | if (sample.scale >= this->root_size * 2.0) 183 | return find_node_expand(sample); 184 | 185 | return this->find_node_descend(sample, this->root, 186 | this->get_node_geom_for_root()); 187 | } 188 | 189 | Octree::Node* 190 | Octree::find_node_descend (Sample const& sample, Node* node, 191 | NodeGeom const& node_geom) 192 | { 193 | /* 194 | * The current level l is appropriate if sample scale s is 195 | * scale(l) <= s < scale(l) * 2. As a sanity check, this function 196 | * must not be called if s >= scale(l) * 2. Otherwise descend. 197 | */ 198 | if (sample.scale > node_geom.size * 2.0) 199 | std::cerr << "WARNING: Sanity check S0 failed! " 200 | << "Root size: " << this->root_size 201 | << ", sample scale: " << sample.scale << std::endl; 202 | 203 | if (node_geom.size <= sample.scale) 204 | return node; 205 | 206 | /* Find octant to descend. */ 207 | int octant = 0; 208 | for (int i = 0; i < 3; ++i) 209 | if (sample.pos[i] > node_geom.center[i]) 210 | octant |= (1 << i); 211 | 212 | if (node->children[octant] == NULL) 213 | { 214 | node->children[octant] = new Node(); 215 | this->num_nodes += 1; 216 | } 217 | 218 | return this->find_node_descend(sample, node->children[octant], 219 | node_geom.descend(octant)); 220 | } 221 | 222 | Octree::Node* 223 | Octree::find_node_expand (Sample const& sample) 224 | { 225 | /* 226 | * The current level l is appropriate if sample scale s is 227 | * scale(l) <= scale < scale(l) * 2. As a sanity check, this function 228 | * must not be called if scale(l) > s. Otherwise expand. 229 | */ 230 | 231 | //std::cout << "INFO: Find node expanding..." << std::endl; 232 | if (this->root_size > sample.scale) 233 | std::cerr << "WARNING: Sanity check S1 failed! " 234 | << "Root size: " << this->root_size 235 | << ", sample scale: " << sample.scale << std::endl; 236 | 237 | if (sample.scale < this->root_size * 2.0) 238 | return this->root; 239 | 240 | this->expand_root_for_point(sample.pos); 241 | return this->find_node_expand(sample); 242 | } 243 | 244 | int 245 | Octree::get_num_levels (Node const* node) const 246 | { 247 | if (node == NULL) 248 | return 0; 249 | int max_level = 0; 250 | for (int i = 0; i < 8; ++i) 251 | max_level = std::max(max_level, 252 | this->get_num_levels(node->children[i])); 253 | return max_level + 1; 254 | } 255 | 256 | void 257 | Octree::get_points_per_level (std::vector* stats, 258 | Node const* node, std::size_t level) const 259 | { 260 | if (node == NULL) 261 | return; 262 | if (stats->size() <= level) 263 | stats->resize(level + 1, 0); 264 | stats->at(level) += node->samples.size(); 265 | for (int i = 0; i < 8; ++i) 266 | this->get_points_per_level(stats, node->children[i], level + 1); 267 | } 268 | 269 | void 270 | Octree::influence_query (math::Vec3d const& pos, double factor, 271 | std::vector* result, 272 | Node const* node, NodeGeom const& node_geom) const 273 | { 274 | if (node == NULL) 275 | return; 276 | 277 | /* 278 | * Strategy is the following: Try to rule out this octree node. Assume 279 | * the largest scale sample (node_size * 2) in this node and compute 280 | * an estimate for the closest possible distance of any sample in the 281 | * node to the query. If 'factor' times the largest scale is less than 282 | * the closest distance, the node can be skipped and traversal stops. 283 | * Otherwise the node has to be tested and all samples in the node 284 | * are tested. 285 | */ 286 | 287 | /* Estimate for the minimum distance. No sample is closer to pos. */ 288 | double const min_distance = (pos - node_geom.center).norm() 289 | - MATH_SQRT3 * node_geom.size / 2.0; 290 | double const max_scale = node_geom.size * 2.0; 291 | if (min_distance > max_scale * factor) 292 | return; 293 | 294 | /* Node could not be ruled out. Test all samples. */ 295 | for (std::size_t i = 0; i < node->samples.size(); ++i) 296 | { 297 | Sample const& s = node->samples[i]; 298 | if ((pos - s.pos).square_norm() > MATH_POW2(factor * s.scale)) 299 | continue; 300 | result->push_back(&s); 301 | } 302 | 303 | /* Descend into octree. */ 304 | for (int i = 0; i < 8; ++i) 305 | { 306 | if (node->children[i] == NULL) 307 | continue; 308 | this->influence_query(pos, factor, result, node->children[i], 309 | node_geom.descend(i)); 310 | } 311 | } 312 | 313 | void 314 | Octree::influenced_query (Sample const& sample, double factor, 315 | std::vector* result, Iterator const& iter) 316 | { 317 | if (iter.node == NULL) 318 | return; 319 | 320 | /* 321 | * Strategy: Try to rule out this octree node. The node can be skipped 322 | * if 'factor' times the sample scale is smaller than the minimal 323 | * distance estimate to the node. 324 | */ 325 | double const min_distance = (sample.pos - iter.node_geom.center).norm() 326 | - MATH_SQRT3 * iter.node_geom.size / 2.0; 327 | if (factor * sample.scale < min_distance) 328 | return; 329 | 330 | /* Descend into octree. */ 331 | bool is_leaf = true; 332 | for (int i = 0; i < 8; ++i) 333 | { 334 | if (iter.node->children[i] == NULL) 335 | continue; 336 | is_leaf = false; 337 | this->influenced_query(sample, factor, result, iter.descend(i)); 338 | } 339 | 340 | /* Only leafs are considered. */ 341 | if (!is_leaf) 342 | return; 343 | 344 | /* Add this node to the result set. */ 345 | result->push_back(iter); 346 | } 347 | 348 | void 349 | Octree::make_regular_octree (Node* node) 350 | { 351 | bool is_leaf = true; 352 | for (int i = 0; i < 8 && is_leaf; ++i) 353 | if (node->children[i] != NULL) 354 | is_leaf = false; 355 | 356 | if (is_leaf) 357 | return; 358 | 359 | for (int i = 0; i < 8; ++i) 360 | { 361 | if (node->children[i] == NULL) 362 | { 363 | node->children[i] = new Node(); 364 | this->num_nodes += 1; 365 | } 366 | else 367 | { 368 | this->make_regular_octree(node->children[i]); 369 | } 370 | } 371 | } 372 | 373 | void 374 | Octree::refine_octree (void) 375 | { 376 | if (this->root == NULL) 377 | return; 378 | 379 | std::list queue; 380 | queue.push_back(this->root); 381 | while (!queue.empty()) 382 | { 383 | Node* node = queue.front(); 384 | queue.pop_front(); 385 | 386 | bool is_leaf = true; 387 | for (int i = 0; i < 8 && is_leaf; ++i) 388 | if (node->children[i] != NULL) 389 | is_leaf = false; 390 | 391 | if (is_leaf) 392 | for (int i = 0; i < 8; ++i) 393 | { 394 | node->children[i] = new Node(); 395 | this->num_nodes += 1; 396 | } 397 | else 398 | for (int i = 0; i < 8; ++i) 399 | if (node->children[i] != NULL) 400 | queue.push_back(node->children[i]); 401 | } 402 | } 403 | 404 | void 405 | Octree::remove_low_res_samples (int min_level) 406 | { 407 | if (this->root == NULL) 408 | return; 409 | 410 | std::size_t removed_samples = 0; 411 | std::list > queue; 412 | queue.push_back(std::make_pair(this->root, 0)); 413 | while (!queue.empty()) 414 | { 415 | Node* node = queue.front().first; 416 | int const level = queue.front().second; 417 | queue.pop_front(); 418 | if (level > min_level) 419 | continue; 420 | removed_samples += node->samples.size(); 421 | node->samples.clear(); 422 | for (int i = 0; i < 8; ++i) 423 | if (node->children[i] != NULL && level < min_level) 424 | queue.push_back(std::make_pair(node->children[i], level + 1)); 425 | } 426 | 427 | std::cout << "Removed " << removed_samples << " samples." << std::endl; 428 | } 429 | 430 | void 431 | Octree::write_hierarchy (std::ostream& out, bool with_meta) const 432 | { 433 | if (with_meta) 434 | { 435 | out.write(reinterpret_cast(&this->num_nodes), 436 | sizeof(std::size_t)); 437 | out.write(reinterpret_cast(*this->root_center), 438 | 3 * sizeof(double)); 439 | out.write(reinterpret_cast(&this->root_size), 440 | sizeof(double)); 441 | } 442 | 443 | std::list queue; 444 | queue.push_back(this->root); 445 | while (!queue.empty()) 446 | { 447 | Octree::Node const* node = queue.front(); 448 | queue.pop_front(); 449 | out << (node == NULL ? "0" : "1"); 450 | if (node == NULL) 451 | continue; 452 | for (int i = 0; i < 8; ++i) 453 | queue.push_back(node->children[i]); 454 | } 455 | } 456 | 457 | void 458 | Octree::read_hierarchy (std::istream& in, bool with_meta) 459 | { 460 | /* Clear octree. */ 461 | delete this->root; 462 | this->root = NULL; 463 | this->num_nodes = 0; 464 | this->num_samples = 0; 465 | 466 | if (with_meta) 467 | { 468 | in.read(reinterpret_cast(&this->num_nodes), 469 | sizeof(std::size_t)); 470 | in.read(reinterpret_cast(*this->root_center), 471 | 3 * sizeof(double)); 472 | in.read(reinterpret_cast(&this->root_size), 473 | sizeof(double)); 474 | } 475 | 476 | std::list queue; 477 | 478 | char byte; 479 | in >> byte; 480 | if (byte == '1') 481 | { 482 | this->root = new Octree::Node(); 483 | queue.push_back(this->root); 484 | } 485 | 486 | while (!queue.empty()) 487 | { 488 | Octree::Node* node = queue.front(); 489 | queue.pop_front(); 490 | for (int i = 0; i < 8; ++i) 491 | { 492 | in >> byte; 493 | if (byte == '1') 494 | { 495 | node->children[i] = new Octree::Node(); 496 | queue.push_back(node->children[i]); 497 | } 498 | } 499 | } 500 | } 501 | 502 | void 503 | Octree::octree_to_mesh (mve::TriangleMesh::Ptr mesh, 504 | Node const* node, NodeGeom const& node_geom) 505 | { 506 | mve::TriangleMesh::VertexList& verts = mesh->get_vertices(); 507 | mve::TriangleMesh::ColorList& colors = mesh->get_vertex_colors(); 508 | 509 | /* Descend into child nodes first. */ 510 | bool is_leaf = true; 511 | for (int i = 0; i < 8; ++i) 512 | { 513 | if (node->children[i] == NULL) 514 | continue; 515 | is_leaf = false; 516 | this->octree_to_mesh(mesh, node->children[i], node_geom.descend(i)); 517 | } 518 | 519 | if (!is_leaf) 520 | return; 521 | 522 | /* Generate the 8 corner coordinates. */ 523 | math::Vec3d corners[8]; 524 | { 525 | math::Vec3d aabb[2] = { node_geom.aabb_min(), node_geom.aabb_max() }; 526 | for (int i = 0; i < 8; ++i) 527 | for (int j = 0; j < 3; ++j) 528 | corners[i][j] = aabb[(i & (1 << j)) >> j][j]; 529 | } 530 | 531 | /* Draw the edges between the corners. */ 532 | int edges[12][2] = { 533 | {0, 1}, {0, 2}, {1, 3}, {2, 3}, 534 | {4, 5}, {4, 6}, {5, 7}, {6, 7}, 535 | {0, 4}, {1, 5}, {2, 6}, {3, 7} 536 | }; 537 | for (int i = 0; i < 12; ++i) 538 | for (float j = 0.0f; j <= 1.0f; j += 0.02f) 539 | { 540 | verts.push_back(corners[edges[i][0]] * j + corners[edges[i][1]] * (1.0f - j)); 541 | colors.push_back(math::Vec4f(0.0f, 1.0f, 0.0f, 1.0f)); 542 | } 543 | } 544 | 545 | void 546 | Octree::octree_to_mesh (std::string const& filename) 547 | { 548 | mve::TriangleMesh::Ptr mesh = mve::TriangleMesh::create(); 549 | this->octree_to_mesh(mesh, this->root, this->get_node_geom_for_root()); 550 | mve::geom::save_mesh(mesh, filename); 551 | } 552 | 553 | math::Vec3d 554 | Octree::child_center_for_octant (math::Vec3d const& old_center, 555 | double old_size, int octant) 556 | { 557 | math::Vec3d new_center; 558 | 559 | double const center_offset = old_size / 4.0; 560 | for (int j = 0; j < 3; ++j) 561 | new_center[j] = old_center[j] 562 | + ((octant & (1 << j)) ? center_offset : -center_offset); 563 | 564 | return new_center; 565 | } 566 | 567 | void 568 | Octree::print_stats (std::ostream& out) 569 | { 570 | out << "Octree contains " << this->get_num_samples() 571 | << " samples in " << this->get_num_nodes() << " nodes on " 572 | << this->get_num_levels() << " levels." << std::endl; 573 | 574 | std::vector octree_stats; 575 | this->get_points_per_level(&octree_stats); 576 | 577 | std::size_t index = 0; 578 | while (index < octree_stats.size() && octree_stats[index] == 0) 579 | index += 1; 580 | 581 | out << "Samples per level:" << std::endl; 582 | while (index < octree_stats.size()) 583 | { 584 | out << " Level " << index << ": " 585 | << octree_stats[index] << " samples" << std::endl; 586 | index += 1; 587 | } 588 | } 589 | 590 | FSSR_NAMESPACE_END 591 | -------------------------------------------------------------------------------- /libs/iso/MarchingCubes.inl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Contains modifications by Simon Fuhrmann, 2013. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of 11 | conditions and the following disclaimer. Redistributions in binary form must reproduce 12 | the above copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the distribution. 14 | 15 | Neither the name of the Johns Hopkins University nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 25 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 27 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | DAMAGE. 29 | */ 30 | 31 | //////////// 32 | // Square // 33 | //////////// 34 | int Square::CornerIndex(const int& x,const int& y){return (y<<1)|x;} 35 | void Square::FactorCornerIndex(const int& idx,int& x,int& y) 36 | { 37 | x=(idx>>0)%2; 38 | y=(idx>>1)%2; 39 | } 40 | 41 | int Square::EdgeIndex(const int& orientation,const int& i) 42 | { 43 | switch(orientation){ 44 | case 0: // x 45 | if(!i) {return 0;} // (0,0) -> (1,0) 46 | else {return 2;} // (0,1) -> (1,1) 47 | case 1: // y 48 | if(!i) {return 3;} // (0,0) -> (0,1) 49 | else {return 1;} // (1,0) -> (1,1) 50 | }; 51 | return -1; 52 | } 53 | 54 | void Square::FactorEdgeIndex(const int& idx,int& orientation,int& i) 55 | { 56 | switch(idx){ 57 | case 0: case 2: 58 | orientation=0; 59 | i=idx/2; 60 | return; 61 | case 1: case 3: 62 | orientation=1; 63 | i=((idx/2)+1)%2; 64 | return; 65 | default: 66 | orientation = 0; 67 | i = 0; 68 | return; 69 | }; 70 | } 71 | 72 | void Square::EdgeCorners(const int& idx,int& c1,int& c2) 73 | { 74 | int orientation = 0,i = 0; 75 | FactorEdgeIndex(idx,orientation,i); 76 | switch(orientation){ 77 | case 0: 78 | c1=CornerIndex(0,i); 79 | c2=CornerIndex(1,i); 80 | break; 81 | case 1: 82 | c1=CornerIndex(i,0); 83 | c2=CornerIndex(i,1); 84 | break; 85 | }; 86 | } 87 | 88 | void Square::OrientedEdgeCorners(const int& idx,int& c1,int& c2) 89 | { 90 | int orientation = 0,i = 0; 91 | FactorEdgeIndex(idx,orientation,i); 92 | switch(orientation){ 93 | case 0: 94 | c1=CornerIndex((i )&1,i); 95 | c2=CornerIndex((i+1)&1,i); 96 | break; 97 | case 1: 98 | c1=CornerIndex(i,(i+1)&1); 99 | c2=CornerIndex(i,(i )&1); 100 | break; 101 | }; 102 | } 103 | 104 | int Square::ReflectEdgeIndex(const int& idx,const int& edgeIndex) 105 | { 106 | int orientation=edgeIndex%2; 107 | int o,i; 108 | FactorEdgeIndex(idx,o,i); 109 | if(o!=orientation){return idx;} 110 | else{return EdgeIndex(o,(i+1)%2);} 111 | } 112 | 113 | int Square::ReflectCornerIndex(const int& idx,const int& edgeIndex) 114 | { 115 | int orientation=edgeIndex%2; 116 | int x,y; 117 | FactorCornerIndex(idx,x,y); 118 | switch(orientation){ 119 | case 0: return CornerIndex((x+1)%2,y); 120 | case 1: return CornerIndex(x,(y+1)%2); 121 | }; 122 | return -1; 123 | } 124 | 125 | ////////// 126 | // Cube // 127 | ////////// 128 | int Cube::CornerIndex(const int& x,const int& y,const int& z){return (z<<2)|(y<<1)|x;} 129 | void Cube::FactorCornerIndex(const int& idx,int& x,int& y,int& z) 130 | { 131 | x=(idx>>0)%2; 132 | y=(idx>>1)%2; 133 | z=(idx>>2)%2; 134 | } 135 | 136 | int 137 | Cube::EdgeIndex (const int& orientation,const int& i,const int& j) 138 | { 139 | return (i | (j<<1))|(orientation<<2); 140 | } 141 | 142 | void Cube::FactorEdgeIndex(const int& idx,int& orientation,int& i,int &j) 143 | { 144 | orientation=idx>>2; 145 | i=idx&1; 146 | j=(idx&2)>>1; 147 | } 148 | 149 | int Cube::FaceIndex(const int& x,const int& y,const int& z) 150 | { 151 | if (x<0) {return 0;} 152 | else if (x>0) {return 1;} 153 | else if (y<0) {return 2;} 154 | else if (y>0) {return 3;} 155 | else if (z<0) {return 4;} 156 | else if (z>0) {return 5;} 157 | else {return -1;} 158 | } 159 | 160 | int Cube::FaceIndex(const int& dir,const int& offSet) 161 | { 162 | return (dir<<1)|offSet; 163 | } 164 | 165 | void Cube::FactorFaceIndex(const int& idx,int& x,int& y,int& z) 166 | { 167 | x=y=z=0; 168 | switch(idx){ 169 | case 0: x=-1; break; 170 | case 1: x= 1; break; 171 | case 2: y=-1; break; 172 | case 3: y= 1; break; 173 | case 4: z=-1; break; 174 | case 5: z= 1; break; 175 | }; 176 | } 177 | void Cube::FactorFaceIndex(const int& idx,int& dir,int& offSet) 178 | { 179 | dir = idx>>1; 180 | offSet=idx &1; 181 | } 182 | 183 | int Cube::FaceAdjacentToEdges(const int& eIndex1,const int& eIndex2) 184 | { 185 | int f1,f2,g1,g2; 186 | FacesAdjacentToEdge(eIndex1,f1,f2); 187 | FacesAdjacentToEdge(eIndex2,g1,g2); 188 | if(f1==g1 || f1==g2){return f1;} 189 | if(f2==g1 || f2==g2){return f2;} 190 | return -1; 191 | } 192 | 193 | void Cube::FacesAdjacentToEdge(const int& eIndex,int& f1Index,int& f2Index) 194 | { 195 | int orientation,i1,i2; 196 | FactorEdgeIndex(eIndex,orientation,i1,i2); 197 | i1<<=1; 198 | i2<<=1; 199 | i1--; 200 | i2--; 201 | switch(orientation){ 202 | case 0: 203 | f1Index=FaceIndex( 0,i1, 0); 204 | f2Index=FaceIndex( 0, 0,i2); 205 | break; 206 | case 1: 207 | f1Index=FaceIndex(i1, 0, 0); 208 | f2Index=FaceIndex( 0, 0,i2); 209 | break; 210 | case 2: 211 | f1Index=FaceIndex(i1, 0, 0); 212 | f2Index=FaceIndex( 0,i2, 0); 213 | break; 214 | }; 215 | } 216 | 217 | void Cube::EdgeCorners(const int& idx,int& c1,int& c2) 218 | { 219 | int orientation,i1,i2; 220 | FactorEdgeIndex(idx,orientation,i1,i2); 221 | switch(orientation){ 222 | case 0: 223 | c1=CornerIndex(0,i1,i2); 224 | c2=CornerIndex(1,i1,i2); 225 | break; 226 | case 1: 227 | c1=CornerIndex(i1,0,i2); 228 | c2=CornerIndex(i1,1,i2); 229 | break; 230 | case 2: 231 | c1=CornerIndex(i1,i2,0); 232 | c2=CornerIndex(i1,i2,1); 233 | break; 234 | }; 235 | } 236 | 237 | void Cube::FaceCorners(const int& idx,int& c1,int& c2,int& c3,int& c4) 238 | { 239 | int i=idx%2; 240 | switch(idx/2){ 241 | case 0: 242 | c1=CornerIndex(i,0,0); 243 | c2=CornerIndex(i,1,0); 244 | c3=CornerIndex(i,0,1); 245 | c4=CornerIndex(i,1,1); 246 | return; 247 | case 1: 248 | c1=CornerIndex(0,i,0); 249 | c2=CornerIndex(1,i,0); 250 | c3=CornerIndex(0,i,1); 251 | c4=CornerIndex(1,i,1); 252 | return; 253 | case 2: 254 | c1=CornerIndex(0,0,i); 255 | c2=CornerIndex(1,0,i); 256 | c3=CornerIndex(0,1,i); 257 | c4=CornerIndex(1,1,i); 258 | return; 259 | } 260 | } 261 | 262 | int Cube::AntipodalCornerIndex(const int& idx) 263 | { 264 | int x,y,z; 265 | FactorCornerIndex(idx,x,y,z); 266 | return CornerIndex((x+1)%2,(y+1)%2,(z+1)%2); 267 | } 268 | 269 | int Cube::FaceReflectFaceIndex(const int& idx,const int& faceIndex) 270 | { 271 | if(idx/2!=faceIndex/2){return idx;} 272 | else{ 273 | if(idx%2) {return idx-1;} 274 | else {return idx+1;} 275 | } 276 | } 277 | 278 | int Cube::FaceReflectEdgeIndex(const int& idx,const int& faceIndex) 279 | { 280 | int orientation=faceIndex/2; 281 | int o,i,j; 282 | FactorEdgeIndex(idx,o,i,j); 283 | if(o==orientation){return idx;} 284 | switch(orientation){ 285 | case 0: return EdgeIndex(o,(i+1)%2,j); 286 | case 1: 287 | switch(o){ 288 | case 0: return EdgeIndex(o,(i+1)%2,j); 289 | case 2: return EdgeIndex(o,i,(j+1)%2); 290 | }; 291 | case 2: return EdgeIndex(o,i,(j+1)%2); 292 | }; 293 | return -1; 294 | } 295 | 296 | int Cube::FaceReflectCornerIndex(const int& idx,const int& faceIndex) 297 | { 298 | int orientation=faceIndex/2; 299 | int x,y,z; 300 | FactorCornerIndex(idx,x,y,z); 301 | switch(orientation){ 302 | case 0: return CornerIndex((x+1)%2,y,z); 303 | case 1: return CornerIndex(x,(y+1)%2,z); 304 | case 2: return CornerIndex(x,y,(z+1)%2); 305 | }; 306 | return -1; 307 | } 308 | 309 | int Cube::EdgeReflectCornerIndex(const int& idx,const int& edgeIndex) 310 | { 311 | int orientation,x,y,z; 312 | FactorEdgeIndex(edgeIndex,orientation,x,y); 313 | FactorCornerIndex(idx,x,y,z); 314 | switch(orientation){ 315 | case 0: return CornerIndex( x ,(y+1)%2,(z+1)%2); 316 | case 1: return CornerIndex((x+1)%2, y ,(z+1)%2); 317 | case 2: return CornerIndex((x+1)%2,(y+1)%2, z ); 318 | }; 319 | return -1; 320 | } 321 | 322 | int Cube::EdgeReflectEdgeIndex(const int& edgeIndex) 323 | { 324 | int o,i1,i2; 325 | FactorEdgeIndex(edgeIndex,o,i1,i2); 326 | return Cube::EdgeIndex(o,(i1+1)%2,(i2+1)%2); 327 | } 328 | 329 | int Cube::SquareToCubeCorner(const int& fIndex,const int& cIndex) 330 | { 331 | // Assuming that the offset is 0, this returns corners in a consistent orientation 332 | int dir,off,i1,i2; 333 | FactorFaceIndex(fIndex,dir,off); 334 | Square::FactorCornerIndex(cIndex,i1,i2); 335 | switch(dir) 336 | { 337 | case 0: 338 | return CornerIndex(off,i1,i2); 339 | case 1: 340 | return CornerIndex(i1,off,(i2+1)&1); 341 | case 2: 342 | return CornerIndex(i1,i2,off); 343 | } 344 | return -1; 345 | } 346 | 347 | int Cube::SquareToCubeEdge(const int& fIndex,const int& eIndex) 348 | { 349 | // Assuming that the offset is 0, this returns corners in a consistent orientation 350 | int dir,off,o,i; 351 | FactorFaceIndex(fIndex,dir,off); 352 | Square::FactorEdgeIndex(eIndex,o,i); 353 | switch(dir) 354 | { 355 | case 0: 356 | if(o==0) 357 | return EdgeIndex(1,off,i); 358 | else if(o==1) 359 | return EdgeIndex(2,off,i); 360 | else 361 | return -1; 362 | case 1: 363 | if(o==0) 364 | return EdgeIndex(0,off,(i+1)&1); 365 | else if(o==1) 366 | return EdgeIndex(2,i,off); 367 | else 368 | return -1; 369 | case 2: 370 | if(o==0) 371 | return EdgeIndex(0,i,off); 372 | else if(o==1) 373 | return EdgeIndex(1,i,off); 374 | else 375 | return -1; 376 | } 377 | return -1; 378 | } 379 | 380 | ///////////////////// 381 | // MarchingSquares // 382 | ///////////////////// 383 | template 384 | int MarchingSquares::GetIndex(const Real v[Square::CORNERS],const Real& iso) 385 | { 386 | int idx=0; 387 | for(int i=0;i 392 | int MarchingSquares::GetFullIndex(const Real v[Square::CORNERS],const Real& iso) 393 | { 394 | int idx=0; 395 | Real sum=0; 396 | // Corner Index 397 | for(int i=0;i 477 | int MarchingCubes::GetIndex(const Real v[Cube::CORNERS],const Real& iso) 478 | { 479 | int idx=0; 480 | for(int i=0;i 487 | int MarchingCubes::GetFullIndex(const Real values[Cube::CORNERS],const Real& iso) 488 | { 489 | int idx=0; 490 | int c1,c2,c3,c4; 491 | for(int i=0;i >& edges,std::vector >& loops) 526 | { 527 | int loopSize=0; 528 | int idx; 529 | std::pair e,temp; 530 | loops.clear(); 531 | while(edges.size()) 532 | { 533 | e=edges[0]; 534 | loops.resize(loopSize+1); 535 | edges[0]=edges[edges.size()-1]; 536 | edges.pop_back(); 537 | loops[loopSize].push_back(e.first); 538 | idx=e.second; 539 | for(int j=int(edges.size())-1;j>=0;j--){ 540 | if(edges[j].first==idx || edges[j].second==idx){ 541 | if(edges[j].first==idx) {temp=edges[j];} 542 | else {temp.first=edges[j].second;temp.second=edges[j].first;} 543 | idx=temp.second; 544 | loops[loopSize].push_back(temp.first); 545 | edges[j]=edges[edges.size()-1]; 546 | edges.pop_back(); 547 | j=int(edges.size()); 548 | } 549 | } 550 | loopSize++; 551 | } 552 | } 553 | 554 | std::vector< std::vector > MarchingCubes::__caseTable[1< >& MarchingCubes::caseTable(const int& idx) 557 | { 558 | return __caseTable[idx]; 559 | } 560 | 561 | void MarchingCubes::SetCaseTable(void) 562 | { 563 | MarchingSquares::SetCaseTable(); 564 | int dir,off; 565 | for(int idx=0;idx<(1< > edges; 568 | for(int f=0;f(Cube::SquareToCubeEdge(f,MarchingSquares::caseTable(fIdx).edge[i].first),Cube::SquareToCubeEdge(f,MarchingSquares::caseTable(fIdx).edge[i].second))); 579 | else 580 | edges.push_back(std::pair(Cube::SquareToCubeEdge(f,MarchingSquares::caseTable(fIdx).edge[i].second),Cube::SquareToCubeEdge(f,MarchingSquares::caseTable(fIdx).edge[i].first))); 581 | } 582 | __caseTable[idx].clear(); 583 | GetEdgeLoops(edges,__caseTable[idx]); 584 | } 585 | } 586 | 587 | int MarchingCubes::__fullCaseMap[1<<(Cube::CORNERS+Cube::FACES)]; 588 | std::vector< std::vector< std::vector > > MarchingCubes::__fullCaseTable; 589 | 590 | const std::vector< std::vector >& MarchingCubes::caseTable(const int& idx,const int& useFull) 591 | { 592 | if(useFull) 593 | return __fullCaseTable[__fullCaseMap[idx] ]; 594 | else 595 | return __caseTable[idx]; 596 | } 597 | 598 | const std::vector< std::vector >& MarchingCubes::fullCaseTable(const int& idx) 599 | { 600 | return __fullCaseTable[__fullCaseMap[idx] ]; 601 | } 602 | 603 | void MarchingCubes::SetFullCaseTable(void) 604 | { 605 | MarchingSquares::SetFullCaseTable(); 606 | int dir,off,tSize=0; 607 | 608 | memset(__fullCaseMap,-1,sizeof(int)*(1<<(Cube::CORNERS+Cube::FACES))); 609 | for(int idx=0;idx<(1< > edges; 656 | int aFlag=0; 657 | for(int i=0;i(Cube::SquareToCubeEdge(f,MarchingSquares::fullCaseTable(fIdx).edge[i].first),Cube::SquareToCubeEdge(f,MarchingSquares::fullCaseTable(fIdx).edge[i].second))); 674 | else 675 | edges.push_back(std::pair(Cube::SquareToCubeEdge(f,MarchingSquares::fullCaseTable(fIdx).edge[i].second),Cube::SquareToCubeEdge(f,MarchingSquares::fullCaseTable(fIdx).edge[i].first))); 676 | } 677 | for(int uaIndex=0;uaIndex<(1< 32 | #include 33 | #include 34 | #include 35 | 36 | //////////////////////// 37 | // OctNode::NodeIndex // 38 | //////////////////////// 39 | template 40 | OctNode::NodeIndex::NodeIndex(void) 41 | { 42 | depth=offset[0]=offset[1]=offset[2]=0; 43 | } 44 | template 45 | typename OctNode::NodeIndex OctNode::NodeIndex::child(const int& cIndex) const 46 | { 47 | int x,y,z; 48 | NodeIndex idx; 49 | Cube::FactorCornerIndex(cIndex,x,y,z); 50 | idx.depth=depth+1; 51 | idx.offset[0]=(offset[0]<<1)|x; 52 | idx.offset[1]=(offset[1]<<1)|y; 53 | idx.offset[2]=(offset[2]<<1)|z; 54 | return idx; 55 | } 56 | template 57 | typename OctNode::NodeIndex OctNode::NodeIndex::parent(void) const 58 | { 59 | NodeIndex idx; 60 | idx.depth=depth-1; 61 | idx.offset[0]=offset[0]>>1; 62 | idx.offset[1]=offset[1]>>1; 63 | idx.offset[2]=offset[2]>>1; 64 | return idx; 65 | } 66 | template 67 | typename OctNode::NodeIndex& OctNode::NodeIndex::operator += (const int& cIndex) 68 | { 69 | int x,y,z; 70 | Cube::FactorCornerIndex(cIndex,x,y,z); 71 | depth++; 72 | offset[0]=(offset[0]<<1)|x; 73 | offset[1]=(offset[1]<<1)|y; 74 | offset[2]=(offset[2]<<1)|z; 75 | return *this; 76 | } 77 | template 78 | typename OctNode::NodeIndex& OctNode::NodeIndex::operator -- (void) 79 | { 80 | depth--; 81 | offset[0]>>=1; 82 | offset[1]>>=1; 83 | offset[2]>>=1; 84 | return *this; 85 | } 86 | 87 | ///////////// 88 | // OctNode // 89 | ///////////// 90 | 91 | template 92 | OctNode::OctNode(void) 93 | { 94 | parent=children=NULL; 95 | } 96 | 97 | template 98 | OctNode::~OctNode(void) 99 | { 100 | if(children){delete[] children;} 101 | parent=children=NULL; 102 | } 103 | 104 | template 105 | int OctNode::initChildren(void) 106 | { 107 | int i,j,k; 108 | 109 | if(children){delete[] children;} 110 | children=NULL; 111 | children=new OctNode[Cube::CORNERS]; 112 | if(!children){ 113 | fprintf(stderr,"Failed to initialize children in OctNode::initChildren\n"); 114 | exit(0); 115 | return 0; 116 | } 117 | for(i=0;i<2;i++){ 118 | for(j=0;j<2;j++){ 119 | for(k=0;k<2;k++){ 120 | int idx=Cube::CornerIndex(i,j,k); 121 | children[idx].parent=this; 122 | children[idx].children=NULL; 123 | } 124 | } 125 | } 126 | return 1; 127 | } 128 | 129 | template 130 | void OctNode::deleteChildren(void) 131 | { 132 | if(children) 133 | delete[] children; 134 | children=NULL; 135 | } 136 | 137 | template 138 | inline void OctNode::CenterAndWidth(const NodeIndex &nIndex,VertexType& center,Real& width) 139 | { 140 | width=Real(1.0/(1< 146 | int OctNode::maxDepth(void) const 147 | { 148 | if(!children){return 0;} 149 | else{ 150 | int c,d; 151 | for(int i=0;ic){c=d;} 154 | } 155 | return c+1; 156 | } 157 | } 158 | 159 | template 160 | int OctNode::nodes(void) const 161 | { 162 | if(!children){return 1;} 163 | else{ 164 | int c=0; 165 | for(int i=0;i 171 | int OctNode::leaves(void) const 172 | { 173 | if(!children){return 1;} 174 | else{ 175 | int c=0; 176 | for(int i=0;i 182 | int OctNode::maxDepthLeaves(const int& maxDepth) const 183 | { 184 | //if(depth>maxDepth){return 0;} // was: depth() 185 | if(!children){return 1;} 186 | else{ 187 | int c=0; 188 | for(int i=0;i 194 | const OctNode* OctNode::root(void) const 195 | { 196 | const OctNode* temp=this; 197 | while(temp->parent){temp=temp->parent;} 198 | return temp; 199 | } 200 | 201 | template 202 | const OctNode* OctNode::nextBranch(const OctNode* current) const 203 | { 204 | if(!current->parent || current==this){return NULL;} 205 | if(current-current->parent->children==Cube::CORNERS-1){return nextBranch(current->parent);} 206 | else{return current+1;} 207 | } 208 | 209 | template 210 | OctNode* OctNode::nextBranch(OctNode* current) 211 | { 212 | if(!current->parent || current==this){return NULL;} 213 | if(current-current->parent->children==Cube::CORNERS-1){return nextBranch(current->parent);} 214 | else{return current+1;} 215 | } 216 | 217 | template 218 | const OctNode* OctNode::nextLeaf(const OctNode* current) const 219 | { 220 | if(!current) 221 | { 222 | const OctNode* temp=this; 223 | while(temp->children){temp=&temp->children[0];} 224 | return temp; 225 | } 226 | if(current->children){return current->nextLeaf(NULL);} 227 | const OctNode* temp=nextBranch(current); 228 | if(!temp){return NULL;} 229 | else{return temp->nextLeaf(NULL);} 230 | } 231 | 232 | template 233 | OctNode* OctNode::nextLeaf(OctNode* current) 234 | { 235 | if(!current) 236 | { 237 | OctNode* temp=this; 238 | while(temp->children){temp=&temp->children[0];} 239 | return temp; 240 | } 241 | if(current->children){return current->nextLeaf(NULL);} 242 | OctNode* temp=nextBranch(current); 243 | if(!temp){return NULL;} 244 | else{return temp->nextLeaf(NULL);} 245 | } 246 | 247 | template 248 | const OctNode* OctNode::nextNode(const OctNode* current) const 249 | { 250 | if(!current){return this;} 251 | else if(current->children){return ¤t->children[0];} 252 | else{return nextBranch(current);} 253 | } 254 | 255 | template 256 | OctNode* OctNode::nextNode(OctNode* current) 257 | { 258 | if(!current){return this;} 259 | else if(current->children){return ¤t->children[0];} 260 | else{return nextBranch(current);} 261 | } 262 | 263 | template 264 | const OctNode* OctNode::nextBranch(const OctNode* current,NodeIndex& nIndex) const 265 | { 266 | if(!current->parent || current==this) 267 | return NULL; 268 | int c=current-current->parent->children; 269 | nIndex--; 270 | if(c==Cube::CORNERS-1) 271 | return nextBranch(current->parent,nIndex); 272 | else 273 | { 274 | nIndex+=c; 275 | return current+1; 276 | } 277 | } 278 | 279 | template 280 | OctNode* OctNode::nextBranch(OctNode* current,NodeIndex& nIndex) 281 | { 282 | if(!current->parent || current==this) 283 | return NULL; 284 | int c=int(current-current->parent->children); 285 | --nIndex; 286 | if(c==Cube::CORNERS-1) 287 | return nextBranch(current->parent,nIndex); 288 | else 289 | { 290 | nIndex+=c+1; 291 | return current+1; 292 | } 293 | } 294 | 295 | template 296 | const OctNode* OctNode::nextLeaf(const OctNode* current,NodeIndex& nIndex) const 297 | { 298 | if(!current) 299 | { 300 | const OctNode* temp=this; 301 | while(temp->children) 302 | { 303 | nIndex+=0; 304 | temp=&temp->children[0]; 305 | } 306 | return temp; 307 | } 308 | if(current->children) 309 | return current->nextLeaf(NULL,nIndex); 310 | const OctNode* temp=nextBranch(current,nIndex); 311 | if(!temp) 312 | return NULL; 313 | else 314 | return temp->nextLeaf(NULL,nIndex); 315 | } 316 | 317 | template 318 | OctNode* OctNode::nextLeaf(OctNode* current,NodeIndex& nIndex) 319 | { 320 | if(!current){ 321 | OctNode* temp=this; 322 | while(temp->children) 323 | { 324 | nIndex+=0; 325 | temp=&temp->children[0]; 326 | } 327 | return temp; 328 | } 329 | if(current->children) 330 | return current->nextLeaf(NULL,nIndex); 331 | OctNode* temp=nextBranch(current,nIndex); 332 | if(!temp) 333 | return NULL; 334 | else 335 | return temp->nextLeaf(NULL,nIndex); 336 | } 337 | 338 | template 339 | const OctNode* OctNode::nextNode(const OctNode* current,NodeIndex& nIndex) const 340 | { 341 | if(!current) 342 | return this; 343 | else if(current->children) 344 | { 345 | nIndex+=0; 346 | return ¤t->children[0]; 347 | } 348 | else 349 | return nextBranch(current,nIndex); 350 | } 351 | 352 | template 353 | OctNode* OctNode::nextNode(OctNode* current,NodeIndex& nIndex) 354 | { 355 | if(!current) 356 | return this; 357 | else if(current->children) 358 | { 359 | nIndex+=0; 360 | return ¤t->children[0]; 361 | } 362 | else 363 | return nextBranch(current,nIndex); 364 | } 365 | 366 | template 367 | void OctNode::setFullDepth(const int& maxDepth) 368 | { 369 | if(maxDepth){ 370 | if(!children){initChildren();} 371 | for(int i=0;i<8;i++){children[i].setFullDepth(maxDepth-1);} 372 | } 373 | } 374 | 375 | template 376 | OctNode* OctNode::faceNeighbor(const int& faceIndex,const int& forceChildren) 377 | { 378 | return __faceNeighbor(faceIndex>>1,faceIndex&1,forceChildren); 379 | } 380 | 381 | template 382 | const OctNode* OctNode::faceNeighbor(const int& faceIndex) const 383 | { 384 | return __faceNeighbor(faceIndex>>1,faceIndex&1); 385 | } 386 | 387 | template 388 | OctNode* OctNode::__faceNeighbor(const int& dir,const int& off,const int& forceChildren) 389 | { 390 | if(!parent){return NULL;} 391 | int pIndex=int(this-parent->children); 392 | pIndex^=(1<children[pIndex];} 394 | // if(!(((pIndex>>dir)^off)&1)){return &parent->children[pIndex];} 395 | else{ 396 | OctNode* temp=parent->__faceNeighbor(dir,off,forceChildren); 397 | if(!temp){return NULL;} 398 | if(!temp->children){ 399 | if(forceChildren){temp->initChildren();} 400 | else{return temp;} 401 | } 402 | return &temp->children[pIndex]; 403 | } 404 | } 405 | 406 | template 407 | const OctNode* OctNode::__faceNeighbor(const int& dir,const int& off) const { 408 | if(!parent){return NULL;} 409 | int pIndex=int(this-parent->children); 410 | pIndex^=(1<children[pIndex];} 412 | // if(!(((pIndex>>dir)^off)&1)){return &parent->children[pIndex];} 413 | else{ 414 | const OctNode* temp=parent->__faceNeighbor(dir,off); 415 | if(!temp || !temp->children){return temp;} 416 | else{return &temp->children[pIndex];} 417 | } 418 | } 419 | 420 | template 421 | OctNode* OctNode::edgeNeighbor(const int& edgeIndex,const int& forceChildren){ 422 | int idx[2],o,i[2]; 423 | Cube::FactorEdgeIndex(edgeIndex,o,i[0],i[1]); 424 | switch(o){ 425 | case 0: idx[0]=1; idx[1]=2; break; 426 | case 1: idx[0]=0; idx[1]=2; break; 427 | case 2: idx[0]=0; idx[1]=1; break; 428 | }; 429 | return __edgeNeighbor(o,i,idx,forceChildren); 430 | } 431 | 432 | template 433 | const OctNode* OctNode::edgeNeighbor(const int& edgeIndex) const { 434 | int idx[2],o,i[2]; 435 | Cube::FactorEdgeIndex(edgeIndex,o,i[0],i[1]); 436 | switch(o){ 437 | case 0: idx[0]=1; idx[1]=2; break; 438 | case 1: idx[0]=0; idx[1]=2; break; 439 | case 2: idx[0]=0; idx[1]=1; break; 440 | }; 441 | return __edgeNeighbor(o,i,idx); 442 | } 443 | 444 | template 445 | const OctNode* OctNode::__edgeNeighbor(const int& o,const int i[2],const int idx[2]) const{ 446 | if(!parent){return NULL;} 447 | int pIndex=int(this-parent->children); 448 | int aIndex,x[3]; 449 | 450 | Cube::FactorCornerIndex(pIndex,x[0],x[1],x[2]); 451 | aIndex=(~((i[0] ^ x[idx[0]]) | ((i[1] ^ x[idx[1]])<<1))) & 3; 452 | pIndex^=(7 ^ (1<__faceNeighbor(idx[0],i[0]); 455 | if(!temp || !temp->children){return NULL;} 456 | else{return &temp->children[pIndex];} 457 | } 458 | else if(aIndex==2) { // I can get the neighbor from the parent's face adjacent neighbor 459 | const OctNode* temp=parent->__faceNeighbor(idx[1],i[1]); 460 | if(!temp || !temp->children){return NULL;} 461 | else{return &temp->children[pIndex];} 462 | } 463 | else if(aIndex==0) { // I can get the neighbor from the parent 464 | return &parent->children[pIndex]; 465 | } 466 | else if(aIndex==3) { // I can get the neighbor from the parent's edge adjacent neighbor 467 | const OctNode* temp=parent->__edgeNeighbor(o,i,idx); 468 | if(!temp || !temp->children){return temp;} 469 | else{return &temp->children[pIndex];} 470 | } 471 | else{return NULL;} 472 | } 473 | 474 | template 475 | OctNode* OctNode::__edgeNeighbor(const int& o,const int i[2],const int idx[2],const int& forceChildren){ 476 | if(!parent){return NULL;} 477 | int pIndex=int(this-parent->children); 478 | int aIndex,x[3]; 479 | 480 | Cube::FactorCornerIndex(pIndex,x[0],x[1],x[2]); 481 | aIndex=(~((i[0] ^ x[idx[0]]) | ((i[1] ^ x[idx[1]])<<1))) & 3; 482 | pIndex^=(7 ^ (1<__faceNeighbor(idx[0],i[0],0); 485 | if(!temp || !temp->children){return NULL;} 486 | else{return &temp->children[pIndex];} 487 | } 488 | else if(aIndex==2) { // I can get the neighbor from the parent's face adjacent neighbor 489 | OctNode* temp=parent->__faceNeighbor(idx[1],i[1],0); 490 | if(!temp || !temp->children){return NULL;} 491 | else{return &temp->children[pIndex];} 492 | } 493 | else if(aIndex==0) { // I can get the neighbor from the parent 494 | return &parent->children[pIndex]; 495 | } 496 | else if(aIndex==3) { // I can get the neighbor from the parent's edge adjacent neighbor 497 | OctNode* temp=parent->__edgeNeighbor(o,i,idx,forceChildren); 498 | if(!temp){return NULL;} 499 | if(!temp->children){ 500 | if(forceChildren){temp->initChildren();} 501 | else{return temp;} 502 | } 503 | return &temp->children[pIndex]; 504 | } 505 | else{return NULL;} 506 | } 507 | 508 | template 509 | const OctNode* OctNode::cornerNeighbor(const int& cornerIndex) const { 510 | int pIndex,aIndex=0; 511 | if(!parent){return NULL;} 512 | 513 | pIndex=int(this-parent->children); 514 | aIndex=(cornerIndex ^ pIndex); // The disagreement bits 515 | pIndex=(~pIndex)&7; // The antipodal point 516 | if(aIndex==7){ // Agree on no bits 517 | return &parent->children[pIndex]; 518 | } 519 | else if(aIndex==0){ // Agree on all bits 520 | const OctNode* temp=((const OctNode*)parent)->cornerNeighbor(cornerIndex); 521 | if(!temp || !temp->children){return temp;} 522 | else{return &temp->children[pIndex];} 523 | } 524 | else if(aIndex==6){ // Agree on face 0 525 | const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(0,cornerIndex & 1); 526 | if(!temp || !temp->children){return NULL;} 527 | else{return & temp->children[pIndex];} 528 | } 529 | else if(aIndex==5){ // Agree on face 1 530 | const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(1,(cornerIndex & 2)>>1); 531 | if(!temp || !temp->children){return NULL;} 532 | else{return & temp->children[pIndex];} 533 | } 534 | else if(aIndex==3){ // Agree on face 2 535 | const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(2,(cornerIndex & 4)>>2); 536 | if(!temp || !temp->children){return NULL;} 537 | else{return & temp->children[pIndex];} 538 | } 539 | else if(aIndex==4){ // Agree on edge 2 540 | const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(8 | (cornerIndex & 1) | (cornerIndex & 2) ); 541 | if(!temp || !temp->children){return NULL;} 542 | else{return & temp->children[pIndex];} 543 | } 544 | else if(aIndex==2){ // Agree on edge 1 545 | const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(4 | (cornerIndex & 1) | ((cornerIndex & 4)>>1) ); 546 | if(!temp || !temp->children){return NULL;} 547 | else{return & temp->children[pIndex];} 548 | } 549 | else if(aIndex==1){ // Agree on edge 0 550 | const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(((cornerIndex & 2) | (cornerIndex & 4))>>1 ); 551 | if(!temp || !temp->children){return NULL;} 552 | else{return & temp->children[pIndex];} 553 | } 554 | else{return NULL;} 555 | } 556 | 557 | template 558 | OctNode* OctNode::cornerNeighbor(const int& cornerIndex,const int& forceChildren){ 559 | int pIndex,aIndex=0; 560 | if(!parent){return NULL;} 561 | 562 | pIndex=int(this-parent->children); 563 | aIndex=(cornerIndex ^ pIndex); // The disagreement bits 564 | pIndex=(~pIndex)&7; // The antipodal point 565 | if(aIndex==7){ // Agree on no bits 566 | return &parent->children[pIndex]; 567 | } 568 | else if(aIndex==0){ // Agree on all bits 569 | OctNode* temp=((OctNode*)parent)->cornerNeighbor(cornerIndex,forceChildren); 570 | if(!temp){return NULL;} 571 | if(!temp->children){ 572 | if(forceChildren){temp->initChildren();} 573 | else{return temp;} 574 | } 575 | return &temp->children[pIndex]; 576 | } 577 | else if(aIndex==6){ // Agree on face 0 578 | OctNode* temp=((OctNode*)parent)->__faceNeighbor(0,cornerIndex & 1,0); 579 | if(!temp || !temp->children){return NULL;} 580 | else{return & temp->children[pIndex];} 581 | } 582 | else if(aIndex==5){ // Agree on face 1 583 | OctNode* temp=((OctNode*)parent)->__faceNeighbor(1,(cornerIndex & 2)>>1,0); 584 | if(!temp || !temp->children){return NULL;} 585 | else{return & temp->children[pIndex];} 586 | } 587 | else if(aIndex==3){ // Agree on face 2 588 | OctNode* temp=((OctNode*)parent)->__faceNeighbor(2,(cornerIndex & 4)>>2,0); 589 | if(!temp || !temp->children){return NULL;} 590 | else{return & temp->children[pIndex];} 591 | } 592 | else if(aIndex==4){ // Agree on edge 2 593 | OctNode* temp=((OctNode*)parent)->edgeNeighbor(8 | (cornerIndex & 1) | (cornerIndex & 2) ); 594 | if(!temp || !temp->children){return NULL;} 595 | else{return & temp->children[pIndex];} 596 | } 597 | else if(aIndex==2){ // Agree on edge 1 598 | OctNode* temp=((OctNode*)parent)->edgeNeighbor(4 | (cornerIndex & 1) | ((cornerIndex & 4)>>1) ); 599 | if(!temp || !temp->children){return NULL;} 600 | else{return & temp->children[pIndex];} 601 | } 602 | else if(aIndex==1){ // Agree on edge 0 603 | OctNode* temp=((OctNode*)parent)->edgeNeighbor(((cornerIndex & 2) | (cornerIndex & 4))>>1 ); 604 | if(!temp || !temp->children){return NULL;} 605 | else{return & temp->children[pIndex];} 606 | } 607 | else{return NULL;} 608 | } 609 | 610 | //////////////// 611 | // VertexData // 612 | //////////////// 613 | 614 | template 615 | long long OctNode::CornerIndex( 616 | const NodeIndex& nIndex, const int& cIndex, const int& maxDepth) 617 | { 618 | int idx[3]; 619 | return CornerIndex(nIndex,cIndex,maxDepth,idx); 620 | } 621 | 622 | template 623 | long long OctNode::CornerIndex( 624 | const NodeIndex& nIndex,const int& cIndex,const int& maxDepth,int idx[3]){ 625 | int x[3]; 626 | Cube::FactorCornerIndex(cIndex,x[0],x[1],x[2]); 627 | for(int i=0;i<3;i++) 628 | idx[i]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[i],x[i]); 629 | 630 | // SIMON: Modified such that 20 octree levels are possible 631 | return (long long)(idx[0]) | (long long)(idx[1])<<21 | (long long)(idx[2])<<42; 632 | } 633 | --------------------------------------------------------------------------------