From f399a7368052b1702e92ab0ccb0cc5c14190be61 Mon Sep 17 00:00:00 2001 From: lab pc Date: Fri, 25 Feb 2022 21:48:35 +0800 Subject: [PATCH] add voxel func --- CMakeLists.txt | 2 + include/Octree/AABB.h | 138 ++++++------ include/Octree/BaseOctree.h | 231 +++++++++++---------- include/Octree/OctreeBuilder.h | 2 +- include/Octree/voxel/SolidVoxelBuilder.h | 21 ++ include/Octree/voxel/SurfaceVoxelBuilder.h | 35 ++++ include/Octree/voxel/Voxel.h | 42 ++++ src/AABB.cpp | 128 +++++++++--- src/BaseOctree.cpp | 33 ++- src/voxel/SolidVoxelBuilder.cpp | 10 + src/voxel/SurfaceVoxelBuilder.cpp | 60 ++++++ src/voxel/Voxel.cpp | 24 +++ tests/solid_octree_test/.gitignore | 1 + tests/solid_octree_test/CMakeLists.txt | 6 + tests/solid_octree_test/main.cpp | 178 ++++++++++++++++ tests/solid_octree_test/test-path.h.in | 16 ++ 16 files changed, 715 insertions(+), 212 deletions(-) create mode 100644 include/Octree/voxel/SolidVoxelBuilder.h create mode 100644 include/Octree/voxel/SurfaceVoxelBuilder.h create mode 100644 include/Octree/voxel/Voxel.h create mode 100644 src/voxel/SolidVoxelBuilder.cpp create mode 100644 src/voxel/SurfaceVoxelBuilder.cpp create mode 100644 src/voxel/Voxel.cpp create mode 100644 tests/solid_octree_test/.gitignore create mode 100644 tests/solid_octree_test/CMakeLists.txt create mode 100644 tests/solid_octree_test/main.cpp create mode 100644 tests/solid_octree_test/test-path.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c57106..44ff777 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ endif() if(Octree_BUILD_TEST) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests/octree_test) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests/solid_octree_test) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests/sdf_test) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests/udf_test) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests/search_assign_test) @@ -45,6 +46,7 @@ endif() enable_testing() if(Octree_BUILD_TEST) add_test(NAME octree_test COMMAND $) + add_test(NAME solid_octree_test COMMAND $) add_test(NAME sdf_test COMMAND $) add_test(NAME udf_test COMMAND $) add_test(NAME search_assign_test COMMAND $) diff --git a/include/Octree/AABB.h b/include/Octree/AABB.h index f9e5bd1..34dd15b 100644 --- a/include/Octree/AABB.h +++ b/include/Octree/AABB.h @@ -15,68 +15,82 @@ #include namespace Octree { - class AABB { - Eigen::Vector3d _min, _max; - - public: - explicit AABB(const Eigen::Vector3d &min, const Eigen::Vector3d &max); - - explicit AABB(double minx, double maxx, double miny, double maxy, double minz, double maxz); - - explicit AABB(const std::array &aabb); - - explicit AABB() = default; - - public: - Eigen::Vector3d size() const; - - Eigen::Vector3d min() const; - - Eigen::Vector3d max() const; - - Eigen::Vector3d center() const; - - /** - * @reference: https://github.com/diwi/Space_Partitioning_Octree_BVH - * @param a - * @param b - * @param c - * @return - */ - bool intersect_triangle(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c) const; - - /** - * If the AABB contains the point v - * @param v - * @return - */ - bool contains(const Eigen::Vector3d &v) const; - - /** - * If the AABB fully contains a triangle - * @param a - * @param b - * @param c - * @return - */ - bool fully_contains_triangle(const Eigen::Vector3d &a, - const Eigen::Vector3d &b, - const Eigen::Vector3d &c) const; - - /** - * Divide this AABB into 8 parts - * @return - */ - std::array half_divide() const; - - public: - - /** - * Extend boundary with @param step - * @param step - */ - void extend(double step); - }; +class AABB { + Eigen::Vector3d _min, _max; + +public: + explicit AABB(const Eigen::Vector3d &min, const Eigen::Vector3d &max); + + explicit AABB(double minx, double maxx, double miny, double maxy, double minz, double maxz); + + explicit AABB(const std::array &aabb); + + explicit AABB() = default; + +public: + Eigen::Vector3d size() const; + + Eigen::Vector3d min() const; + + Eigen::Vector3d max() const; + + Eigen::Vector3d center() const; + + /** + * @reference: https://github.com/diwi/Space_Partitioning_Octree_BVH + * @param a + * @param b + * @param c + * @return + */ + bool intersect_triangle(const Eigen::Vector3d &a, const Eigen::Vector3d &b, const Eigen::Vector3d &c) const; + + /** + * If the AABB contains the point v + * @param v + * @return + */ + bool contains(const Eigen::Vector3d &v) const; + + /** + * If the AABB fully contains a triangle + * @param a + * @param b + * @param c + * @return + */ + bool fully_contains_triangle(const Eigen::Vector3d &a, + const Eigen::Vector3d &b, + const Eigen::Vector3d &c) const; + + /** + * If the AABB intersects with another one + * @param aabb + * @return + */ + bool intersect_aabb(const AABB &aabb) const; + + /** + * Divide this AABB into 8 parts + * @return + */ + std::array half_divide() const; + +public: + + /** + * Extend boundary with @param step + * @param step + */ + void extend(double step); + +public: + static AABB ToCubeAABBWithSize(const AABB &aabb, const double res); + + static AABB ToCubeAABBWithSize(const AABB &aabb, const double res, int &num_on_axis); + + static AABB ToIntegerSizeAABB(const AABB &aabb, const double res, int &num_on_x, int &num_on_y, int &num_on_z); +}; } #endif //OCTREE_AABB_H diff --git a/include/Octree/BaseOctree.h b/include/Octree/BaseOctree.h index 540b10b..4bfc9dd 100644 --- a/include/Octree/BaseOctree.h +++ b/include/Octree/BaseOctree.h @@ -19,124 +19,131 @@ #include "AABB.h" namespace Octree { - class BaseOctree; +class BaseOctree; - class OctreeNode; +class OctreeNode; - using std::shared_ptr; - using std::weak_ptr; - using std::vector; - using std::set; - using std::array; +using std::shared_ptr; +using std::weak_ptr; +using std::vector; +using std::set; +using std::array; #if 0 - using octree_sptr = shared_ptr; - using octree_wptr = weak_ptr; +using octree_sptr = shared_ptr; +using octree_wptr = weak_ptr; #endif - using ocnode_sptr = shared_ptr; - using ocnode_wptr = weak_ptr; - using ocnode_ptr = OctreeNode *; - - - struct OctreeNode : public std::enable_shared_from_this { - friend class BaseOctree; - friend class OctreeBuilder; - - protected: - uint32_t level; - array children; - ocnode_ptr father; - AABB aabb; - - public: - set tri_ids; - - public: - explicit OctreeNode(ocnode_ptr father, const AABB &aabb, uint32_t level); - - /** - * Insert a face index into `tri_ids` - * TODO: This function and property `tri_ids` are considered to be discarded - * @param tri - */ - void insert_triangle_id(uint32_t tri); - - /** - * Split node into 8 part - */ - void split_if_no_children(); - - /** - * Make sub node via aabb - * @param aabb - * @return - */ - virtual ocnode_sptr make_child(const AABB &aabb); - - /** - * If this node is a leaf - * @return - */ - bool is_leaf() const; - - /** - * Return all-8 sub node - * @return - */ - array get_children() const; - - /** - * Find the leaf that contains the @param pos - * @param pos - * @return - */ - ocnode_sptr map_node(const Eigen::Vector3d &pos); - - public: //Getter - uint32_t get_level(){ return level; } - - AABB get_aabb() { return aabb; } - }; - - class BaseOctree { - public: - uint32_t max_level; - - ocnode_sptr root; - - public: - explicit BaseOctree(uint32_t max_level, const AABB &aabb); - - explicit BaseOctree(uint32_t max_level, ocnode_sptr root); - - public: - /** - * Find the leaf that contains the @param pos - * @param pos - * @return - */ - ocnode_sptr map_node(const Eigen::Vector3d &pos); - - /** - * Return the root node - * @return - */ - ocnode_sptr get_root() const { return root; } - - /** - * Return the max_level - * @return - */ - uint32_t get_max_level() const { return max_level; } - - /** - * Get the aabb of root - * @return - */ - AABB aabb() const; - }; +using ocnode_sptr = shared_ptr; +using ocnode_wptr = weak_ptr; +using ocnode_ptr = OctreeNode *; + +struct OctreeNode : public std::enable_shared_from_this { + friend class BaseOctree; + friend class OctreeBuilder; + +protected: + uint32_t level; + array children; + ocnode_ptr father; + AABB aabb; + +public: + set tri_ids; + +public: + explicit OctreeNode(ocnode_ptr father, const AABB &aabb, uint32_t level); + + /** + * Insert a face index into `tri_ids` + * TODO: This function and property `tri_ids` are considered to be discarded + * @param tri + */ + void insert_triangle_id(uint32_t tri); + + /** + * Split node into 8 part + */ + void split_if_no_children(); + + /** + * Make sub node via aabb + * @param aabb + * @return + */ + virtual ocnode_sptr make_child(const AABB &aabb); + + /** + * If this node is a leaf + * @return + */ + bool is_leaf() const; + + /** + * Return all-8 sub node + * @return + */ + array get_children() const; + + /** + * Find the leaf that contains the @param pos + * @param pos + * @return + */ + ocnode_sptr map_node(const Eigen::Vector3d &pos); + + void map_voxel(const AABB &aabb, std::set &nodes); + +public: //Getter + uint32_t get_level() { return level; } + + AABB get_aabb() { return aabb; } +}; + +class BaseOctree { +public: + uint32_t max_level; + + ocnode_sptr root; + +public: + explicit BaseOctree(uint32_t max_level, const AABB &aabb); + + explicit BaseOctree(uint32_t max_level, ocnode_sptr root); + +public: + /** + * Find the leaf that contains the @param pos + * @param pos + * @return + */ + ocnode_sptr map_node(const Eigen::Vector3d &pos); + + /** + * Find the leaves that intersect with @param aabb + * @param aabb + * @return + */ + std::set map_voxel(const AABB &aabb); + + /** + * Return the root node + * @return + */ + ocnode_sptr get_root() const { return root; } + + /** + * Return the max_level + * @return + */ + uint32_t get_max_level() const { return max_level; } + + /** + * Get the aabb of root + * @return + */ + AABB aabb() const; +}; } - #endif //OCTREE_BASEOCTREE_H diff --git a/include/Octree/OctreeBuilder.h b/include/Octree/OctreeBuilder.h index f3bd877..b87ab6d 100644 --- a/include/Octree/OctreeBuilder.h +++ b/include/Octree/OctreeBuilder.h @@ -15,7 +15,7 @@ #include #include #include "AABB.h" - +#include "BaseOctree.h" namespace Octree { diff --git a/include/Octree/voxel/SolidVoxelBuilder.h b/include/Octree/voxel/SolidVoxelBuilder.h new file mode 100644 index 0000000..a47dda4 --- /dev/null +++ b/include/Octree/voxel/SolidVoxelBuilder.h @@ -0,0 +1,21 @@ +/** + * ------------------------------------ + * @author: Weipeng Kong + * @date: 2022/2/23 + * @email: yjxkwp\@foxmail.com + * @description: + * ------------------------------------ +**/ + +#ifndef OCTREE_SRC_SOLIDVOXELBUILDER_H_ +#define OCTREE_SRC_SOLIDVOXELBUILDER_H_ + +#include "Octree/voxel/Voxel.h" + +namespace Octree { +class SolidVoxelBuilder { + +}; +} + +#endif //OCTREE_SRC_SOLIDVOXELBUILDER_H_ diff --git a/include/Octree/voxel/SurfaceVoxelBuilder.h b/include/Octree/voxel/SurfaceVoxelBuilder.h new file mode 100644 index 0000000..5c38837 --- /dev/null +++ b/include/Octree/voxel/SurfaceVoxelBuilder.h @@ -0,0 +1,35 @@ +/** + * ------------------------------------ + * @author: Weipeng Kong + * @date: 2022/2/23 + * @email: yjxkwp\@foxmail.com + * @description: + * ------------------------------------ +**/ + +#ifndef OCTREE_INCLUDE_OCTREE_VOXEL_SURFACEVOXELBUILDER_H_ +#define OCTREE_INCLUDE_OCTREE_VOXEL_SURFACEVOXELBUILDER_H_ + + +#include "Octree/BaseOctree.h" +#include "Octree/OctreeBuilder.h" + +namespace Octree { +class Voxel; + +class SurfaceVoxelBuilder { +private: + AABB aabb; + int _x, _y, _z; + double block_size; + BaseOctree &octree; + const pMesh::Triangle3dMesh<> &mesh; + +public: + explicit SurfaceVoxelBuilder(BaseOctree &octree, const pMesh::Triangle3dMesh<> &mesh, double block_size); + + void build(Voxel &voxel); +}; +} + +#endif //OCTREE_INCLUDE_OCTREE_VOXEL_SURFACEVOXELBUILDER_H_ diff --git a/include/Octree/voxel/Voxel.h b/include/Octree/voxel/Voxel.h new file mode 100644 index 0000000..81fc776 --- /dev/null +++ b/include/Octree/voxel/Voxel.h @@ -0,0 +1,42 @@ +/** + * ------------------------------------ + * @author: Weipeng Kong + * @date: 2022/2/23 + * @email: yjxkwp\@foxmail.com + * @description: + * ------------------------------------ +**/ + +#ifndef OCTREE_INCLUDE_OCTREE_VOXEL_VOXEL_H_ +#define OCTREE_INCLUDE_OCTREE_VOXEL_VOXEL_H_ + +#include +#include + +namespace Octree { +class Voxel { +public: + AABB aabb; + int _x, _y, _z; + std::vector voxels; + +public: + Voxel() = default; + Voxel(int _x, int _y, int _z); + Voxel(const std::vector &value, int _x, int _y, int _z); + +public: + /** + * Tool function: calc 1D index from 3D coordinate + * @param x + * @param y + * @param z + * @return + */ + int id_at(int x, int y, int z) const; + + bool at(int x, int y, int z) const; +}; +} + +#endif //OCTREE_INCLUDE_OCTREE_VOXEL_VOXEL_H_ diff --git a/src/AABB.cpp b/src/AABB.cpp index 97f3e08..5e3c4b2 100644 --- a/src/AABB.cpp +++ b/src/AABB.cpp @@ -15,32 +15,32 @@ /********************* TOOLS ************************************/ namespace AABBTools { - static bool AXISTEST(double rad, double p0, double p1) { - return (std::min(p0, p1) > rad || std::max(p0, p1) < -rad); - } +static bool AXISTEST(double rad, double p0, double p1) { + return (std::min(p0, p1) > rad || std::max(p0, p1) < -rad); +} - inline double vec_min_component(double a, double b, double c) { - return std::min(std::min(a, b), c); - } +inline double vec_min_component(double a, double b, double c) { + return std::min(std::min(a, b), c); +} - inline double vec_max_component(double a, double b, double c) { - return std::max(std::max(a, b), c); - } +inline double vec_max_component(double a, double b, double c) { + return std::max(std::max(a, b), c); +} - bool directionTest(double a, double b, double c, double hs) { - return (vec_min_component(a, b, c) > hs || vec_max_component(a, b, c) < -hs); - } +bool directionTest(double a, double b, double c, double hs) { + return (vec_min_component(a, b, c) > hs || vec_max_component(a, b, c) < -hs); +} - bool planeBoxOverlap(const Eigen::Vector3d &normal, double d, const Eigen::Vector3d &hs) { - Eigen::Vector3d vmin = {(normal[0] > 0.0) ? -hs[0] : +hs[0], - (normal[1] > 0.0) ? -hs[1] : +hs[1], - (normal[2] > 0.0) ? -hs[2] : +hs[2]}; +bool planeBoxOverlap(const Eigen::Vector3d &normal, double d, const Eigen::Vector3d &hs) { + Eigen::Vector3d vmin = {(normal[0] > 0.0) ? -hs[0] : +hs[0], + (normal[1] > 0.0) ? -hs[1] : +hs[1], + (normal[2] > 0.0) ? -hs[2] : +hs[2]}; - if (normal.dot(vmin) + d > 0.0) return false; - Eigen::Vector3d vmax = -vmin; - if (normal.dot(vmax) + d >= 0.0) return true; - return false; - } + if (normal.dot(vmin) + d > 0.0) return false; + Eigen::Vector3d vmax = -vmin; + if (normal.dot(vmax) + d >= 0.0) return true; + return false; +} } /*********************************************************/ @@ -52,20 +52,17 @@ Octree::AABB::AABB(const Eigen::Vector3d &min, const Eigen::Vector3d &max) : _mi } Octree::AABB::AABB(double minx, double maxx, double miny, double maxy, double minz, double maxz) : - _min(minx, miny, minz), _max(maxx, maxy, maxz) { + _min(minx, miny, minz), _max(maxx, maxy, maxz) { } - Octree::AABB::AABB(const std::array &aabb) : - AABB(aabb[0], aabb[1], aabb[2], aabb[3], aabb[4], aabb[5]) { + AABB(aabb[0], aabb[1], aabb[2], aabb[3], aabb[4], aabb[5]) { } - Eigen::Vector3d Octree::AABB::size() const { return _max - _min; } - Eigen::Vector3d Octree::AABB::center() const { return (_max + _min) / 2.0; } @@ -141,10 +138,9 @@ bool Octree::AABB::intersect_triangle(const Eigen::Vector3d &a, return true; // box and triangle overlaps } - bool Octree::AABB::contains(const Eigen::Vector3d &v) const { return (v.x() >= _min.x() && v.y() >= _min.y() && v.z() >= _min.z()) && - (v.x() <= _max.x() && v.y() <= _max.y() && v.z() <= _max.z()); + (v.x() <= _max.x() && v.y() <= _max.y() && v.z() <= _max.z()); } bool Octree::AABB::fully_contains_triangle(const Eigen::Vector3d &a, @@ -170,3 +166,79 @@ void Octree::AABB::extend(double step) { this->_min -= Eigen::Vector3d(step, step, step); this->_max += Eigen::Vector3d(step, step, step); } + +Octree::AABB Octree::AABB::ToCubeAABBWithSize(const Octree::AABB &aabb, const double res) { + int _; + return ToCubeAABBWithSize(aabb, res, _); +} + +Octree::AABB Octree::AABB::ToCubeAABBWithSize(const Octree::AABB &aabb, const double res, int &num_on_axis) { + AABB cube = aabb; + Eigen::Vector3i num; + + auto fit_to_integer_size = [](double &m, double &M, double res) -> int { + double S = M - m; + int num = std::ceil(S / res); + + // padding num to pow of 2 + if (num & (num - 1)) { + num |= num >> 1; + num |= num >> 2; + num |= num >> 4; + num |= num >> 8; + num |= num >> 16; + num = num + 1; + } else { + num = num == 0 ? 1 : num; + } + + double error = (num * res - S) / 2.0; + m -= error; + M += error; + return num; + }; + num.x() = fit_to_integer_size(cube._min.x(), cube._max.x(), res); + num.y() = fit_to_integer_size(cube._min.y(), cube._max.y(), res); + num.z() = fit_to_integer_size(cube._min.z(), cube._max.z(), res); + int max_id = std::max_element(num.data(), num.data() + 3) - num.data(); + + Eigen::Vector3d half_size = cube.size() / 2.0; + Eigen::Vector3d center = cube.center(); + + for (int i = 0; i < 3; ++i) { + if (max_id == i) continue; + cube._min.data()[i] = center.data()[i] - half_size.data()[max_id]; + cube._max.data()[i] = center.data()[i] + half_size.data()[max_id]; + } + num_on_axis = num.data()[max_id]; + return cube; +} + +bool Octree::AABB::intersect_aabb(const Octree::AABB &other) const { + return (_min.x() <= other._max.x() && _max.x() >= other._min.x()) && + (_min.y() <= other._max.y() && _max.y() >= other._min.y()) && + (_min.z() <= other._max.z() && _max.z() >= other._min.z()); +} + + +Octree::AABB Octree::AABB::ToIntegerSizeAABB(const Octree::AABB &aabb, + const double res, + int &num_on_x, + int &num_on_y, + int &num_on_z) { + AABB cube = aabb; + + auto fit_to_integer_size = [](double &m, double &M, double res) -> int { + double S = M - m; + int num = std::ceil(S / res); + double error = (num * res - S) / 2.0; + m -= error; + M += error; + return num; + }; + num_on_x = fit_to_integer_size(cube._min.x(), cube._max.x(), res); + num_on_y = fit_to_integer_size(cube._min.y(), cube._max.y(), res); + num_on_z = fit_to_integer_size(cube._min.z(), cube._max.z(), res); + + return cube; +} diff --git a/src/BaseOctree.cpp b/src/BaseOctree.cpp index 38a5568..1f8b3a8 100644 --- a/src/BaseOctree.cpp +++ b/src/BaseOctree.cpp @@ -10,15 +10,27 @@ #include "Octree/BaseOctree.h" - Octree::ocnode_sptr Octree::BaseOctree::map_node(const Eigen::Vector3d &pos) { auto fd = (root == nullptr) ? nullptr : root->map_node(pos); return fd; return fd == nullptr ? root : fd; } +void Octree::OctreeNode::map_voxel(const AABB &aabb, set &nodes) { + if (aabb.intersect_aabb(this->aabb)) { + if (this->is_leaf()) { + nodes.insert(shared_from_this()); + } else { + for (auto &child : this->children) { + if(child != nullptr) + child->map_voxel(aabb, nodes); + } + } + } +} + Octree::BaseOctree::BaseOctree(uint32_t max_level, const Octree::AABB &aabb) : - BaseOctree(max_level, std::make_shared(nullptr, aabb, 0)) { + BaseOctree(max_level, std::make_shared(nullptr, aabb, 0)) { } Octree::BaseOctree::BaseOctree(uint32_t max_level, Octree::ocnode_sptr root) : max_level(max_level), root(root) { @@ -26,14 +38,13 @@ Octree::BaseOctree::BaseOctree(uint32_t max_level, Octree::ocnode_sptr root) : m } Octree::AABB Octree::BaseOctree::aabb() const { - if(root == nullptr){ + if (root == nullptr) { return AABB(); - }else{ + } else { return root->aabb; } } - Octree::OctreeNode::OctreeNode(ocnode_ptr father, const Octree::AABB &aabb, uint32_t level) : father(father), level(level), aabb(aabb) { @@ -53,12 +64,10 @@ void Octree::OctreeNode::split_if_no_children() { } } - Octree::ocnode_sptr Octree::OctreeNode::make_child(const Octree::AABB &aabb) { - return std::make_shared(this, aabb, level+1); + return std::make_shared(this, aabb, level + 1); } - bool Octree::OctreeNode::is_leaf() const { return std::all_of(children.begin(), children.end(), [](ocnode_sptr child) { return child == nullptr; }); } @@ -72,7 +81,7 @@ Octree::ocnode_sptr Octree::OctreeNode::map_node(const Eigen::Vector3d &pos) { bool ct = aabb.contains(pos); if (ct) { if (is_leaf()) return shared_from_this(); - for (const auto &child: this->children) { + for (const auto &child : this->children) { if (child == nullptr) continue; auto rt = child->map_node(pos); if (rt != nullptr) return rt; @@ -81,3 +90,9 @@ Octree::ocnode_sptr Octree::OctreeNode::map_node(const Eigen::Vector3d &pos) { } return nullptr; } + +std::set Octree::BaseOctree::map_voxel(const Octree::AABB &aabb) { + std::set s; + this->root->map_voxel(aabb, s); + return std::move(s); +} diff --git a/src/voxel/SolidVoxelBuilder.cpp b/src/voxel/SolidVoxelBuilder.cpp new file mode 100644 index 0000000..98566e9 --- /dev/null +++ b/src/voxel/SolidVoxelBuilder.cpp @@ -0,0 +1,10 @@ +/** + * ------------------------------------ + * @author: Weipeng Kong + * @date: 2022/2/23 + * @email: yjxkwp\@foxmail.com + * @description: + * ------------------------------------ +**/ + +#include "Octree/voxel/SolidVoxelBuilder.h" diff --git a/src/voxel/SurfaceVoxelBuilder.cpp b/src/voxel/SurfaceVoxelBuilder.cpp new file mode 100644 index 0000000..a6fe438 --- /dev/null +++ b/src/voxel/SurfaceVoxelBuilder.cpp @@ -0,0 +1,60 @@ +/** + * ------------------------------------ + * @author: Weipeng Kong + * @date: 2022/2/23 + * @email: yjxkwp\@foxmail.com + * @description: + * ------------------------------------ +**/ + +#include "Octree/voxel/SurfaceVoxelBuilder.h" +#include "Octree/voxel/Voxel.h" +#include + +Octree::SurfaceVoxelBuilder::SurfaceVoxelBuilder(BaseOctree &octree, + const pMesh::Triangle3dMesh<> &mesh, + double block_size) : + block_size(block_size), octree(octree), mesh(mesh) { + aabb = AABB::ToIntegerSizeAABB(octree.aabb(), block_size, _x, _y, _z); +} + +void Octree::SurfaceVoxelBuilder::build(Octree::Voxel &voxel) { + voxel._x = _x; + voxel._y = _y; + voxel._z = _z; + voxel.aabb = aabb; + voxel.voxels.clear(); + voxel.voxels = std::vector(_x * _y * _z, false); + + BOOST_LOG_TRIVIAL(debug) << "Voxel size: " << _x << " " << _y << " " << _z; + + for (int i = 0; i < _x; ++i) { + double base_x = this->aabb.min().x() + i * block_size; + for (int j = 0; j < _y; ++j) { + double base_y = this->aabb.min().y() + j * block_size; + for (int k = 0; k < _z; ++k) { + double base_z = this->aabb.min().z() + k * block_size; + + AABB vox({base_x, base_y, base_z}, {base_x + block_size, base_y + block_size, base_z + block_size}); + auto candidates_vox = octree.map_voxel(vox); + bool exit_flag = false; + for (auto candidate : candidates_vox) { + for (uint32_t tid : candidate->tri_ids) { + const pMesh::Triangle3dMesh<>::Face &face = mesh.faces[tid].attr; + if (vox.intersect_triangle(mesh.vertices[face.vertices[0].id()].attr.coordinate, + mesh.vertices[face.vertices[1].id()].attr.coordinate, + mesh.vertices[face.vertices[2].id()].attr.coordinate)) { + voxel.voxels[voxel.id_at(i, j, k)] = true; + exit_flag = true; + break; + } + } + if(exit_flag) { + break; + } + } + } + } + } +} + diff --git a/src/voxel/Voxel.cpp b/src/voxel/Voxel.cpp new file mode 100644 index 0000000..46c9933 --- /dev/null +++ b/src/voxel/Voxel.cpp @@ -0,0 +1,24 @@ +/** + * ------------------------------------ + * @author: Weipeng Kong + * @date: 2022/2/23 + * @email: yjxkwp\@foxmail.com + * @description: + * ------------------------------------ +**/ + +#include "Octree/voxel/Voxel.h" + +Octree::Voxel::Voxel(const std::vector &value, int _x, int _y, int _z): voxels(value), _x(_x), _y(_y), _z(_z) { + +} + +Octree::Voxel::Voxel(int _x, int _y, int _z): Voxel(std::vector(_x * _y * _z, false), _x, _y, _z) { + +} +int Octree::Voxel::id_at(int x, int y, int z) const { + return ((z * _y) + y) * _x + x; +} +bool Octree::Voxel::at(int x, int y, int z) const { + return voxels[id_at(x,y,z)]; +} diff --git a/tests/solid_octree_test/.gitignore b/tests/solid_octree_test/.gitignore new file mode 100644 index 0000000..298482a --- /dev/null +++ b/tests/solid_octree_test/.gitignore @@ -0,0 +1 @@ +test-path.h \ No newline at end of file diff --git a/tests/solid_octree_test/CMakeLists.txt b/tests/solid_octree_test/CMakeLists.txt new file mode 100644 index 0000000..aec9b46 --- /dev/null +++ b/tests/solid_octree_test/CMakeLists.txt @@ -0,0 +1,6 @@ +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/test-path.h.in" + "${CMAKE_CURRENT_SOURCE_DIR}/test-path.h" +) +add_executable(solid_octree_test main.cpp) +target_link_libraries(solid_octree_test Octree pMesh) diff --git a/tests/solid_octree_test/main.cpp b/tests/solid_octree_test/main.cpp new file mode 100644 index 0000000..2ab9bbe --- /dev/null +++ b/tests/solid_octree_test/main.cpp @@ -0,0 +1,178 @@ +/** + * ------------------------------------ + * @author: Weipeng Kong + * @date: 2021/11/17 + * @email: yjxkwp@foxmail.com + * @site: https://donot.fit + * @description: + * ------------------------------------ +**/ + +#include +#include "Octree/sdf/SDFOctree.h" +#include "Octree/OctreeBuilder.h" +#include "Octree/OctreeTraverser.h" +#include "Octree/sdf/SDFTraversalSampler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "test-path.h" + +#include "Octree/voxel/SurfaceVoxelBuilder.h" +#include "Octree/voxel/Voxel.h" + +class VTKTraverser : public pMesh::io::BaseReader, public Octree::OctreeTraverser { + int node_cnt = 0; +public: + explicit VTKTraverser(const Octree::BaseOctree &octree, const TraverseStrategy strategy = DFS); + + void visit_node(Octree::OctreeNode &node) override; + + pMesh::io::ReadAdapter *reader_adapter; + + bool operator>>(pMesh::io::ReadAdapter &adapter) override; +}; + + +int main() { + pMesh::io::fs_path data_base_path = TEST_DATA_BASE_PATH; + BOOST_LOG_TRIVIAL(debug) << "base data path is " << boost::filesystem::absolute(data_base_path); + + auto mesh_path = data_base_path / "stanford-bunny.obj"; + auto out_octree_path = data_base_path / "stanford-bunny-octree.vtk"; + auto out_voxel_path = data_base_path / "stanford-bunny-voxel.vtk"; + auto out_error_path = data_base_path / "stanford-bunny-error_points.vtk"; + + pMesh::Triangle3dMesh<> mesh; + pMesh::io::OBJReader(mesh_path) + >> pMesh::io::DefaultSurfaceReadAdapter<>(mesh, false)(); + + BOOST_LOG_TRIVIAL(info) << "Load Face #" << mesh.f_size(); + + const int level = 4; + const double block_size = 0.005; + + boost::timer t; + Octree::AABB aabb(mesh.aabb()); + Octree::BaseOctree octree(level, aabb); + + Octree::OctreeBuilder builder(mesh, octree); + builder.build(); + BOOST_LOG_TRIVIAL(debug) << "time elapse " << t.elapsed(); + + auto aabb_size = octree.aabb().size(); + BOOST_LOG_TRIVIAL(debug) << "Global AABB " << aabb_size.transpose(); + + auto final_aabb_size = aabb_size / pow(2, level); + BOOST_LOG_TRIVIAL(debug) << "Local AABB " << final_aabb_size.transpose(); + + pMesh::HexahedronMesh<> hexahedron_mesh; + VTKTraverser(octree) >> pMesh::io::DefaultVolumeReadAdapter<>(hexahedron_mesh)(); + + pMesh::io::VTKWriter(12, out_octree_path) + << pMesh::io::DefaultVolumeWriteAdapter<>(hexahedron_mesh)(); + + t.restart(); + Octree::Voxel voxel; + Octree::SurfaceVoxelBuilder voxel_builder(octree, mesh, block_size); + voxel_builder.build(voxel); + BOOST_LOG_TRIVIAL(debug) << "SurfaceVoxelBuilder time elapse " << t.elapsed(); + + // output voxel + pMesh::HexahedronMesh<> voxelMesh; + { + auto aabb = voxel.aabb; + int _x = voxel._x; + int _y = voxel._y; + int _z = voxel._z; + + for (int i = 0; i < _x; ++i) { + double base_x = aabb.min().x() + i * block_size; + for (int j = 0; j < _y; ++j) { + double base_y = aabb.min().y() + j * block_size; + for (int k = 0; k < _z; ++k) { + double base_z = aabb.min().z() + k * block_size; + + if(voxel.voxels[voxel.id_at(i, j, k)]){ + Octree::AABB v({base_x, base_y, base_z}, {base_x + block_size, base_y + block_size, base_z + block_size}); + voxelMesh.vertices.emplace_back(pMesh::Volume::Vertex(0, {v.min().x(),v.min().y(),v.min().z()})); + voxelMesh.vertices.emplace_back(pMesh::Volume::Vertex(1, {v.max().x(),v.min().y(),v.min().z()})); + voxelMesh.vertices.emplace_back(pMesh::Volume::Vertex(2, {v.max().x(),v.max().y(),v.min().z()})); + voxelMesh.vertices.emplace_back(pMesh::Volume::Vertex(3, {v.min().x(),v.max().y(),v.min().z()})); + voxelMesh.vertices.emplace_back(pMesh::Volume::Vertex(4, {v.min().x(),v.min().y(),v.max().z()})); + voxelMesh.vertices.emplace_back(pMesh::Volume::Vertex(5, {v.max().x(),v.min().y(),v.max().z()})); + voxelMesh.vertices.emplace_back(pMesh::Volume::Vertex(6, {v.max().x(),v.max().y(),v.max().z()})); + voxelMesh.vertices.emplace_back(pMesh::Volume::Vertex(7, {v.min().x(),v.max().y(),v.max().z()})); + + int base_id = voxelMesh.v_size(); + pMesh::Volume::Cell cell; + using PPP = pMesh::Volume::VertexHandle; + cell.vertices = { + PPP(base_id + 0), PPP(base_id + 1), PPP(base_id + 2), PPP(base_id + 3), + PPP(base_id + 4), PPP(base_id + 5), PPP(base_id + 6), PPP(base_id + 7) + }; + voxelMesh.cells.emplace_back(cell); + } + } + } + } + } + + pMesh::io::VTKWriter(12, out_voxel_path) + << pMesh::io::DefaultVolumeWriteAdapter<>(voxelMesh)(); + + return 0; +} + +void VTKTraverser::visit_node(Octree::OctreeNode &node) { + if (node.get_level() != octree.get_max_level()) { +// assert(!node.is_leaf()); + assert(node.tri_ids.empty()); + return; + } + + auto min = node.get_aabb().min(); + auto max = node.get_aabb().max(); + reader_adapter->feed_vertex({min.x(), min.y(), min.z()}); + reader_adapter->feed_vertex({max.x(), min.y(), min.z()}); + reader_adapter->feed_vertex({max.x(), max.y(), min.z()}); + reader_adapter->feed_vertex({min.x(), max.y(), min.z()}); + reader_adapter->feed_vertex({min.x(), min.y(), max.z()}); + reader_adapter->feed_vertex({max.x(), min.y(), max.z()}); + reader_adapter->feed_vertex({max.x(), max.y(), max.z()}); + reader_adapter->feed_vertex({min.x(), max.y(), max.z()}); + + reader_adapter->feed_collection( + { + 0 + node_cnt, 1 + node_cnt, 2 + node_cnt, 3 + node_cnt, 4 + node_cnt, + 5 + node_cnt, 6 + node_cnt, 7 + node_cnt + }); + node_cnt += 8; +} + +bool VTKTraverser::operator>>(pMesh::io::ReadAdapter &adapter) { + reader_adapter = &adapter; + node_cnt = 0; + adapter.start(); + + this->traverse(); + + adapter.end(); + return true; +} + +VTKTraverser::VTKTraverser(const Octree::BaseOctree &octree, const OctreeTraverser::TraverseStrategy strategy) + : OctreeTraverser( + octree, strategy) { +} diff --git a/tests/solid_octree_test/test-path.h.in b/tests/solid_octree_test/test-path.h.in new file mode 100644 index 0000000..0e3ea76 --- /dev/null +++ b/tests/solid_octree_test/test-path.h.in @@ -0,0 +1,16 @@ +/** + * ------------------------------------ + * @author: Weipeng Kong + * @date: 2021/12/1 + * @email: yjxkwp@foxmail.com + * @site: https://donot.fit + * @description: + * ------------------------------------ +**/ + +#ifndef OCTREE_SDF_TEST_PATH_H +#define OCTREE_SDF_TEST_PATH_H + +#define TEST_DATA_BASE_PATH "@TEST_DATA_BASE_PATH@" + +#endif //OCTREE_SDF_TEST_PATH_H