├── .gitignore ├── src ├── .DS_Store ├── tri_box.h ├── timer.h ├── CMakeLists.txt ├── ray_caster.h ├── thread_pool.h ├── thread_pool.cpp ├── timer.cpp ├── ray_caster.cpp ├── commons.cpp ├── commons.h ├── voxelizer_main.cpp ├── voxelizer.h ├── tri_box.cpp └── voxelizer.cpp ├── test ├── .DS_Store ├── CMakeLists.txt └── test_voxel_meta.cpp ├── .gitmodules ├── data ├── .gitignore └── README.md ├── examples ├── CMakeLists.txt ├── read_rawvox.cpp ├── read_meta.cpp ├── check_side.cpp ├── collision_checker.h └── collision_checker.cpp ├── LICENSE ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store 3 | playground 4 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topskychen/voxelizer/HEAD/src/.DS_Store -------------------------------------------------------------------------------- /test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/topskychen/voxelizer/HEAD/test/.DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/abseil-cpp"] 2 | path = src/abseil-cpp 3 | url = https://github.com/abseil/abseil-cpp.git 4 | -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | bike.stl 2 | bun000.ply 3 | ironman.obj 4 | kawada-hironx.stl 5 | porsche.obj 6 | racecar.stl 7 | teapot.obj 8 | torus.obj -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | More testing objects can be found in the this [link](https://drive.google.com/drive/folders/1VXgNJWkpXx0Pn4EiN7Afwp5jrvJOuUr1?usp=sharing). -------------------------------------------------------------------------------- /src/tri_box.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TriBox.h 3 | * 4 | * Created on: 23 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #ifndef TRIBOX_H_ 9 | #define TRIBOX_H_ 10 | 11 | namespace voxelizer { 12 | 13 | int TriBoxOverlap(float boxcenter[3], float boxhalfsize[3], 14 | float triverts[3][3]); 15 | 16 | } 17 | 18 | #endif /* TRIBOX_H_ */ 19 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | macro(add_check_test test_name) 2 | add_executable(${ARGV}) 3 | target_link_libraries(${test_name} 4 | voxelizer_lib) 5 | add_test(${test_name} ${EXECUTABLE_OUTPUT_PATH}/${test_name}) 6 | endmacro(add_check_test) 7 | 8 | include_directories ("${PROJECT_SOURCE_DIR}/src/") 9 | 10 | add_check_test(test_voxel_meta test_voxel_meta.cpp) -------------------------------------------------------------------------------- /test/test_voxel_meta.cpp: -------------------------------------------------------------------------------- 1 | #include "voxelizer.h" 2 | 3 | using voxelizer::VoxelMeta; 4 | 5 | void PromptForVoxel(VoxelMeta& voxel_meta) { 6 | voxel_meta.SetIndex(1); 7 | voxel_meta.SetFilled(); 8 | } 9 | 10 | int main(int argc, char* argv[]) { 11 | VoxelMeta voxel_meta; 12 | PromptForVoxel(voxel_meta); 13 | if (voxel_meta.Index() != 1) { 14 | return 1; 15 | } 16 | if (!voxel_meta.Filled()) { 17 | return 1; 18 | } 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories ("${PROJECT_SOURCE_DIR}/src/") 2 | 3 | add_executable(collision_checker collision_checker.cpp collision_checker.h ${voxelizer_SRC}) 4 | target_link_libraries(collision_checker 5 | voxelizer_lib 6 | ) 7 | 8 | add_executable(read_rawvox read_rawvox.cpp) 9 | target_link_libraries(read_rawvox 10 | voxelizer_lib 11 | ) 12 | 13 | add_executable(read_meta read_meta.cpp) 14 | target_link_libraries(read_meta 15 | voxelizer_lib 16 | ) 17 | 18 | add_executable(check_side check_side.cpp) 19 | target_link_libraries(check_side 20 | voxelizer_lib 21 | ) 22 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Timer.h 3 | * 4 | * Created on: 22 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #ifndef TIMER_H_ 9 | #define TIMER_H_ 10 | 11 | #include "absl/time/time.h" 12 | 13 | namespace voxelizer { 14 | 15 | class Timer { 16 | absl::Time start_, end_; 17 | 18 | public: 19 | Timer(); 20 | virtual ~Timer(); 21 | void Restart(); 22 | void Stop(); 23 | int64_t TimeInS(); 24 | int64_t TimeInMs(); 25 | int64_t TimeInNs(); 26 | void PrintTimeInS(); 27 | void PrintTimeInMs(); 28 | void PrintTimeInNs(); 29 | }; 30 | 31 | } // namespace voxelizer 32 | 33 | #endif /* TIMER_H_ */ 34 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(abseil-cpp) 2 | 3 | set(voxelizer_SRC timer.cpp timer.h voxelizer.cpp voxelizer.h thread_pool.cpp thread_pool.h commons.cpp commons.h tri_box.cpp tri_box.h ray_caster.cpp ray_caster.h) 4 | 5 | add_library(voxelizer_lib ${voxelizer_SRC}) 6 | target_link_libraries(voxelizer_lib 7 | ${ASSIMP_LIBRARIES} 8 | ${FCL_LIBRARIES} 9 | ${Boost_ATOMIC_LIBRARY} 10 | ${Boost_SYSTEM_LIBRARY} 11 | ${Boost_THREAD_LIBRARY} 12 | ${Boost_DATE_TIME_LIBRARY} 13 | ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} 14 | absl::flags 15 | absl::flags_parse 16 | absl::base 17 | absl::strings 18 | absl::time 19 | absl::status 20 | ) 21 | 22 | add_executable(voxelizer voxelizer_main.cpp) 23 | target_link_libraries(voxelizer 24 | voxelizer_lib 25 | ) -------------------------------------------------------------------------------- /src/ray_caster.h: -------------------------------------------------------------------------------- 1 | #ifndef RAY_CASTER_H_ 2 | #define RAY_CASTER_H_ 3 | 4 | #include "commons.h" 5 | #include "fcl/collision.h" 6 | #include "fcl/shape/geometric_shapes.h" 7 | #include "voxelizer.h" 8 | 9 | namespace voxelizer { 10 | 11 | class RayCaster { 12 | std::vector vertices_; 13 | std::vector triangles_; 14 | std::unique_ptr mesh_co_; 15 | 16 | public: 17 | RayCaster(const std::vector& vertices, const std::vector& triangles) 18 | : vertices_(vertices), triangles_(triangles) {} 19 | bool Init(); 20 | // Cacluates the number of hit points for this casting ray. 21 | int RayCast(const Vec3f& source); 22 | // Whether the source inside the mesh object. 23 | bool Inside(const Vec3f& source); 24 | }; 25 | 26 | } 27 | 28 | #endif -------------------------------------------------------------------------------- /src/thread_pool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ThreadPool.h 3 | * 4 | * Created on: 22 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #ifndef THREADPOOL_H_ 9 | #define THREADPOOL_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace voxelizer { 17 | 18 | class ThreadPool { 19 | boost::shared_ptr work_; 20 | boost::asio::io_service service_; 21 | boost::thread_group thread_pool_; 22 | 23 | public: 24 | void Stop(bool wait = true); 25 | ThreadPool(size_t n_threads = 1); 26 | void Restart(size_t n_threads = 1); 27 | template 28 | void Run(T func) { 29 | service_.post(func); 30 | } 31 | virtual ~ThreadPool(); 32 | }; 33 | 34 | } // namespace voxelizer 35 | 36 | #endif /* THREADPOOL_H_ */ 37 | -------------------------------------------------------------------------------- /examples/read_rawvox.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int main(int argc, char* argv[]) { 7 | ifstream* input = new ifstream(argv[1], ios::in | ios::binary); 8 | int grid_size; 9 | double lx, ly, lz, voxel_size; 10 | *input >> grid_size; 11 | *input >> lx >> ly >> lz; 12 | *input >> voxel_size; 13 | int x, y, z, count = 0; 14 | while (*input >> x >> y >> z) { 15 | // do something 16 | if (count == 0) 17 | cout << "first line : " << x << ", " << y << ", " << z 18 | << endl; // first line 19 | ++count; 20 | } 21 | cout << "last line : " << x << ", " << y << ", " << z << endl; // last line 22 | input->close(); 23 | cout << "grid size : " << grid_size << endl; 24 | cout << "lower bound : " << lx << " " << ly << " " << lz << endl; 25 | cout << "voxel size : " << voxel_size << endl; 26 | cout << "voxel count : " << count << endl; 27 | } 28 | -------------------------------------------------------------------------------- /src/thread_pool.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ThreadPool.cpp 3 | * 4 | * Created on: 22 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #include "thread_pool.h" 9 | 10 | namespace voxelizer { 11 | 12 | ThreadPool::ThreadPool(size_t n_threads) { Restart(n_threads); } 13 | 14 | void ThreadPool::Stop(bool wait) { 15 | if (wait) { 16 | work_.reset(); // if not delete this, io_service::run will never exit 17 | thread_pool_.join_all(); 18 | service_.stop(); 19 | } else { 20 | service_.stop(); 21 | thread_pool_.join_all(); 22 | work_.reset(); 23 | } 24 | } 25 | 26 | void ThreadPool::Restart(size_t nThreads) { 27 | work_.reset(new boost::asio::io_service::work(service_)); 28 | for (std::size_t i = 0; i < nThreads; ++i) 29 | thread_pool_.create_thread( 30 | boost::bind(&boost::asio::io_service::run, &service_)); 31 | } 32 | 33 | ThreadPool::~ThreadPool() { thread_pool_.join_all(); } 34 | 35 | } // namespace voxelizer 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2020 Qian Chen 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /examples/read_meta.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "commons.h" 5 | #include "voxelizer.h" 6 | 7 | using voxelizer::VoxelIndex; 8 | using voxelizer::VoxelFlags; 9 | using voxelizer::VoxelMeta; 10 | 11 | int main(int argc, char* argv[]) { 12 | std::ifstream* input = new std::ifstream(argv[1], std::ios::in | std::ios::binary); 13 | std::string format; 14 | VoxelIndex size_x, size_y, size_z; 15 | getline(*input, format); 16 | std::cout << format << std::endl; 17 | *input >> size_x >> size_y >> size_z; 18 | std::cout << size_x << ", " << size_y << ", " << size_z << std::endl; 19 | VoxelIndex total_size = size_x * size_y * size_z; 20 | int filled = 0; 21 | for (VoxelIndex voxel_index = 0; voxel_index < total_size; ++voxel_index) { 22 | VoxelIndex read_index; 23 | VoxelFlags read_flag; 24 | *input >> read_index >> read_flag; 25 | VoxelMeta voxel_meta(read_index, read_flag); 26 | if (voxel_meta.Filled()) ++filled; 27 | } 28 | std::cout << "Filled: " << filled << std::endl; 29 | input->close(); 30 | } 31 | -------------------------------------------------------------------------------- /src/timer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Timer.cpp 3 | * 4 | * Created on: 22 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #include 9 | 10 | #include "timer.h" 11 | #include "absl/time/clock.h" 12 | 13 | namespace voxelizer { 14 | 15 | Timer::Timer() {} 16 | 17 | void Timer::Restart() { 18 | start_ = absl::Now(); 19 | } 20 | 21 | void Timer::Stop() { 22 | end_ = absl::Now(); 23 | } 24 | 25 | int64_t Timer::TimeInS() { 26 | absl::Duration dur = end_ - start_; 27 | return dur / absl::Seconds(1); 28 | } 29 | 30 | int64_t Timer::TimeInMs() { 31 | absl::Duration dur = end_ - start_; 32 | return dur / absl::Milliseconds(1); 33 | } 34 | 35 | int64_t Timer::TimeInNs() { 36 | absl::Duration dur = end_ - start_; 37 | return dur / absl::Nanoseconds(1); 38 | } 39 | 40 | Timer::~Timer() {} 41 | 42 | void Timer::PrintTimeInS() { 43 | std::cout << "consumes " << TimeInS() << " s." << std::endl; 44 | } 45 | 46 | void Timer::PrintTimeInMs() { 47 | std::cout << "consumes " << TimeInMs() << " ms." << std::endl; 48 | } 49 | 50 | void Timer::PrintTimeInNs() { 51 | std::cout << "consumes " << TimeInNs() << " ns." << std::endl; 52 | } 53 | 54 | } // namespace voxelizer -------------------------------------------------------------------------------- /src/ray_caster.cpp: -------------------------------------------------------------------------------- 1 | #include "ray_caster.h" 2 | 3 | #include "fcl/collision.h" 4 | #include "fcl/BV/BV.h" 5 | #include "fcl/BVH/BVH_model.h" 6 | 7 | namespace voxelizer { 8 | 9 | const Vec3f kFarPoint = Vec3f(-1e5, 1e7, -1e6); 10 | const Triangle kSingleTraingle = Triangle(0, 1, 2); 11 | const int kMaxNumContact = 10; 12 | typedef fcl::BVHModel Model; 13 | 14 | using fcl::collide; 15 | 16 | bool RayCaster::Init() { 17 | boost::shared_ptr model(new Model()); 18 | model->beginModel(triangles_.size(), vertices_.size()); 19 | model->addSubModel(vertices_, triangles_); 20 | model->endModel(); 21 | mesh_co_.reset(new CollisionObject(model)); 22 | mesh_co_->computeAABB(); 23 | return true; 24 | } 25 | 26 | int RayCaster::RayCast(const Vec3f& source) { 27 | boost::shared_ptr model(new Model()); 28 | model->beginModel(1, 3); 29 | std::vector vertices = {source, kFarPoint, kFarPoint}; 30 | std::vector triangles = {kSingleTraingle}; 31 | model->addSubModel(vertices, triangles); 32 | model->endModel(); 33 | CollisionObject* ray_co = new CollisionObject(model); 34 | CollisionRequest request(kMaxNumContact, true); 35 | CollisionResult result; 36 | if (collide(mesh_co_.get(), ray_co, request, result)) 37 | return result.numContacts(); 38 | return 0; 39 | } 40 | 41 | bool RayCaster::Inside(const Vec3f& source) { 42 | const int num_contacts = RayCast(source); 43 | return num_contacts % 2; 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/commons.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Commons.cpp 3 | * 4 | * Created on: 23 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #include "commons.h" 9 | 10 | #include "absl/strings/numbers.h" 11 | 12 | namespace voxelizer { 13 | 14 | const int kRandMax = 1e6; 15 | 16 | float RandFloat(const float rmin, const float rmax) { 17 | float t = static_cast(rand() % kRandMax) / kRandMax; 18 | return (t * (rmax - rmin) + rmin); 19 | } 20 | 21 | int Random(const int l, const int r) { 22 | return (int)((1.0 * random()) / RAND_MAX * (r - l)) + l; 23 | } 24 | 25 | inline void Fill(const Vec3f& vc, float ft[3]) { 26 | ft[0] = vc[0]; 27 | ft[1] = vc[1]; 28 | ft[2] = vc[2]; 29 | } 30 | 31 | bool Collide(const Vec3f& half_unit, const Vec3f& box_aa, const TriangleP& tri) { 32 | Vec3f v_box_center = box_aa + half_unit; 33 | float half_size[3]; 34 | Fill(half_unit, half_size); 35 | float box_center[3]; 36 | Fill(v_box_center, box_center); 37 | float tricerts[3][3]; 38 | Fill(tri.a, tricerts[0]); 39 | Fill(tri.b, tricerts[1]); 40 | Fill(tri.c, tricerts[2]); 41 | return TriBoxOverlap(box_center, half_size, tricerts); 42 | } 43 | 44 | bool ToVector3Int(const std::vector& vs, std::vector& vi) { 45 | vi.clear(); 46 | for (const auto& v : vs) { 47 | int i; 48 | if (!absl::SimpleAtoi(v, &i)) { 49 | return false; 50 | } 51 | vi.push_back(i); 52 | } 53 | return true; 54 | } 55 | 56 | bool ToVector3Float(const std::vector& vs, std::vector& vf) { 57 | vf.clear(); 58 | for (const auto& v : vs) { 59 | float f; 60 | if (!absl::SimpleAtof(v, &f)) { 61 | return false; 62 | } 63 | vf.push_back(f); 64 | } 65 | return true; 66 | } 67 | 68 | } // namespace voxelizer 69 | -------------------------------------------------------------------------------- /examples/check_side.cpp: -------------------------------------------------------------------------------- 1 | #include "fcl/shape/geometric_shapes.h" 2 | #include "commons.h" 3 | #include "ray_caster.h" 4 | #include "voxelizer.h" 5 | #include "time.h" 6 | 7 | using voxelizer::Voxelizer; 8 | using voxelizer::RayCaster; 9 | using voxelizer::RandFloat; 10 | 11 | bool InitVoxleizer(std::unique_ptr& vox_ptr) { 12 | const std::string in_file = "../data/sphere.obj"; 13 | voxelizer::Option option; 14 | option.SetInFilePath(in_file); 15 | vox_ptr.reset(new Voxelizer(128, option)); 16 | auto status = vox_ptr->Init(); 17 | if (!status.ok()) { 18 | std::cout << "init failure: " << status.message() << std::endl; 19 | return false; 20 | } 21 | vox_ptr->VoxelizeSurface(4); 22 | return true; 23 | } 24 | 25 | Vec3f RandomPoint(const Vec3f& lb, const Vec3f& ub) { 26 | return lb + (ub - lb) * Vec3f(RandFloat(), RandFloat(), RandFloat()); 27 | } 28 | 29 | int main(int argc, char* argv[]) { 30 | srand((unsigned)time(NULL)); 31 | std::unique_ptr vox_ptr; 32 | if (!InitVoxleizer(vox_ptr)) return 1; 33 | std::vector vertices = vox_ptr->VerticesVec(); 34 | std::vector triangles = vox_ptr->TrianglesVec(); 35 | std::cout << vertices.size() << ", " << triangles.size() << std::endl; 36 | RayCaster ray_caster(vertices, triangles); 37 | if (!ray_caster.Init()) { 38 | std::cout << "Fail to init ray_caster" << std::endl; 39 | return 1; 40 | } 41 | std::cout << "Space: " << vox_ptr->MeshLowerBound() << ", " << vox_ptr->MeshUpperBound() << std::endl; 42 | Vec3f source = RandomPoint(vox_ptr->MeshLowerBound(), vox_ptr->MeshUpperBound()); 43 | std::cout << "Source: " << source << std::endl; 44 | std::cout << "Inside: " << ray_caster.Inside(source) << std::endl; 45 | std::cout << RandFloat() << ", " << RandFloat() << ", " << RandFloat() << std::endl; 46 | return 0; 47 | } -------------------------------------------------------------------------------- /examples/collision_checker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * collisionChecker.h 3 | * 4 | * Created on: 30 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #ifndef COLLISIONCHECKER_H_ 9 | #define COLLISIONCHECKER_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "commons.h" 16 | #include "fcl/BV/BV.h" 17 | #include "fcl/BVH/BVH_model.h" 18 | #include "fcl/collision.h" 19 | #include "fcl/math/transform.h" 20 | #include "fcl/shape/geometric_shapes.h" 21 | #include "time.h" 22 | #include "voxelizer.h" 23 | 24 | namespace collision_checker { 25 | 26 | typedef std::unique_ptr VoxUP; 27 | typedef boost::shared_ptr COSP; 28 | typedef std::unique_ptr COUP; 29 | typedef fcl::BVHModel Model; 30 | typedef boost::shared_ptr ModelSP; 31 | 32 | class CollisionChecker { 33 | void EulerToMatrix(FCL_REAL a, FCL_REAL b, FCL_REAL c, Matrix3f& R); 34 | COSP GenRandomCO(double ratio); 35 | void PreMeshCO(); 36 | float RandInterval(float rmin, float rmax); 37 | FCL_REAL extents_[6]; 38 | void GenRandomTransform(FCL_REAL extents[6], Transform3f& transform); 39 | VoxUP voxelizer_; 40 | COUP mesh_co_; 41 | std::vector vertices_; 42 | std::vector triangles_; 43 | int size_, size2_, num_thread_; 44 | std::string p_file_; 45 | voxelizer::VISP voxels_; 46 | voxelizer::BoxSP unit_; 47 | 48 | public: 49 | inline bool TestVoxel(const COSP& cube_co); 50 | inline bool TestMesh(const COSP& cube_co); 51 | void Test(int num_cases, double ratio); 52 | bool Init(); 53 | CollisionChecker(int size, int num_thread, const std::string& p_file) 54 | : size_(size), 55 | num_thread_(num_thread) { 56 | voxelizer::Option option; 57 | option.SetInFilePath(p_file); 58 | voxelizer_ = absl::make_unique(size, option); 59 | } 60 | virtual ~CollisionChecker(); 61 | }; 62 | 63 | } // namespace collision_checker 64 | 65 | #endif /* COLLISIONCHECKER_H_ */ 66 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(voxelizer) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | find_package(PkgConfig) # pkg_check_modules 7 | 8 | find_package(Boost COMPONENTS atomic thread date_time filesystem system unit_test_framework REQUIRED) 9 | 10 | if(Boost_FOUND) 11 | include_directories(${Boost_INCLUDE_DIR}) 12 | else() 13 | message(FATAL_ERROR "Boost is required by Voxelizer") 14 | endif() 15 | 16 | if(PKG_CONFIG_FOUND) 17 | pkg_check_modules(FCL fcl) 18 | # check to see if the pkg is installed under the libccd name 19 | if(NOT FCL_FOUND) 20 | pkg_check_modules(FCL libfcl) 21 | endif() 22 | endif() 23 | 24 | if(NOT FCL_FOUND) 25 | # if pkfconfig is not installed, then fall back on more fragile detection 26 | # of fcl 27 | find_path(FCL_INCLUDE_DIRS collision.h 28 | PATH_SUFFIXES fcl) 29 | find_path(FCL_LIBRARY_DIRS 30 | ${CMAKE_SHARED_LIBRARY_PREFIX}fcl${CMAKE_SHARED_LIBRARY_SUFFIX}) 31 | if(FCL_INCLUDE_DIRS AND FCL_LIBRARY_DIRS) 32 | set(FCL_LIBRARIES ${CMAKE_SHARED_LIBRARY_PREFIX}fcl${CMAKE_SHARED_LIBRARY_SUFFIX}) 33 | else() 34 | message(FATAL_ERROR "Libfcl is required by Voxelizer") 35 | endif() 36 | endif() 37 | include_directories(${FCL_INCLUDE_DIRS}) 38 | link_directories(${FCL_LIBRARY_DIRS}) 39 | 40 | find_package(ASSIMP) 41 | if(NOT ASSIMP_FOUND) 42 | pkg_check_modules(ASSIMP assimp) 43 | endif() 44 | 45 | if( ASSIMP_FOUND ) 46 | message(STATUS "Found assimp version ${ASSIMP_VERSION}, ${ASSIMP_PACKAGE_VERSION}, ${ASSIMP_INCLUDE_DIRS}") 47 | # For older versions of libassimp2, 48 | # like the one in Ubuntu 12.04 49 | set(CMAKE_REQUIRED_LIBRARIES assimp) 50 | find_path(ASSIMP_LIBRARY_DIRS 51 | ${CMAKE_SHARED_LIBRARY_PREFIX}assimp${CMAKE_SHARED_LIBRARY_SUFFIX}) 52 | if(ASSIMP_LIBRARY_DIRS) 53 | #set(ASSIMP_LIBRARIES ${CMAKE_SHARED_LIBRARY_PREFIX}assimp${CMAKE_SHARED_LIBRARY_SUFFIX}) 54 | message(STATUS "Assimp library : " ${ASSIMP_LIBRARIES}) 55 | else() 56 | message(FATAL_ERROR "Libassimp is required by Voxelizer") 57 | endif() 58 | if( ${ASSIMP_VERSION} STRGREATER "2.0.0" ) 59 | set(IS_ASSIMP3 1) 60 | endif() 61 | endif() 62 | 63 | add_subdirectory(src bin) 64 | 65 | enable_testing() 66 | add_subdirectory(test) 67 | 68 | add_subdirectory(examples) -------------------------------------------------------------------------------- /src/commons.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Commons.h 3 | * 4 | * Created on: 23 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #ifndef COMMONS_H_ 9 | #define COMMONS_H_ 10 | 11 | #include // Post processing flags 12 | #include // Output data structure 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include // C++ importer interface 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "tri_box.h" 26 | using namespace fcl; 27 | 28 | namespace voxelizer { 29 | 30 | // we use unsigned int64 as the index 31 | typedef uint64_t VoxelIndex; 32 | typedef uint32_t VoxelFlags; 33 | // we batch with 64 bit. 34 | const int kBatchSize = 64; 35 | 36 | typedef boost::shared_ptr TriSP; 37 | typedef boost::shared_ptr BoxSP; 38 | typedef boost::shared_ptr V3SP; 39 | typedef std::unique_ptr V3UP; 40 | 41 | typedef std::atomic AVI; 42 | typedef boost::shared_ptr> AVISP; 43 | 44 | typedef boost::unordered_set HashSet; 45 | typedef boost::shared_ptr VISP; 46 | 47 | typedef unsigned char Byte; 48 | 49 | template 50 | struct ArrayDeleter { 51 | void operator()(T const* p) { delete[] p; } 52 | }; 53 | 54 | bool Collide(const Vec3f& size, const Vec3f& boxAA, const TriangleP& tri); 55 | inline void Fill(const Vec3f& vc, float ft[3]); 56 | 57 | float RandFloat(const float rmin=0.0, const float rmax=1.0); 58 | int Random(const int l, const int r); 59 | 60 | const Vec3f D_8[] = {Vec3f(1, 1, 1), Vec3f(1, 1, -1), Vec3f(1, -1, 1), 61 | Vec3f(1, -1, -1), Vec3f(-1, 1, 1), Vec3f(-1, 1, -1), 62 | Vec3f(-1, -1, 1), Vec3f(-1, -1, -1)}; 63 | 64 | const int DI_4[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; 65 | 66 | const Vec3f D_6[] = { 67 | Vec3f(0, 0, -1), Vec3f(0, 0, 1), Vec3f(-1, 0, 0), 68 | Vec3f(1, 0, 0), Vec3f(0, -1, 0), Vec3f(0, 1, 0), 69 | }; 70 | 71 | const Vec3f D_26[] = { 72 | Vec3f(-1, -1, -1), Vec3f(-1, -1, 0), Vec3f(-1, -1, 1), Vec3f(-1, 0, -1), 73 | Vec3f(-1, 0, 0), Vec3f(-1, 0, 1), Vec3f(-1, 1, -1), Vec3f(-1, 1, 0), 74 | Vec3f(-1, 1, 1), Vec3f(0, -1, -1), Vec3f(0, -1, 0), Vec3f(0, -1, 1), 75 | Vec3f(0, 0, -1), Vec3f(0, 0, 1), Vec3f(0, 1, -1), Vec3f(0, 1, 0), 76 | Vec3f(0, 1, 1), Vec3f(1, -1, -1), Vec3f(1, -1, 0), Vec3f(1, -1, 1), 77 | Vec3f(1, 0, -1), Vec3f(1, 0, 0), Vec3f(1, 0, 1), Vec3f(1, 1, -1), 78 | Vec3f(1, 1, 0), Vec3f(1, 1, 1)}; 79 | 80 | bool ToVector3Int(const std::vector& vs, std::vector& vi); 81 | bool ToVector3Float(const std::vector& vs, std::vector& vf); 82 | 83 | } // namespace voxelizer 84 | 85 | #endif /* COMMONS_H_ */ 86 | -------------------------------------------------------------------------------- /src/voxelizer_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * voxelizerMain.cpp 3 | * 4 | * Created on: 1 Jul, 2014 5 | * Author: chenqian 6 | */ 7 | #include 8 | #include 9 | 10 | #include "absl/flags/flag.h" 11 | #include "absl/flags/parse.h" 12 | #include "absl/base/internal/raw_logging.h" 13 | 14 | #include "voxelizer.h" 15 | 16 | using voxelizer::Timer; 17 | using voxelizer::Voxelizer; 18 | using voxelizer::ToVector3Int; 19 | using voxelizer::ToVector3Float; 20 | using voxelizer::Option; 21 | 22 | ABSL_FLAG(std::vector, grid_size, {}, "grid size, the granularity of voxelizer. if only one integer is set, assuming the X,Y,Z are the same."); 23 | ABSL_FLAG(std::vector, voxel_size, {}, "voxel size, which determines the size of each voxel"); 24 | ABSL_FLAG(int, num_thread, 4, "number of thread to run voxelizer"); 25 | ABSL_FLAG(bool, verbose, false, "print debug info"); 26 | ABSL_FLAG(std::string, input, "", "input file to be voxelized, file type will be inferred from file suffix"); 27 | ABSL_FLAG(std::string, output, "", "output file to store voxelized result"); 28 | ABSL_FLAG(std::string, format, "binvox", "output format, can be binvox or rawvox"); 29 | ABSL_FLAG(std::string, mode, "solid", "voxelizer mode, surface or solid"); 30 | ABSL_FLAG(int, mesh_index, 0, "mesh index to be voxelized"); 31 | ABSL_FLAG(std::vector, clipping_size, {}, "clipping size (x,y,z) to clip the voxelized result. The clipping size is grid size based. If only one integter is specified, clipping size is initialized as (x,x,x). If not set, the result is not clipped."); 32 | ABSL_FLAG(bool, with_meta, false, "write voxel meta info to .meta file if set to true"); 33 | ABSL_FLAG(bool, tight, false, "If true, check whether the center point of each voxel is inside the mesh or outside it, and if it is outside, remove it. Caveat: this option could be time consuming."); 34 | 35 | absl::Status PraseOption(Option& option) { 36 | std::string format = absl::GetFlag(FLAGS_format); 37 | option.SetFormat(format); 38 | 39 | std::vector clipping_size; 40 | if (!ToVector3Int(absl::GetFlag(FLAGS_clipping_size), clipping_size)) { 41 | return absl::InvalidArgumentError("Failed to convert the clipping size to int vector."); 42 | } 43 | if (clipping_size.size() == 1) { 44 | clipping_size.push_back(clipping_size[0]); 45 | clipping_size.push_back(clipping_size[0]); 46 | } 47 | option.SetClippingSize(clipping_size); 48 | 49 | std::string input_file = absl::GetFlag(FLAGS_input); 50 | if (input_file.empty()) { 51 | return absl::InvalidArgumentError("Input should be non-empty"); 52 | } 53 | option.SetInFilePath(input_file); 54 | 55 | std::string output_file = absl::GetFlag(FLAGS_output); 56 | if (output_file.empty()) { 57 | return absl::InvalidArgumentError("Output should be non-empty"); 58 | } 59 | option.SetOutFilePath(output_file); 60 | 61 | int mesh_index = absl::GetFlag(FLAGS_mesh_index); 62 | option.SetMeshIndex(mesh_index); 63 | 64 | bool verbose = absl::GetFlag(FLAGS_verbose); 65 | option.SetVerbose(verbose); 66 | 67 | bool tight = absl::GetFlag(FLAGS_tight); 68 | option.SetTight(tight); 69 | 70 | // tight fit needs with meta. 71 | bool with_meta = absl::GetFlag(FLAGS_with_meta) || tight; 72 | option.SetWithMeta(with_meta); 73 | 74 | 75 | 76 | return absl::OkStatus(); 77 | } 78 | 79 | int main(int argc, char* argv[]) { 80 | absl::ParseCommandLine(argc, argv); 81 | 82 | std::vector grid_size; 83 | ABSL_INTERNAL_CHECK(ToVector3Int(absl::GetFlag(FLAGS_grid_size), grid_size), "failed to parse grid_size"); 84 | ABSL_INTERNAL_CHECK(grid_size.size() == 0 || grid_size.size() == 1 || grid_size.size() == 3, "grid_size should be 1 or 3 dimensions if specifed"); 85 | if (grid_size.size() == 1) { 86 | grid_size.push_back(grid_size[0]); 87 | grid_size.push_back(grid_size[0]); 88 | } 89 | std::vector voxel_size; 90 | ABSL_INTERNAL_CHECK(ToVector3Float(absl::GetFlag(FLAGS_voxel_size), voxel_size), "failed to parse voxel_size"); 91 | ABSL_INTERNAL_CHECK(voxel_size.size() == 0 || voxel_size.size() == 1 || voxel_size.size() == 3, "voxel_size should be 3 dimensions if specifed"); 92 | if (voxel_size.size() == 1) { 93 | voxel_size.push_back(voxel_size[0]); 94 | voxel_size.push_back(voxel_size[0]); 95 | } 96 | int num_thread = absl::GetFlag(FLAGS_num_thread); 97 | 98 | std::string mode = absl::GetFlag(FLAGS_mode); 99 | 100 | ABSL_INTERNAL_CHECK(grid_size.empty() ^ voxel_size.empty(), "if and only if grid_size or voxel_size should be set."); 101 | 102 | Option option; 103 | auto status = PraseOption(option); 104 | if (!status.ok()) { 105 | std::cout << "Failed to parse Option." << std::endl; 106 | return 1; 107 | } 108 | 109 | Timer timer; 110 | 111 | timer.Restart(); 112 | Voxelizer* voxelizer; 113 | if (grid_size.empty()) { 114 | voxelizer = new Voxelizer(voxel_size, option); 115 | } else { 116 | voxelizer = new Voxelizer(grid_size, option); 117 | } 118 | status = voxelizer->Init(); 119 | if (!status.ok()) { 120 | std::cout << "voxelizer fails initialization: " << status.message(); 121 | return 1; 122 | } 123 | timer.Stop(); 124 | std::cout << "voxelizer initialization "; 125 | timer.PrintTimeInMs(); 126 | std::cout << "-------------------------------------------" << std::endl; 127 | timer.Restart(); 128 | voxelizer->VoxelizeSurface(num_thread); 129 | timer.Stop(); 130 | std::cout << "surface voxelization "; 131 | timer.PrintTimeInMs(); 132 | if (mode == "solid") { 133 | std::cout << "-------------------------------------------" << std::endl; 134 | timer.Restart(); 135 | voxelizer->VoxelizeSolid(num_thread); 136 | timer.Stop(); 137 | std::cout << "solid voxelization "; 138 | timer.PrintTimeInMs(); 139 | } 140 | std::cout << "-------------------------------------------" << std::endl; 141 | timer.Restart(); 142 | voxelizer->Write(); 143 | timer.Stop(); 144 | std::cout << "writing file "; 145 | timer.PrintTimeInMs(); 146 | 147 | return 0; 148 | } 149 | -------------------------------------------------------------------------------- /examples/collision_checker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * collisionChecker.cpp 3 | * 4 | * Created on: 30 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #include "collision_checker.h" 9 | 10 | using voxelizer::ArrayDeleter; 11 | using voxelizer::BoxSP; 12 | using voxelizer::kBatchSize; 13 | using voxelizer::Timer; 14 | using voxelizer::V3SP; 15 | using voxelizer::Voxelizer; 16 | using voxelizer::VoxelIndex; 17 | 18 | namespace collision_checker { 19 | 20 | float CollisionChecker::RandInterval(float rmin, float rmax) { 21 | srand (time(NULL)); 22 | float t = static_cast(rand() % 1000) / 1000; 23 | return (t * (rmax - rmin) + rmin); 24 | } 25 | 26 | void CollisionChecker::EulerToMatrix(FCL_REAL a, FCL_REAL b, FCL_REAL c, 27 | Matrix3f& R) { 28 | FCL_REAL c1 = cos(a); 29 | FCL_REAL c2 = cos(b); 30 | FCL_REAL c3 = cos(c); 31 | FCL_REAL s1 = sin(a); 32 | FCL_REAL s2 = sin(b); 33 | FCL_REAL s3 = sin(c); 34 | 35 | R.setValue(c1 * c2, -c2 * s1, s2, c3 * s1 + c1 * s2 * s3, 36 | c1 * c3 - s1 * s2 * s3, -c2 * s3, s1 * s3 - c1 * c3 * s2, 37 | c3 * s1 * s2 + c1 * s3, c2 * c3); 38 | } 39 | 40 | bool CollisionChecker::Init() { 41 | std::cout << "collision checker init..." << std::endl; 42 | 43 | auto status = voxelizer_->Init(); 44 | if (!status.ok()) { 45 | std::cout << "init failure: " << status.message() << std::endl; 46 | return false; 47 | } 48 | 49 | voxelizer_->VoxelizeSurface(num_thread_); 50 | Vec3f lb = voxelizer_->MeshLowerBound(); 51 | Vec3f ub = voxelizer_->MeshUpperBound(); 52 | extents_[0] = lb[0]; 53 | extents_[1] = lb[1]; 54 | extents_[2] = lb[2]; 55 | extents_[3] = ub[0]; 56 | extents_[4] = ub[1]; 57 | extents_[5] = ub[2]; 58 | size2_ = size_ * size_; 59 | voxels_.reset(new VoxelIndex[voxelizer_->TotalVoxelCompressedSize()], 60 | ArrayDeleter()); 61 | for (int i = 0; i < voxelizer_->TotalVoxelCompressedSize(); ++i) 62 | voxels_.get()[i] = voxelizer_->Voxels().get()[i]; 63 | unit_.reset(new Box(voxelizer_->Unit())); 64 | PreMeshCO(); 65 | 66 | std::cout << "done." << std::endl; 67 | 68 | return true; 69 | } 70 | 71 | COSP CollisionChecker::GenRandomCO(double ratio) { 72 | BoxSP box(new Box((voxelizer_->MeshUpperBound() - 73 | voxelizer_->MeshLowerBound()) * 74 | ratio)); 75 | Transform3f tf; 76 | GenRandomTransform(extents_, tf); 77 | COSP boxCO(new CollisionObject(box, tf)); 78 | return boxCO; 79 | } 80 | 81 | inline bool CollisionChecker::TestVoxel(const COSP& cube_co) { 82 | const Vec3f lb = voxelizer_->GetVoxel(cube_co->getAABB().min_); 83 | const Vec3f ub = voxelizer_->GetVoxel(cube_co->getAABB().max_); 84 | int lx = std::max(0, (int)lb[0]), ux = std::min(size_ - 1, (int)ub[0]), 85 | ly = std::max(0, (int)lb[1]), uy = std::min(size_ - 1, (int)ub[1]), 86 | lz = std::max(0, (int)lb[2]), uz = std::min(size_ - 1, (int)ub[2]); 87 | VoxelIndex voxel_index, tmp; 88 | Vec3f vxl_box; 89 | // std::cout << "e" << std::endl; 90 | // std::cout << *lb << ", " << *ub << std::endl; 91 | for (int x = lx, y, z; x <= ux; ++x) { 92 | for (y = ly; y <= uy; ++y) { 93 | for (z = lz; z <= uz; ++z) { 94 | voxel_index = x * size2_ + y * size_ + z; 95 | tmp = (voxels_.get())[voxel_index / kBatchSize]; 96 | if (!GETBIT(tmp, voxel_index)) continue; 97 | vxl_box.setValue(x, y, z); 98 | // Transform3f tf(*voxelizer_->GetLoc(vxl_box)); 99 | Transform3f tf(voxelizer_->GetLoc(vxl_box) + 100 | voxelizer_->HalfUnit()); 101 | // std::cout << *voxelizer_->GetLoc(vxl_box) << std::endl; 102 | CollisionRequest request; 103 | CollisionResult result; 104 | COUP box_co(new CollisionObject(unit_, tf)); 105 | if (fcl::collide(box_co.get(), cube_co.get(), request, result)) { 106 | return true; 107 | } 108 | } 109 | } 110 | } 111 | return false; 112 | } 113 | 114 | inline bool CollisionChecker::TestMesh(const COSP& cube_co) { 115 | CollisionRequest request; 116 | CollisionResult result; 117 | if (fcl::collide(mesh_co_.get(), cube_co.get(), request, result)) { 118 | return true; 119 | } 120 | return false; 121 | } 122 | 123 | void CollisionChecker::Test(int num_cases, double ratio) { 124 | Timer timer; 125 | double t1 = 0, t2 = 0; 126 | int tp = 0, fp = 0, fn = 0, tn = 0; 127 | for (int i = 0; i < num_cases; ++i) { 128 | COSP cube_co = GenRandomCO(ratio); 129 | cube_co->computeAABB(); 130 | // std::cout << cube_co->getAABB().min_ << std::endl; 131 | timer.Restart(); 132 | bool res1 = TestVoxel(cube_co); 133 | timer.Stop(); 134 | t1 += timer.TimeInNs(); 135 | timer.Restart(); 136 | bool res2 = TestMesh(cube_co); 137 | timer.Stop(); 138 | t2 += timer.TimeInNs(); 139 | if (res1 && !res2) { 140 | fp++; 141 | } 142 | if (!res1 && res2) { 143 | fn++; 144 | } 145 | if (res1 && res2) { 146 | tp++; 147 | } 148 | if (!res1 && !res2) { 149 | tn++; 150 | } 151 | } 152 | std::cout << "---------- ratio = " << ratio << " ----------" << std::endl; 153 | std::cout << "accuracy = \t" << 100.0 * (tp + tn) / (num_cases) << "\%" << std::endl; 154 | // std::cout << "# = \t" << tp << ", " << tn << "\%" << std::endl; 155 | std::cout << "voxel consumes \t" << t1 / num_cases << " ns" << std::endl; 156 | std::cout << "mesh consumes \t" << t2 / num_cases << " ns" << std::endl; 157 | } 158 | 159 | void CollisionChecker::PreMeshCO() { 160 | vertices_ = voxelizer_->VerticesVec(); 161 | triangles_ = voxelizer_->TrianglesVec(); 162 | ModelSP model(new Model()); 163 | model->beginModel(triangles_.size(), vertices_.size()); 164 | model->addSubModel(vertices_, triangles_); 165 | model->endModel(); 166 | mesh_co_.reset(new CollisionObject(model)); 167 | mesh_co_->computeAABB(); 168 | } 169 | 170 | void CollisionChecker::GenRandomTransform(FCL_REAL extents[6], 171 | Transform3f& transform) { 172 | FCL_REAL x = RandInterval(extents[0], extents[3]); 173 | FCL_REAL y = RandInterval(extents[1], extents[4]); 174 | FCL_REAL z = RandInterval(extents[2], extents[5]); 175 | 176 | const FCL_REAL pi = 3.1415926; 177 | FCL_REAL a = RandInterval(0, 2 * pi); 178 | FCL_REAL b = RandInterval(0, 2 * pi); 179 | FCL_REAL c = RandInterval(0, 2 * pi); 180 | 181 | Matrix3f R; 182 | EulerToMatrix(a, b, c, R); 183 | Vec3f T(x, y, z); 184 | transform.setTransform(R, T); 185 | } 186 | 187 | CollisionChecker::~CollisionChecker() { 188 | // TODO Auto-generated destructor stub 189 | } 190 | 191 | } // namespace collision_checker 192 | 193 | int run_benchmark () { 194 | const std::string inputFile[] = {"../data/kawada-hironx.stl", "../data/racecar.stl", 195 | "../data/bike.stl"}; 196 | const double ratios[] = {0.005, 0.01, 0.02, 0.04, 0.08}; 197 | const int gridSize[] = {128, 256, 512}; 198 | int testCases = 1000; 199 | for (int k = 0; k < 3; ++k) { 200 | for (int i = 0; i < 3; ++i) { 201 | std::cout << "==================================" << std::endl; 202 | std::cout << "Intput file : " << inputFile[i] << std::endl; 203 | std::cout << "Grid size : " << gridSize[k] << std::endl; 204 | std::cout << "==================================" << std::endl; 205 | collision_checker::CollisionChecker checker(gridSize[k], 4, inputFile[i]); 206 | if (!checker.Init()) { 207 | std::cout << "Checker init error." << std::endl; 208 | return 1; 209 | } 210 | for (int j = 0; j < 5; ++j) { 211 | checker.Test(testCases, ratios[j]); 212 | } 213 | } 214 | } 215 | return 0; 216 | } 217 | 218 | int run_single() { 219 | const std::string in_file = "../data/kawada-hironx.stl"; 220 | collision_checker::CollisionChecker checker(256, 4, in_file); 221 | if (!checker.Init()) { 222 | std::cout << "Checker init error." << std::endl; 223 | return 1; 224 | } 225 | checker.Test(10, 0.08); 226 | return 0; 227 | } 228 | 229 | int main(int argc, char* argv[]) { 230 | if (argc >= 2 && std::string(argv[1]) == "benchmark") { 231 | return run_benchmark(); 232 | } 233 | return run_single(); 234 | } 235 | -------------------------------------------------------------------------------- /src/voxelizer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Voxelizer.h 3 | * 4 | * Created on: 22 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #ifndef VOXELIZER_H_ 9 | #define VOXELIZER_H_ 10 | 11 | #include // Post processing flags 12 | #include // Output data structure 13 | 14 | #include // C++ importer interface 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "absl/status/status.h" 21 | #include "commons.h" 22 | #include "thread_pool.h" 23 | #include "timer.h" 24 | 25 | namespace voxelizer { 26 | 27 | const int kPrimitiveTriangleType = 0x4; 28 | const int kTriangleNumIndices = 3; 29 | const Vec3f kEpsBox(0.0001, 0.0001, 0.0001); // epsilon box 30 | const int kThresholdBfsSurface = 10; 31 | 32 | #define GETBIT(x, i) ((x >> (i % kBatchSize)) & 1) 33 | #define SETBIT(voxels, voxel_index) (voxels.get())[voxel_index / kBatchSize] |= (static_cast(1) << (voxel_index % kBatchSize)) 34 | #define INDEX(x, y, z) x * size_yz_ + y * size_z_ + z 35 | 36 | class Option { 37 | public: 38 | Option() { 39 | mesh_index_ = 0; 40 | verbose_ = false; 41 | with_meta_= false; 42 | tight_ = false; 43 | } 44 | std::vector ClippingSize() const { 45 | return clipping_size_; 46 | } 47 | void SetClippingSize(const std::vector& clipping_size) { 48 | clipping_size_ = clipping_size; 49 | } 50 | std::string Format() const { 51 | return format_; 52 | } 53 | void SetFormat(const std::string& format) { 54 | format_ = format; 55 | } 56 | std::string InFilePath() const { 57 | return in_file_path_; 58 | } 59 | void SetInFilePath(const std::string& in_file_path) { 60 | in_file_path_ = in_file_path; 61 | } 62 | std::string OutFilePath() const { 63 | return out_file_path_; 64 | } 65 | void SetOutFilePath(const std::string& out_file_path) { 66 | out_file_path_ = out_file_path; 67 | } 68 | int MeshIndex() const { 69 | return mesh_index_; 70 | } 71 | void SetMeshIndex(const int mesh_index) { 72 | mesh_index_ = mesh_index; 73 | } 74 | bool Verbose() { 75 | return verbose_; 76 | } 77 | void SetVerbose(const bool verbose) { 78 | verbose_ = verbose; 79 | } 80 | bool WithMeta() const { 81 | return with_meta_; 82 | } 83 | void SetWithMeta(const bool with_meta) { 84 | with_meta_ = with_meta; 85 | } 86 | bool Tight() const { 87 | return tight_; 88 | } 89 | void SetTight(const bool tight) { 90 | tight_ = tight; 91 | } 92 | 93 | private: 94 | std::string in_file_path_; 95 | std::string out_file_path_; 96 | std::string format_; 97 | std::vector clipping_size_; 98 | int mesh_index_; 99 | bool verbose_; 100 | bool with_meta_; 101 | bool tight_; 102 | }; 103 | 104 | enum VoxelFlagsEnum { 105 | FILLED = 0x01 << 0, // Set this bit when a voxel cell is not empty. Unset this bit when this voxel is empty. 106 | INSIDE = 0x01 << 1, // Voxel center is inside the mesh surface , TIGHT 107 | SURFACE = 0x01 << 2, // Voxel is generated from surface in the surface voxelizing pass 108 | SOLID = 0x01 << 3, // Voxel is generated in solid flood filling in the solid voxelizing pass 109 | CLIPPED = 0x01 << 4, // Voxel is clipped 110 | }; 111 | 112 | class VoxelMeta { 113 | public: 114 | VoxelMeta(VoxelIndex index, VoxelFlags flags): index_(index), flags_(flags) {} 115 | VoxelMeta() { 116 | Reset(); 117 | } 118 | void Reset() { 119 | index_ = 0; 120 | ClearFlags(); 121 | } 122 | VoxelIndex Index() const { 123 | return index_; 124 | } 125 | void SetIndex(const VoxelIndex& index) { 126 | index_ = index; 127 | } 128 | VoxelFlags Flags() const { 129 | return flags_; 130 | } 131 | void SetFlags(const VoxelFlags& flags) { 132 | flags_ = flags; 133 | } 134 | void ClearFlags() { 135 | flags_ = 0; 136 | SetInside(); 137 | } 138 | bool Filled() const { 139 | return (flags_ & VoxelFlagsEnum::FILLED) > 0; 140 | } 141 | void SetFilled() { 142 | flags_ |= VoxelFlagsEnum::FILLED; 143 | } 144 | void UnsetFilled() { 145 | flags_ &= ~VoxelFlagsEnum::FILLED; 146 | } 147 | void UpdateFilled() { 148 | if (!Clipped() && (Solid() || (Surface() && Inside()))) { 149 | SetFilled(); 150 | } else { 151 | UnsetFilled(); 152 | } 153 | } 154 | bool Inside() const { 155 | return (flags_ & VoxelFlagsEnum::INSIDE) > 0; 156 | } 157 | void SetInside() { 158 | flags_ |= VoxelFlagsEnum::INSIDE; 159 | UpdateFilled(); 160 | } 161 | void UnsetInside() { 162 | flags_ &= ~VoxelFlagsEnum::INSIDE; 163 | UpdateFilled(); 164 | } 165 | bool Surface() const { 166 | return (flags_ & VoxelFlagsEnum::SURFACE) > 0; 167 | } 168 | void SetSurface() { 169 | flags_ |= VoxelFlagsEnum::SURFACE; 170 | UpdateFilled(); 171 | } 172 | void UnsetSurface() { 173 | flags_ &= ~VoxelFlagsEnum::SURFACE; 174 | UpdateFilled(); 175 | } 176 | bool Solid() const { 177 | return (flags_ & VoxelFlagsEnum::SOLID) > 0; 178 | } 179 | void SetSolid() { 180 | flags_ |= VoxelFlagsEnum::SOLID; 181 | UpdateFilled(); 182 | } 183 | void UnsetSolid() { 184 | flags_ &= ~VoxelFlagsEnum::SOLID; 185 | UpdateFilled(); 186 | } 187 | bool Clipped() const { 188 | return (flags_ & VoxelFlagsEnum::CLIPPED) > 0; 189 | } 190 | void SetClipped() { 191 | flags_ |= VoxelFlagsEnum::CLIPPED; 192 | UpdateFilled(); 193 | } 194 | void UnsetClipped() { 195 | flags_ &= ~VoxelFlagsEnum::CLIPPED; 196 | UpdateFilled(); 197 | } 198 | 199 | private: 200 | VoxelIndex index_; 201 | VoxelFlags flags_; 202 | }; 203 | 204 | class Voxelizer { 205 | Option option_; 206 | boost::shared_ptr voxel_metas_; 207 | bool is_init_; 208 | 209 | V3UP mesh_lb_, mesh_ub_; // location 210 | V3UP mesh_vox_lb_, mesh_vox_ub_; // voxels of location 211 | V3UP output_lb_, output_ub_; // output lower/upper bound locations 212 | 213 | float min_lb_, max_ub_; 214 | V3UP lb_, ub_, bound_; // lowerBound and upperBound of the whole space 215 | V3UP unit_, half_unit_; // full or half size of the unit 216 | 217 | int num_meshes_; 218 | 219 | V3SP faces_; 220 | int num_faces_; 221 | 222 | V3SP vertices_; 223 | int num_vertices_; 224 | 225 | AVISP voxels_buffer_; 226 | AVISP voxels_; 227 | 228 | VoxelIndex size2_, size_x_, size_y_, size_z_, size_xy_, size_xz_, size_yz_, compressed_total_size_; // size_2 = size*size 229 | V3UP size_, scale_; 230 | 231 | std::vector grid_size_; 232 | std::vector voxel_size_; 233 | 234 | absl::Status LoadFromMesh(const aiMesh* mesh); 235 | inline void RunSolidTask(size_t num_thread = 1); 236 | inline void RunSolidTask2(size_t num_thread = 1); 237 | inline void RunSurfaceTask(const int tri_id); 238 | inline void FillYZ(const int x); 239 | inline void FillXZ(const int y); 240 | inline void FillXY(const int z); 241 | inline void FillYZ2(const int x); 242 | inline void FillXZ2(const int y); 243 | inline void FillXY2(const int z); 244 | inline bool InRange(const Vec3f& vc, const Vec3f& lb, const Vec3f& ub); 245 | inline bool InRange(const int x, const int y, const int z, const int lx, 246 | const int ly, const int lz, const int ux, 247 | const int uy, const int uz); 248 | inline bool InRange(const int x, const int y, const int z, 249 | const Vec3f& lb, const Vec3f& ub); 250 | inline TriangleP GetTri(const int tri_id); 251 | void ConvIndexToVoxel(const VoxelIndex coord, int& x, int& y, int& z); 252 | inline Vec3f ConvIndexToVoxel(const VoxelIndex coord); 253 | void ConvIndexToVoxel(const VoxelIndex coord, Vec3f& voxel); 254 | inline VoxelIndex ConvVoxelToIndex(const Vec3f& voxel); 255 | inline VoxelIndex BfsSurface(const TriangleP& tri, const Vec3f& lb, const Vec3f& ub); 256 | void RandomPermutation(const V3SP& data, int num); 257 | void BfsSolid(const VoxelIndex voxel_id); 258 | void InitOutputBound(); 259 | void UpdateSurfaceMeta(); 260 | void UpdateTight(); 261 | void UpdateSolidMeta(); 262 | void InitVoxelMeta(); 263 | 264 | public: 265 | VoxelIndex TotalVoxelSize(); 266 | VoxelIndex TotalVoxelCompressedSize(); 267 | Vec3f HalfUnit(); 268 | Vec3f Unit(); 269 | AVISP Voxels(); 270 | Vec3f GetVoxel(const Vec3f& loc); 271 | Vec3f GetLoc(const Vec3f& voxel); 272 | Vec3f GetCenterLoc(const Vec3f& voxel); 273 | Vec3f MeshLowerBound(); 274 | Vec3f MeshUpperBound(); 275 | Vec3f LowerBound(); 276 | Vec3f UpperBound(); 277 | int VerticesSize(); 278 | int FacesSize(); 279 | V3SP Vertices(); 280 | std::vector VerticesVec(); 281 | V3SP Faces(); 282 | std::vector TrianglesVec(); 283 | void VoxelizeSurface(int num_thread = 1); 284 | void VoxelizeSolid(int num_thread = 1); 285 | // Returns whether a voxel is filled. If meta data is avaiable, use meta.Filled(); otherwise check voxels_. 286 | bool Filled(const VoxelIndex voxel_id); 287 | bool Filled(const int x, const int y, const int z); 288 | void Write(); 289 | void WriteBinvox(); 290 | void WriteRawvox(); 291 | void WriteMeta(); 292 | absl::Status Init(); 293 | void SetOption(const Option& option) { 294 | option_ = option; 295 | } 296 | Voxelizer(int grid_size, const Option& option) 297 | : option_(option) { 298 | grid_size_.push_back(grid_size); 299 | grid_size_.push_back(grid_size); 300 | grid_size_.push_back(grid_size); 301 | } 302 | Voxelizer(float voxel_size, const Option& option) 303 | : option_(option) { 304 | voxel_size_.push_back(voxel_size); 305 | voxel_size_.push_back(voxel_size); 306 | voxel_size_.push_back(voxel_size); 307 | } 308 | Voxelizer(const std::vector& grid_size, const Option& option) 309 | : grid_size_(grid_size), option_(option) {} 310 | Voxelizer(const std::vector& voxel_size, const Option& option) 311 | : voxel_size_(voxel_size), option_(option) {} 312 | virtual ~Voxelizer(); 313 | }; 314 | 315 | } // namespace voxelizer 316 | 317 | #endif /* VOXELIZER_H_ */ 318 | -------------------------------------------------------------------------------- /src/tri_box.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TriBox.cpp 3 | * 4 | * Created on: 23 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #include "tri_box.h" 9 | 10 | /********************************************************/ 11 | 12 | /* AABB-triangle overlap test code */ 13 | 14 | /* by Tomas Akenine-Möller */ 15 | 16 | /* Function: int triBoxOverlap(float boxcenter[3], */ 17 | 18 | /* float boxhalfsize[3],float triverts[3][3]); */ 19 | 20 | /* History: */ 21 | 22 | /* 2001-03-05: released the code in its first version */ 23 | 24 | /* 2001-06-18: changed the order of the tests, faster */ 25 | 26 | /* */ 27 | 28 | /* Acknowledgement: Many thanks to Pierre Terdiman for */ 29 | 30 | /* suggestions and discussions on how to optimize code. */ 31 | 32 | /* Thanks to David Hunt for finding a ">="-bug! */ 33 | 34 | /********************************************************/ 35 | 36 | #include 37 | #include 38 | 39 | namespace voxelizer { 40 | 41 | #define X 0 42 | 43 | #define Y 1 44 | 45 | #define Z 2 46 | 47 | #define CROSS(dest, v1, v2) \ 48 | dest[0] = v1[1] * v2[2] - v1[2] * v2[1]; \ 49 | dest[1] = v1[2] * v2[0] - v1[0] * v2[2]; \ 50 | dest[2] = v1[0] * v2[1] - v1[1] * v2[0]; 51 | 52 | #define DOT(v1, v2) (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) 53 | 54 | #define SUB(dest, v1, v2) \ 55 | dest[0] = v1[0] - v2[0]; \ 56 | dest[1] = v1[1] - v2[1]; \ 57 | dest[2] = v1[2] - v2[2]; 58 | 59 | #define FINDMINMAX(x0, x1, x2, min, max) \ 60 | min = max = x0; \ 61 | if (x1 < min) min = x1; \ 62 | if (x1 > max) max = x1; \ 63 | if (x2 < min) min = x2; \ 64 | if (x2 > max) max = x2; 65 | 66 | int planeBoxOverlap(float normal[3], float vert[3], float maxbox[3]) // -NJMP- 67 | 68 | { 69 | int q; 70 | 71 | float vmin[3], vmax[3], v; 72 | 73 | for (q = X; q <= Z; q++) 74 | 75 | { 76 | v = vert[q]; // -NJMP- 77 | 78 | if (normal[q] > 0.0f) 79 | 80 | { 81 | vmin[q] = -maxbox[q] - v; // -NJMP- 82 | 83 | vmax[q] = maxbox[q] - v; // -NJMP- 84 | 85 | } 86 | 87 | else 88 | 89 | { 90 | vmin[q] = maxbox[q] - v; // -NJMP- 91 | 92 | vmax[q] = -maxbox[q] - v; // -NJMP- 93 | } 94 | } 95 | 96 | if (DOT(normal, vmin) > 0.0f) return 0; // -NJMP- 97 | 98 | if (DOT(normal, vmax) >= 0.0f) return 1; // -NJMP- 99 | 100 | return 0; 101 | } 102 | 103 | /*======================== X-tests ========================*/ 104 | 105 | #define AXISTEST_X01(a, b, fa, fb) \ 106 | p0 = a * v0[Y] - b * v0[Z]; \ 107 | p2 = a * v2[Y] - b * v2[Z]; \ 108 | if (p0 < p2) { \ 109 | min = p0; \ 110 | max = p2; \ 111 | } else { \ 112 | min = p2; \ 113 | max = p0; \ 114 | } \ 115 | rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ 116 | if (min > rad || max < -rad) return 0; 117 | 118 | #define AXISTEST_X2(a, b, fa, fb) \ 119 | p0 = a * v0[Y] - b * v0[Z]; \ 120 | p1 = a * v1[Y] - b * v1[Z]; \ 121 | if (p0 < p1) { \ 122 | min = p0; \ 123 | max = p1; \ 124 | } else { \ 125 | min = p1; \ 126 | max = p0; \ 127 | } \ 128 | rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ 129 | if (min > rad || max < -rad) return 0; 130 | 131 | /*======================== Y-tests ========================*/ 132 | 133 | #define AXISTEST_Y02(a, b, fa, fb) \ 134 | p0 = -a * v0[X] + b * v0[Z]; \ 135 | p2 = -a * v2[X] + b * v2[Z]; \ 136 | if (p0 < p2) { \ 137 | min = p0; \ 138 | max = p2; \ 139 | } else { \ 140 | min = p2; \ 141 | max = p0; \ 142 | } \ 143 | rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ 144 | if (min > rad || max < -rad) return 0; 145 | 146 | #define AXISTEST_Y1(a, b, fa, fb) \ 147 | p0 = -a * v0[X] + b * v0[Z]; \ 148 | p1 = -a * v1[X] + b * v1[Z]; \ 149 | if (p0 < p1) { \ 150 | min = p0; \ 151 | max = p1; \ 152 | } else { \ 153 | min = p1; \ 154 | max = p0; \ 155 | } \ 156 | rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ 157 | if (min > rad || max < -rad) return 0; 158 | 159 | /*======================== Z-tests ========================*/ 160 | 161 | #define AXISTEST_Z12(a, b, fa, fb) \ 162 | p1 = a * v1[X] - b * v1[Y]; \ 163 | p2 = a * v2[X] - b * v2[Y]; \ 164 | if (p2 < p1) { \ 165 | min = p2; \ 166 | max = p1; \ 167 | } else { \ 168 | min = p1; \ 169 | max = p2; \ 170 | } \ 171 | rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ 172 | if (min > rad || max < -rad) return 0; 173 | 174 | #define AXISTEST_Z0(a, b, fa, fb) \ 175 | p0 = a * v0[X] - b * v0[Y]; \ 176 | p1 = a * v1[X] - b * v1[Y]; \ 177 | if (p0 < p1) { \ 178 | min = p0; \ 179 | max = p1; \ 180 | } else { \ 181 | min = p1; \ 182 | max = p0; \ 183 | } \ 184 | rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ 185 | if (min > rad || max < -rad) return 0; 186 | 187 | int TriBoxOverlap(float boxcenter[3], float boxhalfsize[3], 188 | float triverts[3][3]) 189 | 190 | { 191 | /* use separating axis theorem to test overlap between triangle and box */ 192 | 193 | /* need to test for overlap in these directions: */ 194 | 195 | /* 1) the {x,y,z}-directions (actually, since we use the AABB of the 196 | * triangle */ 197 | 198 | /* we do not even need to test these) */ 199 | 200 | /* 2) normal of the triangle */ 201 | 202 | /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ 203 | 204 | /* this gives 3x3=9 more tests */ 205 | 206 | float v0[3], v1[3], v2[3]; 207 | 208 | // float axis[3]; 209 | 210 | float min, max, p0, p1, p2, rad, fex, fey, 211 | fez; // -NJMP- "d" local variable removed 212 | 213 | float normal[3], e0[3], e1[3], e2[3]; 214 | 215 | /* This is the fastest branch on Sun */ 216 | 217 | /* move everything so that the boxcenter is in (0,0,0) */ 218 | 219 | SUB(v0, triverts[0], boxcenter); 220 | 221 | SUB(v1, triverts[1], boxcenter); 222 | 223 | SUB(v2, triverts[2], boxcenter); 224 | 225 | /* compute triangle edges */ 226 | 227 | SUB(e0, v1, v0); /* tri edge 0 */ 228 | 229 | SUB(e1, v2, v1); /* tri edge 1 */ 230 | 231 | SUB(e2, v0, v2); /* tri edge 2 */ 232 | 233 | /* Bullet 3: */ 234 | 235 | /* test the 9 tests first (this was faster) */ 236 | 237 | fex = fabsf(e0[X]); 238 | 239 | fey = fabsf(e0[Y]); 240 | 241 | fez = fabsf(e0[Z]); 242 | 243 | AXISTEST_X01(e0[Z], e0[Y], fez, fey); 244 | 245 | AXISTEST_Y02(e0[Z], e0[X], fez, fex); 246 | 247 | AXISTEST_Z12(e0[Y], e0[X], fey, fex); 248 | 249 | fex = fabsf(e1[X]); 250 | 251 | fey = fabsf(e1[Y]); 252 | 253 | fez = fabsf(e1[Z]); 254 | 255 | AXISTEST_X01(e1[Z], e1[Y], fez, fey); 256 | 257 | AXISTEST_Y02(e1[Z], e1[X], fez, fex); 258 | 259 | AXISTEST_Z0(e1[Y], e1[X], fey, fex); 260 | 261 | fex = fabsf(e2[X]); 262 | 263 | fey = fabsf(e2[Y]); 264 | 265 | fez = fabsf(e2[Z]); 266 | 267 | AXISTEST_X2(e2[Z], e2[Y], fez, fey); 268 | 269 | AXISTEST_Y1(e2[Z], e2[X], fez, fex); 270 | 271 | AXISTEST_Z12(e2[Y], e2[X], fey, fex); 272 | 273 | /* Bullet 1: */ 274 | 275 | /* first test overlap in the {x,y,z}-directions */ 276 | 277 | /* find min, max of the triangle each direction, and test for overlap in */ 278 | 279 | /* that direction -- this is equivalent to testing a minimal AABB around */ 280 | 281 | /* the triangle against the AABB */ 282 | 283 | /* test in X-direction */ 284 | 285 | FINDMINMAX(v0[X], v1[X], v2[X], min, max); 286 | 287 | if (min > boxhalfsize[X] || max < -boxhalfsize[X]) return 0; 288 | 289 | /* test in Y-direction */ 290 | 291 | FINDMINMAX(v0[Y], v1[Y], v2[Y], min, max); 292 | 293 | if (min > boxhalfsize[Y] || max < -boxhalfsize[Y]) return 0; 294 | 295 | /* test in Z-direction */ 296 | 297 | FINDMINMAX(v0[Z], v1[Z], v2[Z], min, max); 298 | 299 | if (min > boxhalfsize[Z] || max < -boxhalfsize[Z]) return 0; 300 | 301 | /* Bullet 2: */ 302 | 303 | /* test if the box intersects the plane of the triangle */ 304 | 305 | /* compute plane equation of triangle: normal*x+d=0 */ 306 | 307 | CROSS(normal, e0, e1); 308 | 309 | // -NJMP- (line removed here) 310 | 311 | if (!planeBoxOverlap(normal, v0, boxhalfsize)) return 0; // -NJMP- 312 | 313 | return 1; /* box and triangle overlaps */ 314 | } 315 | 316 | } // namespace voxelizer 317 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Overview 3 | 4 | 5 | ---------- 6 | 7 | 8 | This project voxelizes the meshes in STL file ***without*** the condition of *watertight*. It supports many formats only now, since we are using `assimp` library to load the file. Basically, the project can be summarized into two steps: 9 | 10 | - Surface voxelization 11 | For each piece of mesh (triangle) , we check the collided voxels in either way: 12 | 1. Get the minimal bounding box of each triangle, check each voxel in this box with the triangle; 13 | 2. Start at any voxel collided with the triangle, and do bfs search to check neighboring voxels. 14 | 15 | The first way is lightweight, but may become worse when the ratio of (triangle's volume/bounding box's volume) is small. While the second way has quite large constant overhead. For each thread in thread pool, it will pick a triangle to voxelize. The time complexity is O(m*c), where m is the triangle number and c is some factor such as the voxel number in the bounding box or constant overhead of bfs. 16 | - Solid voxelization 17 | When equipped with surface voxelization, the solid voxelization can be simple: flood fill. We try to flood fill the outer space of the meshes like carving the wood, since it is more simple and doesn't requires *watertight* property. However, the basic flood fill with bfs is too heavy and time-consuming, optimizations are proposed here (see below). The time complexity is O(n), where n is the voxel number in the bounding box of whole mesh. 18 | 19 | ## Optimizations 20 | ---------- 21 | 22 | - Use thread pool. 23 | - Use bit compression to record and order the voxel. First, I store voxel (x, y, z) to index=x\*size_y\*size_z + y\*size_z + z, where size_{x,y,z} are the voxel grid size, and I call the compressed format as *index*. For instance, with size=4, index=1 indicates voxel (0,0,1), index=4 indicates voxel (0,1,0). With indexes, all voxels can be represented with a binary string. For instance, '010010' means the voxels with indexes of 1 and 4 are collided with the mesh, while others are not. The binary string is further compressed with 64-bit unsigned int array. 24 | - Use atomic on the int array (the conceptional binary string) to guarantee the correctness when multi threads write the results, i.e., set the '0' or '1' to the int array. This design gives better parallel performance. 25 | - Estimate the bounding box size of a triangle to guide which method is more suitable for surface voxelization. 26 | - Randomly permutate the triangles' order to reduce the possibility of lock, thus get better parallel performance. 27 | - Use filter-and-refine strategy to optimize the plain flood fill algorithm as follows: 28 | - Filter 29 | We fill the voxels along each axis, and stop when meet the mesh. For instance, fix (x,y), enumerate z in [0, maxz], if a voxel (x,y,z) is occupied, stop. The intuition is to mark all voxels which are visible along that axis. This idea is similar to flood fill but is much more efficient benefit from the lightweight operations. 30 | - Refine 31 | And it's worthy noting, after filtering, there are some *holes*, because they are not visible along any axis. But these are very few holes, so we can use flood fill again on them efficiently. 32 | 33 | Although the time complexity is still O(n), it runs much faster than basic flood fill search. 34 | - Compress the output file, for your convenience, I choose the simple version of output for default. But optimized version is prepared. Recall that the voxels are stored in binary string, such as '1110000'. To reduce the output file, please be noted only voxels in the minimal bounding box for mesh are output. It is further compressed as $value$ and $count$. For '1110000', it is compressed to (1)(2)(0)(3), (1)(2) means 3 consecutive 1, and (0)(3) means 4 consecutive 0. I use two bytes to record $value$ and $count$. Specifically, $value$ can be 0 or 1, and $count$ can be [0,255] which corresponds to [1,256]. To retrieve the coordination of a voxel in original space, let $(x,y,z)$ denote the voxel coordinate extracted from the output binary string, and the coordinate in voxelized space is $(x+x_{vox\_lb},y+y_{vox\_lb},z+z_{vox\_lb})$. 35 | - [TODO] Combine the coarse and fine strategy, i.e., do coarse grid size first to filter more unnecessary voxels. 36 | - [TODO] Use gpu 37 | - [TODO] Try to fill the inside voxels, such that the time complexity is proportional to size of the result voxels. 38 | 39 | ## Installation 40 | 41 | 42 | ---------- 43 | 44 | 45 | This project requires libraries (dependencies) as follows: 46 | - *absl* (https://github.com/abseil/abseil-cpp), which is installed as git submodule 47 | - *boost* 48 | - *libccd* 49 | - *libfcl* 50 | for collision checking (https://github.com/flexible-collision-library/fcl, version: tags/0.3.3) 51 | - *assimp* 52 | for loading STL file (https://github.com/assimp/assimp) 53 | - *cmake & make* 54 | make sure `pkg-config` is installed. 55 | 56 | 57 | CMakeLists.txt is used to generate makefiles. To build this project, in command line, run 58 | 59 | ``` cmake 60 | mkdir build 61 | cd build 62 | cmake .. 63 | ``` 64 | 65 | Next, in linux, use `make` in 'build' directory to compile the code. 66 | 67 | ## How to Use 68 | 69 | 70 | ---------- 71 | 72 | `voxelizer --help` will give all the parameters. 73 | 74 | ```Allowed options: 75 | --clipping_size (clipping size (x,y,z) to clip the voxelized result. The 76 | clipping size is grid size based. If only one integter is specified, 77 | clipping size is initialized as (x,x,x). If not set, the result is not 78 | clipped.); default: ; 79 | --format (output format, can be binvox or rawvox); default: "binvox"; 80 | --grid_size (grid size, the granularity of voxelizer. if only one integer is 81 | set, assuming the X,Y,Z are the same.); default: ; 82 | --input (input file to be voxelized, file type will be inferred from file 83 | suffix); default: ""; 84 | --mesh_index (mesh index to be voxelized); default: 0; 85 | --mode (voxelizer mode, surface or solid); default: "solid"; 86 | --num_thread (number of thread to run voxelizer); default: 4; 87 | --output (output file to store voxelized result); default: ""; 88 | --tight (If true, check whether the center point of each voxel is inside the 89 | mesh or outside it, and if it is outside, remove it. Caveat: this option 90 | could be time consuming.); default: false; 91 | --verbose (print debug info); default: false; 92 | --voxel_size (voxel size, which determines the size of each voxel); 93 | default: ; 94 | --with_meta (write voxel meta info to .meta file if set to true); 95 | default: false; 96 | ``` 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | When you are in 'build' directory, one running example is: 113 | 114 | ``` ./bin/voxelizer --input=../data/sphere.obj --output=../playground/sphere.binvox --grid_size=256 --verbose=true``` 115 | It outputs a `binvox` file with grid size `256 x 256 x 256`. 116 | 117 | If you specify `--voxel_size=0.1`, instead, it will output a file with each voxel size is `0.1 x 0.1 x 0.1`. Note that the dimensions can be different. 118 | 119 | To view the `sphere.binbox` file, you may use the viewvox here: https://www.patrickmin.com/viewvox/ or https://raahii.github.io/simple_voxel_viewer/index.html. 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 150 | 151 | 152 | 153 | ## Directories 154 | 155 | 156 | ---------- 157 | 158 | 159 | This project has folders and files as follows: 160 | 161 | - *src* 162 | the source files 163 | - *test* 164 | the test files 165 | - *data* 166 | testing data (.stl files) 167 | - *CMakeLists.txt* 168 | for cmake 169 | 170 | ## Testing 171 | 172 | 173 | ---------- 174 | 175 | For simple testing, in `build` directory, run ```./test/collision_checker```. 176 | It is a benchmark of voxel collision checking and mesh collision checking (using the libfcl), with three testing files (kawada-hironx.stl, racecar.stl, and bike.stl). The grid size varies from 128 to 512, and the random cube (with random translate and random rotation) has size of 0.005^3 to 0.08^3 of the total scene size. The accuracy is defined based on the ground truth of mesh collision checking. As you can see, when the grid size becomes larger, the accuracy will increase. The collision checking performance is not specially optimized yet. 177 | 178 | #### Testing Plaform 179 | MacOS 10.9.3 180 | ubuntu 14.04 -------------------------------------------------------------------------------- /src/voxelizer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Voxelizer.cpp 3 | * 4 | * Created on: 22 Jun, 2014 5 | * Author: chenqian 6 | */ 7 | 8 | #include "absl/strings/str_format.h" 9 | #include "absl/strings/str_split.h" 10 | #include "absl/strings/string_view.h" 11 | #include "voxelizer.h" 12 | #include "ray_caster.h" 13 | 14 | namespace voxelizer { 15 | 16 | void Voxelizer::InitVoxelMeta() { 17 | voxel_metas_.reset(new VoxelMeta[size_x_*size_y_*size_z_], ArrayDeleter()); 18 | VoxelIndex tmp; 19 | for (VoxelIndex voxel_index = 0; voxel_index < TotalVoxelSize(); ++voxel_index) { 20 | (voxel_metas_.get())[voxel_index].Reset(); 21 | (voxel_metas_.get())[voxel_index].SetIndex(voxel_index); 22 | 23 | Vec3f voxel = ConvIndexToVoxel(voxel_index); 24 | if (!InRange(voxel, *output_lb_, *output_ub_)) { 25 | (voxel_metas_.get())[voxel_index].SetClipped(); 26 | } 27 | } 28 | } 29 | 30 | absl::Status Voxelizer::Init() { 31 | if (option_.Verbose()) std::cout << "voxelizer init... " << std::endl; 32 | 33 | if (!(grid_size_.empty() ^ voxel_size_.empty())) { 34 | return absl::InvalidArgumentError("only one of grid_size and voxel_size can be specifed."); 35 | } 36 | 37 | is_init_ = false; 38 | const aiScene* scene; 39 | try { 40 | /* 41 | * Load scene 42 | * */ 43 | Assimp::Importer importer; 44 | scene = importer.ReadFile(option_.InFilePath(), aiProcessPreset_TargetRealtime_Quality | 45 | aiProcess_OptimizeGraph | 46 | aiProcess_OptimizeMeshes); 47 | if (!scene) { 48 | return absl::AbortedError("Scene fails to be loaded!"); 49 | } 50 | if (option_.Verbose()) std::cout << "mesh number: " << scene->mNumMeshes << std::endl; 51 | if (scene->mNumMeshes == 0) { 52 | return absl::AbortedError("0 mesh in the scene!"); 53 | } 54 | if (scene->mNumMeshes <= option_.MeshIndex()) { 55 | return absl::AbortedError(absl::StrFormat("Required mesh index %d out of range: %d", option_.MeshIndex(), scene->mNumMeshes)); 56 | } 57 | 58 | // TODO(topskychen@gmail.com): consider all the meshes when mesh_index_ is 59 | // -1. 60 | aiMesh* mesh = scene->mMeshes[option_.MeshIndex()]; 61 | 62 | /** 63 | * Store info. 64 | */ 65 | num_vertices_ = mesh->mNumVertices; 66 | num_faces_ = mesh->mNumFaces; 67 | if (option_.Verbose()) std::cout << "faces : " << num_faces_ << std::endl; 68 | if (option_.Verbose()) std::cout << "vertices : " << num_vertices_ << std::endl; 69 | 70 | /** 71 | * Load meshes. 72 | * TODO(topskychen@gmail.com): refactor this function. 73 | */ 74 | auto status = LoadFromMesh(mesh); 75 | if (!status.ok()) return status; 76 | 77 | if (option_.WithMeta()) { 78 | InitVoxelMeta(); 79 | } 80 | 81 | is_init_ = true; 82 | } catch (std::exception& e) { 83 | return absl::AbortedError(e.what()); 84 | } 85 | if (option_.Verbose()) std::cout << "Init done." << std::endl; 86 | 87 | return absl::OkStatus(); 88 | } 89 | 90 | /** 91 | * Given voxel (int x, int y, int z), return loc (float x, float y, float z) 92 | */ 93 | Vec3f Voxelizer::GetLoc(const Vec3f& voxel) { 94 | return *lb_ + (*scale_) * (voxel); 95 | } 96 | 97 | /** 98 | * Given voxel (int x, int y, int z), return loc (float x, float y, float z) + half_unit 99 | */ 100 | Vec3f Voxelizer::GetCenterLoc(const Vec3f& voxel) { 101 | return GetLoc(voxel) + *half_unit_; 102 | } 103 | 104 | /** 105 | * Given loc (float x, float y, float z), return voxel (int x, int y, int z) 106 | */ 107 | Vec3f Voxelizer::GetVoxel(const Vec3f& loc) { 108 | Vec3f tmp = (loc - (*lb_)) * (*size_) / (*bound_); 109 | return Vec3f(static_cast(tmp[0]), static_cast(tmp[1]), static_cast(tmp[2])); 110 | } 111 | 112 | /** 113 | *Get the collision object form triangle(face) id; 114 | */ 115 | TriangleP Voxelizer::GetTri(const int tri_id) { 116 | const Vec3f& v_ids = faces_.get()[tri_id]; 117 | return TriangleP(vertices_.get()[(int)v_ids[0]], 118 | vertices_.get()[(int)v_ids[1]], 119 | vertices_.get()[(int)v_ids[2]]); 120 | } 121 | 122 | /** 123 | * Load info from mesh. 124 | */ 125 | absl::Status Voxelizer::LoadFromMesh(const aiMesh* mesh) { 126 | // load vertices and update mesh bounds 127 | vertices_.reset(new Vec3f[num_vertices_], ArrayDeleter()); 128 | Vec3f tmp; 129 | for (size_t i = 0; i < num_vertices_; ++i) { 130 | vertices_.get()[i] = 131 | Vec3f(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z); 132 | if (i == 0) { 133 | mesh_lb_.reset(new Vec3f(mesh->mVertices[i].x, mesh->mVertices[i].y, 134 | mesh->mVertices[i].z)); 135 | mesh_ub_.reset(new Vec3f(mesh->mVertices[i].x, mesh->mVertices[i].y, 136 | mesh->mVertices[i].z)); 137 | } else { 138 | mesh_lb_->ubound(vertices_.get()[i]); 139 | mesh_ub_->lbound(vertices_.get()[i]); 140 | } 141 | } 142 | 143 | // Add an epsilon box to bound box the whole mesh spce. 144 | mesh_lb_.reset(new Vec3f((*mesh_lb_) - kEpsBox)); 145 | mesh_ub_.reset(new Vec3f((*mesh_ub_) + kEpsBox)); 146 | 147 | /** 148 | * Calculate a bounding box according to the mesh bounds. 149 | */ 150 | min_lb_ = (*mesh_lb_)[0]; 151 | min_lb_ = std::min(min_lb_, (float)(*mesh_lb_)[1]); 152 | min_lb_ = std::min(min_lb_, (float)(*mesh_lb_)[2]); 153 | max_ub_ = (*mesh_ub_)[0]; 154 | max_ub_ = std::max(max_ub_, (float)(*mesh_ub_)[1]); 155 | max_ub_ = std::max(max_ub_, (float)(*mesh_ub_)[2]); 156 | lb_ = absl::make_unique(min_lb_, min_lb_, min_lb_); 157 | ub_ = absl::make_unique(max_ub_, max_ub_, max_ub_); 158 | bound_ = absl::make_unique(*ub_ - *lb_); 159 | 160 | /** 161 | * Store face info. 162 | */ 163 | faces_.reset(new Vec3f[num_faces_], ArrayDeleter()); 164 | if (mesh->mPrimitiveTypes != kPrimitiveTriangleType) { 165 | return absl::InvalidArgumentError(absl::StrFormat("mesh face primitive type expects: %d indices but received: %d", kPrimitiveTriangleType, mesh->mPrimitiveTypes)); 166 | } 167 | for (size_t i = 0; i < num_faces_; ++i) { 168 | if (mesh->mFaces[i].mNumIndices != kTriangleNumIndices) { 169 | return absl::InvalidArgumentError(absl::StrFormat("triangle face expects: %d indices but received %d", kTriangleNumIndices, mesh->mFaces[i].mNumIndices)); 170 | } 171 | faces_.get()[i] = 172 | Vec3f(mesh->mFaces[i].mIndices[0], mesh->mFaces[i].mIndices[1], 173 | mesh->mFaces[i].mIndices[2]); 174 | } 175 | RandomPermutation(faces_, num_faces_); 176 | 177 | /** 178 | * Calculate the grid size and voxel size. 179 | */ 180 | if (grid_size_.size() > 0) { 181 | if (grid_size_.size() != 3) { 182 | return absl::InvalidArgumentError(absl::StrFormat("grid_size dim error: %d", grid_size_.size())); 183 | } 184 | 185 | size_x_ = grid_size_[0]; 186 | size_y_ = grid_size_[1]; 187 | size_z_ = grid_size_[2]; 188 | 189 | size_ = absl::make_unique(size_x_, size_y_, size_z_); 190 | 191 | unit_ = absl::make_unique((*bound_)/(*size_)); 192 | half_unit_ = absl::make_unique((*unit_)/2); 193 | } else { 194 | if (voxel_size_.size() != 3) { 195 | return absl::InvalidArgumentError(absl::StrFormat("voxel_size dim error: %d", voxel_size_.size())); 196 | } 197 | 198 | float voxel_x = voxel_size_[0]; 199 | float voxel_y = voxel_size_[1]; 200 | float voxel_z = voxel_size_[2]; 201 | 202 | unit_ = absl::make_unique(voxel_x, voxel_y, voxel_z); 203 | half_unit_ = absl::make_unique((*unit_)/2); 204 | 205 | Vec3f size = (*bound_)/(*unit_); 206 | 207 | size_x_ = static_cast(size[0]); 208 | size_y_ = static_cast(size[1]); 209 | size_z_ = static_cast(size[2]); 210 | 211 | size_ = absl::make_unique(size_x_, size_y_, size_z_); 212 | } 213 | 214 | if (option_.Verbose()) { 215 | std::cout << absl::StrFormat("voxel size: %f, %f, %f", (*unit_)[0], (*unit_)[1], (*unit_)[2]) << std::endl; 216 | std::cout << absl::StrFormat("grid size: %d, %d, %d", size_x_, size_y_, size_z_) << std::endl; 217 | } 218 | 219 | // The real voxel bounding box should extract the epsilon box. 220 | mesh_vox_lb_ = absl::make_unique(GetVoxel(*mesh_lb_ + kEpsBox)); 221 | mesh_vox_ub_ = absl::make_unique(GetVoxel(*mesh_ub_ - kEpsBox)); 222 | 223 | if (option_.Verbose()) { 224 | std::cout << "space: " << *lb_ << ", " << *ub_ << std::endl; 225 | std::cout << "mesh bound: " << *mesh_lb_ << ", " << *mesh_ub_ << std::endl; 226 | std::cout << "voxel bound: " << *mesh_vox_lb_ << ", " << *mesh_vox_ub_ << std::endl; 227 | } 228 | 229 | size_xy_ = size_x_*size_y_; 230 | size_xz_ = size_x_*size_z_; 231 | size_yz_ = size_y_*size_z_; 232 | scale_ = absl::make_unique((*bound_) / (*size_)); 233 | compressed_total_size_ = size_x_*size_y_*size_z_/kBatchSize; 234 | 235 | InitOutputBound(); 236 | 237 | /** 238 | * Reset voxels. 239 | */ 240 | voxels_.reset(new AVI[compressed_total_size_], ArrayDeleter()); 241 | voxels_buffer_.reset(new AVI[compressed_total_size_], ArrayDeleter()); 242 | memset(voxels_.get(), 0, compressed_total_size_ * sizeof(VoxelIndex)); 243 | memset(voxels_buffer_.get(), 0, compressed_total_size_ * sizeof(VoxelIndex)); 244 | 245 | return absl::OkStatus(); 246 | } 247 | 248 | void Voxelizer::UpdateSurfaceMeta() { 249 | VoxelIndex tmp; 250 | int surface_cnt = 0; 251 | for (VoxelIndex voxel_index = 0; voxel_index < TotalVoxelSize(); ++voxel_index) { 252 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 253 | if (GETBIT(tmp, voxel_index)) { 254 | surface_cnt ++; 255 | (voxel_metas_.get())[voxel_index].SetSurface(); 256 | } 257 | } 258 | if (option_.Verbose()) { 259 | std::cout << "update surface meta done, surface count: " << surface_cnt << std::endl; 260 | } 261 | } 262 | 263 | // Note this function is called after surface voxelization. 264 | void Voxelizer::UpdateTight() { 265 | RayCaster ray_caster(VerticesVec(), TrianglesVec()); 266 | if (!ray_caster.Init()) { 267 | std::cerr << "ray_caster failed to init." << std::endl; 268 | } else { 269 | if (option_.Verbose()) { 270 | std::cout << "ray_caster succeeded to init." << std::endl; 271 | } 272 | } 273 | int inside_cnt = 0; 274 | VoxelIndex tmp; 275 | for (VoxelIndex voxel_index = 0; voxel_index < TotalVoxelSize(); ++voxel_index) { 276 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 277 | if (GETBIT(tmp, voxel_index)) { 278 | const Vec3f voxel = ConvIndexToVoxel(voxel_index); 279 | if (ray_caster.Inside(GetCenterLoc(voxel))) { 280 | inside_cnt ++; 281 | (voxel_metas_.get())[voxel_index].SetInside(); 282 | } else { 283 | (voxel_metas_.get())[voxel_index].UnsetInside(); 284 | } 285 | } 286 | } 287 | if (option_.Verbose()) { 288 | std::cout << "update tight done, inside count: " << inside_cnt << std::endl; 289 | } 290 | } 291 | 292 | /** 293 | * voxelize the surface. 294 | */ 295 | void Voxelizer::VoxelizeSurface(const int num_thread) { 296 | if (!is_init_) { 297 | return; 298 | } 299 | if (option_.Verbose()) std::cout << "surface voxelizing... " << std::endl; 300 | ThreadPool tp(num_thread); 301 | for (int i = 0; i < num_faces_; ++i) { 302 | tp.Run(boost::bind(&Voxelizer::RunSurfaceTask, this, i)); 303 | } 304 | tp.Stop(); 305 | if (option_.WithMeta()) { 306 | UpdateSurfaceMeta(); 307 | if (option_.Tight()) { 308 | UpdateTight(); 309 | } 310 | } 311 | if (option_.Verbose()) std::cout << "done." << std::endl; 312 | } 313 | 314 | /** 315 | * Details of surface task. 316 | */ 317 | inline void Voxelizer::RunSurfaceTask(const int tri_id) { 318 | TriangleP tri = GetTri(tri_id); 319 | tri.computeLocalAABB(); 320 | const Vec3f lb = GetVoxel(tri.aabb_local.min_); 321 | const Vec3f ub = GetVoxel(tri.aabb_local.max_); 322 | 323 | const int lx = lb[0], ux = ub[0], ly = lb[1], uy = ub[1], lz = lb[2], 324 | uz = ub[2]; 325 | /** 326 | * when the estimated voxels are too large, optimize with bfs. 327 | */ 328 | VoxelIndex count = 0; 329 | int esti = std::min(ux - lx, std::min(uy - ly, uz - lz)); 330 | if (esti < kThresholdBfsSurface) { 331 | VoxelIndex voxel_index, tmp; 332 | Vec3f vxlBox(0, 0, 0); 333 | for (int x = lx, y, z; x <= ux; ++x) { 334 | for (y = ly; y <= uy; ++y) { 335 | for (z = lz; z <= uz; ++z) { 336 | voxel_index = INDEX(x, y, z); 337 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 338 | if (GETBIT(tmp, voxel_index)) continue; 339 | vxlBox.setValue(x, y, z); 340 | if (Collide(*half_unit_, GetLoc(vxlBox), tri)) { 341 | SETBIT(voxels_, voxel_index); 342 | count++; 343 | } 344 | } 345 | } 346 | } 347 | } else { 348 | count = BfsSurface(tri, lb, ub); 349 | } 350 | } 351 | 352 | inline VoxelIndex Voxelizer::BfsSurface(const TriangleP& tri, const Vec3f& lb, 353 | const Vec3f& ub) { 354 | std::queue q; 355 | HashSet set; 356 | VoxelIndex start = ConvVoxelToIndex(GetVoxel(tri.a)), top_voxel_index, tmp, 357 | new_voxel_index; 358 | q.push(start); 359 | set.insert(start); 360 | Vec3f top_voxel; 361 | VoxelIndex count = 0; 362 | while (!q.empty()) { 363 | count++; 364 | top_voxel_index = q.front(); 365 | q.pop(); 366 | tmp = (voxels_.get())[top_voxel_index / kBatchSize].load(); 367 | ConvIndexToVoxel(top_voxel_index, top_voxel); 368 | if (GETBIT(tmp, top_voxel_index) || 369 | Collide(*half_unit_, GetLoc(top_voxel), tri)) { 370 | if (!GETBIT(tmp, top_voxel_index)) { 371 | SETBIT(voxels_, top_voxel_index); 372 | } 373 | for (int i = 0; i < 6; ++i) { 374 | Vec3f newVoxel = top_voxel + D_6[i]; 375 | if (!InRange(newVoxel, lb, ub)) continue; 376 | new_voxel_index = ConvVoxelToIndex(newVoxel); 377 | if (set.find(new_voxel_index) == set.end()) { 378 | set.insert(new_voxel_index); 379 | q.push(new_voxel_index); 380 | } 381 | } 382 | } 383 | } 384 | return count; 385 | } 386 | 387 | void Voxelizer::VoxelizeSolid(int num_thread) { 388 | if (!is_init_) { 389 | return; 390 | } 391 | if (option_.Verbose()) std::cout << "solid voxelizing... " << std::endl; 392 | if (option_.Verbose()) std::cout << "round 1..." << std::endl; 393 | RunSolidTask(num_thread); 394 | if (option_.Verbose()) std::cout << "round 2..." << std::endl; 395 | RunSolidTask2(num_thread); 396 | for (VoxelIndex i = 0; i < compressed_total_size_; ++i) 397 | voxels_.get()[i] = voxels_buffer_.get()[i] ^ (~static_cast(0)); 398 | if (option_.WithMeta()) { 399 | UpdateSolidMeta(); 400 | } 401 | if (option_.Verbose()) std::cout << "done." << std::endl; 402 | } 403 | 404 | void Voxelizer::UpdateSolidMeta() { 405 | int solid_cnt = 0; 406 | VoxelIndex tmp; 407 | for (VoxelIndex voxel_index = 0; voxel_index < TotalVoxelSize(); ++voxel_index) { 408 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 409 | if (GETBIT(tmp, voxel_index) && !(voxel_metas_.get())[voxel_index].Surface()) { 410 | solid_cnt ++; 411 | (voxel_metas_.get())[voxel_index].SetSolid(); 412 | } 413 | } 414 | if (option_.Verbose()) { 415 | std::cout << "update solid meta done, solid count: " << solid_cnt << std::endl; 416 | } 417 | } 418 | 419 | inline void Voxelizer::BfsSolid(const VoxelIndex start_index) { 420 | VoxelIndex voxel_index = start_index, 421 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 422 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 423 | if (GETBIT(tmp, voxel_index)) return; 424 | std::queue q; 425 | q.push(voxel_index); 426 | Vec3f top_voxel; 427 | while (!q.empty()) { 428 | voxel_index = q.front(); 429 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 430 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 431 | q.pop(); 432 | ConvIndexToVoxel(voxel_index, top_voxel); 433 | if (!GETBIT(tmp, voxel_index)) { 434 | SETBIT(voxels_buffer_, voxel_index); 435 | for (int i = 0; i < 6; i++) { 436 | Vec3f newVoxel = top_voxel + D_6[i]; 437 | if (!InRange(newVoxel, *mesh_vox_lb_, *mesh_vox_ub_)) continue; 438 | voxel_index = ConvVoxelToIndex(newVoxel); 439 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 440 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 441 | if (!GETBIT(tmp, voxel_index)) q.push(voxel_index); 442 | } 443 | } 444 | } 445 | } 446 | 447 | inline bool Voxelizer::InRange(const Vec3f& vc, const Vec3f& lb, 448 | const Vec3f& ub) { 449 | return vc[0] >= lb[0] && vc[0] <= ub[0] && vc[1] >= lb[1] && 450 | vc[1] <= ub[1] && vc[2] >= lb[2] && vc[2] <= ub[2]; 451 | } 452 | 453 | inline bool Voxelizer::InRange(const int x, const int y, const int z, 454 | const int lx, const int ly, const int lz, 455 | const int ux, const int uy, const int uz) { 456 | return x >= lx && x <= ux && y >= ly && y <= uy && z >= lz && z <= uz; 457 | } 458 | 459 | inline bool Voxelizer::InRange(const int x, const int y, const int z, 460 | const Vec3f& lb, const Vec3f& ub) { 461 | const int lx = lb[0], ly = lb[1], lz = lb[2], ux = ub[0], uy = ub[1], uz = ub[2]; 462 | return InRange(x, y, z, lx, ly, lz, ux, uy, uz); 463 | } 464 | 465 | void Voxelizer::ConvIndexToVoxel(const VoxelIndex coord, int& x, int& y, int& z) { 466 | x = coord / size_yz_; 467 | y = (coord / size_z_) % size_y_; 468 | z = coord % size_z_; 469 | } 470 | 471 | inline Vec3f Voxelizer::ConvIndexToVoxel(const VoxelIndex coord) { 472 | return Vec3f(coord / size_yz_, (coord / size_z_) % size_y_, coord % size_z_); 473 | } 474 | 475 | void Voxelizer::ConvIndexToVoxel(const VoxelIndex coord, Vec3f& voxel) { 476 | voxel.setValue(coord / size_yz_, (coord / size_z_) % size_y_, coord % size_z_); 477 | } 478 | 479 | inline VoxelIndex Voxelizer::ConvVoxelToIndex(const Vec3f& voxel) { 480 | return INDEX(voxel[0], voxel[1], voxel[2]); 481 | } 482 | 483 | inline void Voxelizer::RandomPermutation(const V3SP& data, int num) { 484 | for (int i = 0, id; i < num; ++i) { 485 | id = Random(i, num - 1); 486 | if (i != id) std::swap((data.get())[i], (data.get())[id]); 487 | } 488 | } 489 | 490 | inline void Voxelizer::FillYZ(const int x) { 491 | const int ly = (*mesh_vox_lb_)[1], uy = (*mesh_vox_ub_)[1], 492 | lz = (*mesh_vox_lb_)[2], uz = (*mesh_vox_ub_)[2]; 493 | VoxelIndex voxel_index, tmp; 494 | for (int y = ly, z; y <= uy; ++y) { 495 | for (z = lz; z <= uz; ++z) { 496 | voxel_index = INDEX(x, y, z); 497 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 498 | if (GETBIT(tmp, voxel_index)) { 499 | break; 500 | } else { 501 | SETBIT(voxels_buffer_, voxel_index); 502 | } 503 | } 504 | if (z == uz + 1) continue; 505 | for (z = uz; z >= lz; --z) { 506 | voxel_index = INDEX(x, y, z); 507 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 508 | if (GETBIT(tmp, voxel_index)) { 509 | break; 510 | } else { 511 | SETBIT(voxels_buffer_, voxel_index); 512 | } 513 | } 514 | } 515 | } 516 | 517 | inline void Voxelizer::FillYZ2(const int x) { 518 | const int lx = (*mesh_vox_lb_)[0], ux = (*mesh_vox_ub_)[0], 519 | ly = (*mesh_vox_lb_)[1], uy = (*mesh_vox_ub_)[1], 520 | lz = (*mesh_vox_lb_)[2], uz = (*mesh_vox_ub_)[2]; 521 | int nx, ny; 522 | VoxelIndex voxel_index, tmp; 523 | for (int y = ly, z; y <= uy; ++y) { 524 | for (z = lz; z <= uz; ++z) { 525 | voxel_index = INDEX(x, y, z); 526 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 527 | if (GETBIT(tmp, voxel_index)) { 528 | break; 529 | } else { 530 | for (int i = 0; i < 4; ++i) { 531 | nx = x + DI_4[i][0]; 532 | ny = y + DI_4[i][1]; 533 | if (nx >= lx && nx <= ux && ny >= ly && ny <= uy) { 534 | voxel_index = INDEX(nx, ny, z); 535 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 536 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 537 | if (!GETBIT(tmp, voxel_index)) BfsSolid(voxel_index); 538 | } 539 | } 540 | } 541 | } 542 | if (z == uz + 1) continue; 543 | for (z = uz; z >= lz; --z) { 544 | voxel_index = INDEX(x, y, z); 545 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 546 | if (GETBIT(tmp, voxel_index)) { 547 | break; 548 | } else { 549 | for (int i = 0; i < 4; ++i) { 550 | nx = x + DI_4[i][0]; 551 | ny = y + DI_4[i][1]; 552 | if (nx >= lx && nx <= ux && ny >= ly && ny <= uy) { 553 | voxel_index = INDEX(nx, ny, z); 554 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 555 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 556 | if (!GETBIT(tmp, voxel_index)) BfsSolid(voxel_index); 557 | } 558 | } 559 | } 560 | } 561 | } 562 | } 563 | 564 | inline void Voxelizer::FillXZ(const int y) { 565 | const int lx = (*mesh_vox_lb_)[0], ux = (*mesh_vox_ub_)[0], 566 | lz = (*mesh_vox_lb_)[2], uz = (*mesh_vox_ub_)[2]; 567 | VoxelIndex voxel_index, tmp; 568 | for (int z = lz, x; z <= uz; ++z) { 569 | for (x = lx; x <= ux; ++x) { 570 | voxel_index = INDEX(x, y, z); 571 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 572 | if (GETBIT(tmp, voxel_index)) { 573 | break; 574 | } else { 575 | SETBIT(voxels_buffer_, voxel_index); 576 | } 577 | } 578 | if (x == ux + 1) continue; 579 | for (x = ux; x >= lx; --x) { 580 | voxel_index = INDEX(x, y, z); 581 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 582 | if (GETBIT(tmp, voxel_index)) { 583 | break; 584 | } else { 585 | SETBIT(voxels_buffer_, voxel_index); 586 | } 587 | } 588 | } 589 | } 590 | 591 | inline void Voxelizer::FillXZ2(const int y) { 592 | const int lx = (*mesh_vox_lb_)[0], ux = (*mesh_vox_ub_)[0], 593 | ly = (*mesh_vox_lb_)[1], uy = (*mesh_vox_ub_)[1], 594 | lz = (*mesh_vox_lb_)[2], uz = (*mesh_vox_ub_)[2]; 595 | int ny, nz; 596 | VoxelIndex voxel_index, tmp; 597 | for (int z = lz, x; z <= uz; ++z) { 598 | for (x = lx; x <= ux; ++x) { 599 | voxel_index = INDEX(x, y, z); 600 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 601 | if (GETBIT(tmp, voxel_index)) { 602 | break; 603 | } else { 604 | for (int i = 0; i < 4; ++i) { 605 | ny = y + DI_4[i][0]; 606 | nz = z + DI_4[i][1]; 607 | if (nz >= lz && nz <= uz && ny >= ly && ny <= uy) { 608 | voxel_index = INDEX(x, ny, nz); 609 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 610 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 611 | if (!GETBIT(tmp, voxel_index)) BfsSolid(voxel_index); 612 | } 613 | } 614 | } 615 | } 616 | if (x == ux + 1) continue; 617 | for (x = ux; x >= lx; --x) { 618 | voxel_index = INDEX(x, y, z); 619 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 620 | if (GETBIT(tmp, voxel_index)) { 621 | break; 622 | } else { 623 | for (int i = 0; i < 4; ++i) { 624 | ny = y + DI_4[i][0]; 625 | nz = z + DI_4[i][1]; 626 | if (nz >= lz && nz <= uz && ny >= ly && ny <= uy) { 627 | voxel_index = INDEX(x, ny, nz); 628 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 629 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 630 | if (!GETBIT(tmp, voxel_index)) BfsSolid(voxel_index); 631 | } 632 | } 633 | } 634 | } 635 | } 636 | } 637 | 638 | inline void Voxelizer::FillXY(const int z) { 639 | const int ly = (*mesh_vox_lb_)[1], uy = (*mesh_vox_ub_)[1], 640 | lx = (*mesh_vox_lb_)[0], ux = (*mesh_vox_ub_)[0]; 641 | VoxelIndex voxel_index, tmp; 642 | for (int x = lx, y; x <= ux; ++x) { 643 | for (y = ly; y <= uy; ++y) { 644 | voxel_index = INDEX(x, y, z); 645 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 646 | if (GETBIT(tmp, voxel_index)) { 647 | break; 648 | } else { 649 | SETBIT(voxels_buffer_, voxel_index); 650 | } 651 | } 652 | if (y == uy + 1) continue; 653 | for (y = uy; y >= ly; --y) { 654 | voxel_index = INDEX(x, y, z); 655 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 656 | if (GETBIT(tmp, voxel_index)) { 657 | break; 658 | } else { 659 | SETBIT(voxels_buffer_, voxel_index); 660 | } 661 | } 662 | } 663 | } 664 | 665 | inline void Voxelizer::FillXY2(const int z) { 666 | const int lx = (*mesh_vox_lb_)[0], ux = (*mesh_vox_ub_)[0], 667 | ly = (*mesh_vox_lb_)[1], uy = (*mesh_vox_ub_)[1], 668 | lz = (*mesh_vox_lb_)[2], uz = (*mesh_vox_ub_)[2]; 669 | int nx, nz; 670 | VoxelIndex voxel_index, tmp; 671 | for (int x = lx, y; x <= ux; ++x) { 672 | for (y = ly; y <= uy; ++y) { 673 | voxel_index = INDEX(x, y, z); 674 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 675 | if (GETBIT(tmp, voxel_index)) { 676 | break; 677 | } else { 678 | for (int i = 0; i < 4; ++i) { 679 | nx = x + DI_4[i][0]; 680 | nz = z + DI_4[i][1]; 681 | if (nz >= lz && nz <= uz && nx >= lx && nx <= ux) { 682 | voxel_index = INDEX(nx, y, nz); 683 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 684 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 685 | if (!GETBIT(tmp, voxel_index)) BfsSolid(voxel_index); 686 | } 687 | } 688 | } 689 | } 690 | if (y == uy + 1) continue; 691 | for (y = uy; y >= ly; --y) { 692 | voxel_index = INDEX(x, y, z); 693 | tmp = (voxels_.get())[voxel_index / kBatchSize].load(); 694 | if (GETBIT(tmp, voxel_index)) { 695 | break; 696 | } else { 697 | for (int i = 0; i < 4; ++i) { 698 | nx = x + DI_4[i][0]; 699 | nz = z + DI_4[i][1]; 700 | if (nz >= lz && nz <= uz && nx >= lx && nx <= ux) { 701 | voxel_index = INDEX(nx, y, nz); 702 | tmp = (voxels_.get())[voxel_index / kBatchSize].load() | 703 | (voxels_buffer_.get())[voxel_index / kBatchSize].load(); 704 | if (!GETBIT(tmp, voxel_index)) BfsSolid(voxel_index); 705 | } 706 | } 707 | } 708 | } 709 | } 710 | } 711 | 712 | inline void Voxelizer::RunSolidTask(size_t num_thread) { 713 | ThreadPool tp(num_thread); 714 | const int lx = (*mesh_vox_lb_)[0], ux = (*mesh_vox_ub_)[0], 715 | ly = (*mesh_vox_lb_)[1], uy = (*mesh_vox_ub_)[1], 716 | lz = (*mesh_vox_lb_)[2], uz = (*mesh_vox_ub_)[2]; 717 | for (int x = lx; x <= ux; ++x) { 718 | tp.Run(boost::bind(&Voxelizer::FillYZ, this, x)); 719 | } 720 | for (int y = ly; y <= uy; ++y) { 721 | tp.Run(boost::bind(&Voxelizer::FillXZ, this, y)); 722 | } 723 | for (int z = lz; z <= uz; ++z) { 724 | tp.Run(boost::bind(&Voxelizer::FillXY, this, z)); 725 | } 726 | tp.Stop(); 727 | } 728 | 729 | inline void Voxelizer::RunSolidTask2(size_t num_thread) { 730 | ThreadPool tp(num_thread); 731 | const int lx = (*mesh_vox_lb_)[0], ux = (*mesh_vox_ub_)[0], 732 | ly = (*mesh_vox_lb_)[1], uy = (*mesh_vox_ub_)[1], 733 | lz = (*mesh_vox_lb_)[2], uz = (*mesh_vox_ub_)[2]; 734 | for (int x = lx; x <= ux; ++x) { 735 | tp.Run(boost::bind(&Voxelizer::FillYZ2, this, x)); 736 | } 737 | for (int z = lz; z <= uz; ++z) { 738 | tp.Run(boost::bind(&Voxelizer::FillXY2, this, z)); 739 | } 740 | for (int y = ly; y <= uy; ++y) { 741 | tp.Run(boost::bind(&Voxelizer::FillXZ2, this, y)); 742 | } 743 | tp.Stop(); 744 | } 745 | 746 | void Voxelizer::InitOutputBound() { 747 | output_lb_ = absl::make_unique(*mesh_vox_lb_); 748 | output_ub_ = absl::make_unique(*mesh_vox_ub_); 749 | 750 | const std::vector vector_clipping_size = option_.ClippingSize(); 751 | if (vector_clipping_size.size() == 3) { 752 | Vec3f clipping_size(vector_clipping_size[0], vector_clipping_size[1], vector_clipping_size[2]); 753 | const Vec3f half = (clipping_size-Vec3f(1,1,1)) / 2.0; 754 | const Vec3f center = (*mesh_vox_lb_ + *mesh_vox_ub_) / 2.0; 755 | const Vec3f clip_vox_lb = center - half, clip_vox_ub = center + clipping_size - half - Vec3f(1,1,1); 756 | if (option_.Verbose()) { 757 | std::cout << "mesh_vox_lb: " << *mesh_vox_lb_ << std::endl; 758 | std::cout << "mesh_vox_ub: " << *mesh_vox_ub_ << std::endl; 759 | std::cout << "center: " << center << std::endl; 760 | std::cout << "half: " << half << std::endl; 761 | } 762 | output_lb_->lbound(clip_vox_lb); 763 | output_ub_->ubound(clip_vox_ub); 764 | } 765 | if (option_.Verbose()) { 766 | std::cout << "init output bound done." << std::endl; 767 | } 768 | } 769 | 770 | /** 771 | * Write to file with a output format 772 | */ 773 | void Voxelizer::Write() { 774 | const std::string format = option_.Format(); 775 | if (format == "binvox") { 776 | WriteBinvox(); 777 | } else if (format == "rawvox") { 778 | WriteRawvox(); 779 | } else { 780 | std::cout << "no such format: " << format << std::endl; 781 | } 782 | if (option_.WithMeta()) { 783 | WriteMeta(); 784 | } 785 | } 786 | 787 | bool Voxelizer::Filled(const VoxelIndex index) { 788 | if (option_.WithMeta()) { 789 | return (voxel_metas_.get())[index].Filled(); 790 | } 791 | int x, y, z; 792 | ConvIndexToVoxel(index, x, y, z); 793 | return InRange(x, y, z, *output_lb_, *output_ub_)? GETBIT((voxels_.get())[index / kBatchSize].load(), index): 0; 794 | } 795 | 796 | bool Voxelizer::Filled(const int x, const int y, const int z) { 797 | return Filled(INDEX(x, y, z)); 798 | } 799 | 800 | /** 801 | * Write to file, with binvox format, check https://www.patrickmin.com/viewvox/ 802 | */ 803 | void Voxelizer::WriteBinvox() { 804 | if (option_.Verbose()) std::cout << "writing voxels to file..." << std::endl; 805 | 806 | const int lx = 0, ux = size_x_ - 1, ly = 0, uy = size_y_ - 1, lz = 0, uz = size_z_ - 1; 807 | const int bx = ux - lx + 1, by = uy - ly + 1, bz = uz - lz + 1; 808 | 809 | std::ofstream* output = new std::ofstream(option_.OutFilePath().c_str(), std::ios::out | std::ios::binary); 810 | 811 | Vec3f& norm_translate = (*lb_); 812 | float norm_scale = (*bound_).norm(); 813 | 814 | // 815 | // write header 816 | // 817 | *output << "#binvox 1" << std::endl; 818 | *output << "dim " << bx << " " << by << " " << bz << std::endl; 819 | if (option_.Verbose()) std::cout << "dim : " << bx << " x " << by << " x " << bz << std::endl; 820 | *output << "translate " << -norm_translate[0] << " " << -norm_translate[2] 821 | << " " << -norm_translate[1] << std::endl; 822 | *output << "scale " << norm_scale << std::endl; 823 | *output << "data" << std::endl; 824 | 825 | Byte value; 826 | Byte count; 827 | VoxelIndex total_ones = 0; 828 | int bytes_written = 0; 829 | 830 | /** 831 | * Compression 832 | */ 833 | int x = lx, y = ly, z = lz; 834 | while (x <= ux) { 835 | value = Filled(x, y, z); 836 | count = 0; 837 | while ((x <= ux) && (count < 255) && value == Filled(x, y, z)) { 838 | z++; 839 | if (z > uz) { 840 | z = lz; 841 | y++; 842 | if (y > uy) { 843 | y = ly; 844 | x++; 845 | } 846 | } 847 | count++; 848 | } 849 | if (value) total_ones += count; 850 | *output << value << count; 851 | bytes_written += 2; 852 | } 853 | 854 | output->close(); 855 | if (option_.Verbose()) 856 | std::cout << "wrote " << total_ones << " set voxels out of " << static_cast(bx) * by * bz 857 | << ", in " << bytes_written << " bytes" << std::endl; 858 | std::cout << "bounds are: " << *output_lb_ << ", " << *output_ub_ << std::endl; 859 | } 860 | 861 | /** 862 | * Write to file with raw format. 863 | */ 864 | void Voxelizer::WriteRawvox() { 865 | if (option_.Verbose()) std::cout << "writing voxels to file..." << std::endl; 866 | int lx = 0, ux = size_x_ - 1, ly = 0, uy = size_y_ - 1, lz = 0, uz = size_z_ - 1; 867 | 868 | std::ofstream* output = new std::ofstream(option_.OutFilePath().c_str(), std::ios::out | std::ios::binary); 869 | 870 | // 871 | // write header 872 | // 873 | *output << size_x_ << " " << size_y_ << " " << size_z_ << std::endl; 874 | *output << (double)(*lb_)[0] << " " << (double)(*lb_)[1] << " " 875 | << (double)(*lb_)[2] << std::endl; 876 | *output << (double)(*unit_)[0] << (double)(*unit_)[1] << (double)(*unit_)[2] << std::endl; 877 | 878 | if (option_.Verbose()) 879 | std::cout << "dim : " << size_x_ << " x " << size_y_ << " x " << size_z_ << std::endl; 880 | if (option_.Verbose()) std::cout << "lower bound : " << (*lb_) << std::endl; 881 | if (option_.Verbose()) std::cout << "voxel size : " << (*unit_)[0] << " " << (*unit_)[1] << " " << (*unit_)[2] << std::endl; 882 | 883 | // 884 | // write data 885 | // 886 | VoxelIndex count = 0; 887 | for (int x = lx; x <= ux; ++x) { 888 | for (int y = ly; y <= uy; ++y) { 889 | for (int z = lz; z <= uz; ++z) { 890 | if (Filled(x, y, z)) { 891 | *output << x << ' ' << y << ' ' << z << '\n'; 892 | ++count; 893 | } 894 | } 895 | } 896 | } 897 | output->close(); 898 | if (option_.Verbose()) std::cout << "wrote " << count << " voxels" << std::endl; 899 | } 900 | 901 | void Voxelizer::WriteMeta() { 902 | std::string base_out_file_path = option_.OutFilePath(); 903 | // remove file extension 904 | if (absl::EndsWith(base_out_file_path, ".rawvox") || absl::EndsWith(base_out_file_path, ".binvox")) { 905 | base_out_file_path = base_out_file_path.substr(0, base_out_file_path.size() - 7); 906 | } 907 | std::string meta_out_file_path = base_out_file_path + ".meta"; 908 | 909 | if (option_.Verbose()) { 910 | std::cout << absl::StrFormat("write meta file to %s.", meta_out_file_path) << std::endl; 911 | } 912 | std::ofstream* output = new std::ofstream(meta_out_file_path.c_str(), std::ios::out | std::ios::binary); 913 | 914 | // write header 915 | *output << "#voxmeta 1" << std::endl; 916 | *output << size_x_ << " " << size_y_ << " " << size_z_ << std::endl; 917 | 918 | // write data 919 | VoxelIndex tmp; 920 | for (VoxelIndex voxel_index = 0; voxel_index < TotalVoxelSize(); ++voxel_index) { 921 | *output << (voxel_metas_.get())[voxel_index].Index() << " " << (voxel_metas_.get())[voxel_index].Flags() << std::endl; 922 | } 923 | 924 | output->close(); 925 | if (option_.Verbose()) { 926 | std::cout << "meta file is written." << std::endl; 927 | } 928 | } 929 | 930 | Voxelizer::~Voxelizer() { 931 | // TODO Auto-generated destructor stub 932 | } 933 | 934 | V3SP Voxelizer::Vertices() { return vertices_; } 935 | 936 | std::vector Voxelizer::VerticesVec() { 937 | std::vector vertices(VerticesSize()); 938 | for (int i = 0; i < VerticesSize(); ++i) { 939 | vertices[i] = (vertices_.get())[i]; 940 | } 941 | return std::move(vertices); 942 | } 943 | 944 | V3SP Voxelizer::Faces() { return faces_; } 945 | 946 | std::vector Voxelizer::TrianglesVec() { 947 | std::vector triangles(FacesSize()); 948 | for (int i = 0; i < FacesSize(); ++i) { 949 | const Vec3f& tmp = (faces_.get())[i]; 950 | triangles[i] = std::move(Triangle(tmp[0], tmp[1], tmp[2])); 951 | } 952 | return std::move(triangles); 953 | } 954 | 955 | int Voxelizer::VerticesSize() { return num_vertices_; } 956 | 957 | int Voxelizer::FacesSize() { return num_faces_; } 958 | 959 | Vec3f Voxelizer::LowerBound() { return *lb_; } 960 | 961 | Vec3f Voxelizer::UpperBound() { return *ub_; } 962 | 963 | Vec3f Voxelizer::MeshLowerBound() { return *mesh_lb_; } 964 | 965 | Vec3f Voxelizer::MeshUpperBound() { return *mesh_ub_; } 966 | 967 | AVISP Voxelizer::Voxels() { return voxels_; } 968 | 969 | Vec3f Voxelizer::HalfUnit() { return *half_unit_; } 970 | 971 | Vec3f Voxelizer::Unit() { return *unit_; } 972 | 973 | VoxelIndex Voxelizer::TotalVoxelCompressedSize() { return compressed_total_size_; } 974 | 975 | VoxelIndex Voxelizer::TotalVoxelSize() { return size_x_*size_y_*size_z_; } 976 | 977 | } // namespace voxelizer 978 | --------------------------------------------------------------------------------