From 59d9554d9b068bdb47a4dd0e8bf20c3a85d49938 Mon Sep 17 00:00:00 2001 From: ScottCSteinhauser Date: Thu, 28 Jan 2021 11:23:00 -0600 Subject: [PATCH 1/4] Edited make files for compatibility with habitat-sim. --- src/VHACD_Lib/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/VHACD_Lib/CMakeLists.txt b/src/VHACD_Lib/CMakeLists.txt index 46fc1b16..e56b6b19 100644 --- a/src/VHACD_Lib/CMakeLists.txt +++ b/src/VHACD_Lib/CMakeLists.txt @@ -1,4 +1,7 @@ project(VHACD_LIB CXX C) + +set(CMAKE_COMMON_INC "${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/cmake_common.cmake") + include(${CMAKE_COMMON_INC}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") @@ -29,11 +32,13 @@ endif() target_include_directories(vhacd PUBLIC + $ $ $ # /include/mylib ) + message("[VHACD] \t -> CMAKE_INSTALL_PREFIX " ${CMAKE_INSTALL_PREFIX}) install(TARGETS vhacd EXPORT vhacd-targets DESTINATION lib) install(FILES ${PROJECT_INC_FILES} DESTINATION include) @@ -74,4 +79,3 @@ install( COMPONENT Devel ) - From 74fc260be16ae5b4980bb2463e767cb986c707ae Mon Sep 17 00:00:00 2001 From: ScottCSteinhauser Date: Tue, 2 Feb 2021 09:04:15 -0600 Subject: [PATCH 2/4] Modified make files. --- scripts/cmake_common.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/cmake_common.cmake b/scripts/cmake_common.cmake index 579f76d3..bf2a7557 100644 --- a/scripts/cmake_common.cmake +++ b/scripts/cmake_common.cmake @@ -3,6 +3,7 @@ file(GLOB PROJECT_INC_FILES "inc/*.h" "public/*.h") file(GLOB PROJECT_INL_FILES "inc/*.inl") file(GLOB PROJECT_CPP_FILES "src/*.cpp") file(GLOB PROJECT_C_FILES "src/*.c") +file(GLOB PROJECT_C_FILES "src/*.h") file(GLOB PROJECT_CL_FILES "cl/*.cl") source_group (Inc FILES ${PROJECT_INC_FILES}) source_group (Inl FILES ${PROJECT_INL_FILES}) From 117d58bd4a22f6459cc03da7261ce3324c059421 Mon Sep 17 00:00:00 2001 From: ScottCSteinhauser Date: Wed, 24 Feb 2021 17:00:27 -0600 Subject: [PATCH 3/4] Added ability to voxelize a mesh and get the resulting Volume. --- src/VHACD_Lib/inc/vhacdVHACD.h | 651 +++---- src/VHACD_Lib/inc/vhacdVolume.h | 430 ----- src/VHACD_Lib/public/VHACD.h | 292 +-- src/VHACD_Lib/public/vhacdVolume.h | 501 +++++ src/VHACD_Lib/src/VHACD-ASYNC.cpp | 633 +++---- src/VHACD_Lib/src/VHACD.cpp | 2747 ++++++++++++++-------------- 6 files changed, 2736 insertions(+), 2518 deletions(-) delete mode 100644 src/VHACD_Lib/inc/vhacdVolume.h create mode 100644 src/VHACD_Lib/public/vhacdVolume.h diff --git a/src/VHACD_Lib/inc/vhacdVHACD.h b/src/VHACD_Lib/inc/vhacdVHACD.h index d67b1076..45ba2662 100644 --- a/src/VHACD_Lib/inc/vhacdVHACD.h +++ b/src/VHACD_Lib/inc/vhacdVHACD.h @@ -2,15 +2,29 @@ All rights reserved. -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. -3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. +3. The names of the contributors may not be used to endorse or promote products +derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #ifndef VHACD_VHACD_H @@ -22,350 +36,363 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #else #include #endif -#endif //OPENCL_FOUND +#endif // OPENCL_FOUND +#include #include "vhacdMutex.h" -#include "vhacdVolume.h" #include "vhacdRaycastMesh.h" -#include +#include "vhacdVolume.h" + +#include #define USE_THREAD 1 #define OCL_MIN_NUM_PRIMITIVES 4096 #define CH_APP_MIN_NUM_PRIMITIVES 64000 namespace VHACD { class VHACD : public IVHACD { -public: - //! Constructor. - VHACD() - { + public: + //! Constructor. + VHACD() { #if USE_THREAD == 1 && _OPENMP - m_ompNumProcessors = 2 * omp_get_num_procs(); - omp_set_num_threads(m_ompNumProcessors); -#else //USE_THREAD == 1 && _OPENMP - m_ompNumProcessors = 1; -#endif //USE_THREAD == 1 && _OPENMP + m_ompNumProcessors = 2 * omp_get_num_procs(); + omp_set_num_threads(m_ompNumProcessors); +#else // USE_THREAD == 1 && _OPENMP + m_ompNumProcessors = 1; +#endif // USE_THREAD == 1 && _OPENMP #ifdef CL_VERSION_1_1 - m_oclWorkGroupSize = 0; - m_oclDevice = 0; - m_oclQueue = 0; - m_oclKernelComputePartialVolumes = 0; - m_oclKernelComputeSum = 0; -#endif //CL_VERSION_1_1 - Init(); - } - //! Destructor. - ~VHACD(void) - { - } - uint32_t GetNConvexHulls() const - { - return (uint32_t)m_convexHulls.Size(); + m_oclWorkGroupSize = 0; + m_oclDevice = 0; + m_oclQueue = 0; + m_oclKernelComputePartialVolumes = 0; + m_oclKernelComputeSum = 0; +#endif // CL_VERSION_1_1 + Init(); + } + //! Destructor. + ~VHACD(void) {} + uint32_t GetNConvexHulls() const { return (uint32_t)m_convexHulls.Size(); } + void Cancel() { SetCancel(true); } + void GetConvexHull(const uint32_t index, ConvexHull& ch) const { + Mesh* mesh = m_convexHulls[index]; + ch.m_nPoints = (uint32_t)mesh->GetNPoints(); + ch.m_nTriangles = (uint32_t)mesh->GetNTriangles(); + ch.m_points = mesh->GetPoints(); + ch.m_triangles = (uint32_t*)mesh->GetTriangles(); + ch.m_volume = mesh->ComputeVolume(); + Vec3& center = mesh->ComputeCenter(); + ch.m_center[0] = center.X(); + ch.m_center[1] = center.Y(); + ch.m_center[2] = center.Z(); + } + void Clean(void) { + if (mRaycastMesh) { + mRaycastMesh->release(); + mRaycastMesh = nullptr; } - void Cancel() - { - SetCancel(true); + delete m_volume; + delete m_pset; + size_t nCH = m_convexHulls.Size(); + for (size_t p = 0; p < nCH; ++p) { + delete m_convexHulls[p]; } - void GetConvexHull(const uint32_t index, ConvexHull& ch) const - { - Mesh* mesh = m_convexHulls[index]; - ch.m_nPoints = (uint32_t)mesh->GetNPoints(); - ch.m_nTriangles = (uint32_t)mesh->GetNTriangles(); - ch.m_points = mesh->GetPoints(); - ch.m_triangles = (uint32_t *)mesh->GetTriangles(); - ch.m_volume = mesh->ComputeVolume(); - Vec3 ¢er = mesh->ComputeCenter(); - ch.m_center[0] = center.X(); - ch.m_center[1] = center.Y(); - ch.m_center[2] = center.Z(); + m_convexHulls.Clear(); + Init(); + } + void Release(void) { delete this; } + bool Compute(const float* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params); + bool Compute(const double* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params); + bool computeVoxelField(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params); + bool computeVoxelField(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params); + Volume* getVoxelField(); + bool OCLInit(void* const oclDevice, IUserLogger* const logger = 0); + bool OCLRelease(IUserLogger* const logger = 0); + + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const; + + private: + void SetCancel(bool cancel) { + m_cancelMutex.Lock(); + m_cancel = cancel; + m_cancelMutex.Unlock(); + } + bool GetCancel() { + m_cancelMutex.Lock(); + bool cancel = m_cancel; + m_cancelMutex.Unlock(); + return cancel; + } + void Update(const double stageProgress, + const double operationProgress, + const Parameters& params) { + m_stageProgress = stageProgress; + m_operationProgress = operationProgress; + if (params.m_callback) { + params.m_callback->Update(m_overallProgress, m_stageProgress, + m_operationProgress, m_stage.c_str(), + m_operation.c_str()); } - void Clean(void) - { - if (mRaycastMesh) - { - mRaycastMesh->release(); - mRaycastMesh = nullptr; - } - delete m_volume; - delete m_pset; - size_t nCH = m_convexHulls.Size(); - for (size_t p = 0; p < nCH; ++p) { - delete m_convexHulls[p]; - } - m_convexHulls.Clear(); - Init(); + } + void Init() { + if (mRaycastMesh) { + mRaycastMesh->release(); + mRaycastMesh = nullptr; } - void Release(void) - { - delete this; + memset(m_rot, 0, sizeof(double) * 9); + m_dim = 64; + m_volume = 0; + m_volumeCH0 = 0.0; + m_pset = 0; + m_overallProgress = 0.0; + m_stageProgress = 0.0; + m_operationProgress = 0.0; + m_stage = ""; + m_operation = ""; + m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; + m_rot[0][0] = m_rot[1][1] = m_rot[2][2] = 1.0; + SetCancel(false); + } + void ComputePrimitiveSet(const Parameters& params); + void ComputeACD(const Parameters& params); + void MergeConvexHulls(const Parameters& params); + void SimplifyConvexHull(Mesh* const ch, + const size_t nvertices, + const double minVolume); + void SimplifyConvexHulls(const Parameters& params); + void ComputeBestClippingPlane(const PrimitiveSet* inputPSet, + const double volume, + const SArray& planes, + const Vec3& preferredCuttingDirection, + const double w, + const double alpha, + const double beta, + const int32_t convexhullDownsampling, + const double progress0, + const double progress1, + Plane& bestPlane, + double& minConcavity, + const Parameters& params); + template + void AlignMesh(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const int32_t* const triangles, + const uint32_t strideTriangles, + const uint32_t nTriangles, + const Parameters& params) { + if (GetCancel() || !params.m_pca) { + return; } - bool Compute(const float* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params); - bool Compute(const double* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params); - bool OCLInit(void* const oclDevice, - IUserLogger* const logger = 0); - bool OCLRelease(IUserLogger* const logger = 0); + m_timer.Tic(); - virtual bool ComputeCenterOfMass(double centerOfMass[3]) const; + m_stage = "Align mesh"; + m_operation = "Voxelization"; -private: - void SetCancel(bool cancel) - { - m_cancelMutex.Lock(); - m_cancel = cancel; - m_cancelMutex.Unlock(); + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); } - bool GetCancel() - { - m_cancelMutex.Lock(); - bool cancel = m_cancel; - m_cancelMutex.Unlock(); - return cancel; + Update(0.0, 0.0, params); + if (GetCancel()) { + return; } - void Update(const double stageProgress, - const double operationProgress, - const Parameters& params) - { - m_stageProgress = stageProgress; - m_operationProgress = operationProgress; - if (params.m_callback) { - params.m_callback->Update(m_overallProgress, - m_stageProgress, - m_operationProgress, - m_stage.c_str(), - m_operation.c_str()); - } + m_dim = (size_t)(pow((double)params.m_resolution, 1.0 / 3.0) + 0.5); + Volume volume; + volume.Voxelize(points, stridePoints, nPoints, triangles, strideTriangles, + nTriangles, m_dim, m_barycenter, m_rot); + size_t n = + volume.GetNPrimitivesOnSurf() + volume.GetNPrimitivesInsideSurf(); + Update(50.0, 100.0, params); + + if (params.m_logger) { + msg.str(""); + msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; + params.m_logger->Log(msg.str().c_str()); } - void Init() - { - if (mRaycastMesh) - { - mRaycastMesh->release(); - mRaycastMesh = nullptr; - } - memset(m_rot, 0, sizeof(double) * 9); - m_dim = 64; - m_volume = 0; - m_volumeCH0 = 0.0; - m_pset = 0; - m_overallProgress = 0.0; - m_stageProgress = 0.0; - m_operationProgress = 0.0; - m_stage = ""; - m_operation = ""; - m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; - m_rot[0][0] = m_rot[1][1] = m_rot[2][2] = 1.0; - SetCancel(false); + if (GetCancel()) { + return; } - void ComputePrimitiveSet(const Parameters& params); - void ComputeACD(const Parameters& params); - void MergeConvexHulls(const Parameters& params); - void SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume); - void SimplifyConvexHulls(const Parameters& params); - void ComputeBestClippingPlane(const PrimitiveSet* inputPSet, - const double volume, - const SArray& planes, - const Vec3& preferredCuttingDirection, - const double w, - const double alpha, - const double beta, - const int32_t convexhullDownsampling, - const double progress0, - const double progress1, - Plane& bestPlane, - double& minConcavity, - const Parameters& params); - template - void AlignMesh(const T* const points, - const uint32_t stridePoints, - const uint32_t nPoints, - const int32_t* const triangles, - const uint32_t strideTriangles, - const uint32_t nTriangles, - const Parameters& params) - { - if (GetCancel() || !params.m_pca) { - return; - } - m_timer.Tic(); - - m_stage = "Align mesh"; - m_operation = "Voxelization"; - - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + m_operation = "PCA"; + Update(50.0, 0.0, params); + volume.AlignToPrincipalAxes(m_rot); + m_overallProgress = 1.0; + Update(100.0, 100.0, params); - Update(0.0, 0.0, params); - if (GetCancel()) { - return; - } - m_dim = (size_t)(pow((double)params.m_resolution, 1.0 / 3.0) + 0.5); - Volume volume; - volume.Voxelize(points, stridePoints, nPoints, - triangles, strideTriangles, nTriangles, - m_dim, m_barycenter, m_rot); - size_t n = volume.GetNPrimitivesOnSurf() + volume.GetNPrimitivesInsideSurf(); - Update(50.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + } + template + void VoxelizeMesh(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const int32_t* const triangles, + const uint32_t strideTriangles, + const uint32_t nTriangles, + const Parameters& params) { + if (GetCancel()) { + return; + } - if (params.m_logger) { - msg.str(""); - msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - if (GetCancel()) { - return; - } - m_operation = "PCA"; - Update(50.0, 0.0, params); - volume.AlignToPrincipalAxes(m_rot); - m_overallProgress = 1.0; - Update(100.0, 100.0, params); + m_timer.Tic(); + m_stage = "Voxelization"; - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); } - template - void VoxelizeMesh(const T* const points, - const uint32_t stridePoints, - const uint32_t nPoints, - const int32_t* const triangles, - const uint32_t strideTriangles, - const uint32_t nTriangles, - const Parameters& params) - { - if (GetCancel()) { - return; - } - m_timer.Tic(); - m_stage = "Voxelization"; + delete m_volume; + m_volume = 0; + int32_t iteration = 0; + const int32_t maxIteration = 5; + double progress = 0.0; + while (iteration++ < maxIteration && !m_cancel) { + msg.str(""); + msg << "Iteration " << iteration; + m_operation = msg.str(); - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + progress = iteration * 100.0 / maxIteration; + Update(progress, 0.0, params); - delete m_volume; - m_volume = 0; - int32_t iteration = 0; - const int32_t maxIteration = 5; - double progress = 0.0; - while (iteration++ < maxIteration && !m_cancel) { - msg.str(""); - msg << "Iteration " << iteration; - m_operation = msg.str(); + m_volume = new Volume; + m_volume->Voxelize(points, stridePoints, nPoints, triangles, + strideTriangles, nTriangles, m_dim, m_barycenter, + m_rot); - progress = iteration * 100.0 / maxIteration; - Update(progress, 0.0, params); + Update(progress, 100.0, params); - m_volume = new Volume; - m_volume->Voxelize(points, stridePoints, nPoints, - triangles, strideTriangles, nTriangles, - m_dim, m_barycenter, m_rot); + size_t n = m_volume->GetNPrimitivesOnSurf() + + m_volume->GetNPrimitivesInsideSurf(); + if (params.m_logger) { + msg.str(""); + msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } - Update(progress, 100.0, params); - - size_t n = m_volume->GetNPrimitivesOnSurf() + m_volume->GetNPrimitivesInsideSurf(); - if (params.m_logger) { - msg.str(""); - msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + double a = pow((double)(params.m_resolution) / n, 0.33); + size_t dim_next = (size_t)(m_dim * a + 0.5); + if (n < params.m_resolution && iteration < maxIteration && + m_volume->GetNPrimitivesOnSurf() < params.m_resolution / 8 && + m_dim != dim_next) { + delete m_volume; + m_volume = 0; + m_dim = dim_next; + } else { + break; + } + } + m_overallProgress = 10.0; + Update(100.0, 100.0, params); - double a = pow((double)(params.m_resolution) / n, 0.33); - size_t dim_next = (size_t)(m_dim * a + 0.5); - if (n < params.m_resolution && iteration < maxIteration && m_volume->GetNPrimitivesOnSurf() < params.m_resolution / 8 && m_dim != dim_next) { - delete m_volume; - m_volume = 0; - m_dim = dim_next; - } - else { - break; - } - } - m_overallProgress = 10.0; - Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + } + template + bool ComputeACD(const T* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params) { + Init(); + if (params.m_projectHullVertices) { + mRaycastMesh = RaycastMesh::createRaycastMesh(nPoints, points, nTriangles, + (const uint32_t*)triangles); + } + if (params.m_oclAcceleration) { + // build kernels + } + AlignMesh(points, 3, nPoints, (int32_t*)triangles, 3, nTriangles, params); + VoxelizeMesh(points, 3, nPoints, (int32_t*)triangles, 3, nTriangles, + params); + ComputePrimitiveSet(params); + ComputeACD(params); + MergeConvexHulls(params); + SimplifyConvexHulls(params); + if (params.m_oclAcceleration) { + // Release kernels + } + if (GetCancel()) { + Clean(); + return false; + } + return true; + } - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + template + bool computeVoxelFieldHelper(const T* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params) { + Init(); + if (params.m_projectHullVertices) { + mRaycastMesh = RaycastMesh::createRaycastMesh(nPoints, points, nTriangles, + (const uint32_t*)triangles); } - template - bool ComputeACD(const T* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params) - { - Init(); - if (params.m_projectHullVertices) - { - mRaycastMesh = RaycastMesh::createRaycastMesh(nPoints, points, nTriangles, (const uint32_t *)triangles); - } - if (params.m_oclAcceleration) { - // build kernels - } - AlignMesh(points, 3, nPoints, (int32_t *)triangles, 3, nTriangles, params); - VoxelizeMesh(points, 3, nPoints, (int32_t *)triangles, 3, nTriangles, params); - ComputePrimitiveSet(params); - ComputeACD(params); - MergeConvexHulls(params); - SimplifyConvexHulls(params); - if (params.m_oclAcceleration) { - // Release kernels - } - if (GetCancel()) { - Clean(); - return false; - } - return true; + if (params.m_oclAcceleration) { + // build kernels } + AlignMesh(points, 3, nPoints, (int32_t*)triangles, 3, nTriangles, params); + VoxelizeMesh(points, 3, nPoints, (int32_t*)triangles, 3, nTriangles, + params); + return true; + } -private: - RaycastMesh *mRaycastMesh{ nullptr }; - SArray m_convexHulls; - std::string m_stage; - std::string m_operation; - double m_overallProgress; - double m_stageProgress; - double m_operationProgress; - double m_rot[3][3]; - double m_volumeCH0; - Vec3 m_barycenter; - Timer m_timer; - size_t m_dim; - Volume* m_volume; - PrimitiveSet* m_pset; - Mutex m_cancelMutex; - bool m_cancel; - int32_t m_ompNumProcessors; + private: + RaycastMesh* mRaycastMesh{nullptr}; + SArray m_convexHulls; + std::string m_stage; + std::string m_operation; + double m_overallProgress; + double m_stageProgress; + double m_operationProgress; + double m_rot[3][3]; + double m_volumeCH0; + Vec3 m_barycenter; + Timer m_timer; + size_t m_dim; + Volume* m_volume; + PrimitiveSet* m_pset; + Mutex m_cancelMutex; + bool m_cancel; + int32_t m_ompNumProcessors; #ifdef CL_VERSION_1_1 - cl_device_id* m_oclDevice; - cl_context m_oclContext; - cl_program m_oclProgram; - cl_command_queue* m_oclQueue; - cl_kernel* m_oclKernelComputePartialVolumes; - cl_kernel* m_oclKernelComputeSum; - size_t m_oclWorkGroupSize; -#endif //CL_VERSION_1_1 + cl_device_id* m_oclDevice; + cl_context m_oclContext; + cl_program m_oclProgram; + cl_command_queue* m_oclQueue; + cl_kernel* m_oclKernelComputePartialVolumes; + cl_kernel* m_oclKernelComputeSum; + size_t m_oclWorkGroupSize; +#endif // CL_VERSION_1_1 }; -} -#endif // VHACD_VHACD_H +} // namespace VHACD +#endif // VHACD_VHACD_H diff --git a/src/VHACD_Lib/inc/vhacdVolume.h b/src/VHACD_Lib/inc/vhacdVolume.h deleted file mode 100644 index c445f201..00000000 --- a/src/VHACD_Lib/inc/vhacdVolume.h +++ /dev/null @@ -1,430 +0,0 @@ -/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) - All rights reserved. - - - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - - 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once -#ifndef VHACD_VOLUME_H -#define VHACD_VOLUME_H -#include "vhacdMesh.h" -#include "vhacdVector.h" -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4456 4701) -#endif - -namespace VHACD { - -enum VOXEL_VALUE { - PRIMITIVE_UNDEFINED = 0, - PRIMITIVE_OUTSIDE_SURFACE = 1, - PRIMITIVE_INSIDE_SURFACE = 2, - PRIMITIVE_ON_SURFACE = 3 -}; - -struct Voxel { -public: - short m_coord[3]; - short m_data; -}; - -class PrimitiveSet { -public: - virtual ~PrimitiveSet(){}; - virtual PrimitiveSet* Create() const = 0; - virtual const size_t GetNPrimitives() const = 0; - virtual const size_t GetNPrimitivesOnSurf() const = 0; - virtual const size_t GetNPrimitivesInsideSurf() const = 0; - virtual const double GetEigenValue(AXIS axis) const = 0; - virtual const double ComputeMaxVolumeError() const = 0; - virtual const double ComputeVolume() const = 0; - virtual void Clip(const Plane& plane, PrimitiveSet* const positivePart, - PrimitiveSet* const negativePart) const = 0; - virtual void Intersect(const Plane& plane, SArray >* const positivePts, - SArray >* const negativePts, const size_t sampling) const = 0; - virtual void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, - SArray >* const exteriorPts) const = 0; - virtual void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, - double& negativeVolume) const = 0; - virtual void SelectOnSurface(PrimitiveSet* const onSurfP) const = 0; - virtual void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const = 0; - virtual void ComputeBB() = 0; - virtual void ComputePrincipalAxes() = 0; - virtual void AlignToPrincipalAxes() = 0; - virtual void RevertAlignToPrincipalAxes() = 0; - virtual void Convert(Mesh& mesh, const VOXEL_VALUE value) const = 0; - const Mesh& GetConvexHull() const { return m_convexHull; }; - Mesh& GetConvexHull() { return m_convexHull; }; -private: - Mesh m_convexHull; -}; - -//! -class VoxelSet : public PrimitiveSet { - friend class Volume; - -public: - //! Destructor. - ~VoxelSet(void); - //! Constructor. - VoxelSet(); - - const size_t GetNPrimitives() const { return m_voxels.Size(); } - const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } - const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; } - const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } - const double ComputeVolume() const { return m_unitVolume * m_voxels.Size(); } - const double ComputeMaxVolumeError() const { return m_unitVolume * m_numVoxelsOnSurface; } - const Vec3& GetMinBBVoxels() const { return m_minBBVoxels; } - const Vec3& GetMaxBBVoxels() const { return m_maxBBVoxels; } - const Vec3& GetMinBB() const { return m_minBB; } - const double& GetScale() const { return m_scale; } - const double& GetUnitVolume() const { return m_unitVolume; } - Vec3 GetPoint(Vec3 voxel) const - { - return Vec3(voxel[0] * m_scale + m_minBB[0], - voxel[1] * m_scale + m_minBB[1], - voxel[2] * m_scale + m_minBB[2]); - } - Vec3 GetPoint(const Voxel& voxel) const - { - return Vec3(voxel.m_coord[0] * m_scale + m_minBB[0], - voxel.m_coord[1] * m_scale + m_minBB[1], - voxel.m_coord[2] * m_scale + m_minBB[2]); - } - Vec3 GetPoint(Vec3 voxel) const - { - return Vec3(voxel[0] * m_scale + m_minBB[0], - voxel[1] * m_scale + m_minBB[1], - voxel[2] * m_scale + m_minBB[2]); - } - void GetPoints(const Voxel& voxel, Vec3* const pts) const; - void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; - void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const; - void Intersect(const Plane& plane, SArray >* const positivePts, - SArray >* const negativePts, const size_t sampling) const; - void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, - SArray >* const exteriorPts) const; - void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const; - void SelectOnSurface(PrimitiveSet* const onSurfP) const; - void ComputeBB(); - void Convert(Mesh& mesh, const VOXEL_VALUE value) const; - void ComputePrincipalAxes(); - PrimitiveSet* Create() const - { - return new VoxelSet(); - } - void AlignToPrincipalAxes(){}; - void RevertAlignToPrincipalAxes(){}; - Voxel* const GetVoxels() { return m_voxels.Data(); } - const Voxel* const GetVoxels() const { return m_voxels.Data(); } - -private: - size_t m_numVoxelsOnSurface; - size_t m_numVoxelsInsideSurface; - Vec3 m_minBB; - double m_scale; - SArray m_voxels; - double m_unitVolume; - Vec3 m_minBBPts; - Vec3 m_maxBBPts; - Vec3 m_minBBVoxels; - Vec3 m_maxBBVoxels; - Vec3 m_barycenter; - double m_Q[3][3]; - double m_D[3][3]; - Vec3 m_barycenterPCA; -}; - -struct Tetrahedron { -public: - Vec3 m_pts[4]; - unsigned char m_data; -}; - -//! -class TetrahedronSet : public PrimitiveSet { - friend class Volume; - -public: - //! Destructor. - ~TetrahedronSet(void); - //! Constructor. - TetrahedronSet(); - - const size_t GetNPrimitives() const { return m_tetrahedra.Size(); } - const size_t GetNPrimitivesOnSurf() const { return m_numTetrahedraOnSurface; } - const size_t GetNPrimitivesInsideSurf() const { return m_numTetrahedraInsideSurface; } - const Vec3& GetMinBB() const { return m_minBB; } - const Vec3& GetMaxBB() const { return m_maxBB; } - const Vec3& GetBarycenter() const { return m_barycenter; } - const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } - const double GetSacle() const { return m_scale; } - const double ComputeVolume() const; - const double ComputeMaxVolumeError() const; - void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; - void ComputePrincipalAxes(); - void AlignToPrincipalAxes(); - void RevertAlignToPrincipalAxes(); - void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const; - void Intersect(const Plane& plane, SArray >* const positivePts, - SArray >* const negativePts, const size_t sampling) const; - void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, - SArray >* const exteriorPts) const; - void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const; - void SelectOnSurface(PrimitiveSet* const onSurfP) const; - void ComputeBB(); - void Convert(Mesh& mesh, const VOXEL_VALUE value) const; - inline bool Add(Tetrahedron& tetrahedron); - PrimitiveSet* Create() const - { - return new TetrahedronSet(); - } - static const double EPS; - -private: - void AddClippedTetrahedra(const Vec3 (&pts)[10], const int32_t nPts); - - size_t m_numTetrahedraOnSurface; - size_t m_numTetrahedraInsideSurface; - double m_scale; - Vec3 m_minBB; - Vec3 m_maxBB; - Vec3 m_barycenter; - SArray m_tetrahedra; - double m_Q[3][3]; - double m_D[3][3]; -}; - -//! -class Volume { -public: - //! Destructor. - ~Volume(void); - - //! Constructor. - Volume(); - - //! Voxelize - template - void Voxelize(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, - const int32_t* const triangles, const uint32_t strideTriangles, const uint32_t nTriangles, - const size_t dim, const Vec3& barycenter, const double (&rot)[3][3]); - unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) - { - assert(i < m_dim[0] || i >= 0); - assert(j < m_dim[0] || j >= 0); - assert(k < m_dim[0] || k >= 0); - return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; - } - const unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) const - { - assert(i < m_dim[0] || i >= 0); - assert(j < m_dim[0] || j >= 0); - assert(k < m_dim[0] || k >= 0); - return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; - } - const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } - const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; } - void Convert(Mesh& mesh, const VOXEL_VALUE value) const; - void Convert(VoxelSet& vset) const; - void Convert(TetrahedronSet& tset) const; - void AlignToPrincipalAxes(double (&rot)[3][3]) const; - -private: - void FillOutsideSurface(const size_t i0, const size_t j0, const size_t k0, const size_t i1, - const size_t j1, const size_t k1); - void FillInsideSurface(); - template - void ComputeBB(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, - const Vec3& barycenter, const double (&rot)[3][3]); - void Allocate(); - void Free(); - - Vec3 m_minBB; - Vec3 m_maxBB; - double m_scale; - size_t m_dim[3]; //>! dim - size_t m_numVoxelsOnSurface; - size_t m_numVoxelsInsideSurface; - size_t m_numVoxelsOutsideSurface; - unsigned char* m_data; -}; -int32_t TriBoxOverlap(const Vec3& boxcenter, const Vec3& boxhalfsize, const Vec3& triver0, - const Vec3& triver1, const Vec3& triver2); -template -inline void ComputeAlignedPoint(const T* const points, const uint32_t idx, const Vec3& barycenter, - const double (&rot)[3][3], Vec3& pt){}; -template <> -inline void ComputeAlignedPoint(const float* const points, const uint32_t idx, const Vec3& barycenter, const double (&rot)[3][3], Vec3& pt) -{ - double x = points[idx + 0] - barycenter[0]; - double y = points[idx + 1] - barycenter[1]; - double z = points[idx + 2] - barycenter[2]; - pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; - pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; - pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; -} -template <> -inline void ComputeAlignedPoint(const double* const points, const uint32_t idx, const Vec3& barycenter, const double (&rot)[3][3], Vec3& pt) -{ - double x = points[idx + 0] - barycenter[0]; - double y = points[idx + 1] - barycenter[1]; - double z = points[idx + 2] - barycenter[2]; - pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; - pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; - pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; -} -template -void Volume::ComputeBB(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, - const Vec3& barycenter, const double (&rot)[3][3]) -{ - Vec3 pt; - ComputeAlignedPoint(points, 0, barycenter, rot, pt); - m_maxBB = pt; - m_minBB = pt; - for (uint32_t v = 1; v < nPoints; ++v) { - ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt); - for (int32_t i = 0; i < 3; ++i) { - if (pt[i] < m_minBB[i]) - m_minBB[i] = pt[i]; - else if (pt[i] > m_maxBB[i]) - m_maxBB[i] = pt[i]; - } - } -} -template -void Volume::Voxelize(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, - const int32_t* const triangles, const uint32_t strideTriangles, const uint32_t nTriangles, - const size_t dim, const Vec3& barycenter, const double (&rot)[3][3]) -{ - if (nPoints == 0) { - return; - } - ComputeBB(points, stridePoints, nPoints, barycenter, rot); - - double d[3] = { m_maxBB[0] - m_minBB[0], m_maxBB[1] - m_minBB[1], m_maxBB[2] - m_minBB[2] }; - double r; - if (d[0] >= d[1] && d[0] >= d[2]) { - r = d[0]; - m_dim[0] = dim; - m_dim[1] = 2 + static_cast(dim * d[1] / d[0]); - m_dim[2] = 2 + static_cast(dim * d[2] / d[0]); - } - else if (d[1] >= d[0] && d[1] >= d[2]) { - r = d[1]; - m_dim[1] = dim; - m_dim[0] = 2 + static_cast(dim * d[0] / d[1]); - m_dim[2] = 2 + static_cast(dim * d[2] / d[1]); - } - else { - r = d[2]; - m_dim[2] = dim; - m_dim[0] = 2 + static_cast(dim * d[0] / d[2]); - m_dim[1] = 2 + static_cast(dim * d[1] / d[2]); - } - - m_scale = r / (dim - 1); - double invScale = (dim - 1) / r; - - Allocate(); - m_numVoxelsOnSurface = 0; - m_numVoxelsInsideSurface = 0; - m_numVoxelsOutsideSurface = 0; - - Vec3 p[3]; - size_t i, j, k; - size_t i0, j0, k0; - size_t i1, j1, k1; - Vec3 boxcenter; - Vec3 pt; - const Vec3 boxhalfsize(0.5, 0.5, 0.5); - for (size_t t = 0, ti = 0; t < nTriangles; ++t, ti += strideTriangles) { - Vec3 tri(triangles[ti + 0], - triangles[ti + 1], - triangles[ti + 2]); - for (int32_t c = 0; c < 3; ++c) { - ComputeAlignedPoint(points, tri[c] * stridePoints, barycenter, rot, pt); - p[c][0] = (pt[0] - m_minBB[0]) * invScale; - p[c][1] = (pt[1] - m_minBB[1]) * invScale; - p[c][2] = (pt[2] - m_minBB[2]) * invScale; - i = static_cast(p[c][0] + 0.5); - j = static_cast(p[c][1] + 0.5); - k = static_cast(p[c][2] + 0.5); - assert(i < m_dim[0] && i >= 0 && j < m_dim[1] && j >= 0 && k < m_dim[2] && k >= 0); - - if (c == 0) { - i0 = i1 = i; - j0 = j1 = j; - k0 = k1 = k; - } - else { - if (i < i0) - i0 = i; - if (j < j0) - j0 = j; - if (k < k0) - k0 = k; - if (i > i1) - i1 = i; - if (j > j1) - j1 = j; - if (k > k1) - k1 = k; - } - } - if (i0 > 0) - --i0; - if (j0 > 0) - --j0; - if (k0 > 0) - --k0; - if (i1 < m_dim[0]) - ++i1; - if (j1 < m_dim[1]) - ++j1; - if (k1 < m_dim[2]) - ++k1; - for (size_t i = i0; i < i1; ++i) { - boxcenter[0] = (double)i; - for (size_t j = j0; j < j1; ++j) { - boxcenter[1] = (double)j; - for (size_t k = k0; k < k1; ++k) { - boxcenter[2] = (double)k; - int32_t res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]); - unsigned char& value = GetVoxel(i, j, k); - if (res == 1 && value == PRIMITIVE_UNDEFINED) { - value = PRIMITIVE_ON_SURFACE; - ++m_numVoxelsOnSurface; - } - } - } - } - } - FillOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1); - FillOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]); - FillOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]); - FillOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]); - FillOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]); - FillOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]); - FillInsideSurface(); -} -} - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - -#endif // VHACD_VOLUME_H diff --git a/src/VHACD_Lib/public/VHACD.h b/src/VHACD_Lib/public/VHACD.h index 65390984..8f868d2a 100644 --- a/src/VHACD_Lib/public/VHACD.h +++ b/src/VHACD_Lib/public/VHACD.h @@ -1,16 +1,30 @@ /* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) All rights reserved. - - - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - - 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #ifndef VHACD_H @@ -21,133 +35,149 @@ // Changes for version 2.3 // -// m_gamma : Has been removed. This used to control the error metric to merge convex hulls. Now it uses the 'm_maxConvexHulls' value instead. -// m_maxConvexHulls : This is the maximum number of convex hulls to produce from the merge operation; replaces 'm_gamma'. +// m_gamma : Has been removed. This used to control the error metric to merge +// convex hulls. Now it uses the 'm_maxConvexHulls' value instead. +// m_maxConvexHulls : This is the maximum number of convex hulls to produce from +// the merge operation; replaces 'm_gamma'. // -// Note that decomposition depth is no longer a user provided value. It is now derived from the -// maximum number of hulls requested. +// Note that decomposition depth is no longer a user provided value. It is now +// derived from the maximum number of hulls requested. // -// As a convenience to the user, each convex hull produced now includes the volume of the hull as well as it's center. +// As a convenience to the user, each convex hull produced now includes the +// volume of the hull as well as it's center. // -// This version supports a convenience method to automatically make V-HACD run asynchronously in a background thread. -// To get a fully asynchronous version, call 'CreateVHACD_ASYNC' instead of 'CreateVHACD'. You get the same interface however, -// now when computing convex hulls, it is no longer a blocking operation. All callback messages are still returned -// in the application's thread so you don't need to worry about mutex locks or anything in that case. -// To tell if the operation is complete, the application should call 'IsReady'. This will return true if -// the last approximation operation is complete and will dispatch any pending messages. -// If you call 'Compute' while a previous operation was still running, it will automatically cancel the last request -// and begin a new one. To cancel a currently running approximation just call 'Cancel'. +// This version supports a convenience method to automatically make V-HACD run +// asynchronously in a background thread. To get a fully asynchronous version, +// call 'CreateVHACD_ASYNC' instead of 'CreateVHACD'. You get the same +// interface however, now when computing convex hulls, it is no longer a +// blocking operation. All callback messages are still returned in the +// application's thread so you don't need to worry about mutex locks or anything +// in that case. To tell if the operation is complete, the application should +// call 'IsReady'. This will return true if the last approximation operation is +// complete and will dispatch any pending messages. If you call 'Compute' while +// a previous operation was still running, it will automatically cancel the last +// request and begin a new one. To cancel a currently running approximation +// just call 'Cancel'. #include +#include "vhacdVolume.h" namespace VHACD { class IVHACD { -public: - class IUserCallback { - public: - virtual ~IUserCallback(){}; - virtual void Update(const double overallProgress, - const double stageProgress, - const double operationProgress, - const char* const stage, - const char* const operation) - = 0; - }; - - class IUserLogger { - public: - virtual ~IUserLogger(){}; - virtual void Log(const char* const msg) = 0; - }; - - class ConvexHull { - public: - double* m_points; - uint32_t* m_triangles; - uint32_t m_nPoints; - uint32_t m_nTriangles; - double m_volume; - double m_center[3]; - }; - - class Parameters { - public: - Parameters(void) { Init(); } - void Init(void) - { - m_resolution = 100000; - m_concavity = 0.001; - m_planeDownsampling = 4; - m_convexhullDownsampling = 4; - m_alpha = 0.05; - m_beta = 0.05; - m_pca = 0; - m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based - m_maxNumVerticesPerCH = 64; - m_minVolumePerCH = 0.0001; - m_callback = 0; - m_logger = 0; - m_convexhullApproximation = true; - m_oclAcceleration = true; - m_maxConvexHulls = 1024; - m_projectHullVertices = true; // This will project the output convex hull vertices onto the original source mesh to increase the floating point accuracy of the results - } - double m_concavity; - double m_alpha; - double m_beta; - double m_minVolumePerCH; - IUserCallback* m_callback; - IUserLogger* m_logger; - uint32_t m_resolution; - uint32_t m_maxNumVerticesPerCH; - uint32_t m_planeDownsampling; - uint32_t m_convexhullDownsampling; - uint32_t m_pca; - uint32_t m_mode; - uint32_t m_convexhullApproximation; - uint32_t m_oclAcceleration; - uint32_t m_maxConvexHulls; - bool m_projectHullVertices; - }; - - virtual void Cancel() = 0; - virtual bool Compute(const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) - = 0; - virtual bool Compute(const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) - = 0; - virtual uint32_t GetNConvexHulls() const = 0; - virtual void GetConvexHull(const uint32_t index, ConvexHull& ch) const = 0; - virtual void Clean(void) = 0; // release internally allocated memory - virtual void Release(void) = 0; // release IVHACD - virtual bool OCLInit(void* const oclDevice, - IUserLogger* const logger = 0) - = 0; - virtual bool OCLRelease(IUserLogger* const logger = 0) = 0; - - // Will compute the center of mass of the convex hull decomposition results and return it - // in 'centerOfMass'. Returns false if the center of mass could not be computed. - virtual bool ComputeCenterOfMass(double centerOfMass[3]) const = 0; - - // In synchronous mode (non-multi-threaded) the state is always 'ready' - // In asynchronous mode, this returns true if the background thread is not still actively computing - // a new solution. In an asynchronous config the 'IsReady' call will report any update or log - // messages in the caller's current thread. - virtual bool IsReady(void) const - { - return true; - } - -protected: - virtual ~IVHACD(void) {} + public: + class IUserCallback { + public: + virtual ~IUserCallback(){}; + virtual void Update(const double overallProgress, + const double stageProgress, + const double operationProgress, + const char* const stage, + const char* const operation) = 0; + }; + + class IUserLogger { + public: + virtual ~IUserLogger(){}; + virtual void Log(const char* const msg) = 0; + }; + + class ConvexHull { + public: + double* m_points; + uint32_t* m_triangles; + uint32_t m_nPoints; + uint32_t m_nTriangles; + double m_volume; + double m_center[3]; + }; + + class Parameters { + public: + Parameters(void) { Init(); } + void Init(void) { + m_resolution = 100000; + m_concavity = 0.001; + m_planeDownsampling = 4; + m_convexhullDownsampling = 4; + m_alpha = 0.05; + m_beta = 0.05; + m_pca = 0; + m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based + m_maxNumVerticesPerCH = 64; + m_minVolumePerCH = 0.0001; + m_callback = 0; + m_logger = 0; + m_convexhullApproximation = true; + m_oclAcceleration = true; + m_maxConvexHulls = 1024; + m_projectHullVertices = + true; // This will project the output convex hull vertices onto the + // original source mesh to increase the floating point accuracy + // of the results + } + double m_concavity; + double m_alpha; + double m_beta; + double m_minVolumePerCH; + IUserCallback* m_callback; + IUserLogger* m_logger; + uint32_t m_resolution; + uint32_t m_maxNumVerticesPerCH; + uint32_t m_planeDownsampling; + uint32_t m_convexhullDownsampling; + uint32_t m_pca; + uint32_t m_mode; + uint32_t m_convexhullApproximation; + uint32_t m_oclAcceleration; + uint32_t m_maxConvexHulls; + bool m_projectHullVertices; + }; + + virtual void Cancel() = 0; + virtual bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) = 0; + virtual bool Compute(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) = 0; + virtual bool computeVoxelField(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) = 0; + virtual bool computeVoxelField(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) = 0; + virtual Volume* getVoxelField() = 0; + virtual uint32_t GetNConvexHulls() const = 0; + virtual void GetConvexHull(const uint32_t index, ConvexHull& ch) const = 0; + virtual void Clean(void) = 0; // release internally allocated memory + virtual void Release(void) = 0; // release IVHACD + virtual bool OCLInit(void* const oclDevice, + IUserLogger* const logger = 0) = 0; + virtual bool OCLRelease(IUserLogger* const logger = 0) = 0; + + // Will compute the center of mass of the convex hull decomposition results + // and return it in 'centerOfMass'. Returns false if the center of mass could + // not be computed. + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const = 0; + + // In synchronous mode (non-multi-threaded) the state is always 'ready' + // In asynchronous mode, this returns true if the background thread is not + // still actively computing a new solution. In an asynchronous config the + // 'IsReady' call will report any update or log messages in the caller's + // current thread. + virtual bool IsReady(void) const { return true; } + + protected: + virtual ~IVHACD(void) {} }; IVHACD* CreateVHACD(void); IVHACD* CreateVHACD_ASYNC(void); -} -#endif // VHACD_H +} // namespace VHACD +#endif // VHACD_H diff --git a/src/VHACD_Lib/public/vhacdVolume.h b/src/VHACD_Lib/public/vhacdVolume.h new file mode 100644 index 00000000..9d2b7b77 --- /dev/null +++ b/src/VHACD_Lib/public/vhacdVolume.h @@ -0,0 +1,501 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_VOLUME_H +#define VHACD_VOLUME_H +#include +#include "vhacdMesh.h" +#include "vhacdVector.h" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4456 4701) +#endif + +namespace VHACD { + +enum VOXEL_VALUE { + PRIMITIVE_UNDEFINED = 0, + PRIMITIVE_OUTSIDE_SURFACE = 1, + PRIMITIVE_INSIDE_SURFACE = 2, + PRIMITIVE_ON_SURFACE = 3 +}; + +struct Voxel { + public: + short m_coord[3]; + short m_data; +}; + +class PrimitiveSet { + public: + virtual ~PrimitiveSet(){}; + virtual PrimitiveSet* Create() const = 0; + virtual const size_t GetNPrimitives() const = 0; + virtual const size_t GetNPrimitivesOnSurf() const = 0; + virtual const size_t GetNPrimitivesInsideSurf() const = 0; + virtual const double GetEigenValue(AXIS axis) const = 0; + virtual const double ComputeMaxVolumeError() const = 0; + virtual const double ComputeVolume() const = 0; + virtual void Clip(const Plane& plane, + PrimitiveSet* const positivePart, + PrimitiveSet* const negativePart) const = 0; + virtual void Intersect(const Plane& plane, + SArray >* const positivePts, + SArray >* const negativePts, + const size_t sampling) const = 0; + virtual void ComputeExteriorPoints( + const Plane& plane, + const Mesh& mesh, + SArray >* const exteriorPts) const = 0; + virtual void ComputeClippedVolumes(const Plane& plane, + double& positiveVolume, + double& negativeVolume) const = 0; + virtual void SelectOnSurface(PrimitiveSet* const onSurfP) const = 0; + virtual void ComputeConvexHull(Mesh& meshCH, + const size_t sampling = 1) const = 0; + virtual void ComputeBB() = 0; + virtual void ComputePrincipalAxes() = 0; + virtual void AlignToPrincipalAxes() = 0; + virtual void RevertAlignToPrincipalAxes() = 0; + virtual void Convert(Mesh& mesh, const VOXEL_VALUE value) const = 0; + const Mesh& GetConvexHull() const { return m_convexHull; }; + Mesh& GetConvexHull() { return m_convexHull; }; + + private: + Mesh m_convexHull; +}; + +//! +class VoxelSet : public PrimitiveSet { + friend class Volume; + + public: + //! Destructor. + ~VoxelSet(void); + //! Constructor. + VoxelSet(); + + const size_t GetNPrimitives() const { return m_voxels.Size(); } + const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { + return m_numVoxelsInsideSurface; + } + const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } + const double ComputeVolume() const { return m_unitVolume * m_voxels.Size(); } + const double ComputeMaxVolumeError() const { + return m_unitVolume * m_numVoxelsOnSurface; + } + const Vec3& GetMinBBVoxels() const { return m_minBBVoxels; } + const Vec3& GetMaxBBVoxels() const { return m_maxBBVoxels; } + const Vec3& GetMinBB() const { return m_minBB; } + const double& GetScale() const { return m_scale; } + const double& GetUnitVolume() const { return m_unitVolume; } + Vec3 GetPoint(Vec3 voxel) const { + return Vec3(voxel[0] * m_scale + m_minBB[0], + voxel[1] * m_scale + m_minBB[1], + voxel[2] * m_scale + m_minBB[2]); + } + Vec3 GetPoint(const Voxel& voxel) const { + return Vec3(voxel.m_coord[0] * m_scale + m_minBB[0], + voxel.m_coord[1] * m_scale + m_minBB[1], + voxel.m_coord[2] * m_scale + m_minBB[2]); + } + Vec3 GetPoint(Vec3 voxel) const { + return Vec3(voxel[0] * m_scale + m_minBB[0], + voxel[1] * m_scale + m_minBB[1], + voxel[2] * m_scale + m_minBB[2]); + } + void GetPoints(const Voxel& voxel, Vec3* const pts) const; + void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; + void Clip(const Plane& plane, + PrimitiveSet* const positivePart, + PrimitiveSet* const negativePart) const; + void Intersect(const Plane& plane, + SArray >* const positivePts, + SArray >* const negativePts, + const size_t sampling) const; + void ComputeExteriorPoints(const Plane& plane, + const Mesh& mesh, + SArray >* const exteriorPts) const; + void ComputeClippedVolumes(const Plane& plane, + double& positiveVolume, + double& negativeVolume) const; + void SelectOnSurface(PrimitiveSet* const onSurfP) const; + void ComputeBB(); + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + void ComputePrincipalAxes(); + PrimitiveSet* Create() const { return new VoxelSet(); } + void AlignToPrincipalAxes(){}; + void RevertAlignToPrincipalAxes(){}; + Voxel* const GetVoxels() { return m_voxels.Data(); } + const Voxel* const GetVoxels() const { return m_voxels.Data(); } + + private: + size_t m_numVoxelsOnSurface; + size_t m_numVoxelsInsideSurface; + Vec3 m_minBB; + double m_scale; + SArray m_voxels; + double m_unitVolume; + Vec3 m_minBBPts; + Vec3 m_maxBBPts; + Vec3 m_minBBVoxels; + Vec3 m_maxBBVoxels; + Vec3 m_barycenter; + double m_Q[3][3]; + double m_D[3][3]; + Vec3 m_barycenterPCA; +}; + +struct Tetrahedron { + public: + Vec3 m_pts[4]; + unsigned char m_data; +}; + +//! +class TetrahedronSet : public PrimitiveSet { + friend class Volume; + + public: + //! Destructor. + ~TetrahedronSet(void); + //! Constructor. + TetrahedronSet(); + + const size_t GetNPrimitives() const { return m_tetrahedra.Size(); } + const size_t GetNPrimitivesOnSurf() const { return m_numTetrahedraOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { + return m_numTetrahedraInsideSurface; + } + const Vec3& GetMinBB() const { return m_minBB; } + const Vec3& GetMaxBB() const { return m_maxBB; } + const Vec3& GetBarycenter() const { return m_barycenter; } + const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } + const double GetSacle() const { return m_scale; } + const double ComputeVolume() const; + const double ComputeMaxVolumeError() const; + void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; + void ComputePrincipalAxes(); + void AlignToPrincipalAxes(); + void RevertAlignToPrincipalAxes(); + void Clip(const Plane& plane, + PrimitiveSet* const positivePart, + PrimitiveSet* const negativePart) const; + void Intersect(const Plane& plane, + SArray >* const positivePts, + SArray >* const negativePts, + const size_t sampling) const; + void ComputeExteriorPoints(const Plane& plane, + const Mesh& mesh, + SArray >* const exteriorPts) const; + void ComputeClippedVolumes(const Plane& plane, + double& positiveVolume, + double& negativeVolume) const; + void SelectOnSurface(PrimitiveSet* const onSurfP) const; + void ComputeBB(); + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + inline bool Add(Tetrahedron& tetrahedron); + PrimitiveSet* Create() const { return new TetrahedronSet(); } + static const double EPS; + + private: + void AddClippedTetrahedra(const Vec3 (&pts)[10], const int32_t nPts); + + size_t m_numTetrahedraOnSurface; + size_t m_numTetrahedraInsideSurface; + double m_scale; + Vec3 m_minBB; + Vec3 m_maxBB; + Vec3 m_barycenter; + SArray m_tetrahedra; + double m_Q[3][3]; + double m_D[3][3]; +}; + +//! +class Volume { + public: + //! Destructor. + ~Volume(void); + + //! Constructor. + Volume(); + + //! Voxelize + template + void Voxelize(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const int32_t* const triangles, + const uint32_t strideTriangles, + const uint32_t nTriangles, + const size_t dim, + const Vec3& barycenter, + const double (&rot)[3][3]); + unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) { + assert(i < m_dim[0] || i >= 0); + assert(j < m_dim[0] || j >= 0); + assert(k < m_dim[0] || k >= 0); + return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; + } + const unsigned char& GetVoxel(const size_t i, + const size_t j, + const size_t k) const { + assert(i < m_dim[0] || i >= 0); + assert(j < m_dim[0] || j >= 0); + assert(k < m_dim[0] || k >= 0); + return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; + } + + const double getScale() const { return m_scale; } + + const size_t* getDimensions() { return &m_dim[0]; } + const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { + return m_numVoxelsInsideSurface; + } + + const Vec3 getCenter() { return m_barycenter; } + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + void Convert(VoxelSet& vset) const; + void Convert(TetrahedronSet& tset) const; + void AlignToPrincipalAxes(double (&rot)[3][3]) const; + + private: + void FillOutsideSurface(const size_t i0, + const size_t j0, + const size_t k0, + const size_t i1, + const size_t j1, + const size_t k1); + void FillInsideSurface(); + template + void ComputeBB(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const Vec3& barycenter, + const double (&rot)[3][3]); + void Allocate(); + void Free(); + + Vec3 m_minBB; + Vec3 m_maxBB; + + Vec3 m_barycenter; + double m_scale; + size_t m_dim[3]; //>! dim + size_t m_numVoxelsOnSurface; + size_t m_numVoxelsInsideSurface; + size_t m_numVoxelsOutsideSurface; + unsigned char* m_data; +}; +int32_t TriBoxOverlap(const Vec3& boxcenter, + const Vec3& boxhalfsize, + const Vec3& triver0, + const Vec3& triver1, + const Vec3& triver2); +template +inline void ComputeAlignedPoint(const T* const points, + const uint32_t idx, + const Vec3& barycenter, + const double (&rot)[3][3], + Vec3& pt){}; +template <> +inline void ComputeAlignedPoint(const float* const points, + const uint32_t idx, + const Vec3& barycenter, + const double (&rot)[3][3], + Vec3& pt) { + double x = points[idx + 0] - barycenter[0]; + double y = points[idx + 1] - barycenter[1]; + double z = points[idx + 2] - barycenter[2]; + pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; + pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; + pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; +} +template <> +inline void ComputeAlignedPoint(const double* const points, + const uint32_t idx, + const Vec3& barycenter, + const double (&rot)[3][3], + Vec3& pt) { + double x = points[idx + 0] - barycenter[0]; + double y = points[idx + 1] - barycenter[1]; + double z = points[idx + 2] - barycenter[2]; + pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; + pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; + pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; +} +template +void Volume::ComputeBB(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const Vec3& barycenter, + const double (&rot)[3][3]) { + Vec3 pt; + ComputeAlignedPoint(points, 0, barycenter, rot, pt); + m_maxBB = pt; + m_minBB = pt; + for (uint32_t v = 1; v < nPoints; ++v) { + ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt); + for (int32_t i = 0; i < 3; ++i) { + if (pt[i] < m_minBB[i]) + m_minBB[i] = pt[i]; + else if (pt[i] > m_maxBB[i]) + m_maxBB[i] = pt[i]; + } + } +} +template +void Volume::Voxelize(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const int32_t* const triangles, + const uint32_t strideTriangles, + const uint32_t nTriangles, + const size_t dim, + const Vec3& barycenter, + const double (&rot)[3][3]) { + if (nPoints == 0) { + return; + } + ComputeBB(points, stridePoints, nPoints, barycenter, rot); + m_barycenter = barycenter; + double d[3] = {m_maxBB[0] - m_minBB[0], m_maxBB[1] - m_minBB[1], + m_maxBB[2] - m_minBB[2]}; + double r; + if (d[0] >= d[1] && d[0] >= d[2]) { + r = d[0]; + m_dim[0] = dim; + m_dim[1] = 2 + static_cast(dim * d[1] / d[0]); + m_dim[2] = 2 + static_cast(dim * d[2] / d[0]); + } else if (d[1] >= d[0] && d[1] >= d[2]) { + r = d[1]; + m_dim[1] = dim; + m_dim[0] = 2 + static_cast(dim * d[0] / d[1]); + m_dim[2] = 2 + static_cast(dim * d[2] / d[1]); + } else { + r = d[2]; + m_dim[2] = dim; + m_dim[0] = 2 + static_cast(dim * d[0] / d[2]); + m_dim[1] = 2 + static_cast(dim * d[1] / d[2]); + } + + m_scale = r / (dim - 1); + double invScale = (dim - 1) / r; + + Allocate(); + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + m_numVoxelsOutsideSurface = 0; + + Vec3 p[3]; + size_t i, j, k; + size_t i0, j0, k0; + size_t i1, j1, k1; + Vec3 boxcenter; + Vec3 pt; + const Vec3 boxhalfsize(0.5, 0.5, 0.5); + for (size_t t = 0, ti = 0; t < nTriangles; ++t, ti += strideTriangles) { + Vec3 tri(triangles[ti + 0], triangles[ti + 1], triangles[ti + 2]); + for (int32_t c = 0; c < 3; ++c) { + ComputeAlignedPoint(points, tri[c] * stridePoints, barycenter, rot, pt); + p[c][0] = (pt[0] - m_minBB[0]) * invScale; + p[c][1] = (pt[1] - m_minBB[1]) * invScale; + p[c][2] = (pt[2] - m_minBB[2]) * invScale; + i = static_cast(p[c][0] + 0.5); + j = static_cast(p[c][1] + 0.5); + k = static_cast(p[c][2] + 0.5); + assert(i < m_dim[0] && i >= 0 && j < m_dim[1] && j >= 0 && k < m_dim[2] && + k >= 0); + + if (c == 0) { + i0 = i1 = i; + j0 = j1 = j; + k0 = k1 = k; + } else { + if (i < i0) + i0 = i; + if (j < j0) + j0 = j; + if (k < k0) + k0 = k; + if (i > i1) + i1 = i; + if (j > j1) + j1 = j; + if (k > k1) + k1 = k; + } + } + if (i0 > 0) + --i0; + if (j0 > 0) + --j0; + if (k0 > 0) + --k0; + if (i1 < m_dim[0]) + ++i1; + if (j1 < m_dim[1]) + ++j1; + if (k1 < m_dim[2]) + ++k1; + for (size_t i = i0; i < i1; ++i) { + boxcenter[0] = (double)i; + for (size_t j = j0; j < j1; ++j) { + boxcenter[1] = (double)j; + for (size_t k = k0; k < k1; ++k) { + boxcenter[2] = (double)k; + int32_t res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]); + unsigned char& value = GetVoxel(i, j, k); + if (res == 1 && value == PRIMITIVE_UNDEFINED) { + value = PRIMITIVE_ON_SURFACE; + ++m_numVoxelsOnSurface; + } + } + } + } + } + FillOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1); + FillOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]); + FillOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]); + FillOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]); + FillInsideSurface(); +} +} // namespace VHACD + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // VHACD_VOLUME_H diff --git a/src/VHACD_Lib/src/VHACD-ASYNC.cpp b/src/VHACD_Lib/src/VHACD-ASYNC.cpp index 5a2f6be6..49aa1679 100644 --- a/src/VHACD_Lib/src/VHACD-ASYNC.cpp +++ b/src/VHACD_Lib/src/VHACD-ASYNC.cpp @@ -1,12 +1,12 @@ -#include "../public/VHACD.h" +#include +#include #include #include -#include -#include #include #include #include -#include +#include +#include "../public/VHACD.h" #define ENABLE_ASYNC 1 @@ -14,321 +14,324 @@ #define HACD_FREE(x) free(x) #define HACD_ASSERT(x) assert(x) -namespace VHACD -{ - -class MyHACD_API : public VHACD::IVHACD, public VHACD::IVHACD::IUserCallback, VHACD::IVHACD::IUserLogger -{ -public: - MyHACD_API(void) - { - mVHACD = VHACD::CreateVHACD(); - } - - virtual ~MyHACD_API(void) - { - releaseHACD(); - Cancel(); - mVHACD->Release(); - } - - - virtual bool Compute(const double* const _points, - const uint32_t countPoints, - const uint32_t* const _triangles, - const uint32_t countTriangles, - const Parameters& _desc) final - { +namespace VHACD { + +class MyHACD_API : public VHACD::IVHACD, + public VHACD::IVHACD::IUserCallback, + VHACD::IVHACD::IUserLogger { + public: + MyHACD_API(void) { mVHACD = VHACD::CreateVHACD(); } + + virtual ~MyHACD_API(void) { + releaseHACD(); + Cancel(); + mVHACD->Release(); + } + + virtual bool Compute(const double* const _points, + const uint32_t countPoints, + const uint32_t* const _triangles, + const uint32_t countTriangles, + const Parameters& _desc) final { #if ENABLE_ASYNC - Cancel(); // if we previously had a solution running; cancel it. - releaseHACD(); - - // We need to copy the input vertices and triangles into our own buffers so we can operate - // on them safely from the background thread. - mVertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3); - mIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*countTriangles * 3); - memcpy(mVertices, _points, sizeof(double)*countPoints * 3); - memcpy(mIndices, _triangles, sizeof(uint32_t)*countTriangles * 3); - mRunning = true; - mThread = new std::thread([this, countPoints, countTriangles, _desc]() - { - ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc); - mRunning = false; - }); + Cancel(); // if we previously had a solution running; cancel it. + releaseHACD(); + // We need to copy the input vertices and triangles into our own buffers so + // we can operate on them safely from the background thread. + mVertices = (double*)HACD_ALLOC(sizeof(double) * countPoints * 3); + mIndices = (uint32_t*)HACD_ALLOC(sizeof(uint32_t) * countTriangles * 3); + memcpy(mVertices, _points, sizeof(double) * countPoints * 3); + memcpy(mIndices, _triangles, sizeof(uint32_t) * countTriangles * 3); + mRunning = true; + mThread = new std::thread([this, countPoints, countTriangles, _desc]() { + ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc); + mRunning = false; + }); #else - releaseHACD(); - ComputeNow(_points, countPoints, _triangles, countTriangles, _desc); + releaseHACD(); + ComputeNow(_points, countPoints, _triangles, countTriangles, _desc); #endif - return true; - } - - bool ComputeNow(const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& _desc) - { - uint32_t ret = 0; - - mHullCount = 0; - mCallback = _desc.m_callback; - mLogger = _desc.m_logger; - - IVHACD::Parameters desc = _desc; - // Set our intercepting callback interfaces if non-null - desc.m_callback = desc.m_callback ? this : nullptr; - desc.m_logger = desc.m_logger ? this : nullptr; - - if ( countPoints ) - { - bool ok = mVHACD->Compute(points, countPoints, triangles, countTriangles, desc); - if (ok) - { - ret = mVHACD->GetNConvexHulls(); - mHulls = new IVHACD::ConvexHull[ret]; - for (uint32_t i = 0; i < ret; i++) - { - VHACD::IVHACD::ConvexHull vhull; - mVHACD->GetConvexHull(i, vhull); - VHACD::IVHACD::ConvexHull h; - h.m_nPoints = vhull.m_nPoints; - h.m_points = (double *)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints); - memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints); - h.m_nTriangles = vhull.m_nTriangles; - h.m_triangles = (uint32_t *)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles); - memcpy(h.m_triangles, vhull.m_triangles, sizeof(uint32_t) * 3 * h.m_nTriangles); - h.m_volume = vhull.m_volume; - h.m_center[0] = vhull.m_center[0]; - h.m_center[1] = vhull.m_center[1]; - h.m_center[2] = vhull.m_center[2]; - mHulls[i] = h; - if (mCancel) - { - ret = 0; - break; - } - } - } - } - - mHullCount = ret; - return ret ? true : false; - } - - void releaseHull(VHACD::IVHACD::ConvexHull &h) - { - HACD_FREE((void *)h.m_triangles); - HACD_FREE((void *)h.m_points); - h.m_triangles = nullptr; - h.m_points = nullptr; - } - - virtual void GetConvexHull(const uint32_t index, VHACD::IVHACD::ConvexHull& ch) const final - { - if ( index < mHullCount ) - { - ch = mHulls[index]; - } - } - - void releaseHACD(void) // release memory associated with the last HACD request - { - for (uint32_t i=0; iCancel(); // Set the cancel signal to the base VHACD - } - if (mThread) - { - mThread->join(); // Wait for the thread to fully exit before we delete the instance - delete mThread; - mThread = nullptr; - Log("Convex Decomposition thread canceled\n"); - } - mCancel = false; // clear the cancel semaphore - } - - virtual bool Compute(const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) final - { - - double *vertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3); - const float *source = points; - double *dest = vertices; - for (uint32_t i = 0; i < countPoints; i++) - { - dest[0] = source[0]; - dest[1] = source[1]; - dest[2] = source[2]; - dest += 3; - source += 3; - } - - bool ret = Compute(vertices, countPoints, triangles, countTriangles, params); - HACD_FREE(vertices); - return ret; - } - - virtual uint32_t GetNConvexHulls() const final - { - processPendingMessages(); - return mHullCount; - } - - virtual void Clean(void) final // release internally allocated memory - { - Cancel(); - releaseHACD(); - mVHACD->Clean(); - } - - virtual void Release(void) final // release IVHACD - { - delete this; - } - - virtual bool OCLInit(void* const oclDevice, - IVHACD::IUserLogger* const logger = 0) final - { - return mVHACD->OCLInit(oclDevice, logger); - } - - virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final - { - return mVHACD->OCLRelease(logger); - } - - virtual void Update(const double overallProgress, - const double stageProgress, - const double operationProgress, - const char* const stage, - const char* const operation) final - { - mMessageMutex.lock(); - mHaveUpdateMessage = true; - mOverallProgress = overallProgress; - mStageProgress = stageProgress; - mOperationProgress = operationProgress; - mStage = std::string(stage); - mOperation = std::string(operation); - mMessageMutex.unlock(); - } - - virtual void Log(const char* const msg) final - { - mMessageMutex.lock(); - mHaveLogMessage = true; - mMessage = std::string(msg); - mMessageMutex.unlock(); - } - - virtual bool IsReady(void) const final - { - processPendingMessages(); - return !mRunning; - } - - // As a convenience for the calling application we only send it update and log messages from it's own main - // thread. This reduces the complexity burden on the caller by making sure it only has to deal with log - // messages in it's main application thread. - void processPendingMessages(void) const - { - // If we have a new update message and the user has specified a callback we send the message and clear the semaphore - if (mHaveUpdateMessage && mCallback) - { - mMessageMutex.lock(); - mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, mStage.c_str(), mOperation.c_str()); - mHaveUpdateMessage = false; - mMessageMutex.unlock(); - } - // If we have a new log message and the user has specified a callback we send the message and clear the semaphore - if (mHaveLogMessage && mLogger) - { - mMessageMutex.lock(); - mLogger->Log(mMessage.c_str()); - mHaveLogMessage = false; - mMessageMutex.unlock(); - } - } - - // Will compute the center of mass of the convex hull decomposition results and return it - // in 'centerOfMass'. Returns false if the center of mass could not be computed. - virtual bool ComputeCenterOfMass(double centerOfMass[3]) const - { - bool ret = false; - - centerOfMass[0] = 0; - centerOfMass[1] = 0; - centerOfMass[2] = 0; - - if (mVHACD && IsReady() ) - { - ret = mVHACD->ComputeCenterOfMass(centerOfMass); - } - return ret; - } - -private: - double *mVertices{ nullptr }; - uint32_t *mIndices{ nullptr }; - std::atomic< uint32_t> mHullCount{ 0 }; - VHACD::IVHACD::ConvexHull *mHulls{ nullptr }; - VHACD::IVHACD::IUserCallback *mCallback{ nullptr }; - VHACD::IVHACD::IUserLogger *mLogger{ nullptr }; - VHACD::IVHACD *mVHACD{ nullptr }; - std::thread *mThread{ nullptr }; - std::atomic< bool > mRunning{ false }; - std::atomic mCancel{ false }; - - // Thread safe caching mechanism for messages and update status. - // This is so that caller always gets messages in his own thread - // Member variables are marked as 'mutable' since the message dispatch function - // is called from const query methods. - mutable std::mutex mMessageMutex; - mutable std::atomic< bool > mHaveUpdateMessage{ false }; - mutable std::atomic< bool > mHaveLogMessage{ false }; - mutable double mOverallProgress{ 0 }; - mutable double mStageProgress{ 0 }; - mutable double mOperationProgress{ 0 }; - mutable std::string mStage; - mutable std::string mOperation; - mutable std::string mMessage; + return true; + } + virtual bool computeVoxelField(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& _desc) { + return mVHACD->computeVoxelField(points, countPoints, triangles, + countTriangles, _desc); + } + + virtual Volume* getVoxelField() { return mVHACD->getVoxelField(); } + + bool ComputeNow(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& _desc) { + uint32_t ret = 0; + + mHullCount = 0; + mCallback = _desc.m_callback; + mLogger = _desc.m_logger; + + IVHACD::Parameters desc = _desc; + // Set our intercepting callback interfaces if non-null + desc.m_callback = desc.m_callback ? this : nullptr; + desc.m_logger = desc.m_logger ? this : nullptr; + + if (countPoints) { + bool ok = + mVHACD->Compute(points, countPoints, triangles, countTriangles, desc); + if (ok) { + ret = mVHACD->GetNConvexHulls(); + mHulls = new IVHACD::ConvexHull[ret]; + for (uint32_t i = 0; i < ret; i++) { + VHACD::IVHACD::ConvexHull vhull; + mVHACD->GetConvexHull(i, vhull); + VHACD::IVHACD::ConvexHull h; + h.m_nPoints = vhull.m_nPoints; + h.m_points = (double*)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints); + memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints); + h.m_nTriangles = vhull.m_nTriangles; + h.m_triangles = + (uint32_t*)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles); + memcpy(h.m_triangles, vhull.m_triangles, + sizeof(uint32_t) * 3 * h.m_nTriangles); + h.m_volume = vhull.m_volume; + h.m_center[0] = vhull.m_center[0]; + h.m_center[1] = vhull.m_center[1]; + h.m_center[2] = vhull.m_center[2]; + mHulls[i] = h; + if (mCancel) { + ret = 0; + break; + } + } + } + } + + mHullCount = ret; + return ret ? true : false; + } + + void releaseHull(VHACD::IVHACD::ConvexHull& h) { + HACD_FREE((void*)h.m_triangles); + HACD_FREE((void*)h.m_points); + h.m_triangles = nullptr; + h.m_points = nullptr; + } + + virtual void GetConvexHull(const uint32_t index, + VHACD::IVHACD::ConvexHull& ch) const final { + if (index < mHullCount) { + ch = mHulls[index]; + } + } + + void releaseHACD( + void) // release memory associated with the last HACD request + { + for (uint32_t i = 0; i < mHullCount; i++) { + releaseHull(mHulls[i]); + } + delete[] mHulls; + mHulls = nullptr; + mHullCount = 0; + HACD_FREE(mVertices); + mVertices = nullptr; + HACD_FREE(mIndices); + mIndices = nullptr; + } + + virtual void release(void) // release the HACD_API interface + { + delete this; + } + + virtual uint32_t getHullCount(void) { return mHullCount; } + + virtual void Cancel() final { + if (mRunning) { + mVHACD->Cancel(); // Set the cancel signal to the base VHACD + } + if (mThread) { + mThread->join(); // Wait for the thread to fully exit before we delete + // the instance + delete mThread; + mThread = nullptr; + Log("Convex Decomposition thread canceled\n"); + } + mCancel = false; // clear the cancel semaphore + } + + virtual bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) final { + double* vertices = (double*)HACD_ALLOC(sizeof(double) * countPoints * 3); + const float* source = points; + double* dest = vertices; + for (uint32_t i = 0; i < countPoints; i++) { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest += 3; + source += 3; + } + + bool ret = + Compute(vertices, countPoints, triangles, countTriangles, params); + HACD_FREE(vertices); + return ret; + } + + virtual bool computeVoxelField(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) final { + double* vertices = (double*)HACD_ALLOC(sizeof(double) * countPoints * 3); + const float* source = points; + double* dest = vertices; + for (uint32_t i = 0; i < countPoints; i++) { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest += 3; + source += 3; + } + + bool ret = computeVoxelField(vertices, countPoints, triangles, + countTriangles, params); + HACD_FREE(vertices); + return ret; + } + + virtual uint32_t GetNConvexHulls() const final { + processPendingMessages(); + return mHullCount; + } + + virtual void Clean(void) final // release internally allocated memory + { + Cancel(); + releaseHACD(); + mVHACD->Clean(); + } + + virtual void Release(void) final // release IVHACD + { + delete this; + } + + virtual bool OCLInit(void* const oclDevice, + IVHACD::IUserLogger* const logger = 0) final { + return mVHACD->OCLInit(oclDevice, logger); + } + + virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final { + return mVHACD->OCLRelease(logger); + } + + virtual void Update(const double overallProgress, + const double stageProgress, + const double operationProgress, + const char* const stage, + const char* const operation) final { + mMessageMutex.lock(); + mHaveUpdateMessage = true; + mOverallProgress = overallProgress; + mStageProgress = stageProgress; + mOperationProgress = operationProgress; + mStage = std::string(stage); + mOperation = std::string(operation); + mMessageMutex.unlock(); + } + + virtual void Log(const char* const msg) final { + mMessageMutex.lock(); + mHaveLogMessage = true; + mMessage = std::string(msg); + mMessageMutex.unlock(); + } + + virtual bool IsReady(void) const final { + processPendingMessages(); + return !mRunning; + } + + // As a convenience for the calling application we only send it update and log + // messages from it's own main thread. This reduces the complexity burden on + // the caller by making sure it only has to deal with log messages in it's + // main application thread. + void processPendingMessages(void) const { + // If we have a new update message and the user has specified a callback we + // send the message and clear the semaphore + if (mHaveUpdateMessage && mCallback) { + mMessageMutex.lock(); + mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, + mStage.c_str(), mOperation.c_str()); + mHaveUpdateMessage = false; + mMessageMutex.unlock(); + } + // If we have a new log message and the user has specified a callback we + // send the message and clear the semaphore + if (mHaveLogMessage && mLogger) { + mMessageMutex.lock(); + mLogger->Log(mMessage.c_str()); + mHaveLogMessage = false; + mMessageMutex.unlock(); + } + } + + // Will compute the center of mass of the convex hull decomposition results + // and return it in 'centerOfMass'. Returns false if the center of mass could + // not be computed. + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const { + bool ret = false; + + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + + if (mVHACD && IsReady()) { + ret = mVHACD->ComputeCenterOfMass(centerOfMass); + } + return ret; + } + + private: + double* mVertices{nullptr}; + uint32_t* mIndices{nullptr}; + std::atomic mHullCount{0}; + VHACD::IVHACD::ConvexHull* mHulls{nullptr}; + VHACD::IVHACD::IUserCallback* mCallback{nullptr}; + VHACD::IVHACD::IUserLogger* mLogger{nullptr}; + VHACD::IVHACD* mVHACD{nullptr}; + std::thread* mThread{nullptr}; + std::atomic mRunning{false}; + std::atomic mCancel{false}; + + // Thread safe caching mechanism for messages and update status. + // This is so that caller always gets messages in his own thread + // Member variables are marked as 'mutable' since the message dispatch + // function is called from const query methods. + mutable std::mutex mMessageMutex; + mutable std::atomic mHaveUpdateMessage{false}; + mutable std::atomic mHaveLogMessage{false}; + mutable double mOverallProgress{0}; + mutable double mStageProgress{0}; + mutable double mOperationProgress{0}; + mutable std::string mStage; + mutable std::string mOperation; + mutable std::string mMessage; }; -IVHACD* CreateVHACD_ASYNC(void) -{ - MyHACD_API *m = new MyHACD_API; - return static_cast(m); +IVHACD* CreateVHACD_ASYNC(void) { + MyHACD_API* m = new MyHACD_API; + return static_cast(m); } - -}; // end of VHACD namespace - +}; // namespace VHACD diff --git a/src/VHACD_Lib/src/VHACD.cpp b/src/VHACD_Lib/src/VHACD.cpp index 54cc6e37..0a65346a 100644 --- a/src/VHACD_Lib/src/VHACD.cpp +++ b/src/VHACD_Lib/src/VHACD.cpp @@ -1,16 +1,30 @@ /* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) All rights reserved. - - - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - - 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CRT_SECURE_NO_WARNINGS @@ -20,13 +34,15 @@ #include #include #include +#include #include #include #if _OPENMP #include -#endif // _OPENMP +#endif // _OPENMP #include "../public/VHACD.h" +#include "FloatMath.h" #include "btConvexHullComputer.h" #include "vhacdICHull.h" #include "vhacdMesh.h" @@ -34,8 +50,6 @@ #include "vhacdTimer.h" #include "vhacdVHACD.h" #include "vhacdVector.h" -#include "vhacdVolume.h" -#include "FloatMath.h" #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -44,97 +58,104 @@ #define MAX_DOUBLE (1.79769e+308) #ifdef _MSC_VER -#pragma warning(disable:4267 4100 4244 4456) +#pragma warning(disable : 4267 4100 4244 4456) #endif #ifdef USE_SSE #include const int32_t SIMD_WIDTH = 4; -inline int32_t FindMinimumElement(const float* const d, float* const _, const int32_t n) -{ - // Min within vectors - __m128 min_i = _mm_set1_ps(-1.0f); - __m128 min_v = _mm_set1_ps(std::numeric_limits::max()); - for (int32_t i = 0; i <= n - SIMD_WIDTH; i += SIMD_WIDTH) { - const __m128 data = _mm_load_ps(&d[i]); - const __m128 pred = _mm_cmplt_ps(data, min_v); - - min_i = _mm_blendv_ps(min_i, _mm_set1_ps(i), pred); - min_v = _mm_min_ps(data, min_v); - } - - /* Min within vector */ - const __m128 min1 = _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(1, 0, 3, 2)); - const __m128 min2 = _mm_min_ps(min_v, min1); - const __m128 min3 = _mm_shuffle_ps(min2, min2, _MM_SHUFFLE(0, 1, 0, 1)); - const __m128 min4 = _mm_min_ps(min2, min3); - float min_d = _mm_cvtss_f32(min4); - - // Min index - const int32_t min_idx = __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4))); - int32_t ret = min_i[min_idx] + min_idx; - - // Trailing elements - for (int32_t i = (n & ~(SIMD_WIDTH - 1)); i < n; ++i) { - if (d[i] < min_d) { - min_d = d[i]; - ret = i; - } - } - - *m = min_d; - return ret; +inline int32_t FindMinimumElement(const float* const d, + float* const _, + const int32_t n) { + // Min within vectors + __m128 min_i = _mm_set1_ps(-1.0f); + __m128 min_v = _mm_set1_ps(std::numeric_limits::max()); + for (int32_t i = 0; i <= n - SIMD_WIDTH; i += SIMD_WIDTH) { + const __m128 data = _mm_load_ps(&d[i]); + const __m128 pred = _mm_cmplt_ps(data, min_v); + + min_i = _mm_blendv_ps(min_i, _mm_set1_ps(i), pred); + min_v = _mm_min_ps(data, min_v); + } + + /* Min within vector */ + const __m128 min1 = _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 min2 = _mm_min_ps(min_v, min1); + const __m128 min3 = _mm_shuffle_ps(min2, min2, _MM_SHUFFLE(0, 1, 0, 1)); + const __m128 min4 = _mm_min_ps(min2, min3); + float min_d = _mm_cvtss_f32(min4); + + // Min index + const int32_t min_idx = + __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4))); + int32_t ret = min_i[min_idx] + min_idx; + + // Trailing elements + for (int32_t i = (n & ~(SIMD_WIDTH - 1)); i < n; ++i) { + if (d[i] < min_d) { + min_d = d[i]; + ret = i; + } + } + + *m = min_d; + return ret; } -inline int32_t FindMinimumElement(const float* const d, float* const m, const int32_t begin, const int32_t end) -{ - // Leading elements - int32_t min_i = -1; - float min_d = std::numeric_limits::max(); - const int32_t aligned = (begin & ~(SIMD_WIDTH - 1)) + ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0); - for (int32_t i = begin; i < std::min(end, aligned); ++i) { - if (d[i] < min_d) { - min_d = d[i]; - min_i = i; - } - } - - // Middle and trailing elements - float r_m = std::numeric_limits::max(); - const int32_t n = end - aligned; - const int32_t r_i = (n > 0) ? FindMinimumElement(&d[aligned], &r_m, n) : 0; - - // Pick the lowest - if (r_m < min_d) { - *m = r_m; - return r_i + aligned; - } - else { - *m = min_d; - return min_i; - } +inline int32_t FindMinimumElement(const float* const d, + float* const m, + const int32_t begin, + const int32_t end) { + // Leading elements + int32_t min_i = -1; + float min_d = std::numeric_limits::max(); + const int32_t aligned = (begin & ~(SIMD_WIDTH - 1)) + + ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0); + for (int32_t i = begin; i < std::min(end, aligned); ++i) { + if (d[i] < min_d) { + min_d = d[i]; + min_i = i; + } + } + + // Middle and trailing elements + float r_m = std::numeric_limits::max(); + const int32_t n = end - aligned; + const int32_t r_i = (n > 0) ? FindMinimumElement(&d[aligned], &r_m, n) : 0; + + // Pick the lowest + if (r_m < min_d) { + *m = r_m; + return r_i + aligned; + } else { + *m = min_d; + return min_i; + } } #else -inline int32_t FindMinimumElement(const float* const d, float* const m, const int32_t begin, const int32_t end) -{ - int32_t idx = -1; - float min = (std::numeric_limits::max)(); - for (size_t i = begin; i < size_t(end); ++i) { - if (d[i] < min) { - idx = i; - min = d[i]; - } - } - - *m = min; - return idx; +inline int32_t FindMinimumElement(const float* const d, + float* const m, + const int32_t begin, + const int32_t end) { + int32_t idx = -1; + float min = (std::numeric_limits::max)(); + for (size_t i = begin; i < size_t(end); ++i) { + if (d[i] < min) { + idx = i; + min = d[i]; + } + } + + *m = min; + return idx; } #endif //#define OCL_SOURCE_FROM_FILE #ifndef OCL_SOURCE_FROM_FILE -const char* oclProgramSource = "\ +const char* oclProgramSource = + "\ __kernel void ComputePartialVolumes(__global short4 * voxels, \ const int numVoxels, \ const float4 plane, \ @@ -201,1389 +222,1455 @@ __kernel void ComputePartialSums(__global uint4 * data, data[get_group_id(0)] = partialSums[0]; \ } \ }"; -#endif //OCL_SOURCE_FROM_FILE +#endif // OCL_SOURCE_FROM_FILE namespace VHACD { -IVHACD* CreateVHACD(void) -{ - return new VHACD(); +IVHACD* CreateVHACD(void) { + return new VHACD(); } -bool VHACD::OCLInit(void* const oclDevice, IUserLogger* const logger) -{ +bool VHACD::OCLInit(void* const oclDevice, IUserLogger* const logger) { #ifdef CL_VERSION_1_1 - m_oclDevice = (cl_device_id*)oclDevice; - cl_int error; - m_oclContext = clCreateContext(NULL, 1, m_oclDevice, NULL, NULL, &error); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create context\n"); - } - return false; + m_oclDevice = (cl_device_id*)oclDevice; + cl_int error; + m_oclContext = clCreateContext(NULL, 1, m_oclDevice, NULL, NULL, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create context\n"); } + return false; + } #ifdef OCL_SOURCE_FROM_FILE - std::string cl_files = OPENCL_CL_FILES; + std::string cl_files = OPENCL_CL_FILES; // read kernal from file #ifdef _WIN32 - std::replace(cl_files.begin(), cl_files.end(), '/', '\\'); -#endif // _WIN32 - - FILE* program_handle = fopen(cl_files.c_str(), "rb"); - fseek(program_handle, 0, SEEK_END); - size_t program_size = ftell(program_handle); - rewind(program_handle); - char* program_buffer = new char[program_size + 1]; - program_buffer[program_size] = '\0'; - fread(program_buffer, sizeof(char), program_size, program_handle); - fclose(program_handle); - // create program - m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&program_buffer, &program_size, &error); - delete[] program_buffer; + std::replace(cl_files.begin(), cl_files.end(), '/', '\\'); +#endif // _WIN32 + + FILE* program_handle = fopen(cl_files.c_str(), "rb"); + fseek(program_handle, 0, SEEK_END); + size_t program_size = ftell(program_handle); + rewind(program_handle); + char* program_buffer = new char[program_size + 1]; + program_buffer[program_size] = '\0'; + fread(program_buffer, sizeof(char), program_size, program_handle); + fclose(program_handle); + // create program + m_oclProgram = clCreateProgramWithSource( + m_oclContext, 1, (const char**)&program_buffer, &program_size, &error); + delete[] program_buffer; #else - size_t program_size = strlen(oclProgramSource); - m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&oclProgramSource, &program_size, &error); + size_t program_size = strlen(oclProgramSource); + m_oclProgram = clCreateProgramWithSource( + m_oclContext, 1, (const char**)&oclProgramSource, &program_size, &error); #endif + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create program\n"); + } + return false; + } + + /* Build program */ + error = clBuildProgram(m_oclProgram, 1, m_oclDevice, "-cl-denorms-are-zero", + NULL, NULL); + if (error != CL_SUCCESS) { + size_t log_size; + /* Find Size of log and print to std output */ + clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, 0, + NULL, &log_size); + char* program_log = new char[log_size + 2]; + program_log[log_size] = '\n'; + program_log[log_size + 1] = '\0'; + clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, + log_size + 1, program_log, NULL); + if (logger) { + logger->Log("Couldn't build program\n"); + logger->Log(program_log); + } + delete[] program_log; + return false; + } + + delete[] m_oclQueue; + delete[] m_oclKernelComputePartialVolumes; + delete[] m_oclKernelComputeSum; + m_oclQueue = new cl_command_queue[m_ompNumProcessors]; + m_oclKernelComputePartialVolumes = new cl_kernel[m_ompNumProcessors]; + m_oclKernelComputeSum = new cl_kernel[m_ompNumProcessors]; + + const char nameKernelComputePartialVolumes[] = "ComputePartialVolumes"; + const char nameKernelComputeSum[] = "ComputePartialSums"; + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + m_oclKernelComputePartialVolumes[k] = + clCreateKernel(m_oclProgram, nameKernelComputePartialVolumes, &error); if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create program\n"); - } - return false; + if (logger) { + logger->Log("Couldn't create kernel\n"); + } + return false; } - - /* Build program */ - error = clBuildProgram(m_oclProgram, 1, m_oclDevice, "-cl-denorms-are-zero", NULL, NULL); + m_oclKernelComputeSum[k] = + clCreateKernel(m_oclProgram, nameKernelComputeSum, &error); if (error != CL_SUCCESS) { - size_t log_size; - /* Find Size of log and print to std output */ - clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size); - char* program_log = new char[log_size + 2]; - program_log[log_size] = '\n'; - program_log[log_size + 1] = '\0'; - clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, log_size + 1, program_log, NULL); - if (logger) { - logger->Log("Couldn't build program\n"); - logger->Log(program_log); - } - delete[] program_log; - return false; + if (logger) { + logger->Log("Couldn't create kernel\n"); + } + return false; + } + } + + error = clGetKernelWorkGroupInfo(m_oclKernelComputePartialVolumes[0], + *m_oclDevice, CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), &m_oclWorkGroupSize, NULL); + size_t workGroupSize = 0; + error = clGetKernelWorkGroupInfo(m_oclKernelComputeSum[0], *m_oclDevice, + CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), + &workGroupSize, NULL); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't query work group info\n"); } + return false; + } - delete[] m_oclQueue; - delete[] m_oclKernelComputePartialVolumes; - delete[] m_oclKernelComputeSum; - m_oclQueue = new cl_command_queue[m_ompNumProcessors]; - m_oclKernelComputePartialVolumes = new cl_kernel[m_ompNumProcessors]; - m_oclKernelComputeSum = new cl_kernel[m_ompNumProcessors]; - - const char nameKernelComputePartialVolumes[] = "ComputePartialVolumes"; - const char nameKernelComputeSum[] = "ComputePartialSums"; - for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - m_oclKernelComputePartialVolumes[k] = clCreateKernel(m_oclProgram, nameKernelComputePartialVolumes, &error); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create kernel\n"); - } - return false; - } - m_oclKernelComputeSum[k] = clCreateKernel(m_oclProgram, nameKernelComputeSum, &error); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create kernel\n"); - } - return false; - } - } + if (workGroupSize < m_oclWorkGroupSize) { + m_oclWorkGroupSize = workGroupSize; + } - error = clGetKernelWorkGroupInfo(m_oclKernelComputePartialVolumes[0], - *m_oclDevice, - CL_KERNEL_WORK_GROUP_SIZE, - sizeof(size_t), - &m_oclWorkGroupSize, - NULL); - size_t workGroupSize = 0; - error = clGetKernelWorkGroupInfo(m_oclKernelComputeSum[0], - *m_oclDevice, - CL_KERNEL_WORK_GROUP_SIZE, - sizeof(size_t), - &workGroupSize, - NULL); + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + m_oclQueue[k] = clCreateCommandQueue( + m_oclContext, *m_oclDevice, 0 /*CL_QUEUE_PROFILING_ENABLE*/, &error); if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create queue\n"); + } + return false; + } + } + return true; +#else // CL_VERSION_1_1 + return false; +#endif // CL_VERSION_1_1 +} +bool VHACD::OCLRelease(IUserLogger* const logger) { +#ifdef CL_VERSION_1_1 + cl_int error; + if (m_oclKernelComputePartialVolumes) { + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseKernel(m_oclKernelComputePartialVolumes[k]); + if (error != CL_SUCCESS) { if (logger) { - logger->Log("Couldn't query work group info\n"); + logger->Log("Couldn't release kernal\n"); } return false; + } } - - if (workGroupSize < m_oclWorkGroupSize) { - m_oclWorkGroupSize = workGroupSize; - } - + delete[] m_oclKernelComputePartialVolumes; + } + if (m_oclKernelComputeSum) { for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - m_oclQueue[k] = clCreateCommandQueue(m_oclContext, *m_oclDevice, 0 /*CL_QUEUE_PROFILING_ENABLE*/, &error); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create queue\n"); - } - return false; - } - } - return true; -#else //CL_VERSION_1_1 - return false; -#endif //CL_VERSION_1_1 -} -bool VHACD::OCLRelease(IUserLogger* const logger) -{ -#ifdef CL_VERSION_1_1 - cl_int error; - if (m_oclKernelComputePartialVolumes) { - for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - error = clReleaseKernel(m_oclKernelComputePartialVolumes[k]); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't release kernal\n"); - } - return false; - } - } - delete[] m_oclKernelComputePartialVolumes; - } - if (m_oclKernelComputeSum) { - for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - error = clReleaseKernel(m_oclKernelComputeSum[k]); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't release kernal\n"); - } - return false; - } - } - delete[] m_oclKernelComputeSum; - } - if (m_oclQueue) { - for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - error = clReleaseCommandQueue(m_oclQueue[k]); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't release queue\n"); - } - return false; - } - } - delete[] m_oclQueue; - } - error = clReleaseProgram(m_oclProgram); - if (error != CL_SUCCESS) { + error = clReleaseKernel(m_oclKernelComputeSum[k]); + if (error != CL_SUCCESS) { if (logger) { - logger->Log("Couldn't release program\n"); + logger->Log("Couldn't release kernal\n"); } return false; + } } - error = clReleaseContext(m_oclContext); - if (error != CL_SUCCESS) { + delete[] m_oclKernelComputeSum; + } + if (m_oclQueue) { + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseCommandQueue(m_oclQueue[k]); + if (error != CL_SUCCESS) { if (logger) { - logger->Log("Couldn't release context\n"); + logger->Log("Couldn't release queue\n"); } return false; + } } - - return true; -#else //CL_VERSION_1_1 - return false; -#endif //CL_VERSION_1_1 -} -void VHACD::ComputePrimitiveSet(const Parameters& params) -{ - if (GetCancel()) { - return; - } - m_timer.Tic(); - - m_stage = "Compute primitive set"; - m_operation = "Convert volume to pset"; - - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - Update(0.0, 0.0, params); - if (params.m_mode == 0) { - VoxelSet* vset = new VoxelSet; - m_volume->Convert(*vset); - m_pset = vset; + delete[] m_oclQueue; + } + error = clReleaseProgram(m_oclProgram); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release program\n"); } - else { - TetrahedronSet* tset = new TetrahedronSet; - m_volume->Convert(*tset); - m_pset = tset; + return false; + } + error = clReleaseContext(m_oclContext); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release context\n"); } + return false; + } - delete m_volume; - m_volume = 0; - - if (params.m_logger) { - msg.str(""); - msg << "\t # primitives " << m_pset->GetNPrimitives() << std::endl; - msg << "\t # inside surface " << m_pset->GetNPrimitivesInsideSurf() << std::endl; - msg << "\t # on surface " << m_pset->GetNPrimitivesOnSurf() << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + return true; +#else // CL_VERSION_1_1 + return false; +#endif // CL_VERSION_1_1 +} +void VHACD::ComputePrimitiveSet(const Parameters& params) { + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Compute primitive set"; + m_operation = "Convert volume to pset"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + if (params.m_mode == 0) { + VoxelSet* vset = new VoxelSet; + m_volume->Convert(*vset); + m_pset = vset; + } else { + TetrahedronSet* tset = new TetrahedronSet; + m_volume->Convert(*tset); + m_pset = tset; + } + + delete m_volume; + m_volume = 0; + + if (params.m_logger) { + msg.str(""); + msg << "\t # primitives " << m_pset->GetNPrimitives() + << std::endl; + msg << "\t # inside surface " + << m_pset->GetNPrimitivesInsideSurf() << std::endl; + msg << "\t # on surface " << m_pset->GetNPrimitivesOnSurf() + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + m_overallProgress = 15.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } +} +bool VHACD::Compute(const double* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params) { + return ComputeACD(points, nPoints, triangles, nTriangles, params); +} +bool VHACD::Compute(const float* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params) { + return ComputeACD(points, nPoints, triangles, nTriangles, params); +} +bool VHACD::computeVoxelField(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) { + return computeVoxelFieldHelper(points, countPoints, triangles, countTriangles, + params); +} +bool VHACD::computeVoxelField(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) { + return computeVoxelFieldHelper(points, countPoints, triangles, countTriangles, + params); +} - m_overallProgress = 15.0; - Update(100.0, 100.0, params); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } +Volume* VHACD::getVoxelField() { + return (Volume*)m_volume; // VoxelSet* vset = (VoxelSet*)pset } -bool VHACD::Compute(const double* const points, const uint32_t nPoints, - const uint32_t* const triangles,const uint32_t nTriangles, const Parameters& params) -{ - return ComputeACD(points, nPoints, triangles, nTriangles, params); + +double ComputePreferredCuttingDirection(const PrimitiveSet* const tset, + Vec3& dir) { + double ex = tset->GetEigenValue(AXIS_X); + double ey = tset->GetEigenValue(AXIS_Y); + double ez = tset->GetEigenValue(AXIS_Z); + double vx = (ey - ez) * (ey - ez); + double vy = (ex - ez) * (ex - ez); + double vz = (ex - ey) * (ex - ey); + if (vx < vy && vx < vz) { + double e = ey * ey + ez * ez; + dir[0] = 1.0; + dir[1] = 0.0; + dir[2] = 0.0; + return (e == 0.0) ? 0.0 : 1.0 - vx / e; + } else if (vy < vx && vy < vz) { + double e = ex * ex + ez * ez; + dir[0] = 0.0; + dir[1] = 1.0; + dir[2] = 0.0; + return (e == 0.0) ? 0.0 : 1.0 - vy / e; + } else { + double e = ex * ex + ey * ey; + dir[0] = 0.0; + dir[1] = 0.0; + dir[2] = 1.0; + return (e == 0.0) ? 0.0 : 1.0 - vz / e; + } } -bool VHACD::Compute(const float* const points,const uint32_t nPoints, - const uint32_t* const triangles,const uint32_t nTriangles, const Parameters& params) -{ - return ComputeACD(points, nPoints, triangles, nTriangles, params); +void ComputeAxesAlignedClippingPlanes(const VoxelSet& vset, + const short downsampling, + SArray& planes) { + const Vec3 minV = vset.GetMinBBVoxels(); + const Vec3 maxV = vset.GetMaxBBVoxels(); + Vec3 pt; + Plane plane; + const short i0 = minV[0]; + const short i1 = maxV[0]; + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; i += downsampling) { + pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); + plane.m_d = -pt[0]; + plane.m_index = i; + planes.PushBack(plane); + } + const short j0 = minV[1]; + const short j1 = maxV[1]; + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; j += downsampling) { + pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); + plane.m_d = -pt[1]; + plane.m_index = j; + planes.PushBack(plane); + } + const short k0 = minV[2]; + const short k1 = maxV[2]; + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; k += downsampling) { + pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); + plane.m_d = -pt[2]; + plane.m_index = k; + planes.PushBack(plane); + } } -double ComputePreferredCuttingDirection(const PrimitiveSet* const tset, Vec3& dir) -{ - double ex = tset->GetEigenValue(AXIS_X); - double ey = tset->GetEigenValue(AXIS_Y); - double ez = tset->GetEigenValue(AXIS_Z); - double vx = (ey - ez) * (ey - ez); - double vy = (ex - ez) * (ex - ez); - double vz = (ex - ey) * (ex - ey); - if (vx < vy && vx < vz) { - double e = ey * ey + ez * ez; - dir[0] = 1.0; - dir[1] = 0.0; - dir[2] = 0.0; - return (e == 0.0) ? 0.0 : 1.0 - vx / e; - } - else if (vy < vx && vy < vz) { - double e = ex * ex + ez * ez; - dir[0] = 0.0; - dir[1] = 1.0; - dir[2] = 0.0; - return (e == 0.0) ? 0.0 : 1.0 - vy / e; - } - else { - double e = ex * ex + ey * ey; - dir[0] = 0.0; - dir[1] = 0.0; - dir[2] = 1.0; - return (e == 0.0) ? 0.0 : 1.0 - vz / e; - } +void ComputeAxesAlignedClippingPlanes(const TetrahedronSet& tset, + const short downsampling, + SArray& planes) { + const Vec3 minV = tset.GetMinBB(); + const Vec3 maxV = tset.GetMaxBB(); + const double scale = tset.GetSacle(); + const short i0 = 0; + const short j0 = 0; + const short k0 = 0; + const short i1 = static_cast((maxV[0] - minV[0]) / scale + 0.5); + const short j1 = static_cast((maxV[1] - minV[1]) / scale + 0.5); + const short k1 = static_cast((maxV[2] - minV[2]) / scale + 0.5); + + Plane plane; + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; i += downsampling) { + double x = minV[0] + scale * i; + plane.m_d = -x; + plane.m_index = i; + planes.PushBack(plane); + } + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; j += downsampling) { + double y = minV[1] + scale * j; + plane.m_d = -y; + plane.m_index = j; + planes.PushBack(plane); + } + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; k += downsampling) { + double z = minV[2] + scale * k; + plane.m_d = -z; + plane.m_index = k; + planes.PushBack(plane); + } } -void ComputeAxesAlignedClippingPlanes(const VoxelSet& vset, const short downsampling, SArray& planes) -{ - const Vec3 minV = vset.GetMinBBVoxels(); - const Vec3 maxV = vset.GetMaxBBVoxels(); - Vec3 pt; - Plane plane; - const short i0 = minV[0]; - const short i1 = maxV[0]; +void RefineAxesAlignedClippingPlanes(const VoxelSet& vset, + const Plane& bestPlane, + const short downsampling, + SArray& planes) { + const Vec3 minV = vset.GetMinBBVoxels(); + const Vec3 maxV = vset.GetMaxBBVoxels(); + Vec3 pt; + Plane plane; + + if (bestPlane.m_axis == AXIS_X) { + const short i0 = MAX(minV[0], bestPlane.m_index - downsampling); + const short i1 = MIN(maxV[0], bestPlane.m_index + downsampling); plane.m_a = 1.0; plane.m_b = 0.0; plane.m_c = 0.0; plane.m_axis = AXIS_X; - for (short i = i0; i <= i1; i += downsampling) { - pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); - plane.m_d = -pt[0]; - plane.m_index = i; - planes.PushBack(plane); - } - const short j0 = minV[1]; - const short j1 = maxV[1]; + for (short i = i0; i <= i1; ++i) { + pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); + plane.m_d = -pt[0]; + plane.m_index = i; + planes.PushBack(plane); + } + } else if (bestPlane.m_axis == AXIS_Y) { + const short j0 = MAX(minV[1], bestPlane.m_index - downsampling); + const short j1 = MIN(maxV[1], bestPlane.m_index + downsampling); plane.m_a = 0.0; plane.m_b = 1.0; plane.m_c = 0.0; plane.m_axis = AXIS_Y; - for (short j = j0; j <= j1; j += downsampling) { - pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); - plane.m_d = -pt[1]; - plane.m_index = j; - planes.PushBack(plane); - } - const short k0 = minV[2]; - const short k1 = maxV[2]; + for (short j = j0; j <= j1; ++j) { + pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); + plane.m_d = -pt[1]; + plane.m_index = j; + planes.PushBack(plane); + } + } else { + const short k0 = MAX(minV[2], bestPlane.m_index - downsampling); + const short k1 = MIN(maxV[2], bestPlane.m_index + downsampling); plane.m_a = 0.0; plane.m_b = 0.0; plane.m_c = 1.0; plane.m_axis = AXIS_Z; - for (short k = k0; k <= k1; k += downsampling) { - pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); - plane.m_d = -pt[2]; - plane.m_index = k; - planes.PushBack(plane); + for (short k = k0; k <= k1; ++k) { + pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); + plane.m_d = -pt[2]; + plane.m_index = k; + planes.PushBack(plane); } + } } -void ComputeAxesAlignedClippingPlanes(const TetrahedronSet& tset, const short downsampling, SArray& planes) -{ - const Vec3 minV = tset.GetMinBB(); - const Vec3 maxV = tset.GetMaxBB(); - const double scale = tset.GetSacle(); - const short i0 = 0; - const short j0 = 0; - const short k0 = 0; - const short i1 = static_cast((maxV[0] - minV[0]) / scale + 0.5); - const short j1 = static_cast((maxV[1] - minV[1]) / scale + 0.5); - const short k1 = static_cast((maxV[2] - minV[2]) / scale + 0.5); - - Plane plane; +void RefineAxesAlignedClippingPlanes(const TetrahedronSet& tset, + const Plane& bestPlane, + const short downsampling, + SArray& planes) { + const Vec3 minV = tset.GetMinBB(); + const Vec3 maxV = tset.GetMaxBB(); + const double scale = tset.GetSacle(); + Plane plane; + + if (bestPlane.m_axis == AXIS_X) { + const short i0 = MAX(0, bestPlane.m_index - downsampling); + const short i1 = static_cast(MIN((maxV[0] - minV[0]) / scale + 0.5, + bestPlane.m_index + downsampling)); plane.m_a = 1.0; plane.m_b = 0.0; plane.m_c = 0.0; plane.m_axis = AXIS_X; - for (short i = i0; i <= i1; i += downsampling) { - double x = minV[0] + scale * i; - plane.m_d = -x; - plane.m_index = i; - planes.PushBack(plane); - } + for (short i = i0; i <= i1; ++i) { + double x = minV[0] + scale * i; + plane.m_d = -x; + plane.m_index = i; + planes.PushBack(plane); + } + } else if (bestPlane.m_axis == AXIS_Y) { + const short j0 = MAX(0, bestPlane.m_index - downsampling); + const short j1 = static_cast(MIN((maxV[1] - minV[1]) / scale + 0.5, + bestPlane.m_index + downsampling)); plane.m_a = 0.0; plane.m_b = 1.0; plane.m_c = 0.0; plane.m_axis = AXIS_Y; - for (short j = j0; j <= j1; j += downsampling) { - double y = minV[1] + scale * j; - plane.m_d = -y; - plane.m_index = j; - planes.PushBack(plane); - } + for (short j = j0; j <= j1; ++j) { + double y = minV[1] + scale * j; + plane.m_d = -y; + plane.m_index = j; + planes.PushBack(plane); + } + } else { + const short k0 = MAX(0, bestPlane.m_index - downsampling); + const short k1 = static_cast(MIN((maxV[2] - minV[2]) / scale + 0.5, + bestPlane.m_index + downsampling)); plane.m_a = 0.0; plane.m_b = 0.0; plane.m_c = 1.0; plane.m_axis = AXIS_Z; - for (short k = k0; k <= k1; k += downsampling) { - double z = minV[2] + scale * k; - plane.m_d = -z; - plane.m_index = k; - planes.PushBack(plane); + for (short k = k0; k <= k1; ++k) { + double z = minV[2] + scale * k; + plane.m_d = -z; + plane.m_index = k; + planes.PushBack(plane); } + } } -void RefineAxesAlignedClippingPlanes(const VoxelSet& vset, const Plane& bestPlane, const short downsampling, - SArray& planes) -{ - const Vec3 minV = vset.GetMinBBVoxels(); - const Vec3 maxV = vset.GetMaxBBVoxels(); - Vec3 pt; - Plane plane; - - if (bestPlane.m_axis == AXIS_X) { - const short i0 = MAX(minV[0], bestPlane.m_index - downsampling); - const short i1 = MIN(maxV[0], bestPlane.m_index + downsampling); - plane.m_a = 1.0; - plane.m_b = 0.0; - plane.m_c = 0.0; - plane.m_axis = AXIS_X; - for (short i = i0; i <= i1; ++i) { - pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); - plane.m_d = -pt[0]; - plane.m_index = i; - planes.PushBack(plane); - } - } - else if (bestPlane.m_axis == AXIS_Y) { - const short j0 = MAX(minV[1], bestPlane.m_index - downsampling); - const short j1 = MIN(maxV[1], bestPlane.m_index + downsampling); - plane.m_a = 0.0; - plane.m_b = 1.0; - plane.m_c = 0.0; - plane.m_axis = AXIS_Y; - for (short j = j0; j <= j1; ++j) { - pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); - plane.m_d = -pt[1]; - plane.m_index = j; - planes.PushBack(plane); - } - } - else { - const short k0 = MAX(minV[2], bestPlane.m_index - downsampling); - const short k1 = MIN(maxV[2], bestPlane.m_index + downsampling); - plane.m_a = 0.0; - plane.m_b = 0.0; - plane.m_c = 1.0; - plane.m_axis = AXIS_Z; - for (short k = k0; k <= k1; ++k) { - pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); - plane.m_d = -pt[2]; - plane.m_index = k; - planes.PushBack(plane); - } - } +inline double ComputeLocalConcavity(const double volume, + const double volumeCH) { + return fabs(volumeCH - volume) / volumeCH; } -void RefineAxesAlignedClippingPlanes(const TetrahedronSet& tset, const Plane& bestPlane, const short downsampling, - SArray& planes) -{ - const Vec3 minV = tset.GetMinBB(); - const Vec3 maxV = tset.GetMaxBB(); - const double scale = tset.GetSacle(); - Plane plane; - - if (bestPlane.m_axis == AXIS_X) { - const short i0 = MAX(0, bestPlane.m_index - downsampling); - const short i1 = static_cast(MIN((maxV[0] - minV[0]) / scale + 0.5, bestPlane.m_index + downsampling)); - plane.m_a = 1.0; - plane.m_b = 0.0; - plane.m_c = 0.0; - plane.m_axis = AXIS_X; - for (short i = i0; i <= i1; ++i) { - double x = minV[0] + scale * i; - plane.m_d = -x; - plane.m_index = i; - planes.PushBack(plane); - } - } - else if (bestPlane.m_axis == AXIS_Y) { - const short j0 = MAX(0, bestPlane.m_index - downsampling); - const short j1 = static_cast(MIN((maxV[1] - minV[1]) / scale + 0.5, bestPlane.m_index + downsampling)); - plane.m_a = 0.0; - plane.m_b = 1.0; - plane.m_c = 0.0; - plane.m_axis = AXIS_Y; - for (short j = j0; j <= j1; ++j) { - double y = minV[1] + scale * j; - plane.m_d = -y; - plane.m_index = j; - planes.PushBack(plane); - } - } - else { - const short k0 = MAX(0, bestPlane.m_index - downsampling); - const short k1 = static_cast(MIN((maxV[2] - minV[2]) / scale + 0.5, bestPlane.m_index + downsampling)); - plane.m_a = 0.0; - plane.m_b = 0.0; - plane.m_c = 1.0; - plane.m_axis = AXIS_Z; - for (short k = k0; k <= k1; ++k) { - double z = minV[2] + scale * k; - plane.m_d = -z; - plane.m_index = k; - planes.PushBack(plane); - } - } -} -inline double ComputeLocalConcavity(const double volume, const double volumeCH) -{ - return fabs(volumeCH - volume) / volumeCH; -} -inline double ComputeConcavity(const double volume, const double volumeCH, const double volume0) -{ - return fabs(volumeCH - volume) / volume0; +inline double ComputeConcavity(const double volume, + const double volumeCH, + const double volume0) { + return fabs(volumeCH - volume) / volume0; } //#define DEBUG_TEMP -void VHACD::ComputeBestClippingPlane(const PrimitiveSet* inputPSet, const double volume, const SArray& planes, - const Vec3& preferredCuttingDirection, const double w, const double alpha, const double beta, - const int32_t convexhullDownsampling, const double progress0, const double progress1, Plane& bestPlane, - double& minConcavity, const Parameters& params) -{ - if (GetCancel()) { - return; - } - char msg[256]; - size_t nPrimitives = inputPSet->GetNPrimitives(); - bool oclAcceleration = (nPrimitives > OCL_MIN_NUM_PRIMITIVES && params.m_oclAcceleration && params.m_mode == 0) ? true : false; - int32_t iBest = -1; - int32_t nPlanes = static_cast(planes.Size()); - bool cancel = false; - int32_t done = 0; - double minTotal = MAX_DOUBLE; - double minBalance = MAX_DOUBLE; - double minSymmetry = MAX_DOUBLE; - minConcavity = MAX_DOUBLE; - - SArray >* chPts = new SArray >[2 * m_ompNumProcessors]; - Mesh* chs = new Mesh[2 * m_ompNumProcessors]; - PrimitiveSet* onSurfacePSet = inputPSet->Create(); - inputPSet->SelectOnSurface(onSurfacePSet); - - PrimitiveSet** psets = 0; - if (!params.m_convexhullApproximation) { - psets = new PrimitiveSet*[2 * m_ompNumProcessors]; - for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { - psets[i] = inputPSet->Create(); - } - } +void VHACD::ComputeBestClippingPlane( + const PrimitiveSet* inputPSet, + const double volume, + const SArray& planes, + const Vec3& preferredCuttingDirection, + const double w, + const double alpha, + const double beta, + const int32_t convexhullDownsampling, + const double progress0, + const double progress1, + Plane& bestPlane, + double& minConcavity, + const Parameters& params) { + if (GetCancel()) { + return; + } + char msg[256]; + size_t nPrimitives = inputPSet->GetNPrimitives(); + bool oclAcceleration = (nPrimitives > OCL_MIN_NUM_PRIMITIVES && + params.m_oclAcceleration && params.m_mode == 0) + ? true + : false; + int32_t iBest = -1; + int32_t nPlanes = static_cast(planes.Size()); + bool cancel = false; + int32_t done = 0; + double minTotal = MAX_DOUBLE; + double minBalance = MAX_DOUBLE; + double minSymmetry = MAX_DOUBLE; + minConcavity = MAX_DOUBLE; + + SArray >* chPts = + new SArray >[2 * m_ompNumProcessors]; + Mesh* chs = new Mesh[2 * m_ompNumProcessors]; + PrimitiveSet* onSurfacePSet = inputPSet->Create(); + inputPSet->SelectOnSurface(onSurfacePSet); + + PrimitiveSet** psets = 0; + if (!params.m_convexhullApproximation) { + psets = new PrimitiveSet*[2 * m_ompNumProcessors]; + for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { + psets[i] = inputPSet->Create(); + } + } #ifdef CL_VERSION_1_1 - // allocate OpenCL data structures - cl_mem voxels; - cl_mem* partialVolumes = 0; - size_t globalSize = 0; - size_t nWorkGroups = 0; - double unitVolume = 0.0; - if (oclAcceleration) { - VoxelSet* vset = (VoxelSet*)inputPSet; - const Vec3 minBB = vset->GetMinBB(); - const float fMinBB[4] = { (float)minBB[0], (float)minBB[1], (float)minBB[2], 1.0f }; - const float fSclae[4] = { (float)vset->GetScale(), (float)vset->GetScale(), (float)vset->GetScale(), 0.0f }; - const int32_t nVoxels = (int32_t)nPrimitives; - unitVolume = vset->GetUnitVolume(); - nWorkGroups = (nPrimitives + 4 * m_oclWorkGroupSize - 1) / (4 * m_oclWorkGroupSize); - globalSize = nWorkGroups * m_oclWorkGroupSize; - cl_int error; - voxels = clCreateBuffer(m_oclContext, - CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, - sizeof(Voxel) * nPrimitives, - vset->GetVoxels(), - &error); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't create buffer\n"); - } - SetCancel(true); + // allocate OpenCL data structures + cl_mem voxels; + cl_mem* partialVolumes = 0; + size_t globalSize = 0; + size_t nWorkGroups = 0; + double unitVolume = 0.0; + if (oclAcceleration) { + VoxelSet* vset = (VoxelSet*)inputPSet; + const Vec3 minBB = vset->GetMinBB(); + const float fMinBB[4] = {(float)minBB[0], (float)minBB[1], (float)minBB[2], + 1.0f}; + const float fSclae[4] = {(float)vset->GetScale(), (float)vset->GetScale(), + (float)vset->GetScale(), 0.0f}; + const int32_t nVoxels = (int32_t)nPrimitives; + unitVolume = vset->GetUnitVolume(); + nWorkGroups = + (nPrimitives + 4 * m_oclWorkGroupSize - 1) / (4 * m_oclWorkGroupSize); + globalSize = nWorkGroups * m_oclWorkGroupSize; + cl_int error; + voxels = + clCreateBuffer(m_oclContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(Voxel) * nPrimitives, vset->GetVoxels(), &error); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't create buffer\n"); + } + SetCancel(true); + } + + partialVolumes = new cl_mem[m_ompNumProcessors]; + for (int32_t i = 0; i < m_ompNumProcessors; ++i) { + partialVolumes[i] = + clCreateBuffer(m_oclContext, CL_MEM_WRITE_ONLY, + sizeof(uint32_t) * 4 * nWorkGroups, NULL, &error); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't create buffer\n"); } - - partialVolumes = new cl_mem[m_ompNumProcessors]; - for (int32_t i = 0; i < m_ompNumProcessors; ++i) { - partialVolumes[i] = clCreateBuffer(m_oclContext, - CL_MEM_WRITE_ONLY, - sizeof(uint32_t) * 4 * nWorkGroups, - NULL, - &error); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't create buffer\n"); - } - SetCancel(true); - break; - } - error = clSetKernelArg(m_oclKernelComputePartialVolumes[i], 0, sizeof(cl_mem), &voxels); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 1, sizeof(uint32_t), &nVoxels); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 3, sizeof(float) * 4, fMinBB); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 4, sizeof(float) * 4, &fSclae); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 5, sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 6, sizeof(cl_mem), &(partialVolumes[i])); - error |= clSetKernelArg(m_oclKernelComputeSum[i], 0, sizeof(cl_mem), &(partialVolumes[i])); - error |= clSetKernelArg(m_oclKernelComputeSum[i], 2, sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't kernel arguments \n"); - } - SetCancel(true); - } + SetCancel(true); + break; + } + error = clSetKernelArg(m_oclKernelComputePartialVolumes[i], 0, + sizeof(cl_mem), &voxels); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 1, + sizeof(uint32_t), &nVoxels); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 3, + sizeof(float) * 4, fMinBB); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 4, + sizeof(float) * 4, &fSclae); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 5, + sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 6, + sizeof(cl_mem), &(partialVolumes[i])); + error |= clSetKernelArg(m_oclKernelComputeSum[i], 0, sizeof(cl_mem), + &(partialVolumes[i])); + error |= clSetKernelArg(m_oclKernelComputeSum[i], 2, + sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel arguments \n"); } + SetCancel(true); + } } -#else // CL_VERSION_1_1 - oclAcceleration = false; -#endif // CL_VERSION_1_1 + } +#else // CL_VERSION_1_1 + oclAcceleration = false; +#endif // CL_VERSION_1_1 #ifdef DEBUG_TEMP - Timer timerComputeCost; - timerComputeCost.Tic(); -#endif // DEBUG_TEMP + Timer timerComputeCost; + timerComputeCost.Tic(); +#endif // DEBUG_TEMP #if USE_THREAD == 1 && _OPENMP #pragma omp parallel for #endif - for (int32_t x = 0; x < nPlanes; ++x) { - int32_t threadID = 0; + for (int32_t x = 0; x < nPlanes; ++x) { + int32_t threadID = 0; #if USE_THREAD == 1 && _OPENMP - threadID = omp_get_thread_num(); + threadID = omp_get_thread_num(); #pragma omp flush(cancel) #endif - if (!cancel) { - //Update progress - if (GetCancel()) { - cancel = true; + if (!cancel) { + // Update progress + if (GetCancel()) { + cancel = true; #if USE_THREAD == 1 && _OPENMP #pragma omp flush(cancel) #endif - } - Plane plane = planes[x]; + } + Plane plane = planes[x]; - if (oclAcceleration) { + if (oclAcceleration) { #ifdef CL_VERSION_1_1 - const float fPlane[4] = { (float)plane.m_a, (float)plane.m_b, (float)plane.m_c, (float)plane.m_d }; - cl_int error = clSetKernelArg(m_oclKernelComputePartialVolumes[threadID], 2, sizeof(float) * 4, fPlane); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't kernel atguments \n"); - } - SetCancel(true); - } - - error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputePartialVolumes[threadID], - 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't run kernel \n"); - } - SetCancel(true); - } - int32_t nValues = (int32_t)nWorkGroups; - while (nValues > 1) { - error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, sizeof(int32_t), &nValues); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't kernel atguments \n"); - } - SetCancel(true); - } - size_t nWorkGroups = (nValues + m_oclWorkGroupSize - 1) / m_oclWorkGroupSize; - size_t globalSize = nWorkGroups * m_oclWorkGroupSize; - error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputeSum[threadID], - 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't run kernel \n"); - } - SetCancel(true); - } - nValues = (int32_t)nWorkGroups; - } -#endif // CL_VERSION_1_1 + const float fPlane[4] = {(float)plane.m_a, (float)plane.m_b, + (float)plane.m_c, (float)plane.m_d}; + cl_int error = + clSetKernelArg(m_oclKernelComputePartialVolumes[threadID], 2, + sizeof(float) * 4, fPlane); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); + } + SetCancel(true); + } + + error = clEnqueueNDRangeKernel( + m_oclQueue[threadID], m_oclKernelComputePartialVolumes[threadID], 1, + NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't run kernel \n"); + } + SetCancel(true); + } + int32_t nValues = (int32_t)nWorkGroups; + while (nValues > 1) { + error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, + sizeof(int32_t), &nValues); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); } + SetCancel(true); + } + size_t nWorkGroups = + (nValues + m_oclWorkGroupSize - 1) / m_oclWorkGroupSize; + size_t globalSize = nWorkGroups * m_oclWorkGroupSize; + error = clEnqueueNDRangeKernel( + m_oclQueue[threadID], m_oclKernelComputeSum[threadID], 1, NULL, + &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't run kernel \n"); + } + SetCancel(true); + } + nValues = (int32_t)nWorkGroups; + } +#endif // CL_VERSION_1_1 + } - Mesh& leftCH = chs[threadID]; - Mesh& rightCH = chs[threadID + m_ompNumProcessors]; - rightCH.ResizePoints(0); - leftCH.ResizePoints(0); - rightCH.ResizeTriangles(0); - leftCH.ResizeTriangles(0); + Mesh& leftCH = chs[threadID]; + Mesh& rightCH = chs[threadID + m_ompNumProcessors]; + rightCH.ResizePoints(0); + leftCH.ResizePoints(0); + rightCH.ResizeTriangles(0); + leftCH.ResizeTriangles(0); // compute convex-hulls #ifdef TEST_APPROX_CH - double volumeLeftCH1; - double volumeRightCH1; -#endif //TEST_APPROX_CH - if (params.m_convexhullApproximation) { - SArray >& leftCHPts = chPts[threadID]; - SArray >& rightCHPts = chPts[threadID + m_ompNumProcessors]; - rightCHPts.Resize(0); - leftCHPts.Resize(0); - onSurfacePSet->Intersect(plane, &rightCHPts, &leftCHPts, convexhullDownsampling * 32); - inputPSet->GetConvexHull().Clip(plane, rightCHPts, leftCHPts); - rightCH.ComputeConvexHull((double*)rightCHPts.Data(), rightCHPts.Size()); - leftCH.ComputeConvexHull((double*)leftCHPts.Data(), leftCHPts.Size()); + double volumeLeftCH1; + double volumeRightCH1; +#endif // TEST_APPROX_CH + if (params.m_convexhullApproximation) { + SArray >& leftCHPts = chPts[threadID]; + SArray >& rightCHPts = + chPts[threadID + m_ompNumProcessors]; + rightCHPts.Resize(0); + leftCHPts.Resize(0); + onSurfacePSet->Intersect(plane, &rightCHPts, &leftCHPts, + convexhullDownsampling * 32); + inputPSet->GetConvexHull().Clip(plane, rightCHPts, leftCHPts); + rightCH.ComputeConvexHull((double*)rightCHPts.Data(), + rightCHPts.Size()); + leftCH.ComputeConvexHull((double*)leftCHPts.Data(), leftCHPts.Size()); #ifdef TEST_APPROX_CH - Mesh leftCH1; - Mesh rightCH1; - VoxelSet right; - VoxelSet left; - onSurfacePSet->Clip(plane, &right, &left); - right.ComputeConvexHull(rightCH1, convexhullDownsampling); - left.ComputeConvexHull(leftCH1, convexhullDownsampling); - - volumeLeftCH1 = leftCH1.ComputeVolume(); - volumeRightCH1 = rightCH1.ComputeVolume(); -#endif //TEST_APPROX_CH - } - else { - PrimitiveSet* const right = psets[threadID]; - PrimitiveSet* const left = psets[threadID + m_ompNumProcessors]; - onSurfacePSet->Clip(plane, right, left); - right->ComputeConvexHull(rightCH, convexhullDownsampling); - left->ComputeConvexHull(leftCH, convexhullDownsampling); - } - double volumeLeftCH = leftCH.ComputeVolume(); - double volumeRightCH = rightCH.ComputeVolume(); - - // compute clipped volumes - double volumeLeft = 0.0; - double volumeRight = 0.0; - if (oclAcceleration) { + Mesh leftCH1; + Mesh rightCH1; + VoxelSet right; + VoxelSet left; + onSurfacePSet->Clip(plane, &right, &left); + right.ComputeConvexHull(rightCH1, convexhullDownsampling); + left.ComputeConvexHull(leftCH1, convexhullDownsampling); + + volumeLeftCH1 = leftCH1.ComputeVolume(); + volumeRightCH1 = rightCH1.ComputeVolume(); +#endif // TEST_APPROX_CH + } else { + PrimitiveSet* const right = psets[threadID]; + PrimitiveSet* const left = psets[threadID + m_ompNumProcessors]; + onSurfacePSet->Clip(plane, right, left); + right->ComputeConvexHull(rightCH, convexhullDownsampling); + left->ComputeConvexHull(leftCH, convexhullDownsampling); + } + double volumeLeftCH = leftCH.ComputeVolume(); + double volumeRightCH = rightCH.ComputeVolume(); + + // compute clipped volumes + double volumeLeft = 0.0; + double volumeRight = 0.0; + if (oclAcceleration) { #ifdef CL_VERSION_1_1 - uint32_t volumes[4]; - cl_int error = clEnqueueReadBuffer(m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE, - 0, sizeof(uint32_t) * 4, volumes, 0, NULL, NULL); - size_t nPrimitivesRight = volumes[0] + volumes[1] + volumes[2] + volumes[3]; - size_t nPrimitivesLeft = nPrimitives - nPrimitivesRight; - volumeRight = nPrimitivesRight * unitVolume; - volumeLeft = nPrimitivesLeft * unitVolume; - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't read buffer \n"); - } - SetCancel(true); - } -#endif // CL_VERSION_1_1 - } - else { - inputPSet->ComputeClippedVolumes(plane, volumeRight, volumeLeft); - } - double concavityLeft = ComputeConcavity(volumeLeft, volumeLeftCH, m_volumeCH0); - double concavityRight = ComputeConcavity(volumeRight, volumeRightCH, m_volumeCH0); - double concavity = (concavityLeft + concavityRight); - - // compute cost - double balance = alpha * fabs(volumeLeft - volumeRight) / m_volumeCH0; - double d = w * (preferredCuttingDirection[0] * plane.m_a + preferredCuttingDirection[1] * plane.m_b + preferredCuttingDirection[2] * plane.m_c); - double symmetry = beta * d; - double total = concavity + balance + symmetry; + uint32_t volumes[4]; + cl_int error = clEnqueueReadBuffer( + m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE, 0, + sizeof(uint32_t) * 4, volumes, 0, NULL, NULL); + size_t nPrimitivesRight = + volumes[0] + volumes[1] + volumes[2] + volumes[3]; + size_t nPrimitivesLeft = nPrimitives - nPrimitivesRight; + volumeRight = nPrimitivesRight * unitVolume; + volumeLeft = nPrimitivesLeft * unitVolume; + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't read buffer \n"); + } + SetCancel(true); + } +#endif // CL_VERSION_1_1 + } else { + inputPSet->ComputeClippedVolumes(plane, volumeRight, volumeLeft); + } + double concavityLeft = + ComputeConcavity(volumeLeft, volumeLeftCH, m_volumeCH0); + double concavityRight = + ComputeConcavity(volumeRight, volumeRightCH, m_volumeCH0); + double concavity = (concavityLeft + concavityRight); + + // compute cost + double balance = alpha * fabs(volumeLeft - volumeRight) / m_volumeCH0; + double d = w * (preferredCuttingDirection[0] * plane.m_a + + preferredCuttingDirection[1] * plane.m_b + + preferredCuttingDirection[2] * plane.m_c); + double symmetry = beta * d; + double total = concavity + balance + symmetry; #if USE_THREAD == 1 && _OPENMP #pragma omp critical #endif - { - if (total < minTotal || (total == minTotal && x < iBest)) { - minConcavity = concavity; - minBalance = balance; - minSymmetry = symmetry; - bestPlane = plane; - minTotal = total; - iBest = x; - } - ++done; - if (!(done & 127)) // reduce update frequency - { - double progress = done * (progress1 - progress0) / nPlanes + progress0; - Update(m_stageProgress, progress, params); - } - } + { + if (total < minTotal || (total == minTotal && x < iBest)) { + minConcavity = concavity; + minBalance = balance; + minSymmetry = symmetry; + bestPlane = plane; + minTotal = total; + iBest = x; } + ++done; + if (!(done & 127)) // reduce update frequency + { + double progress = + done * (progress1 - progress0) / nPlanes + progress0; + Update(m_stageProgress, progress, params); + } + } } + } #ifdef DEBUG_TEMP - timerComputeCost.Toc(); - printf_s("Cost[%i] = %f\n", nPlanes, timerComputeCost.GetElapsedTime()); -#endif // DEBUG_TEMP + timerComputeCost.Toc(); + printf_s("Cost[%i] = %f\n", nPlanes, timerComputeCost.GetElapsedTime()); +#endif // DEBUG_TEMP #ifdef CL_VERSION_1_1 - if (oclAcceleration) { - clReleaseMemObject(voxels); - for (int32_t i = 0; i < m_ompNumProcessors; ++i) { - clReleaseMemObject(partialVolumes[i]); - } - delete[] partialVolumes; - } -#endif // CL_VERSION_1_1 - - if (psets) { - for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { - delete psets[i]; - } - delete[] psets; - } - delete onSurfacePSet; - delete[] chPts; - delete[] chs; - if (params.m_logger) { - sprintf(msg, "\n\t\t\t Best %04i T=%2.6f C=%2.6f B=%2.6f S=%2.6f (%1.1f, %1.1f, %1.1f, %3.3f)\n\n", iBest, minTotal, minConcavity, minBalance, minSymmetry, bestPlane.m_a, bestPlane.m_b, bestPlane.m_c, bestPlane.m_d); - params.m_logger->Log(msg); - } + if (oclAcceleration) { + clReleaseMemObject(voxels); + for (int32_t i = 0; i < m_ompNumProcessors; ++i) { + clReleaseMemObject(partialVolumes[i]); + } + delete[] partialVolumes; + } +#endif // CL_VERSION_1_1 + + if (psets) { + for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { + delete psets[i]; + } + delete[] psets; + } + delete onSurfacePSet; + delete[] chPts; + delete[] chs; + if (params.m_logger) { + sprintf(msg, + "\n\t\t\t Best %04i T=%2.6f C=%2.6f B=%2.6f S=%2.6f (%1.1f, " + "%1.1f, %1.1f, %3.3f)\n\n", + iBest, minTotal, minConcavity, minBalance, minSymmetry, + bestPlane.m_a, bestPlane.m_b, bestPlane.m_c, bestPlane.m_d); + params.m_logger->Log(msg); + } } -void VHACD::ComputeACD(const Parameters& params) -{ - if (GetCancel()) { - return; - } - m_timer.Tic(); +void VHACD::ComputeACD(const Parameters& params) { + if (GetCancel()) { + return; + } + std::cout << "@@@@ COMPUTE_ACD ====" << '\n'; + m_timer.Tic(); + + m_stage = "Approximate Convex Decomposition"; + m_stageProgress = 0.0; + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + SArray parts; + SArray inputParts; + SArray temp; + inputParts.PushBack(m_pset); + m_pset = 0; + SArray planes; + SArray planesRef; + uint32_t sub = 0; + bool firstIteration = true; + m_volumeCH0 = 1.0; + + // Compute the decomposition depth based on the number of convex hulls being + // requested.. + uint32_t hullCount = 2; + uint32_t depth = 1; + while (params.m_maxConvexHulls > hullCount) { + depth++; + hullCount *= 2; + } + // We must always increment the decomposition depth one higher than the + // maximum number of hulls requested. The reason for this is as follows. Say, + // for example, the user requests 32 convex hulls exactly. This would be a + // decomposition depth of 5. However, when we do that, we do *not* necessarily + // get 32 hulls as a result. This is because, during the recursive descent of + // the binary tree, one or more of the leaf nodes may have no concavity and + // will not be split. So, in this way, even with a decomposition depth of 5, + // you can produce fewer than 32 hulls. So, in this case, we would set the + // decomposition depth to 6 (producing up to as high as 64 convex hulls). + // Then, the merge step which combines over-described hulls down to the user + // requested amount, we will end up getting exactly 32 convex hulls as a + // result. We could just allow the artist to directly control the + // decomposition depth directly, but this would be a bit too complex and the + // preference is simply to let them specify how many hulls they want and + // derive the solution from that. + depth++; + + while (sub++ < depth && inputParts.Size() > 0 && !m_cancel) { + msg.str(""); + msg << "Subdivision level " << sub; + m_operation = msg.str(); - m_stage = "Approximate Convex Decomposition"; - m_stageProgress = 0.0; - std::ostringstream msg; if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); + msg.str(""); + msg << "\t Subdivision level " << sub << std::endl; + params.m_logger->Log(msg.str().c_str()); } - SArray parts; - SArray inputParts; - SArray temp; - inputParts.PushBack(m_pset); - m_pset = 0; - SArray planes; - SArray planesRef; - uint32_t sub = 0; - bool firstIteration = true; - m_volumeCH0 = 1.0; - - // Compute the decomposition depth based on the number of convex hulls being requested.. - uint32_t hullCount = 2; - uint32_t depth = 1; - while (params.m_maxConvexHulls > hullCount) - { - depth++; - hullCount *= 2; - } - // We must always increment the decomposition depth one higher than the maximum number of hulls requested. - // The reason for this is as follows. - // Say, for example, the user requests 32 convex hulls exactly. This would be a decomposition depth of 5. - // However, when we do that, we do *not* necessarily get 32 hulls as a result. This is because, during - // the recursive descent of the binary tree, one or more of the leaf nodes may have no concavity and - // will not be split. So, in this way, even with a decomposition depth of 5, you can produce fewer than - // 32 hulls. So, in this case, we would set the decomposition depth to 6 (producing up to as high as 64 convex hulls). - // Then, the merge step which combines over-described hulls down to the user requested amount, we will end up - // getting exactly 32 convex hulls as a result. - // We could just allow the artist to directly control the decomposition depth directly, but this would be a bit - // too complex and the preference is simply to let them specify how many hulls they want and derive the solution - // from that. - depth++; - - - while (sub++ < depth && inputParts.Size() > 0 && !m_cancel) { + double maxConcavity = 0.0; + const size_t nInputParts = inputParts.Size(); + Update(m_stageProgress, 0.0, params); + for (size_t p = 0; p < nInputParts && !m_cancel; ++p) { + const double progress0 = p * 100.0 / nInputParts; + const double progress1 = (p + 0.75) * 100.0 / nInputParts; + const double progress2 = (p + 1.00) * 100.0 / nInputParts; + + Update(m_stageProgress, progress0, params); + + PrimitiveSet* pset = inputParts[p]; + inputParts[p] = 0; + double volume = pset->ComputeVolume(); + pset->ComputeBB(); + pset->ComputePrincipalAxes(); + if (params.m_pca) { + pset->AlignToPrincipalAxes(); + } + + pset->ComputeConvexHull(pset->GetConvexHull()); + double volumeCH = fabs(pset->GetConvexHull().ComputeVolume()); + if (firstIteration) { + m_volumeCH0 = volumeCH; + } + + double concavity = ComputeConcavity(volume, volumeCH, m_volumeCH0); + double error = 1.01 * pset->ComputeMaxVolumeError() / m_volumeCH0; + + if (firstIteration) { + firstIteration = false; + } + + if (params.m_logger) { msg.str(""); - msg << "Subdivision level " << sub; - m_operation = msg.str(); + msg << "\t -> Part[" << p << "] C = " << concavity + << ", E = " << error << ", VS = " << pset->GetNPrimitivesOnSurf() + << ", VI = " << pset->GetNPrimitivesInsideSurf() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + if (concavity > params.m_concavity && concavity > error) { + Vec3 preferredCuttingDirection; + double w = + ComputePreferredCuttingDirection(pset, preferredCuttingDirection); + planes.Resize(0); + if (params.m_mode == 0) { + VoxelSet* vset = (VoxelSet*)pset; + ComputeAxesAlignedClippingPlanes(*vset, params.m_planeDownsampling, + planes); + } else { + TetrahedronSet* tset = (TetrahedronSet*)pset; + ComputeAxesAlignedClippingPlanes(*tset, params.m_planeDownsampling, + planes); + } if (params.m_logger) { - msg.str(""); - msg << "\t Subdivision level " << sub << std::endl; - params.m_logger->Log(msg.str().c_str()); + msg.str(""); + msg << "\t\t [Regular sampling] Number of clipping planes " + << planes.Size() << std::endl; + params.m_logger->Log(msg.str().c_str()); } - double maxConcavity = 0.0; - const size_t nInputParts = inputParts.Size(); - Update(m_stageProgress, 0.0, params); - for (size_t p = 0; p < nInputParts && !m_cancel; ++p) { - const double progress0 = p * 100.0 / nInputParts; - const double progress1 = (p + 0.75) * 100.0 / nInputParts; - const double progress2 = (p + 1.00) * 100.0 / nInputParts; - - Update(m_stageProgress, progress0, params); - - PrimitiveSet* pset = inputParts[p]; - inputParts[p] = 0; - double volume = pset->ComputeVolume(); - pset->ComputeBB(); - pset->ComputePrincipalAxes(); - if (params.m_pca) { - pset->AlignToPrincipalAxes(); - } - - pset->ComputeConvexHull(pset->GetConvexHull()); - double volumeCH = fabs(pset->GetConvexHull().ComputeVolume()); - if (firstIteration) { - m_volumeCH0 = volumeCH; - } - - double concavity = ComputeConcavity(volume, volumeCH, m_volumeCH0); - double error = 1.01 * pset->ComputeMaxVolumeError() / m_volumeCH0; - - if (firstIteration) { - firstIteration = false; - } - - if (params.m_logger) { - msg.str(""); - msg << "\t -> Part[" << p - << "] C = " << concavity - << ", E = " << error - << ", VS = " << pset->GetNPrimitivesOnSurf() - << ", VI = " << pset->GetNPrimitivesInsideSurf() - << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - if (concavity > params.m_concavity && concavity > error) { - Vec3 preferredCuttingDirection; - double w = ComputePreferredCuttingDirection(pset, preferredCuttingDirection); - planes.Resize(0); - if (params.m_mode == 0) { - VoxelSet* vset = (VoxelSet*)pset; - ComputeAxesAlignedClippingPlanes(*vset, params.m_planeDownsampling, planes); - } - else { - TetrahedronSet* tset = (TetrahedronSet*)pset; - ComputeAxesAlignedClippingPlanes(*tset, params.m_planeDownsampling, planes); - } - - if (params.m_logger) { - msg.str(""); - msg << "\t\t [Regular sampling] Number of clipping planes " << planes.Size() << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - Plane bestPlane; - double minConcavity = MAX_DOUBLE; - ComputeBestClippingPlane(pset, - volume, - planes, - preferredCuttingDirection, - w, - concavity * params.m_alpha, - concavity * params.m_beta, - params.m_convexhullDownsampling, - progress0, - progress1, - bestPlane, - minConcavity, - params); - if (!m_cancel && (params.m_planeDownsampling > 1 || params.m_convexhullDownsampling > 1)) { - planesRef.Resize(0); - - if (params.m_mode == 0) { - VoxelSet* vset = (VoxelSet*)pset; - RefineAxesAlignedClippingPlanes(*vset, bestPlane, params.m_planeDownsampling, planesRef); - } - else { - TetrahedronSet* tset = (TetrahedronSet*)pset; - RefineAxesAlignedClippingPlanes(*tset, bestPlane, params.m_planeDownsampling, planesRef); - } - - if (params.m_logger) { - msg.str(""); - msg << "\t\t [Refining] Number of clipping planes " << planesRef.Size() << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - ComputeBestClippingPlane(pset, - volume, - planesRef, - preferredCuttingDirection, - w, - concavity * params.m_alpha, - concavity * params.m_beta, - 1, // convexhullDownsampling = 1 - progress1, - progress2, - bestPlane, - minConcavity, - params); - } - if (GetCancel()) { - delete pset; // clean up - break; - } - else { - if (maxConcavity < minConcavity) { - maxConcavity = minConcavity; - } - PrimitiveSet* bestLeft = pset->Create(); - PrimitiveSet* bestRight = pset->Create(); - temp.PushBack(bestLeft); - temp.PushBack(bestRight); - pset->Clip(bestPlane, bestRight, bestLeft); - if (params.m_pca) { - bestRight->RevertAlignToPrincipalAxes(); - bestLeft->RevertAlignToPrincipalAxes(); - } - delete pset; - } - } - else { - if (params.m_pca) { - pset->RevertAlignToPrincipalAxes(); - } - parts.PushBack(pset); - } + Plane bestPlane; + double minConcavity = MAX_DOUBLE; + ComputeBestClippingPlane( + pset, volume, planes, preferredCuttingDirection, w, + concavity * params.m_alpha, concavity * params.m_beta, + params.m_convexhullDownsampling, progress0, progress1, bestPlane, + minConcavity, params); + if (!m_cancel && (params.m_planeDownsampling > 1 || + params.m_convexhullDownsampling > 1)) { + planesRef.Resize(0); + + if (params.m_mode == 0) { + VoxelSet* vset = (VoxelSet*)pset; + RefineAxesAlignedClippingPlanes( + *vset, bestPlane, params.m_planeDownsampling, planesRef); + } else { + TetrahedronSet* tset = (TetrahedronSet*)pset; + RefineAxesAlignedClippingPlanes( + *tset, bestPlane, params.m_planeDownsampling, planesRef); + } + + if (params.m_logger) { + msg.str(""); + msg << "\t\t [Refining] Number of clipping planes " + << planesRef.Size() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + ComputeBestClippingPlane( + pset, volume, planesRef, preferredCuttingDirection, w, + concavity * params.m_alpha, concavity * params.m_beta, + 1, // convexhullDownsampling = 1 + progress1, progress2, bestPlane, minConcavity, params); } - - Update(95.0 * (1.0 - maxConcavity) / (1.0 - params.m_concavity), 100.0, params); if (GetCancel()) { - const size_t nTempParts = temp.Size(); - for (size_t p = 0; p < nTempParts; ++p) { - delete temp[p]; - } - temp.Resize(0); + delete pset; // clean up + break; + } else { + if (maxConcavity < minConcavity) { + maxConcavity = minConcavity; + } + PrimitiveSet* bestLeft = pset->Create(); + PrimitiveSet* bestRight = pset->Create(); + temp.PushBack(bestLeft); + temp.PushBack(bestRight); + pset->Clip(bestPlane, bestRight, bestLeft); + if (params.m_pca) { + bestRight->RevertAlignToPrincipalAxes(); + bestLeft->RevertAlignToPrincipalAxes(); + } + delete pset; } - else { - inputParts = temp; - temp.Resize(0); + } else { + if (params.m_pca) { + pset->RevertAlignToPrincipalAxes(); } - } - const size_t nInputParts = inputParts.Size(); - for (size_t p = 0; p < nInputParts; ++p) { - parts.PushBack(inputParts[p]); + parts.PushBack(pset); + } } + Update(95.0 * (1.0 - maxConcavity) / (1.0 - params.m_concavity), 100.0, + params); if (GetCancel()) { - const size_t nParts = parts.Size(); - for (size_t p = 0; p < nParts; ++p) { - delete parts[p]; - } - return; - } - - m_overallProgress = 90.0; - Update(m_stageProgress, 100.0, params); - - msg.str(""); - msg << "Generate convex-hulls"; - m_operation = msg.str(); - size_t nConvexHulls = parts.Size(); - if (params.m_logger) { - msg.str(""); - msg << "+ Generate " << nConvexHulls << " convex-hulls " << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - Update(m_stageProgress, 0.0, params); - m_convexHulls.Resize(0); - for (size_t p = 0; p < nConvexHulls && !m_cancel; ++p) { - Update(m_stageProgress, p * 100.0 / nConvexHulls, params); - m_convexHulls.PushBack(new Mesh); - parts[p]->ComputeConvexHull(*m_convexHulls[p]); - size_t nv = m_convexHulls[p]->GetNPoints(); - double x, y, z; - for (size_t i = 0; i < nv; ++i) { - Vec3& pt = m_convexHulls[p]->GetPoint(i); - x = pt[0]; - y = pt[1]; - z = pt[2]; - pt[0] = m_rot[0][0] * x + m_rot[0][1] * y + m_rot[0][2] * z + m_barycenter[0]; - pt[1] = m_rot[1][0] * x + m_rot[1][1] * y + m_rot[1][2] * z + m_barycenter[1]; - pt[2] = m_rot[2][0] * x + m_rot[2][1] * y + m_rot[2][2] * z + m_barycenter[2]; - } - } - + const size_t nTempParts = temp.Size(); + for (size_t p = 0; p < nTempParts; ++p) { + delete temp[p]; + } + temp.Resize(0); + } else { + inputParts = temp; + temp.Resize(0); + } + } + const size_t nInputParts = inputParts.Size(); + for (size_t p = 0; p < nInputParts; ++p) { + parts.PushBack(inputParts[p]); + } + + if (GetCancel()) { const size_t nParts = parts.Size(); for (size_t p = 0; p < nParts; ++p) { - delete parts[p]; - parts[p] = 0; + delete parts[p]; } - parts.Resize(0); + return; + } - if (GetCancel()) { - const size_t nConvexHulls = m_convexHulls.Size(); - for (size_t p = 0; p < nConvexHulls; ++p) { - delete m_convexHulls[p]; - } - m_convexHulls.Clear(); - return; - } + m_overallProgress = 90.0; + Update(m_stageProgress, 100.0, params); - m_overallProgress = 95.0; - Update(100.0, 100.0, params); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); + msg.str(""); + msg << "Generate convex-hulls"; + m_operation = msg.str(); + size_t nConvexHulls = parts.Size(); + if (params.m_logger) { + msg.str(""); + msg << "+ Generate " << nConvexHulls << " convex-hulls " << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(m_stageProgress, 0.0, params); + m_convexHulls.Resize(0); + for (size_t p = 0; p < nConvexHulls && !m_cancel; ++p) { + Update(m_stageProgress, p * 100.0 / nConvexHulls, params); + m_convexHulls.PushBack(new Mesh); + parts[p]->ComputeConvexHull(*m_convexHulls[p]); + size_t nv = m_convexHulls[p]->GetNPoints(); + double x, y, z; + for (size_t i = 0; i < nv; ++i) { + Vec3& pt = m_convexHulls[p]->GetPoint(i); + x = pt[0]; + y = pt[1]; + z = pt[2]; + pt[0] = + m_rot[0][0] * x + m_rot[0][1] * y + m_rot[0][2] * z + m_barycenter[0]; + pt[1] = + m_rot[1][0] * x + m_rot[1][1] * y + m_rot[1][2] * z + m_barycenter[1]; + pt[2] = + m_rot[2][0] * x + m_rot[2][1] * y + m_rot[2][2] * z + m_barycenter[2]; + } + } + + const size_t nParts = parts.Size(); + for (size_t p = 0; p < nParts; ++p) { + delete parts[p]; + parts[p] = 0; + } + parts.Resize(0); + + if (GetCancel()) { + const size_t nConvexHulls = m_convexHulls.Size(); + for (size_t p = 0; p < nConvexHulls; ++p) { + delete m_convexHulls[p]; } + m_convexHulls.Clear(); + return; + } + + m_overallProgress = 95.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } } -void AddPoints(const Mesh* const mesh, SArray >& pts) -{ - const int32_t n = (int32_t)mesh->GetNPoints(); - for (int32_t i = 0; i < n; ++i) { - pts.PushBack(mesh->GetPoint(i)); - } +void AddPoints(const Mesh* const mesh, SArray >& pts) { + const int32_t n = (int32_t)mesh->GetNPoints(); + for (int32_t i = 0; i < n; ++i) { + pts.PushBack(mesh->GetPoint(i)); + } } -void ComputeConvexHull(const Mesh* const ch1, const Mesh* const ch2, SArray >& pts, Mesh* const combinedCH) -{ - pts.Resize(0); - AddPoints(ch1, pts); - AddPoints(ch2, pts); - - btConvexHullComputer ch; - ch.compute((double*)pts.Data(), 3 * sizeof(double), (int32_t)pts.Size(), -1.0, -1.0); - combinedCH->ResizePoints(0); - combinedCH->ResizeTriangles(0); - for (int32_t v = 0; v < ch.vertices.size(); v++) { - combinedCH->AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); - } - const int32_t nt = ch.faces.size(); - for (int32_t t = 0; t < nt; ++t) { - const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); - int32_t a = sourceEdge->getSourceVertex(); - int32_t b = sourceEdge->getTargetVertex(); - const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); - int32_t c = edge->getTargetVertex(); - while (c != a) { - combinedCH->AddTriangle(Vec3(a, b, c)); - edge = edge->getNextEdgeOfFace(); - b = c; - c = edge->getTargetVertex(); - } - } +void ComputeConvexHull(const Mesh* const ch1, + const Mesh* const ch2, + SArray >& pts, + Mesh* const combinedCH) { + pts.Resize(0); + AddPoints(ch1, pts); + AddPoints(ch2, pts); + + btConvexHullComputer ch; + ch.compute((double*)pts.Data(), 3 * sizeof(double), (int32_t)pts.Size(), -1.0, + -1.0); + combinedCH->ResizePoints(0); + combinedCH->ResizeTriangles(0); + for (int32_t v = 0; v < ch.vertices.size(); v++) { + combinedCH->AddPoint(Vec3( + ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int32_t nt = ch.faces.size(); + for (int32_t t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int32_t a = sourceEdge->getSourceVertex(); + int32_t b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int32_t c = edge->getTargetVertex(); + while (c != a) { + combinedCH->AddTriangle(Vec3(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } } -void VHACD::MergeConvexHulls(const Parameters& params) -{ - if (GetCancel()) { - return; - } - m_timer.Tic(); - - m_stage = "Merge Convex Hulls"; - - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; +void VHACD::MergeConvexHulls(const Parameters& params) { + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Merge Convex Hulls"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + // Get the current number of convex hulls + size_t nConvexHulls = m_convexHulls.Size(); + // Iteration counter + int32_t iteration = 0; + // While we have more than at least one convex hull and the user has not asked + // us to cancel the operation + if (nConvexHulls > 1 && !m_cancel) { + // Get the gamma error threshold for when to exit + SArray > pts; + Mesh combinedCH; + + // Populate the cost matrix + size_t idx = 0; + SArray costMatrix; + costMatrix.Resize(((nConvexHulls * nConvexHulls) - nConvexHulls) >> 1); + for (size_t p1 = 1; p1 < nConvexHulls; ++p1) { + const float volume1 = m_convexHulls[p1]->ComputeVolume(); + for (size_t p2 = 0; p2 < p1; ++p2) { + ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, + &combinedCH); + costMatrix[idx++] = + ComputeConcavity(volume1 + m_convexHulls[p2]->ComputeVolume(), + combinedCH.ComputeVolume(), m_volumeCH0); + } + } + + // Until we cant merge below the maximum cost + size_t costSize = m_convexHulls.Size(); + while (!m_cancel) { + msg.str(""); + msg << "Iteration " << iteration++; + m_operation = msg.str(); + + // Search for lowest cost + float bestCost = (std::numeric_limits::max)(); + const size_t addr = FindMinimumElement(costMatrix.Data(), &bestCost, 0, + costMatrix.Size()); + if ((costSize - 1) < params.m_maxConvexHulls) { + break; + } + const size_t addrI = + (static_cast(sqrt(1 + (8 * addr))) - 1) >> 1; + const size_t p1 = addrI + 1; + const size_t p2 = addr - ((addrI * (addrI + 1)) >> 1); + assert(p1 >= 0); + assert(p2 >= 0); + assert(p1 < costSize); + assert(p2 < costSize); + + if (params.m_logger) { + msg.str(""); + msg << "\t\t Merging (" << p1 << ", " << p2 << ") " << bestCost + << std::endl + << std::endl; params.m_logger->Log(msg.str().c_str()); - } - - // Get the current number of convex hulls - size_t nConvexHulls = m_convexHulls.Size(); - // Iteration counter - int32_t iteration = 0; - // While we have more than at least one convex hull and the user has not asked us to cancel the operation - if (nConvexHulls > 1 && !m_cancel) - { - // Get the gamma error threshold for when to exit - SArray > pts; - Mesh combinedCH; - - // Populate the cost matrix - size_t idx = 0; - SArray costMatrix; - costMatrix.Resize(((nConvexHulls * nConvexHulls) - nConvexHulls) >> 1); - for (size_t p1 = 1; p1 < nConvexHulls; ++p1) - { - const float volume1 = m_convexHulls[p1]->ComputeVolume(); - for (size_t p2 = 0; p2 < p1; ++p2) - { - ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, &combinedCH); - costMatrix[idx++] = ComputeConcavity(volume1 + m_convexHulls[p2]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); - } + } + + // Make the lowest cost row and column into a new hull + Mesh* cch = new Mesh; + ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, cch); + delete m_convexHulls[p2]; + m_convexHulls[p2] = cch; + + delete m_convexHulls[p1]; + std::swap(m_convexHulls[p1], m_convexHulls[m_convexHulls.Size() - 1]); + m_convexHulls.PopBack(); + + costSize = costSize - 1; + + // Calculate costs versus the new hull + size_t rowIdx = ((p2 - 1) * p2) >> 1; + const float volume1 = m_convexHulls[p2]->ComputeVolume(); + for (size_t i = 0; (i < p2) && (!m_cancel); ++i) { + ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, + &combinedCH); + costMatrix[rowIdx++] = + ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), + combinedCH.ComputeVolume(), m_volumeCH0); + } + + rowIdx += p2; + for (size_t i = p2 + 1; (i < costSize) && (!m_cancel); ++i) { + ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, + &combinedCH); + costMatrix[rowIdx] = + ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), + combinedCH.ComputeVolume(), m_volumeCH0); + rowIdx += i; + assert(rowIdx >= 0); + } + + // Move the top column in to replace its space + const size_t erase_idx = ((costSize - 1) * costSize) >> 1; + if (p1 < costSize) { + rowIdx = (addrI * p1) >> 1; + size_t top_row = erase_idx; + for (size_t i = 0; i < p1; ++i) { + if (i != p2) { + costMatrix[rowIdx] = costMatrix[top_row]; + } + ++rowIdx; + ++top_row; } - // Until we cant merge below the maximum cost - size_t costSize = m_convexHulls.Size(); - while (!m_cancel) - { - msg.str(""); - msg << "Iteration " << iteration++; - m_operation = msg.str(); - - // Search for lowest cost - float bestCost = (std::numeric_limits::max)(); - const size_t addr = FindMinimumElement(costMatrix.Data(), &bestCost, 0, costMatrix.Size()); - if ( (costSize-1) < params.m_maxConvexHulls) - { - break; - } - const size_t addrI = (static_cast(sqrt(1 + (8 * addr))) - 1) >> 1; - const size_t p1 = addrI + 1; - const size_t p2 = addr - ((addrI * (addrI + 1)) >> 1); - assert(p1 >= 0); - assert(p2 >= 0); - assert(p1 < costSize); - assert(p2 < costSize); - - if (params.m_logger) - { - msg.str(""); - msg << "\t\t Merging (" << p1 << ", " << p2 << ") " << bestCost << std::endl - << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - // Make the lowest cost row and column into a new hull - Mesh* cch = new Mesh; - ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, cch); - delete m_convexHulls[p2]; - m_convexHulls[p2] = cch; - - delete m_convexHulls[p1]; - std::swap(m_convexHulls[p1], m_convexHulls[m_convexHulls.Size() - 1]); - m_convexHulls.PopBack(); - - costSize = costSize - 1; - - // Calculate costs versus the new hull - size_t rowIdx = ((p2 - 1) * p2) >> 1; - const float volume1 = m_convexHulls[p2]->ComputeVolume(); - for (size_t i = 0; (i < p2) && (!m_cancel); ++i) - { - ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH); - costMatrix[rowIdx++] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); - } - - rowIdx += p2; - for (size_t i = p2 + 1; (i < costSize) && (!m_cancel); ++i) - { - ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH); - costMatrix[rowIdx] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); - rowIdx += i; - assert(rowIdx >= 0); - } - - // Move the top column in to replace its space - const size_t erase_idx = ((costSize - 1) * costSize) >> 1; - if (p1 < costSize) { - rowIdx = (addrI * p1) >> 1; - size_t top_row = erase_idx; - for (size_t i = 0; i < p1; ++i) { - if (i != p2) { - costMatrix[rowIdx] = costMatrix[top_row]; - } - ++rowIdx; - ++top_row; - } - - ++top_row; - rowIdx += p1; - for (size_t i = p1 + 1; i < (costSize + 1); ++i) { - costMatrix[rowIdx] = costMatrix[top_row++]; - rowIdx += i; - assert(rowIdx >= 0); - } - } - costMatrix.Resize(erase_idx); + ++top_row; + rowIdx += p1; + for (size_t i = p1 + 1; i < (costSize + 1); ++i) { + costMatrix[rowIdx] = costMatrix[top_row++]; + rowIdx += i; + assert(rowIdx >= 0); } - } - m_overallProgress = 99.0; - Update(100.0, 100.0, params); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + } + costMatrix.Resize(erase_idx); + } + } + m_overallProgress = 99.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } } -void VHACD::SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume) -{ - if (nvertices <= 4) { - return; - } - ICHull icHull; - if (mRaycastMesh) - { - // We project these points onto the original source mesh to increase precision - // The voxelization process drops floating point precision so returned data points are not exactly lying on the - // surface of the original source mesh. - // The first step is we need to compute the bounding box of the mesh we are trying to build a convex hull for. - // From this bounding box, we compute the length of the diagonal to get a relative size and center for point projection - uint32_t nPoints = ch->GetNPoints(); - Vec3 *inputPoints = ch->GetPointsBuffer(); - Vec3 bmin(inputPoints[0]); - Vec3 bmax(inputPoints[1]); - for (uint32_t i = 1; i < nPoints; i++) - { - const Vec3 &p = inputPoints[i]; - p.UpdateMinMax(bmin, bmax); +void VHACD::SimplifyConvexHull(Mesh* const ch, + const size_t nvertices, + const double minVolume) { + if (nvertices <= 4) { + return; + } + ICHull icHull; + if (mRaycastMesh) { + // We project these points onto the original source mesh to increase + // precision The voxelization process drops floating point precision so + // returned data points are not exactly lying on the surface of the original + // source mesh. The first step is we need to compute the bounding box of the + // mesh we are trying to build a convex hull for. From this bounding box, we + // compute the length of the diagonal to get a relative size and center for + // point projection + uint32_t nPoints = ch->GetNPoints(); + Vec3* inputPoints = ch->GetPointsBuffer(); + Vec3 bmin(inputPoints[0]); + Vec3 bmax(inputPoints[1]); + for (uint32_t i = 1; i < nPoints; i++) { + const Vec3& p = inputPoints[i]; + p.UpdateMinMax(bmin, bmax); + } + Vec3 center; + double diagonalLength = + center.GetCenter(bmin, bmax); // Get the center of the bounding box + // This is the error threshold for determining if we should use the raycast + // result data point vs. the voxelized result. + double pointDistanceThreshold = diagonalLength * 0.05; + // If a new point is within 1/100th the diagonal length of the bounding + // volume we do not add it. To do so would create a thin sliver in the + // resulting convex hull + double snapDistanceThreshold = diagonalLength * 0.01; + double snapDistanceThresholdSquared = + snapDistanceThreshold * snapDistanceThreshold; + + // Allocate buffer for projected vertices + Vec3* outputPoints = new Vec3[nPoints]; + uint32_t outCount = 0; + for (uint32_t i = 0; i < nPoints; i++) { + Vec3& inputPoint = inputPoints[i]; + Vec3& outputPoint = outputPoints[outCount]; + // Compute the direction vector from the center of this mesh to the vertex + Vec3 dir = inputPoint - center; + // Normalize the direction vector. + dir.Normalize(); + // Multiply times the diagonal length of the mesh + dir *= diagonalLength; + // Add the center back in again to get the destination point + dir += center; + // By default the output point is equal to the input point + outputPoint = inputPoint; + double pointDistance; + if (mRaycastMesh->raycast(center.GetData(), dir.GetData(), + inputPoint.GetData(), outputPoint.GetData(), + &pointDistance)) { + // If the nearest intersection point is too far away, we keep the + // original source data point. Not all points lie directly on the + // original mesh surface + if (pointDistance > pointDistanceThreshold) { + outputPoint = inputPoint; } - Vec3 center; - double diagonalLength = center.GetCenter(bmin, bmax); // Get the center of the bounding box - // This is the error threshold for determining if we should use the raycast result data point vs. the voxelized result. - double pointDistanceThreshold = diagonalLength * 0.05; - // If a new point is within 1/100th the diagonal length of the bounding volume we do not add it. To do so would create a - // thin sliver in the resulting convex hull - double snapDistanceThreshold = diagonalLength * 0.01; - double snapDistanceThresholdSquared = snapDistanceThreshold*snapDistanceThreshold; - - // Allocate buffer for projected vertices - Vec3 *outputPoints = new Vec3[nPoints]; - uint32_t outCount = 0; - for (uint32_t i = 0; i < nPoints; i++) - { - Vec3 &inputPoint = inputPoints[i]; - Vec3 &outputPoint = outputPoints[outCount]; - // Compute the direction vector from the center of this mesh to the vertex - Vec3 dir = inputPoint - center; - // Normalize the direction vector. - dir.Normalize(); - // Multiply times the diagonal length of the mesh - dir *= diagonalLength; - // Add the center back in again to get the destination point - dir += center; - // By default the output point is equal to the input point - outputPoint = inputPoint; - double pointDistance; - if (mRaycastMesh->raycast(center.GetData(), dir.GetData(), inputPoint.GetData(), outputPoint.GetData(),&pointDistance) ) - { - // If the nearest intersection point is too far away, we keep the original source data point. - // Not all points lie directly on the original mesh surface - if (pointDistance > pointDistanceThreshold) - { - outputPoint = inputPoint; - } - } - // Ok, before we add this point, we do not want to create points which are extremely close to each other. - // This will result in tiny sliver triangles which are really bad for collision detection. - bool foundNearbyPoint = false; - for (uint32_t j = 0; j < outCount; j++) - { - // If this new point is extremely close to an existing point, we do not add it! - double squaredDistance = outputPoints[j].GetDistanceSquared(outputPoint); - if (squaredDistance < snapDistanceThresholdSquared ) - { - foundNearbyPoint = true; - break; - } - } - if (!foundNearbyPoint) - { - outCount++; - } + } + // Ok, before we add this point, we do not want to create points which are + // extremely close to each other. This will result in tiny sliver + // triangles which are really bad for collision detection. + bool foundNearbyPoint = false; + for (uint32_t j = 0; j < outCount; j++) { + // If this new point is extremely close to an existing point, we do not + // add it! + double squaredDistance = + outputPoints[j].GetDistanceSquared(outputPoint); + if (squaredDistance < snapDistanceThresholdSquared) { + foundNearbyPoint = true; + break; } - icHull.AddPoints(outputPoints, outCount); - delete[]outputPoints; - } - else - { - icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints()); - } - icHull.Process((uint32_t)nvertices, minVolume); - TMMesh& mesh = icHull.GetMesh(); - const size_t nT = mesh.GetNTriangles(); - const size_t nV = mesh.GetNVertices(); - ch->ResizePoints(nV); - ch->ResizeTriangles(nT); - mesh.GetIFS(ch->GetPointsBuffer(), ch->GetTrianglesBuffer()); + } + if (!foundNearbyPoint) { + outCount++; + } + } + icHull.AddPoints(outputPoints, outCount); + delete[] outputPoints; + } else { + icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints()); + } + icHull.Process((uint32_t)nvertices, minVolume); + TMMesh& mesh = icHull.GetMesh(); + const size_t nT = mesh.GetNTriangles(); + const size_t nV = mesh.GetNVertices(); + ch->ResizePoints(nV); + ch->ResizeTriangles(nT); + mesh.GetIFS(ch->GetPointsBuffer(), ch->GetTrianglesBuffer()); } -void VHACD::SimplifyConvexHulls(const Parameters& params) -{ - if (m_cancel || params.m_maxNumVerticesPerCH < 4) { - return; - } - m_timer.Tic(); - - m_stage = "Simplify convex-hulls"; - m_operation = "Simplify convex-hulls"; - - std::ostringstream msg; - const size_t nConvexHulls = m_convexHulls.Size(); +void VHACD::SimplifyConvexHulls(const Parameters& params) { + if (m_cancel || params.m_maxNumVerticesPerCH < 4) { + return; + } + m_timer.Tic(); + + m_stage = "Simplify convex-hulls"; + m_operation = "Simplify convex-hulls"; + + std::ostringstream msg; + const size_t nConvexHulls = m_convexHulls.Size(); + if (params.m_logger) { + msg << "+ Simplify " << nConvexHulls << " convex-hulls " << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + for (size_t i = 0; i < nConvexHulls && !m_cancel; ++i) { if (params.m_logger) { - msg << "+ Simplify " << nConvexHulls << " convex-hulls " << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - Update(0.0, 0.0, params); - for (size_t i = 0; i < nConvexHulls && !m_cancel; ++i) { - if (params.m_logger) { - msg.str(""); - msg << "\t\t Simplify CH[" << std::setfill('0') << std::setw(5) << i << "] " << m_convexHulls[i]->GetNPoints() << " V, " << m_convexHulls[i]->GetNTriangles() << " T" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - SimplifyConvexHull(m_convexHulls[i], params.m_maxNumVerticesPerCH, m_volumeCH0 * params.m_minVolumePerCH); - } - - m_overallProgress = 100.0; - Update(100.0, 100.0, params); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + msg.str(""); + msg << "\t\t Simplify CH[" << std::setfill('0') << std::setw(5) << i + << "] " << m_convexHulls[i]->GetNPoints() << " V, " + << m_convexHulls[i]->GetNTriangles() << " T" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + SimplifyConvexHull(m_convexHulls[i], params.m_maxNumVerticesPerCH, + m_volumeCH0 * params.m_minVolumePerCH); + } + + m_overallProgress = 100.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } } -bool VHACD::ComputeCenterOfMass(double centerOfMass[3]) const -{ - bool ret = false; - - centerOfMass[0] = 0; - centerOfMass[1] = 0; - centerOfMass[2] = 0; - // Get number of convex hulls in the result - uint32_t hullCount = GetNConvexHulls(); - if (hullCount) // if we have results - { - ret = true; - double totalVolume = 0; - // Initialize the center of mass to zero - centerOfMass[0] = 0; - centerOfMass[1] = 0; - centerOfMass[2] = 0; - // Compute the total volume of all convex hulls - for (uint32_t i = 0; i < hullCount; i++) - { - ConvexHull ch; - GetConvexHull(i, ch); - totalVolume += ch.m_volume; - } - // compute the reciprocal of the total volume - double recipVolume = 1.0 / totalVolume; - // Add in the weighted by volume average of the center point of each convex hull - for (uint32_t i = 0; i < hullCount; i++) - { - ConvexHull ch; - GetConvexHull(i, ch); - double ratio = ch.m_volume*recipVolume; - centerOfMass[0] += ch.m_center[0] * ratio; - centerOfMass[1] += ch.m_center[1] * ratio; - centerOfMass[2] += ch.m_center[2] * ratio; - } - } - return ret; +bool VHACD::ComputeCenterOfMass(double centerOfMass[3]) const { + bool ret = false; + + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + // Get number of convex hulls in the result + uint32_t hullCount = GetNConvexHulls(); + if (hullCount) // if we have results + { + ret = true; + double totalVolume = 0; + // Initialize the center of mass to zero + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + // Compute the total volume of all convex hulls + for (uint32_t i = 0; i < hullCount; i++) { + ConvexHull ch; + GetConvexHull(i, ch); + totalVolume += ch.m_volume; + } + // compute the reciprocal of the total volume + double recipVolume = 1.0 / totalVolume; + // Add in the weighted by volume average of the center point of each convex + // hull + for (uint32_t i = 0; i < hullCount; i++) { + ConvexHull ch; + GetConvexHull(i, ch); + double ratio = ch.m_volume * recipVolume; + centerOfMass[0] += ch.m_center[0] * ratio; + centerOfMass[1] += ch.m_center[1] * ratio; + centerOfMass[2] += ch.m_center[2] * ratio; + } + } + return ret; } -} // end of VHACD namespace +} // namespace VHACD From de1c34bead1abd1ca57534d781d0611f06851c69 Mon Sep 17 00:00:00 2001 From: ScottCSteinhauser Date: Wed, 24 Feb 2021 17:44:36 -0600 Subject: [PATCH 4/4] Revert "Added ability to voxelize a mesh and get the resulting Volume." This reverts commit 117d58bd4a22f6459cc03da7261ce3324c059421. --- src/VHACD_Lib/inc/vhacdVHACD.h | 651 ++++--- src/VHACD_Lib/inc/vhacdVolume.h | 430 +++++ src/VHACD_Lib/public/VHACD.h | 292 ++- src/VHACD_Lib/public/vhacdVolume.h | 501 ----- src/VHACD_Lib/src/VHACD-ASYNC.cpp | 633 ++++--- src/VHACD_Lib/src/VHACD.cpp | 2747 ++++++++++++++-------------- 6 files changed, 2518 insertions(+), 2736 deletions(-) create mode 100644 src/VHACD_Lib/inc/vhacdVolume.h delete mode 100644 src/VHACD_Lib/public/vhacdVolume.h diff --git a/src/VHACD_Lib/inc/vhacdVHACD.h b/src/VHACD_Lib/inc/vhacdVHACD.h index 45ba2662..d67b1076 100644 --- a/src/VHACD_Lib/inc/vhacdVHACD.h +++ b/src/VHACD_Lib/inc/vhacdVHACD.h @@ -2,29 +2,15 @@ All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. The names of the contributors may not be used to endorse or promote products -derived from this software without specific prior written permission. +3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #ifndef VHACD_VHACD_H @@ -36,363 +22,350 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #else #include #endif -#endif // OPENCL_FOUND +#endif //OPENCL_FOUND -#include #include "vhacdMutex.h" -#include "vhacdRaycastMesh.h" #include "vhacdVolume.h" - -#include +#include "vhacdRaycastMesh.h" +#include #define USE_THREAD 1 #define OCL_MIN_NUM_PRIMITIVES 4096 #define CH_APP_MIN_NUM_PRIMITIVES 64000 namespace VHACD { class VHACD : public IVHACD { - public: - //! Constructor. - VHACD() { +public: + //! Constructor. + VHACD() + { #if USE_THREAD == 1 && _OPENMP - m_ompNumProcessors = 2 * omp_get_num_procs(); - omp_set_num_threads(m_ompNumProcessors); -#else // USE_THREAD == 1 && _OPENMP - m_ompNumProcessors = 1; -#endif // USE_THREAD == 1 && _OPENMP + m_ompNumProcessors = 2 * omp_get_num_procs(); + omp_set_num_threads(m_ompNumProcessors); +#else //USE_THREAD == 1 && _OPENMP + m_ompNumProcessors = 1; +#endif //USE_THREAD == 1 && _OPENMP #ifdef CL_VERSION_1_1 - m_oclWorkGroupSize = 0; - m_oclDevice = 0; - m_oclQueue = 0; - m_oclKernelComputePartialVolumes = 0; - m_oclKernelComputeSum = 0; -#endif // CL_VERSION_1_1 - Init(); - } - //! Destructor. - ~VHACD(void) {} - uint32_t GetNConvexHulls() const { return (uint32_t)m_convexHulls.Size(); } - void Cancel() { SetCancel(true); } - void GetConvexHull(const uint32_t index, ConvexHull& ch) const { - Mesh* mesh = m_convexHulls[index]; - ch.m_nPoints = (uint32_t)mesh->GetNPoints(); - ch.m_nTriangles = (uint32_t)mesh->GetNTriangles(); - ch.m_points = mesh->GetPoints(); - ch.m_triangles = (uint32_t*)mesh->GetTriangles(); - ch.m_volume = mesh->ComputeVolume(); - Vec3& center = mesh->ComputeCenter(); - ch.m_center[0] = center.X(); - ch.m_center[1] = center.Y(); - ch.m_center[2] = center.Z(); - } - void Clean(void) { - if (mRaycastMesh) { - mRaycastMesh->release(); - mRaycastMesh = nullptr; + m_oclWorkGroupSize = 0; + m_oclDevice = 0; + m_oclQueue = 0; + m_oclKernelComputePartialVolumes = 0; + m_oclKernelComputeSum = 0; +#endif //CL_VERSION_1_1 + Init(); } - delete m_volume; - delete m_pset; - size_t nCH = m_convexHulls.Size(); - for (size_t p = 0; p < nCH; ++p) { - delete m_convexHulls[p]; + //! Destructor. + ~VHACD(void) + { } - m_convexHulls.Clear(); - Init(); - } - void Release(void) { delete this; } - bool Compute(const float* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params); - bool Compute(const double* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params); - bool computeVoxelField(const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params); - bool computeVoxelField(const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params); - Volume* getVoxelField(); - bool OCLInit(void* const oclDevice, IUserLogger* const logger = 0); - bool OCLRelease(IUserLogger* const logger = 0); - - virtual bool ComputeCenterOfMass(double centerOfMass[3]) const; - - private: - void SetCancel(bool cancel) { - m_cancelMutex.Lock(); - m_cancel = cancel; - m_cancelMutex.Unlock(); - } - bool GetCancel() { - m_cancelMutex.Lock(); - bool cancel = m_cancel; - m_cancelMutex.Unlock(); - return cancel; - } - void Update(const double stageProgress, - const double operationProgress, - const Parameters& params) { - m_stageProgress = stageProgress; - m_operationProgress = operationProgress; - if (params.m_callback) { - params.m_callback->Update(m_overallProgress, m_stageProgress, - m_operationProgress, m_stage.c_str(), - m_operation.c_str()); + uint32_t GetNConvexHulls() const + { + return (uint32_t)m_convexHulls.Size(); } - } - void Init() { - if (mRaycastMesh) { - mRaycastMesh->release(); - mRaycastMesh = nullptr; + void Cancel() + { + SetCancel(true); } - memset(m_rot, 0, sizeof(double) * 9); - m_dim = 64; - m_volume = 0; - m_volumeCH0 = 0.0; - m_pset = 0; - m_overallProgress = 0.0; - m_stageProgress = 0.0; - m_operationProgress = 0.0; - m_stage = ""; - m_operation = ""; - m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; - m_rot[0][0] = m_rot[1][1] = m_rot[2][2] = 1.0; - SetCancel(false); - } - void ComputePrimitiveSet(const Parameters& params); - void ComputeACD(const Parameters& params); - void MergeConvexHulls(const Parameters& params); - void SimplifyConvexHull(Mesh* const ch, - const size_t nvertices, - const double minVolume); - void SimplifyConvexHulls(const Parameters& params); - void ComputeBestClippingPlane(const PrimitiveSet* inputPSet, - const double volume, - const SArray& planes, - const Vec3& preferredCuttingDirection, - const double w, - const double alpha, - const double beta, - const int32_t convexhullDownsampling, - const double progress0, - const double progress1, - Plane& bestPlane, - double& minConcavity, - const Parameters& params); - template - void AlignMesh(const T* const points, - const uint32_t stridePoints, - const uint32_t nPoints, - const int32_t* const triangles, - const uint32_t strideTriangles, - const uint32_t nTriangles, - const Parameters& params) { - if (GetCancel() || !params.m_pca) { - return; + void GetConvexHull(const uint32_t index, ConvexHull& ch) const + { + Mesh* mesh = m_convexHulls[index]; + ch.m_nPoints = (uint32_t)mesh->GetNPoints(); + ch.m_nTriangles = (uint32_t)mesh->GetNTriangles(); + ch.m_points = mesh->GetPoints(); + ch.m_triangles = (uint32_t *)mesh->GetTriangles(); + ch.m_volume = mesh->ComputeVolume(); + Vec3 ¢er = mesh->ComputeCenter(); + ch.m_center[0] = center.X(); + ch.m_center[1] = center.Y(); + ch.m_center[2] = center.Z(); } - m_timer.Tic(); - - m_stage = "Align mesh"; - m_operation = "Voxelization"; - - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); + void Clean(void) + { + if (mRaycastMesh) + { + mRaycastMesh->release(); + mRaycastMesh = nullptr; + } + delete m_volume; + delete m_pset; + size_t nCH = m_convexHulls.Size(); + for (size_t p = 0; p < nCH; ++p) { + delete m_convexHulls[p]; + } + m_convexHulls.Clear(); + Init(); } - - Update(0.0, 0.0, params); - if (GetCancel()) { - return; + void Release(void) + { + delete this; } - m_dim = (size_t)(pow((double)params.m_resolution, 1.0 / 3.0) + 0.5); - Volume volume; - volume.Voxelize(points, stridePoints, nPoints, triangles, strideTriangles, - nTriangles, m_dim, m_barycenter, m_rot); - size_t n = - volume.GetNPrimitivesOnSurf() + volume.GetNPrimitivesInsideSurf(); - Update(50.0, 100.0, params); + bool Compute(const float* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params); + bool Compute(const double* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params); + bool OCLInit(void* const oclDevice, + IUserLogger* const logger = 0); + bool OCLRelease(IUserLogger* const logger = 0); - if (params.m_logger) { - msg.str(""); - msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - if (GetCancel()) { - return; + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const; + +private: + void SetCancel(bool cancel) + { + m_cancelMutex.Lock(); + m_cancel = cancel; + m_cancelMutex.Unlock(); } - m_operation = "PCA"; - Update(50.0, 0.0, params); - volume.AlignToPrincipalAxes(m_rot); - m_overallProgress = 1.0; - Update(100.0, 100.0, params); + bool GetCancel() + { - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" - << std::endl; - params.m_logger->Log(msg.str().c_str()); + m_cancelMutex.Lock(); + bool cancel = m_cancel; + m_cancelMutex.Unlock(); + return cancel; } - } - template - void VoxelizeMesh(const T* const points, - const uint32_t stridePoints, - const uint32_t nPoints, - const int32_t* const triangles, - const uint32_t strideTriangles, - const uint32_t nTriangles, - const Parameters& params) { - if (GetCancel()) { - return; + void Update(const double stageProgress, + const double operationProgress, + const Parameters& params) + { + m_stageProgress = stageProgress; + m_operationProgress = operationProgress; + if (params.m_callback) { + params.m_callback->Update(m_overallProgress, + m_stageProgress, + m_operationProgress, + m_stage.c_str(), + m_operation.c_str()); + } } + void Init() + { + if (mRaycastMesh) + { + mRaycastMesh->release(); + mRaycastMesh = nullptr; + } + memset(m_rot, 0, sizeof(double) * 9); + m_dim = 64; + m_volume = 0; + m_volumeCH0 = 0.0; + m_pset = 0; + m_overallProgress = 0.0; + m_stageProgress = 0.0; + m_operationProgress = 0.0; + m_stage = ""; + m_operation = ""; + m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; + m_rot[0][0] = m_rot[1][1] = m_rot[2][2] = 1.0; + SetCancel(false); + } + void ComputePrimitiveSet(const Parameters& params); + void ComputeACD(const Parameters& params); + void MergeConvexHulls(const Parameters& params); + void SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume); + void SimplifyConvexHulls(const Parameters& params); + void ComputeBestClippingPlane(const PrimitiveSet* inputPSet, + const double volume, + const SArray& planes, + const Vec3& preferredCuttingDirection, + const double w, + const double alpha, + const double beta, + const int32_t convexhullDownsampling, + const double progress0, + const double progress1, + Plane& bestPlane, + double& minConcavity, + const Parameters& params); + template + void AlignMesh(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const int32_t* const triangles, + const uint32_t strideTriangles, + const uint32_t nTriangles, + const Parameters& params) + { + if (GetCancel() || !params.m_pca) { + return; + } + m_timer.Tic(); - m_timer.Tic(); - m_stage = "Voxelization"; + m_stage = "Align mesh"; + m_operation = "Voxelization"; - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } - delete m_volume; - m_volume = 0; - int32_t iteration = 0; - const int32_t maxIteration = 5; - double progress = 0.0; - while (iteration++ < maxIteration && !m_cancel) { - msg.str(""); - msg << "Iteration " << iteration; - m_operation = msg.str(); + Update(0.0, 0.0, params); + if (GetCancel()) { + return; + } + m_dim = (size_t)(pow((double)params.m_resolution, 1.0 / 3.0) + 0.5); + Volume volume; + volume.Voxelize(points, stridePoints, nPoints, + triangles, strideTriangles, nTriangles, + m_dim, m_barycenter, m_rot); + size_t n = volume.GetNPrimitivesOnSurf() + volume.GetNPrimitivesInsideSurf(); + Update(50.0, 100.0, params); - progress = iteration * 100.0 / maxIteration; - Update(progress, 0.0, params); + if (params.m_logger) { + msg.str(""); + msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + if (GetCancel()) { + return; + } + m_operation = "PCA"; + Update(50.0, 0.0, params); + volume.AlignToPrincipalAxes(m_rot); + m_overallProgress = 1.0; + Update(100.0, 100.0, params); - m_volume = new Volume; - m_volume->Voxelize(points, stridePoints, nPoints, triangles, - strideTriangles, nTriangles, m_dim, m_barycenter, - m_rot); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + } + template + void VoxelizeMesh(const T* const points, + const uint32_t stridePoints, + const uint32_t nPoints, + const int32_t* const triangles, + const uint32_t strideTriangles, + const uint32_t nTriangles, + const Parameters& params) + { + if (GetCancel()) { + return; + } - Update(progress, 100.0, params); + m_timer.Tic(); + m_stage = "Voxelization"; - size_t n = m_volume->GetNPrimitivesOnSurf() + - m_volume->GetNPrimitivesInsideSurf(); - if (params.m_logger) { - msg.str(""); - msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } - double a = pow((double)(params.m_resolution) / n, 0.33); - size_t dim_next = (size_t)(m_dim * a + 0.5); - if (n < params.m_resolution && iteration < maxIteration && - m_volume->GetNPrimitivesOnSurf() < params.m_resolution / 8 && - m_dim != dim_next) { delete m_volume; m_volume = 0; - m_dim = dim_next; - } else { - break; - } - } - m_overallProgress = 10.0; - Update(100.0, 100.0, params); + int32_t iteration = 0; + const int32_t maxIteration = 5; + double progress = 0.0; + while (iteration++ < maxIteration && !m_cancel) { + msg.str(""); + msg << "Iteration " << iteration; + m_operation = msg.str(); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" - << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - } - template - bool ComputeACD(const T* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params) { - Init(); - if (params.m_projectHullVertices) { - mRaycastMesh = RaycastMesh::createRaycastMesh(nPoints, points, nTriangles, - (const uint32_t*)triangles); - } - if (params.m_oclAcceleration) { - // build kernels - } - AlignMesh(points, 3, nPoints, (int32_t*)triangles, 3, nTriangles, params); - VoxelizeMesh(points, 3, nPoints, (int32_t*)triangles, 3, nTriangles, - params); - ComputePrimitiveSet(params); - ComputeACD(params); - MergeConvexHulls(params); - SimplifyConvexHulls(params); - if (params.m_oclAcceleration) { - // Release kernels - } - if (GetCancel()) { - Clean(); - return false; - } - return true; - } + progress = iteration * 100.0 / maxIteration; + Update(progress, 0.0, params); + + m_volume = new Volume; + m_volume->Voxelize(points, stridePoints, nPoints, + triangles, strideTriangles, nTriangles, + m_dim, m_barycenter, m_rot); + + Update(progress, 100.0, params); + + size_t n = m_volume->GetNPrimitivesOnSurf() + m_volume->GetNPrimitivesInsideSurf(); + if (params.m_logger) { + msg.str(""); + msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + double a = pow((double)(params.m_resolution) / n, 0.33); + size_t dim_next = (size_t)(m_dim * a + 0.5); + if (n < params.m_resolution && iteration < maxIteration && m_volume->GetNPrimitivesOnSurf() < params.m_resolution / 8 && m_dim != dim_next) { + delete m_volume; + m_volume = 0; + m_dim = dim_next; + } + else { + break; + } + } + m_overallProgress = 10.0; + Update(100.0, 100.0, params); - template - bool computeVoxelFieldHelper(const T* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params) { - Init(); - if (params.m_projectHullVertices) { - mRaycastMesh = RaycastMesh::createRaycastMesh(nPoints, points, nTriangles, - (const uint32_t*)triangles); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } } - if (params.m_oclAcceleration) { - // build kernels + template + bool ComputeACD(const T* const points, + const uint32_t nPoints, + const uint32_t* const triangles, + const uint32_t nTriangles, + const Parameters& params) + { + Init(); + if (params.m_projectHullVertices) + { + mRaycastMesh = RaycastMesh::createRaycastMesh(nPoints, points, nTriangles, (const uint32_t *)triangles); + } + if (params.m_oclAcceleration) { + // build kernels + } + AlignMesh(points, 3, nPoints, (int32_t *)triangles, 3, nTriangles, params); + VoxelizeMesh(points, 3, nPoints, (int32_t *)triangles, 3, nTriangles, params); + ComputePrimitiveSet(params); + ComputeACD(params); + MergeConvexHulls(params); + SimplifyConvexHulls(params); + if (params.m_oclAcceleration) { + // Release kernels + } + if (GetCancel()) { + Clean(); + return false; + } + return true; } - AlignMesh(points, 3, nPoints, (int32_t*)triangles, 3, nTriangles, params); - VoxelizeMesh(points, 3, nPoints, (int32_t*)triangles, 3, nTriangles, - params); - return true; - } - private: - RaycastMesh* mRaycastMesh{nullptr}; - SArray m_convexHulls; - std::string m_stage; - std::string m_operation; - double m_overallProgress; - double m_stageProgress; - double m_operationProgress; - double m_rot[3][3]; - double m_volumeCH0; - Vec3 m_barycenter; - Timer m_timer; - size_t m_dim; - Volume* m_volume; - PrimitiveSet* m_pset; - Mutex m_cancelMutex; - bool m_cancel; - int32_t m_ompNumProcessors; +private: + RaycastMesh *mRaycastMesh{ nullptr }; + SArray m_convexHulls; + std::string m_stage; + std::string m_operation; + double m_overallProgress; + double m_stageProgress; + double m_operationProgress; + double m_rot[3][3]; + double m_volumeCH0; + Vec3 m_barycenter; + Timer m_timer; + size_t m_dim; + Volume* m_volume; + PrimitiveSet* m_pset; + Mutex m_cancelMutex; + bool m_cancel; + int32_t m_ompNumProcessors; #ifdef CL_VERSION_1_1 - cl_device_id* m_oclDevice; - cl_context m_oclContext; - cl_program m_oclProgram; - cl_command_queue* m_oclQueue; - cl_kernel* m_oclKernelComputePartialVolumes; - cl_kernel* m_oclKernelComputeSum; - size_t m_oclWorkGroupSize; -#endif // CL_VERSION_1_1 + cl_device_id* m_oclDevice; + cl_context m_oclContext; + cl_program m_oclProgram; + cl_command_queue* m_oclQueue; + cl_kernel* m_oclKernelComputePartialVolumes; + cl_kernel* m_oclKernelComputeSum; + size_t m_oclWorkGroupSize; +#endif //CL_VERSION_1_1 }; -} // namespace VHACD -#endif // VHACD_VHACD_H +} +#endif // VHACD_VHACD_H diff --git a/src/VHACD_Lib/inc/vhacdVolume.h b/src/VHACD_Lib/inc/vhacdVolume.h new file mode 100644 index 00000000..c445f201 --- /dev/null +++ b/src/VHACD_Lib/inc/vhacdVolume.h @@ -0,0 +1,430 @@ +/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) + All rights reserved. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#ifndef VHACD_VOLUME_H +#define VHACD_VOLUME_H +#include "vhacdMesh.h" +#include "vhacdVector.h" +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4456 4701) +#endif + +namespace VHACD { + +enum VOXEL_VALUE { + PRIMITIVE_UNDEFINED = 0, + PRIMITIVE_OUTSIDE_SURFACE = 1, + PRIMITIVE_INSIDE_SURFACE = 2, + PRIMITIVE_ON_SURFACE = 3 +}; + +struct Voxel { +public: + short m_coord[3]; + short m_data; +}; + +class PrimitiveSet { +public: + virtual ~PrimitiveSet(){}; + virtual PrimitiveSet* Create() const = 0; + virtual const size_t GetNPrimitives() const = 0; + virtual const size_t GetNPrimitivesOnSurf() const = 0; + virtual const size_t GetNPrimitivesInsideSurf() const = 0; + virtual const double GetEigenValue(AXIS axis) const = 0; + virtual const double ComputeMaxVolumeError() const = 0; + virtual const double ComputeVolume() const = 0; + virtual void Clip(const Plane& plane, PrimitiveSet* const positivePart, + PrimitiveSet* const negativePart) const = 0; + virtual void Intersect(const Plane& plane, SArray >* const positivePts, + SArray >* const negativePts, const size_t sampling) const = 0; + virtual void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray >* const exteriorPts) const = 0; + virtual void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, + double& negativeVolume) const = 0; + virtual void SelectOnSurface(PrimitiveSet* const onSurfP) const = 0; + virtual void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const = 0; + virtual void ComputeBB() = 0; + virtual void ComputePrincipalAxes() = 0; + virtual void AlignToPrincipalAxes() = 0; + virtual void RevertAlignToPrincipalAxes() = 0; + virtual void Convert(Mesh& mesh, const VOXEL_VALUE value) const = 0; + const Mesh& GetConvexHull() const { return m_convexHull; }; + Mesh& GetConvexHull() { return m_convexHull; }; +private: + Mesh m_convexHull; +}; + +//! +class VoxelSet : public PrimitiveSet { + friend class Volume; + +public: + //! Destructor. + ~VoxelSet(void); + //! Constructor. + VoxelSet(); + + const size_t GetNPrimitives() const { return m_voxels.Size(); } + const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; } + const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } + const double ComputeVolume() const { return m_unitVolume * m_voxels.Size(); } + const double ComputeMaxVolumeError() const { return m_unitVolume * m_numVoxelsOnSurface; } + const Vec3& GetMinBBVoxels() const { return m_minBBVoxels; } + const Vec3& GetMaxBBVoxels() const { return m_maxBBVoxels; } + const Vec3& GetMinBB() const { return m_minBB; } + const double& GetScale() const { return m_scale; } + const double& GetUnitVolume() const { return m_unitVolume; } + Vec3 GetPoint(Vec3 voxel) const + { + return Vec3(voxel[0] * m_scale + m_minBB[0], + voxel[1] * m_scale + m_minBB[1], + voxel[2] * m_scale + m_minBB[2]); + } + Vec3 GetPoint(const Voxel& voxel) const + { + return Vec3(voxel.m_coord[0] * m_scale + m_minBB[0], + voxel.m_coord[1] * m_scale + m_minBB[1], + voxel.m_coord[2] * m_scale + m_minBB[2]); + } + Vec3 GetPoint(Vec3 voxel) const + { + return Vec3(voxel[0] * m_scale + m_minBB[0], + voxel[1] * m_scale + m_minBB[1], + voxel[2] * m_scale + m_minBB[2]); + } + void GetPoints(const Voxel& voxel, Vec3* const pts) const; + void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; + void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const; + void Intersect(const Plane& plane, SArray >* const positivePts, + SArray >* const negativePts, const size_t sampling) const; + void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray >* const exteriorPts) const; + void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const; + void SelectOnSurface(PrimitiveSet* const onSurfP) const; + void ComputeBB(); + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + void ComputePrincipalAxes(); + PrimitiveSet* Create() const + { + return new VoxelSet(); + } + void AlignToPrincipalAxes(){}; + void RevertAlignToPrincipalAxes(){}; + Voxel* const GetVoxels() { return m_voxels.Data(); } + const Voxel* const GetVoxels() const { return m_voxels.Data(); } + +private: + size_t m_numVoxelsOnSurface; + size_t m_numVoxelsInsideSurface; + Vec3 m_minBB; + double m_scale; + SArray m_voxels; + double m_unitVolume; + Vec3 m_minBBPts; + Vec3 m_maxBBPts; + Vec3 m_minBBVoxels; + Vec3 m_maxBBVoxels; + Vec3 m_barycenter; + double m_Q[3][3]; + double m_D[3][3]; + Vec3 m_barycenterPCA; +}; + +struct Tetrahedron { +public: + Vec3 m_pts[4]; + unsigned char m_data; +}; + +//! +class TetrahedronSet : public PrimitiveSet { + friend class Volume; + +public: + //! Destructor. + ~TetrahedronSet(void); + //! Constructor. + TetrahedronSet(); + + const size_t GetNPrimitives() const { return m_tetrahedra.Size(); } + const size_t GetNPrimitivesOnSurf() const { return m_numTetrahedraOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numTetrahedraInsideSurface; } + const Vec3& GetMinBB() const { return m_minBB; } + const Vec3& GetMaxBB() const { return m_maxBB; } + const Vec3& GetBarycenter() const { return m_barycenter; } + const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } + const double GetSacle() const { return m_scale; } + const double ComputeVolume() const; + const double ComputeMaxVolumeError() const; + void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; + void ComputePrincipalAxes(); + void AlignToPrincipalAxes(); + void RevertAlignToPrincipalAxes(); + void Clip(const Plane& plane, PrimitiveSet* const positivePart, PrimitiveSet* const negativePart) const; + void Intersect(const Plane& plane, SArray >* const positivePts, + SArray >* const negativePts, const size_t sampling) const; + void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh, + SArray >* const exteriorPts) const; + void ComputeClippedVolumes(const Plane& plane, double& positiveVolume, double& negativeVolume) const; + void SelectOnSurface(PrimitiveSet* const onSurfP) const; + void ComputeBB(); + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + inline bool Add(Tetrahedron& tetrahedron); + PrimitiveSet* Create() const + { + return new TetrahedronSet(); + } + static const double EPS; + +private: + void AddClippedTetrahedra(const Vec3 (&pts)[10], const int32_t nPts); + + size_t m_numTetrahedraOnSurface; + size_t m_numTetrahedraInsideSurface; + double m_scale; + Vec3 m_minBB; + Vec3 m_maxBB; + Vec3 m_barycenter; + SArray m_tetrahedra; + double m_Q[3][3]; + double m_D[3][3]; +}; + +//! +class Volume { +public: + //! Destructor. + ~Volume(void); + + //! Constructor. + Volume(); + + //! Voxelize + template + void Voxelize(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, + const int32_t* const triangles, const uint32_t strideTriangles, const uint32_t nTriangles, + const size_t dim, const Vec3& barycenter, const double (&rot)[3][3]); + unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) + { + assert(i < m_dim[0] || i >= 0); + assert(j < m_dim[0] || j >= 0); + assert(k < m_dim[0] || k >= 0); + return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; + } + const unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) const + { + assert(i < m_dim[0] || i >= 0); + assert(j < m_dim[0] || j >= 0); + assert(k < m_dim[0] || k >= 0); + return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; + } + const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } + const size_t GetNPrimitivesInsideSurf() const { return m_numVoxelsInsideSurface; } + void Convert(Mesh& mesh, const VOXEL_VALUE value) const; + void Convert(VoxelSet& vset) const; + void Convert(TetrahedronSet& tset) const; + void AlignToPrincipalAxes(double (&rot)[3][3]) const; + +private: + void FillOutsideSurface(const size_t i0, const size_t j0, const size_t k0, const size_t i1, + const size_t j1, const size_t k1); + void FillInsideSurface(); + template + void ComputeBB(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, + const Vec3& barycenter, const double (&rot)[3][3]); + void Allocate(); + void Free(); + + Vec3 m_minBB; + Vec3 m_maxBB; + double m_scale; + size_t m_dim[3]; //>! dim + size_t m_numVoxelsOnSurface; + size_t m_numVoxelsInsideSurface; + size_t m_numVoxelsOutsideSurface; + unsigned char* m_data; +}; +int32_t TriBoxOverlap(const Vec3& boxcenter, const Vec3& boxhalfsize, const Vec3& triver0, + const Vec3& triver1, const Vec3& triver2); +template +inline void ComputeAlignedPoint(const T* const points, const uint32_t idx, const Vec3& barycenter, + const double (&rot)[3][3], Vec3& pt){}; +template <> +inline void ComputeAlignedPoint(const float* const points, const uint32_t idx, const Vec3& barycenter, const double (&rot)[3][3], Vec3& pt) +{ + double x = points[idx + 0] - barycenter[0]; + double y = points[idx + 1] - barycenter[1]; + double z = points[idx + 2] - barycenter[2]; + pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; + pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; + pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; +} +template <> +inline void ComputeAlignedPoint(const double* const points, const uint32_t idx, const Vec3& barycenter, const double (&rot)[3][3], Vec3& pt) +{ + double x = points[idx + 0] - barycenter[0]; + double y = points[idx + 1] - barycenter[1]; + double z = points[idx + 2] - barycenter[2]; + pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; + pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; + pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; +} +template +void Volume::ComputeBB(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, + const Vec3& barycenter, const double (&rot)[3][3]) +{ + Vec3 pt; + ComputeAlignedPoint(points, 0, barycenter, rot, pt); + m_maxBB = pt; + m_minBB = pt; + for (uint32_t v = 1; v < nPoints; ++v) { + ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt); + for (int32_t i = 0; i < 3; ++i) { + if (pt[i] < m_minBB[i]) + m_minBB[i] = pt[i]; + else if (pt[i] > m_maxBB[i]) + m_maxBB[i] = pt[i]; + } + } +} +template +void Volume::Voxelize(const T* const points, const uint32_t stridePoints, const uint32_t nPoints, + const int32_t* const triangles, const uint32_t strideTriangles, const uint32_t nTriangles, + const size_t dim, const Vec3& barycenter, const double (&rot)[3][3]) +{ + if (nPoints == 0) { + return; + } + ComputeBB(points, stridePoints, nPoints, barycenter, rot); + + double d[3] = { m_maxBB[0] - m_minBB[0], m_maxBB[1] - m_minBB[1], m_maxBB[2] - m_minBB[2] }; + double r; + if (d[0] >= d[1] && d[0] >= d[2]) { + r = d[0]; + m_dim[0] = dim; + m_dim[1] = 2 + static_cast(dim * d[1] / d[0]); + m_dim[2] = 2 + static_cast(dim * d[2] / d[0]); + } + else if (d[1] >= d[0] && d[1] >= d[2]) { + r = d[1]; + m_dim[1] = dim; + m_dim[0] = 2 + static_cast(dim * d[0] / d[1]); + m_dim[2] = 2 + static_cast(dim * d[2] / d[1]); + } + else { + r = d[2]; + m_dim[2] = dim; + m_dim[0] = 2 + static_cast(dim * d[0] / d[2]); + m_dim[1] = 2 + static_cast(dim * d[1] / d[2]); + } + + m_scale = r / (dim - 1); + double invScale = (dim - 1) / r; + + Allocate(); + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + m_numVoxelsOutsideSurface = 0; + + Vec3 p[3]; + size_t i, j, k; + size_t i0, j0, k0; + size_t i1, j1, k1; + Vec3 boxcenter; + Vec3 pt; + const Vec3 boxhalfsize(0.5, 0.5, 0.5); + for (size_t t = 0, ti = 0; t < nTriangles; ++t, ti += strideTriangles) { + Vec3 tri(triangles[ti + 0], + triangles[ti + 1], + triangles[ti + 2]); + for (int32_t c = 0; c < 3; ++c) { + ComputeAlignedPoint(points, tri[c] * stridePoints, barycenter, rot, pt); + p[c][0] = (pt[0] - m_minBB[0]) * invScale; + p[c][1] = (pt[1] - m_minBB[1]) * invScale; + p[c][2] = (pt[2] - m_minBB[2]) * invScale; + i = static_cast(p[c][0] + 0.5); + j = static_cast(p[c][1] + 0.5); + k = static_cast(p[c][2] + 0.5); + assert(i < m_dim[0] && i >= 0 && j < m_dim[1] && j >= 0 && k < m_dim[2] && k >= 0); + + if (c == 0) { + i0 = i1 = i; + j0 = j1 = j; + k0 = k1 = k; + } + else { + if (i < i0) + i0 = i; + if (j < j0) + j0 = j; + if (k < k0) + k0 = k; + if (i > i1) + i1 = i; + if (j > j1) + j1 = j; + if (k > k1) + k1 = k; + } + } + if (i0 > 0) + --i0; + if (j0 > 0) + --j0; + if (k0 > 0) + --k0; + if (i1 < m_dim[0]) + ++i1; + if (j1 < m_dim[1]) + ++j1; + if (k1 < m_dim[2]) + ++k1; + for (size_t i = i0; i < i1; ++i) { + boxcenter[0] = (double)i; + for (size_t j = j0; j < j1; ++j) { + boxcenter[1] = (double)j; + for (size_t k = k0; k < k1; ++k) { + boxcenter[2] = (double)k; + int32_t res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]); + unsigned char& value = GetVoxel(i, j, k); + if (res == 1 && value == PRIMITIVE_UNDEFINED) { + value = PRIMITIVE_ON_SURFACE; + ++m_numVoxelsOnSurface; + } + } + } + } + } + FillOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1); + FillOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]); + FillOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]); + FillOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]); + FillInsideSurface(); +} +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + +#endif // VHACD_VOLUME_H diff --git a/src/VHACD_Lib/public/VHACD.h b/src/VHACD_Lib/public/VHACD.h index 8f868d2a..65390984 100644 --- a/src/VHACD_Lib/public/VHACD.h +++ b/src/VHACD_Lib/public/VHACD.h @@ -1,30 +1,16 @@ /* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) All rights reserved. - - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. The names of the contributors may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #ifndef VHACD_H @@ -35,149 +21,133 @@ // Changes for version 2.3 // -// m_gamma : Has been removed. This used to control the error metric to merge -// convex hulls. Now it uses the 'm_maxConvexHulls' value instead. -// m_maxConvexHulls : This is the maximum number of convex hulls to produce from -// the merge operation; replaces 'm_gamma'. +// m_gamma : Has been removed. This used to control the error metric to merge convex hulls. Now it uses the 'm_maxConvexHulls' value instead. +// m_maxConvexHulls : This is the maximum number of convex hulls to produce from the merge operation; replaces 'm_gamma'. // -// Note that decomposition depth is no longer a user provided value. It is now -// derived from the maximum number of hulls requested. +// Note that decomposition depth is no longer a user provided value. It is now derived from the +// maximum number of hulls requested. // -// As a convenience to the user, each convex hull produced now includes the -// volume of the hull as well as it's center. +// As a convenience to the user, each convex hull produced now includes the volume of the hull as well as it's center. // -// This version supports a convenience method to automatically make V-HACD run -// asynchronously in a background thread. To get a fully asynchronous version, -// call 'CreateVHACD_ASYNC' instead of 'CreateVHACD'. You get the same -// interface however, now when computing convex hulls, it is no longer a -// blocking operation. All callback messages are still returned in the -// application's thread so you don't need to worry about mutex locks or anything -// in that case. To tell if the operation is complete, the application should -// call 'IsReady'. This will return true if the last approximation operation is -// complete and will dispatch any pending messages. If you call 'Compute' while -// a previous operation was still running, it will automatically cancel the last -// request and begin a new one. To cancel a currently running approximation -// just call 'Cancel'. +// This version supports a convenience method to automatically make V-HACD run asynchronously in a background thread. +// To get a fully asynchronous version, call 'CreateVHACD_ASYNC' instead of 'CreateVHACD'. You get the same interface however, +// now when computing convex hulls, it is no longer a blocking operation. All callback messages are still returned +// in the application's thread so you don't need to worry about mutex locks or anything in that case. +// To tell if the operation is complete, the application should call 'IsReady'. This will return true if +// the last approximation operation is complete and will dispatch any pending messages. +// If you call 'Compute' while a previous operation was still running, it will automatically cancel the last request +// and begin a new one. To cancel a currently running approximation just call 'Cancel'. #include -#include "vhacdVolume.h" namespace VHACD { class IVHACD { - public: - class IUserCallback { - public: - virtual ~IUserCallback(){}; - virtual void Update(const double overallProgress, - const double stageProgress, - const double operationProgress, - const char* const stage, - const char* const operation) = 0; - }; - - class IUserLogger { - public: - virtual ~IUserLogger(){}; - virtual void Log(const char* const msg) = 0; - }; - - class ConvexHull { - public: - double* m_points; - uint32_t* m_triangles; - uint32_t m_nPoints; - uint32_t m_nTriangles; - double m_volume; - double m_center[3]; - }; - - class Parameters { - public: - Parameters(void) { Init(); } - void Init(void) { - m_resolution = 100000; - m_concavity = 0.001; - m_planeDownsampling = 4; - m_convexhullDownsampling = 4; - m_alpha = 0.05; - m_beta = 0.05; - m_pca = 0; - m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based - m_maxNumVerticesPerCH = 64; - m_minVolumePerCH = 0.0001; - m_callback = 0; - m_logger = 0; - m_convexhullApproximation = true; - m_oclAcceleration = true; - m_maxConvexHulls = 1024; - m_projectHullVertices = - true; // This will project the output convex hull vertices onto the - // original source mesh to increase the floating point accuracy - // of the results - } - double m_concavity; - double m_alpha; - double m_beta; - double m_minVolumePerCH; - IUserCallback* m_callback; - IUserLogger* m_logger; - uint32_t m_resolution; - uint32_t m_maxNumVerticesPerCH; - uint32_t m_planeDownsampling; - uint32_t m_convexhullDownsampling; - uint32_t m_pca; - uint32_t m_mode; - uint32_t m_convexhullApproximation; - uint32_t m_oclAcceleration; - uint32_t m_maxConvexHulls; - bool m_projectHullVertices; - }; - - virtual void Cancel() = 0; - virtual bool Compute(const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) = 0; - virtual bool Compute(const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) = 0; - virtual bool computeVoxelField(const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) = 0; - virtual bool computeVoxelField(const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) = 0; - virtual Volume* getVoxelField() = 0; - virtual uint32_t GetNConvexHulls() const = 0; - virtual void GetConvexHull(const uint32_t index, ConvexHull& ch) const = 0; - virtual void Clean(void) = 0; // release internally allocated memory - virtual void Release(void) = 0; // release IVHACD - virtual bool OCLInit(void* const oclDevice, - IUserLogger* const logger = 0) = 0; - virtual bool OCLRelease(IUserLogger* const logger = 0) = 0; - - // Will compute the center of mass of the convex hull decomposition results - // and return it in 'centerOfMass'. Returns false if the center of mass could - // not be computed. - virtual bool ComputeCenterOfMass(double centerOfMass[3]) const = 0; - - // In synchronous mode (non-multi-threaded) the state is always 'ready' - // In asynchronous mode, this returns true if the background thread is not - // still actively computing a new solution. In an asynchronous config the - // 'IsReady' call will report any update or log messages in the caller's - // current thread. - virtual bool IsReady(void) const { return true; } - - protected: - virtual ~IVHACD(void) {} +public: + class IUserCallback { + public: + virtual ~IUserCallback(){}; + virtual void Update(const double overallProgress, + const double stageProgress, + const double operationProgress, + const char* const stage, + const char* const operation) + = 0; + }; + + class IUserLogger { + public: + virtual ~IUserLogger(){}; + virtual void Log(const char* const msg) = 0; + }; + + class ConvexHull { + public: + double* m_points; + uint32_t* m_triangles; + uint32_t m_nPoints; + uint32_t m_nTriangles; + double m_volume; + double m_center[3]; + }; + + class Parameters { + public: + Parameters(void) { Init(); } + void Init(void) + { + m_resolution = 100000; + m_concavity = 0.001; + m_planeDownsampling = 4; + m_convexhullDownsampling = 4; + m_alpha = 0.05; + m_beta = 0.05; + m_pca = 0; + m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based + m_maxNumVerticesPerCH = 64; + m_minVolumePerCH = 0.0001; + m_callback = 0; + m_logger = 0; + m_convexhullApproximation = true; + m_oclAcceleration = true; + m_maxConvexHulls = 1024; + m_projectHullVertices = true; // This will project the output convex hull vertices onto the original source mesh to increase the floating point accuracy of the results + } + double m_concavity; + double m_alpha; + double m_beta; + double m_minVolumePerCH; + IUserCallback* m_callback; + IUserLogger* m_logger; + uint32_t m_resolution; + uint32_t m_maxNumVerticesPerCH; + uint32_t m_planeDownsampling; + uint32_t m_convexhullDownsampling; + uint32_t m_pca; + uint32_t m_mode; + uint32_t m_convexhullApproximation; + uint32_t m_oclAcceleration; + uint32_t m_maxConvexHulls; + bool m_projectHullVertices; + }; + + virtual void Cancel() = 0; + virtual bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) + = 0; + virtual bool Compute(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) + = 0; + virtual uint32_t GetNConvexHulls() const = 0; + virtual void GetConvexHull(const uint32_t index, ConvexHull& ch) const = 0; + virtual void Clean(void) = 0; // release internally allocated memory + virtual void Release(void) = 0; // release IVHACD + virtual bool OCLInit(void* const oclDevice, + IUserLogger* const logger = 0) + = 0; + virtual bool OCLRelease(IUserLogger* const logger = 0) = 0; + + // Will compute the center of mass of the convex hull decomposition results and return it + // in 'centerOfMass'. Returns false if the center of mass could not be computed. + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const = 0; + + // In synchronous mode (non-multi-threaded) the state is always 'ready' + // In asynchronous mode, this returns true if the background thread is not still actively computing + // a new solution. In an asynchronous config the 'IsReady' call will report any update or log + // messages in the caller's current thread. + virtual bool IsReady(void) const + { + return true; + } + +protected: + virtual ~IVHACD(void) {} }; IVHACD* CreateVHACD(void); IVHACD* CreateVHACD_ASYNC(void); -} // namespace VHACD -#endif // VHACD_H +} +#endif // VHACD_H diff --git a/src/VHACD_Lib/public/vhacdVolume.h b/src/VHACD_Lib/public/vhacdVolume.h deleted file mode 100644 index 9d2b7b77..00000000 --- a/src/VHACD_Lib/public/vhacdVolume.h +++ /dev/null @@ -1,501 +0,0 @@ -/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) - All rights reserved. - - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. The names of the contributors may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#pragma once -#ifndef VHACD_VOLUME_H -#define VHACD_VOLUME_H -#include -#include "vhacdMesh.h" -#include "vhacdVector.h" - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4456 4701) -#endif - -namespace VHACD { - -enum VOXEL_VALUE { - PRIMITIVE_UNDEFINED = 0, - PRIMITIVE_OUTSIDE_SURFACE = 1, - PRIMITIVE_INSIDE_SURFACE = 2, - PRIMITIVE_ON_SURFACE = 3 -}; - -struct Voxel { - public: - short m_coord[3]; - short m_data; -}; - -class PrimitiveSet { - public: - virtual ~PrimitiveSet(){}; - virtual PrimitiveSet* Create() const = 0; - virtual const size_t GetNPrimitives() const = 0; - virtual const size_t GetNPrimitivesOnSurf() const = 0; - virtual const size_t GetNPrimitivesInsideSurf() const = 0; - virtual const double GetEigenValue(AXIS axis) const = 0; - virtual const double ComputeMaxVolumeError() const = 0; - virtual const double ComputeVolume() const = 0; - virtual void Clip(const Plane& plane, - PrimitiveSet* const positivePart, - PrimitiveSet* const negativePart) const = 0; - virtual void Intersect(const Plane& plane, - SArray >* const positivePts, - SArray >* const negativePts, - const size_t sampling) const = 0; - virtual void ComputeExteriorPoints( - const Plane& plane, - const Mesh& mesh, - SArray >* const exteriorPts) const = 0; - virtual void ComputeClippedVolumes(const Plane& plane, - double& positiveVolume, - double& negativeVolume) const = 0; - virtual void SelectOnSurface(PrimitiveSet* const onSurfP) const = 0; - virtual void ComputeConvexHull(Mesh& meshCH, - const size_t sampling = 1) const = 0; - virtual void ComputeBB() = 0; - virtual void ComputePrincipalAxes() = 0; - virtual void AlignToPrincipalAxes() = 0; - virtual void RevertAlignToPrincipalAxes() = 0; - virtual void Convert(Mesh& mesh, const VOXEL_VALUE value) const = 0; - const Mesh& GetConvexHull() const { return m_convexHull; }; - Mesh& GetConvexHull() { return m_convexHull; }; - - private: - Mesh m_convexHull; -}; - -//! -class VoxelSet : public PrimitiveSet { - friend class Volume; - - public: - //! Destructor. - ~VoxelSet(void); - //! Constructor. - VoxelSet(); - - const size_t GetNPrimitives() const { return m_voxels.Size(); } - const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } - const size_t GetNPrimitivesInsideSurf() const { - return m_numVoxelsInsideSurface; - } - const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } - const double ComputeVolume() const { return m_unitVolume * m_voxels.Size(); } - const double ComputeMaxVolumeError() const { - return m_unitVolume * m_numVoxelsOnSurface; - } - const Vec3& GetMinBBVoxels() const { return m_minBBVoxels; } - const Vec3& GetMaxBBVoxels() const { return m_maxBBVoxels; } - const Vec3& GetMinBB() const { return m_minBB; } - const double& GetScale() const { return m_scale; } - const double& GetUnitVolume() const { return m_unitVolume; } - Vec3 GetPoint(Vec3 voxel) const { - return Vec3(voxel[0] * m_scale + m_minBB[0], - voxel[1] * m_scale + m_minBB[1], - voxel[2] * m_scale + m_minBB[2]); - } - Vec3 GetPoint(const Voxel& voxel) const { - return Vec3(voxel.m_coord[0] * m_scale + m_minBB[0], - voxel.m_coord[1] * m_scale + m_minBB[1], - voxel.m_coord[2] * m_scale + m_minBB[2]); - } - Vec3 GetPoint(Vec3 voxel) const { - return Vec3(voxel[0] * m_scale + m_minBB[0], - voxel[1] * m_scale + m_minBB[1], - voxel[2] * m_scale + m_minBB[2]); - } - void GetPoints(const Voxel& voxel, Vec3* const pts) const; - void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; - void Clip(const Plane& plane, - PrimitiveSet* const positivePart, - PrimitiveSet* const negativePart) const; - void Intersect(const Plane& plane, - SArray >* const positivePts, - SArray >* const negativePts, - const size_t sampling) const; - void ComputeExteriorPoints(const Plane& plane, - const Mesh& mesh, - SArray >* const exteriorPts) const; - void ComputeClippedVolumes(const Plane& plane, - double& positiveVolume, - double& negativeVolume) const; - void SelectOnSurface(PrimitiveSet* const onSurfP) const; - void ComputeBB(); - void Convert(Mesh& mesh, const VOXEL_VALUE value) const; - void ComputePrincipalAxes(); - PrimitiveSet* Create() const { return new VoxelSet(); } - void AlignToPrincipalAxes(){}; - void RevertAlignToPrincipalAxes(){}; - Voxel* const GetVoxels() { return m_voxels.Data(); } - const Voxel* const GetVoxels() const { return m_voxels.Data(); } - - private: - size_t m_numVoxelsOnSurface; - size_t m_numVoxelsInsideSurface; - Vec3 m_minBB; - double m_scale; - SArray m_voxels; - double m_unitVolume; - Vec3 m_minBBPts; - Vec3 m_maxBBPts; - Vec3 m_minBBVoxels; - Vec3 m_maxBBVoxels; - Vec3 m_barycenter; - double m_Q[3][3]; - double m_D[3][3]; - Vec3 m_barycenterPCA; -}; - -struct Tetrahedron { - public: - Vec3 m_pts[4]; - unsigned char m_data; -}; - -//! -class TetrahedronSet : public PrimitiveSet { - friend class Volume; - - public: - //! Destructor. - ~TetrahedronSet(void); - //! Constructor. - TetrahedronSet(); - - const size_t GetNPrimitives() const { return m_tetrahedra.Size(); } - const size_t GetNPrimitivesOnSurf() const { return m_numTetrahedraOnSurface; } - const size_t GetNPrimitivesInsideSurf() const { - return m_numTetrahedraInsideSurface; - } - const Vec3& GetMinBB() const { return m_minBB; } - const Vec3& GetMaxBB() const { return m_maxBB; } - const Vec3& GetBarycenter() const { return m_barycenter; } - const double GetEigenValue(AXIS axis) const { return m_D[axis][axis]; } - const double GetSacle() const { return m_scale; } - const double ComputeVolume() const; - const double ComputeMaxVolumeError() const; - void ComputeConvexHull(Mesh& meshCH, const size_t sampling = 1) const; - void ComputePrincipalAxes(); - void AlignToPrincipalAxes(); - void RevertAlignToPrincipalAxes(); - void Clip(const Plane& plane, - PrimitiveSet* const positivePart, - PrimitiveSet* const negativePart) const; - void Intersect(const Plane& plane, - SArray >* const positivePts, - SArray >* const negativePts, - const size_t sampling) const; - void ComputeExteriorPoints(const Plane& plane, - const Mesh& mesh, - SArray >* const exteriorPts) const; - void ComputeClippedVolumes(const Plane& plane, - double& positiveVolume, - double& negativeVolume) const; - void SelectOnSurface(PrimitiveSet* const onSurfP) const; - void ComputeBB(); - void Convert(Mesh& mesh, const VOXEL_VALUE value) const; - inline bool Add(Tetrahedron& tetrahedron); - PrimitiveSet* Create() const { return new TetrahedronSet(); } - static const double EPS; - - private: - void AddClippedTetrahedra(const Vec3 (&pts)[10], const int32_t nPts); - - size_t m_numTetrahedraOnSurface; - size_t m_numTetrahedraInsideSurface; - double m_scale; - Vec3 m_minBB; - Vec3 m_maxBB; - Vec3 m_barycenter; - SArray m_tetrahedra; - double m_Q[3][3]; - double m_D[3][3]; -}; - -//! -class Volume { - public: - //! Destructor. - ~Volume(void); - - //! Constructor. - Volume(); - - //! Voxelize - template - void Voxelize(const T* const points, - const uint32_t stridePoints, - const uint32_t nPoints, - const int32_t* const triangles, - const uint32_t strideTriangles, - const uint32_t nTriangles, - const size_t dim, - const Vec3& barycenter, - const double (&rot)[3][3]); - unsigned char& GetVoxel(const size_t i, const size_t j, const size_t k) { - assert(i < m_dim[0] || i >= 0); - assert(j < m_dim[0] || j >= 0); - assert(k < m_dim[0] || k >= 0); - return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; - } - const unsigned char& GetVoxel(const size_t i, - const size_t j, - const size_t k) const { - assert(i < m_dim[0] || i >= 0); - assert(j < m_dim[0] || j >= 0); - assert(k < m_dim[0] || k >= 0); - return m_data[i + j * m_dim[0] + k * m_dim[0] * m_dim[1]]; - } - - const double getScale() const { return m_scale; } - - const size_t* getDimensions() { return &m_dim[0]; } - const size_t GetNPrimitivesOnSurf() const { return m_numVoxelsOnSurface; } - const size_t GetNPrimitivesInsideSurf() const { - return m_numVoxelsInsideSurface; - } - - const Vec3 getCenter() { return m_barycenter; } - void Convert(Mesh& mesh, const VOXEL_VALUE value) const; - void Convert(VoxelSet& vset) const; - void Convert(TetrahedronSet& tset) const; - void AlignToPrincipalAxes(double (&rot)[3][3]) const; - - private: - void FillOutsideSurface(const size_t i0, - const size_t j0, - const size_t k0, - const size_t i1, - const size_t j1, - const size_t k1); - void FillInsideSurface(); - template - void ComputeBB(const T* const points, - const uint32_t stridePoints, - const uint32_t nPoints, - const Vec3& barycenter, - const double (&rot)[3][3]); - void Allocate(); - void Free(); - - Vec3 m_minBB; - Vec3 m_maxBB; - - Vec3 m_barycenter; - double m_scale; - size_t m_dim[3]; //>! dim - size_t m_numVoxelsOnSurface; - size_t m_numVoxelsInsideSurface; - size_t m_numVoxelsOutsideSurface; - unsigned char* m_data; -}; -int32_t TriBoxOverlap(const Vec3& boxcenter, - const Vec3& boxhalfsize, - const Vec3& triver0, - const Vec3& triver1, - const Vec3& triver2); -template -inline void ComputeAlignedPoint(const T* const points, - const uint32_t idx, - const Vec3& barycenter, - const double (&rot)[3][3], - Vec3& pt){}; -template <> -inline void ComputeAlignedPoint(const float* const points, - const uint32_t idx, - const Vec3& barycenter, - const double (&rot)[3][3], - Vec3& pt) { - double x = points[idx + 0] - barycenter[0]; - double y = points[idx + 1] - barycenter[1]; - double z = points[idx + 2] - barycenter[2]; - pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; - pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; - pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; -} -template <> -inline void ComputeAlignedPoint(const double* const points, - const uint32_t idx, - const Vec3& barycenter, - const double (&rot)[3][3], - Vec3& pt) { - double x = points[idx + 0] - barycenter[0]; - double y = points[idx + 1] - barycenter[1]; - double z = points[idx + 2] - barycenter[2]; - pt[0] = rot[0][0] * x + rot[1][0] * y + rot[2][0] * z; - pt[1] = rot[0][1] * x + rot[1][1] * y + rot[2][1] * z; - pt[2] = rot[0][2] * x + rot[1][2] * y + rot[2][2] * z; -} -template -void Volume::ComputeBB(const T* const points, - const uint32_t stridePoints, - const uint32_t nPoints, - const Vec3& barycenter, - const double (&rot)[3][3]) { - Vec3 pt; - ComputeAlignedPoint(points, 0, barycenter, rot, pt); - m_maxBB = pt; - m_minBB = pt; - for (uint32_t v = 1; v < nPoints; ++v) { - ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt); - for (int32_t i = 0; i < 3; ++i) { - if (pt[i] < m_minBB[i]) - m_minBB[i] = pt[i]; - else if (pt[i] > m_maxBB[i]) - m_maxBB[i] = pt[i]; - } - } -} -template -void Volume::Voxelize(const T* const points, - const uint32_t stridePoints, - const uint32_t nPoints, - const int32_t* const triangles, - const uint32_t strideTriangles, - const uint32_t nTriangles, - const size_t dim, - const Vec3& barycenter, - const double (&rot)[3][3]) { - if (nPoints == 0) { - return; - } - ComputeBB(points, stridePoints, nPoints, barycenter, rot); - m_barycenter = barycenter; - double d[3] = {m_maxBB[0] - m_minBB[0], m_maxBB[1] - m_minBB[1], - m_maxBB[2] - m_minBB[2]}; - double r; - if (d[0] >= d[1] && d[0] >= d[2]) { - r = d[0]; - m_dim[0] = dim; - m_dim[1] = 2 + static_cast(dim * d[1] / d[0]); - m_dim[2] = 2 + static_cast(dim * d[2] / d[0]); - } else if (d[1] >= d[0] && d[1] >= d[2]) { - r = d[1]; - m_dim[1] = dim; - m_dim[0] = 2 + static_cast(dim * d[0] / d[1]); - m_dim[2] = 2 + static_cast(dim * d[2] / d[1]); - } else { - r = d[2]; - m_dim[2] = dim; - m_dim[0] = 2 + static_cast(dim * d[0] / d[2]); - m_dim[1] = 2 + static_cast(dim * d[1] / d[2]); - } - - m_scale = r / (dim - 1); - double invScale = (dim - 1) / r; - - Allocate(); - m_numVoxelsOnSurface = 0; - m_numVoxelsInsideSurface = 0; - m_numVoxelsOutsideSurface = 0; - - Vec3 p[3]; - size_t i, j, k; - size_t i0, j0, k0; - size_t i1, j1, k1; - Vec3 boxcenter; - Vec3 pt; - const Vec3 boxhalfsize(0.5, 0.5, 0.5); - for (size_t t = 0, ti = 0; t < nTriangles; ++t, ti += strideTriangles) { - Vec3 tri(triangles[ti + 0], triangles[ti + 1], triangles[ti + 2]); - for (int32_t c = 0; c < 3; ++c) { - ComputeAlignedPoint(points, tri[c] * stridePoints, barycenter, rot, pt); - p[c][0] = (pt[0] - m_minBB[0]) * invScale; - p[c][1] = (pt[1] - m_minBB[1]) * invScale; - p[c][2] = (pt[2] - m_minBB[2]) * invScale; - i = static_cast(p[c][0] + 0.5); - j = static_cast(p[c][1] + 0.5); - k = static_cast(p[c][2] + 0.5); - assert(i < m_dim[0] && i >= 0 && j < m_dim[1] && j >= 0 && k < m_dim[2] && - k >= 0); - - if (c == 0) { - i0 = i1 = i; - j0 = j1 = j; - k0 = k1 = k; - } else { - if (i < i0) - i0 = i; - if (j < j0) - j0 = j; - if (k < k0) - k0 = k; - if (i > i1) - i1 = i; - if (j > j1) - j1 = j; - if (k > k1) - k1 = k; - } - } - if (i0 > 0) - --i0; - if (j0 > 0) - --j0; - if (k0 > 0) - --k0; - if (i1 < m_dim[0]) - ++i1; - if (j1 < m_dim[1]) - ++j1; - if (k1 < m_dim[2]) - ++k1; - for (size_t i = i0; i < i1; ++i) { - boxcenter[0] = (double)i; - for (size_t j = j0; j < j1; ++j) { - boxcenter[1] = (double)j; - for (size_t k = k0; k < k1; ++k) { - boxcenter[2] = (double)k; - int32_t res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]); - unsigned char& value = GetVoxel(i, j, k); - if (res == 1 && value == PRIMITIVE_UNDEFINED) { - value = PRIMITIVE_ON_SURFACE; - ++m_numVoxelsOnSurface; - } - } - } - } - } - FillOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1); - FillOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]); - FillOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]); - FillOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]); - FillOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]); - FillOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]); - FillInsideSurface(); -} -} // namespace VHACD - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif // VHACD_VOLUME_H diff --git a/src/VHACD_Lib/src/VHACD-ASYNC.cpp b/src/VHACD_Lib/src/VHACD-ASYNC.cpp index 49aa1679..5a2f6be6 100644 --- a/src/VHACD_Lib/src/VHACD-ASYNC.cpp +++ b/src/VHACD_Lib/src/VHACD-ASYNC.cpp @@ -1,12 +1,12 @@ -#include -#include +#include "../public/VHACD.h" #include #include +#include +#include #include #include #include -#include -#include "../public/VHACD.h" +#include #define ENABLE_ASYNC 1 @@ -14,324 +14,321 @@ #define HACD_FREE(x) free(x) #define HACD_ASSERT(x) assert(x) -namespace VHACD { - -class MyHACD_API : public VHACD::IVHACD, - public VHACD::IVHACD::IUserCallback, - VHACD::IVHACD::IUserLogger { - public: - MyHACD_API(void) { mVHACD = VHACD::CreateVHACD(); } - - virtual ~MyHACD_API(void) { - releaseHACD(); - Cancel(); - mVHACD->Release(); - } - - virtual bool Compute(const double* const _points, - const uint32_t countPoints, - const uint32_t* const _triangles, - const uint32_t countTriangles, - const Parameters& _desc) final { +namespace VHACD +{ + +class MyHACD_API : public VHACD::IVHACD, public VHACD::IVHACD::IUserCallback, VHACD::IVHACD::IUserLogger +{ +public: + MyHACD_API(void) + { + mVHACD = VHACD::CreateVHACD(); + } + + virtual ~MyHACD_API(void) + { + releaseHACD(); + Cancel(); + mVHACD->Release(); + } + + + virtual bool Compute(const double* const _points, + const uint32_t countPoints, + const uint32_t* const _triangles, + const uint32_t countTriangles, + const Parameters& _desc) final + { #if ENABLE_ASYNC - Cancel(); // if we previously had a solution running; cancel it. - releaseHACD(); - // We need to copy the input vertices and triangles into our own buffers so - // we can operate on them safely from the background thread. - mVertices = (double*)HACD_ALLOC(sizeof(double) * countPoints * 3); - mIndices = (uint32_t*)HACD_ALLOC(sizeof(uint32_t) * countTriangles * 3); - memcpy(mVertices, _points, sizeof(double) * countPoints * 3); - memcpy(mIndices, _triangles, sizeof(uint32_t) * countTriangles * 3); - mRunning = true; - mThread = new std::thread([this, countPoints, countTriangles, _desc]() { - ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc); - mRunning = false; - }); + Cancel(); // if we previously had a solution running; cancel it. + releaseHACD(); + + // We need to copy the input vertices and triangles into our own buffers so we can operate + // on them safely from the background thread. + mVertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3); + mIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*countTriangles * 3); + memcpy(mVertices, _points, sizeof(double)*countPoints * 3); + memcpy(mIndices, _triangles, sizeof(uint32_t)*countTriangles * 3); + mRunning = true; + mThread = new std::thread([this, countPoints, countTriangles, _desc]() + { + ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc); + mRunning = false; + }); #else - releaseHACD(); - ComputeNow(_points, countPoints, _triangles, countTriangles, _desc); + releaseHACD(); + ComputeNow(_points, countPoints, _triangles, countTriangles, _desc); #endif - return true; - } - virtual bool computeVoxelField(const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& _desc) { - return mVHACD->computeVoxelField(points, countPoints, triangles, - countTriangles, _desc); - } - - virtual Volume* getVoxelField() { return mVHACD->getVoxelField(); } - - bool ComputeNow(const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& _desc) { - uint32_t ret = 0; - - mHullCount = 0; - mCallback = _desc.m_callback; - mLogger = _desc.m_logger; - - IVHACD::Parameters desc = _desc; - // Set our intercepting callback interfaces if non-null - desc.m_callback = desc.m_callback ? this : nullptr; - desc.m_logger = desc.m_logger ? this : nullptr; - - if (countPoints) { - bool ok = - mVHACD->Compute(points, countPoints, triangles, countTriangles, desc); - if (ok) { - ret = mVHACD->GetNConvexHulls(); - mHulls = new IVHACD::ConvexHull[ret]; - for (uint32_t i = 0; i < ret; i++) { - VHACD::IVHACD::ConvexHull vhull; - mVHACD->GetConvexHull(i, vhull); - VHACD::IVHACD::ConvexHull h; - h.m_nPoints = vhull.m_nPoints; - h.m_points = (double*)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints); - memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints); - h.m_nTriangles = vhull.m_nTriangles; - h.m_triangles = - (uint32_t*)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles); - memcpy(h.m_triangles, vhull.m_triangles, - sizeof(uint32_t) * 3 * h.m_nTriangles); - h.m_volume = vhull.m_volume; - h.m_center[0] = vhull.m_center[0]; - h.m_center[1] = vhull.m_center[1]; - h.m_center[2] = vhull.m_center[2]; - mHulls[i] = h; - if (mCancel) { - ret = 0; - break; - } - } - } - } - - mHullCount = ret; - return ret ? true : false; - } - - void releaseHull(VHACD::IVHACD::ConvexHull& h) { - HACD_FREE((void*)h.m_triangles); - HACD_FREE((void*)h.m_points); - h.m_triangles = nullptr; - h.m_points = nullptr; - } - - virtual void GetConvexHull(const uint32_t index, - VHACD::IVHACD::ConvexHull& ch) const final { - if (index < mHullCount) { - ch = mHulls[index]; - } - } - - void releaseHACD( - void) // release memory associated with the last HACD request - { - for (uint32_t i = 0; i < mHullCount; i++) { - releaseHull(mHulls[i]); - } - delete[] mHulls; - mHulls = nullptr; - mHullCount = 0; - HACD_FREE(mVertices); - mVertices = nullptr; - HACD_FREE(mIndices); - mIndices = nullptr; - } - - virtual void release(void) // release the HACD_API interface - { - delete this; - } - - virtual uint32_t getHullCount(void) { return mHullCount; } - - virtual void Cancel() final { - if (mRunning) { - mVHACD->Cancel(); // Set the cancel signal to the base VHACD - } - if (mThread) { - mThread->join(); // Wait for the thread to fully exit before we delete - // the instance - delete mThread; - mThread = nullptr; - Log("Convex Decomposition thread canceled\n"); - } - mCancel = false; // clear the cancel semaphore - } - - virtual bool Compute(const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) final { - double* vertices = (double*)HACD_ALLOC(sizeof(double) * countPoints * 3); - const float* source = points; - double* dest = vertices; - for (uint32_t i = 0; i < countPoints; i++) { - dest[0] = source[0]; - dest[1] = source[1]; - dest[2] = source[2]; - dest += 3; - source += 3; - } - - bool ret = - Compute(vertices, countPoints, triangles, countTriangles, params); - HACD_FREE(vertices); - return ret; - } - - virtual bool computeVoxelField(const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) final { - double* vertices = (double*)HACD_ALLOC(sizeof(double) * countPoints * 3); - const float* source = points; - double* dest = vertices; - for (uint32_t i = 0; i < countPoints; i++) { - dest[0] = source[0]; - dest[1] = source[1]; - dest[2] = source[2]; - dest += 3; - source += 3; - } - - bool ret = computeVoxelField(vertices, countPoints, triangles, - countTriangles, params); - HACD_FREE(vertices); - return ret; - } - - virtual uint32_t GetNConvexHulls() const final { - processPendingMessages(); - return mHullCount; - } - - virtual void Clean(void) final // release internally allocated memory - { - Cancel(); - releaseHACD(); - mVHACD->Clean(); - } - - virtual void Release(void) final // release IVHACD - { - delete this; - } - - virtual bool OCLInit(void* const oclDevice, - IVHACD::IUserLogger* const logger = 0) final { - return mVHACD->OCLInit(oclDevice, logger); - } - - virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final { - return mVHACD->OCLRelease(logger); - } - - virtual void Update(const double overallProgress, - const double stageProgress, - const double operationProgress, - const char* const stage, - const char* const operation) final { - mMessageMutex.lock(); - mHaveUpdateMessage = true; - mOverallProgress = overallProgress; - mStageProgress = stageProgress; - mOperationProgress = operationProgress; - mStage = std::string(stage); - mOperation = std::string(operation); - mMessageMutex.unlock(); - } - - virtual void Log(const char* const msg) final { - mMessageMutex.lock(); - mHaveLogMessage = true; - mMessage = std::string(msg); - mMessageMutex.unlock(); - } - - virtual bool IsReady(void) const final { - processPendingMessages(); - return !mRunning; - } - - // As a convenience for the calling application we only send it update and log - // messages from it's own main thread. This reduces the complexity burden on - // the caller by making sure it only has to deal with log messages in it's - // main application thread. - void processPendingMessages(void) const { - // If we have a new update message and the user has specified a callback we - // send the message and clear the semaphore - if (mHaveUpdateMessage && mCallback) { - mMessageMutex.lock(); - mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, - mStage.c_str(), mOperation.c_str()); - mHaveUpdateMessage = false; - mMessageMutex.unlock(); - } - // If we have a new log message and the user has specified a callback we - // send the message and clear the semaphore - if (mHaveLogMessage && mLogger) { - mMessageMutex.lock(); - mLogger->Log(mMessage.c_str()); - mHaveLogMessage = false; - mMessageMutex.unlock(); - } - } - - // Will compute the center of mass of the convex hull decomposition results - // and return it in 'centerOfMass'. Returns false if the center of mass could - // not be computed. - virtual bool ComputeCenterOfMass(double centerOfMass[3]) const { - bool ret = false; - - centerOfMass[0] = 0; - centerOfMass[1] = 0; - centerOfMass[2] = 0; - - if (mVHACD && IsReady()) { - ret = mVHACD->ComputeCenterOfMass(centerOfMass); - } - return ret; - } - - private: - double* mVertices{nullptr}; - uint32_t* mIndices{nullptr}; - std::atomic mHullCount{0}; - VHACD::IVHACD::ConvexHull* mHulls{nullptr}; - VHACD::IVHACD::IUserCallback* mCallback{nullptr}; - VHACD::IVHACD::IUserLogger* mLogger{nullptr}; - VHACD::IVHACD* mVHACD{nullptr}; - std::thread* mThread{nullptr}; - std::atomic mRunning{false}; - std::atomic mCancel{false}; - - // Thread safe caching mechanism for messages and update status. - // This is so that caller always gets messages in his own thread - // Member variables are marked as 'mutable' since the message dispatch - // function is called from const query methods. - mutable std::mutex mMessageMutex; - mutable std::atomic mHaveUpdateMessage{false}; - mutable std::atomic mHaveLogMessage{false}; - mutable double mOverallProgress{0}; - mutable double mStageProgress{0}; - mutable double mOperationProgress{0}; - mutable std::string mStage; - mutable std::string mOperation; - mutable std::string mMessage; + return true; + } + + bool ComputeNow(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& _desc) + { + uint32_t ret = 0; + + mHullCount = 0; + mCallback = _desc.m_callback; + mLogger = _desc.m_logger; + + IVHACD::Parameters desc = _desc; + // Set our intercepting callback interfaces if non-null + desc.m_callback = desc.m_callback ? this : nullptr; + desc.m_logger = desc.m_logger ? this : nullptr; + + if ( countPoints ) + { + bool ok = mVHACD->Compute(points, countPoints, triangles, countTriangles, desc); + if (ok) + { + ret = mVHACD->GetNConvexHulls(); + mHulls = new IVHACD::ConvexHull[ret]; + for (uint32_t i = 0; i < ret; i++) + { + VHACD::IVHACD::ConvexHull vhull; + mVHACD->GetConvexHull(i, vhull); + VHACD::IVHACD::ConvexHull h; + h.m_nPoints = vhull.m_nPoints; + h.m_points = (double *)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints); + memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints); + h.m_nTriangles = vhull.m_nTriangles; + h.m_triangles = (uint32_t *)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles); + memcpy(h.m_triangles, vhull.m_triangles, sizeof(uint32_t) * 3 * h.m_nTriangles); + h.m_volume = vhull.m_volume; + h.m_center[0] = vhull.m_center[0]; + h.m_center[1] = vhull.m_center[1]; + h.m_center[2] = vhull.m_center[2]; + mHulls[i] = h; + if (mCancel) + { + ret = 0; + break; + } + } + } + } + + mHullCount = ret; + return ret ? true : false; + } + + void releaseHull(VHACD::IVHACD::ConvexHull &h) + { + HACD_FREE((void *)h.m_triangles); + HACD_FREE((void *)h.m_points); + h.m_triangles = nullptr; + h.m_points = nullptr; + } + + virtual void GetConvexHull(const uint32_t index, VHACD::IVHACD::ConvexHull& ch) const final + { + if ( index < mHullCount ) + { + ch = mHulls[index]; + } + } + + void releaseHACD(void) // release memory associated with the last HACD request + { + for (uint32_t i=0; iCancel(); // Set the cancel signal to the base VHACD + } + if (mThread) + { + mThread->join(); // Wait for the thread to fully exit before we delete the instance + delete mThread; + mThread = nullptr; + Log("Convex Decomposition thread canceled\n"); + } + mCancel = false; // clear the cancel semaphore + } + + virtual bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) final + { + + double *vertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3); + const float *source = points; + double *dest = vertices; + for (uint32_t i = 0; i < countPoints; i++) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest += 3; + source += 3; + } + + bool ret = Compute(vertices, countPoints, triangles, countTriangles, params); + HACD_FREE(vertices); + return ret; + } + + virtual uint32_t GetNConvexHulls() const final + { + processPendingMessages(); + return mHullCount; + } + + virtual void Clean(void) final // release internally allocated memory + { + Cancel(); + releaseHACD(); + mVHACD->Clean(); + } + + virtual void Release(void) final // release IVHACD + { + delete this; + } + + virtual bool OCLInit(void* const oclDevice, + IVHACD::IUserLogger* const logger = 0) final + { + return mVHACD->OCLInit(oclDevice, logger); + } + + virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final + { + return mVHACD->OCLRelease(logger); + } + + virtual void Update(const double overallProgress, + const double stageProgress, + const double operationProgress, + const char* const stage, + const char* const operation) final + { + mMessageMutex.lock(); + mHaveUpdateMessage = true; + mOverallProgress = overallProgress; + mStageProgress = stageProgress; + mOperationProgress = operationProgress; + mStage = std::string(stage); + mOperation = std::string(operation); + mMessageMutex.unlock(); + } + + virtual void Log(const char* const msg) final + { + mMessageMutex.lock(); + mHaveLogMessage = true; + mMessage = std::string(msg); + mMessageMutex.unlock(); + } + + virtual bool IsReady(void) const final + { + processPendingMessages(); + return !mRunning; + } + + // As a convenience for the calling application we only send it update and log messages from it's own main + // thread. This reduces the complexity burden on the caller by making sure it only has to deal with log + // messages in it's main application thread. + void processPendingMessages(void) const + { + // If we have a new update message and the user has specified a callback we send the message and clear the semaphore + if (mHaveUpdateMessage && mCallback) + { + mMessageMutex.lock(); + mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, mStage.c_str(), mOperation.c_str()); + mHaveUpdateMessage = false; + mMessageMutex.unlock(); + } + // If we have a new log message and the user has specified a callback we send the message and clear the semaphore + if (mHaveLogMessage && mLogger) + { + mMessageMutex.lock(); + mLogger->Log(mMessage.c_str()); + mHaveLogMessage = false; + mMessageMutex.unlock(); + } + } + + // Will compute the center of mass of the convex hull decomposition results and return it + // in 'centerOfMass'. Returns false if the center of mass could not be computed. + virtual bool ComputeCenterOfMass(double centerOfMass[3]) const + { + bool ret = false; + + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + + if (mVHACD && IsReady() ) + { + ret = mVHACD->ComputeCenterOfMass(centerOfMass); + } + return ret; + } + +private: + double *mVertices{ nullptr }; + uint32_t *mIndices{ nullptr }; + std::atomic< uint32_t> mHullCount{ 0 }; + VHACD::IVHACD::ConvexHull *mHulls{ nullptr }; + VHACD::IVHACD::IUserCallback *mCallback{ nullptr }; + VHACD::IVHACD::IUserLogger *mLogger{ nullptr }; + VHACD::IVHACD *mVHACD{ nullptr }; + std::thread *mThread{ nullptr }; + std::atomic< bool > mRunning{ false }; + std::atomic mCancel{ false }; + + // Thread safe caching mechanism for messages and update status. + // This is so that caller always gets messages in his own thread + // Member variables are marked as 'mutable' since the message dispatch function + // is called from const query methods. + mutable std::mutex mMessageMutex; + mutable std::atomic< bool > mHaveUpdateMessage{ false }; + mutable std::atomic< bool > mHaveLogMessage{ false }; + mutable double mOverallProgress{ 0 }; + mutable double mStageProgress{ 0 }; + mutable double mOperationProgress{ 0 }; + mutable std::string mStage; + mutable std::string mOperation; + mutable std::string mMessage; }; -IVHACD* CreateVHACD_ASYNC(void) { - MyHACD_API* m = new MyHACD_API; - return static_cast(m); +IVHACD* CreateVHACD_ASYNC(void) +{ + MyHACD_API *m = new MyHACD_API; + return static_cast(m); } -}; // namespace VHACD + +}; // end of VHACD namespace + diff --git a/src/VHACD_Lib/src/VHACD.cpp b/src/VHACD_Lib/src/VHACD.cpp index 0a65346a..54cc6e37 100644 --- a/src/VHACD_Lib/src/VHACD.cpp +++ b/src/VHACD_Lib/src/VHACD.cpp @@ -1,30 +1,16 @@ /* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com) All rights reserved. - - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. The names of the contributors may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _CRT_SECURE_NO_WARNINGS @@ -34,15 +20,13 @@ #include #include #include -#include #include #include #if _OPENMP #include -#endif // _OPENMP +#endif // _OPENMP #include "../public/VHACD.h" -#include "FloatMath.h" #include "btConvexHullComputer.h" #include "vhacdICHull.h" #include "vhacdMesh.h" @@ -50,6 +34,8 @@ #include "vhacdTimer.h" #include "vhacdVHACD.h" #include "vhacdVector.h" +#include "vhacdVolume.h" +#include "FloatMath.h" #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -58,104 +44,97 @@ #define MAX_DOUBLE (1.79769e+308) #ifdef _MSC_VER -#pragma warning(disable : 4267 4100 4244 4456) +#pragma warning(disable:4267 4100 4244 4456) #endif #ifdef USE_SSE #include const int32_t SIMD_WIDTH = 4; -inline int32_t FindMinimumElement(const float* const d, - float* const _, - const int32_t n) { - // Min within vectors - __m128 min_i = _mm_set1_ps(-1.0f); - __m128 min_v = _mm_set1_ps(std::numeric_limits::max()); - for (int32_t i = 0; i <= n - SIMD_WIDTH; i += SIMD_WIDTH) { - const __m128 data = _mm_load_ps(&d[i]); - const __m128 pred = _mm_cmplt_ps(data, min_v); - - min_i = _mm_blendv_ps(min_i, _mm_set1_ps(i), pred); - min_v = _mm_min_ps(data, min_v); - } - - /* Min within vector */ - const __m128 min1 = _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(1, 0, 3, 2)); - const __m128 min2 = _mm_min_ps(min_v, min1); - const __m128 min3 = _mm_shuffle_ps(min2, min2, _MM_SHUFFLE(0, 1, 0, 1)); - const __m128 min4 = _mm_min_ps(min2, min3); - float min_d = _mm_cvtss_f32(min4); - - // Min index - const int32_t min_idx = - __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4))); - int32_t ret = min_i[min_idx] + min_idx; - - // Trailing elements - for (int32_t i = (n & ~(SIMD_WIDTH - 1)); i < n; ++i) { - if (d[i] < min_d) { - min_d = d[i]; - ret = i; - } - } - - *m = min_d; - return ret; -} +inline int32_t FindMinimumElement(const float* const d, float* const _, const int32_t n) +{ + // Min within vectors + __m128 min_i = _mm_set1_ps(-1.0f); + __m128 min_v = _mm_set1_ps(std::numeric_limits::max()); + for (int32_t i = 0; i <= n - SIMD_WIDTH; i += SIMD_WIDTH) { + const __m128 data = _mm_load_ps(&d[i]); + const __m128 pred = _mm_cmplt_ps(data, min_v); + + min_i = _mm_blendv_ps(min_i, _mm_set1_ps(i), pred); + min_v = _mm_min_ps(data, min_v); + } + + /* Min within vector */ + const __m128 min1 = _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(1, 0, 3, 2)); + const __m128 min2 = _mm_min_ps(min_v, min1); + const __m128 min3 = _mm_shuffle_ps(min2, min2, _MM_SHUFFLE(0, 1, 0, 1)); + const __m128 min4 = _mm_min_ps(min2, min3); + float min_d = _mm_cvtss_f32(min4); + + // Min index + const int32_t min_idx = __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4))); + int32_t ret = min_i[min_idx] + min_idx; + + // Trailing elements + for (int32_t i = (n & ~(SIMD_WIDTH - 1)); i < n; ++i) { + if (d[i] < min_d) { + min_d = d[i]; + ret = i; + } + } -inline int32_t FindMinimumElement(const float* const d, - float* const m, - const int32_t begin, - const int32_t end) { - // Leading elements - int32_t min_i = -1; - float min_d = std::numeric_limits::max(); - const int32_t aligned = (begin & ~(SIMD_WIDTH - 1)) + - ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0); - for (int32_t i = begin; i < std::min(end, aligned); ++i) { - if (d[i] < min_d) { - min_d = d[i]; - min_i = i; - } - } - - // Middle and trailing elements - float r_m = std::numeric_limits::max(); - const int32_t n = end - aligned; - const int32_t r_i = (n > 0) ? FindMinimumElement(&d[aligned], &r_m, n) : 0; - - // Pick the lowest - if (r_m < min_d) { - *m = r_m; - return r_i + aligned; - } else { *m = min_d; - return min_i; - } + return ret; +} + +inline int32_t FindMinimumElement(const float* const d, float* const m, const int32_t begin, const int32_t end) +{ + // Leading elements + int32_t min_i = -1; + float min_d = std::numeric_limits::max(); + const int32_t aligned = (begin & ~(SIMD_WIDTH - 1)) + ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0); + for (int32_t i = begin; i < std::min(end, aligned); ++i) { + if (d[i] < min_d) { + min_d = d[i]; + min_i = i; + } + } + + // Middle and trailing elements + float r_m = std::numeric_limits::max(); + const int32_t n = end - aligned; + const int32_t r_i = (n > 0) ? FindMinimumElement(&d[aligned], &r_m, n) : 0; + + // Pick the lowest + if (r_m < min_d) { + *m = r_m; + return r_i + aligned; + } + else { + *m = min_d; + return min_i; + } } #else -inline int32_t FindMinimumElement(const float* const d, - float* const m, - const int32_t begin, - const int32_t end) { - int32_t idx = -1; - float min = (std::numeric_limits::max)(); - for (size_t i = begin; i < size_t(end); ++i) { - if (d[i] < min) { - idx = i; - min = d[i]; - } - } - - *m = min; - return idx; +inline int32_t FindMinimumElement(const float* const d, float* const m, const int32_t begin, const int32_t end) +{ + int32_t idx = -1; + float min = (std::numeric_limits::max)(); + for (size_t i = begin; i < size_t(end); ++i) { + if (d[i] < min) { + idx = i; + min = d[i]; + } + } + + *m = min; + return idx; } #endif //#define OCL_SOURCE_FROM_FILE #ifndef OCL_SOURCE_FROM_FILE -const char* oclProgramSource = - "\ +const char* oclProgramSource = "\ __kernel void ComputePartialVolumes(__global short4 * voxels, \ const int numVoxels, \ const float4 plane, \ @@ -222,1455 +201,1389 @@ __kernel void ComputePartialSums(__global uint4 * data, data[get_group_id(0)] = partialSums[0]; \ } \ }"; -#endif // OCL_SOURCE_FROM_FILE +#endif //OCL_SOURCE_FROM_FILE namespace VHACD { -IVHACD* CreateVHACD(void) { - return new VHACD(); +IVHACD* CreateVHACD(void) +{ + return new VHACD(); } -bool VHACD::OCLInit(void* const oclDevice, IUserLogger* const logger) { +bool VHACD::OCLInit(void* const oclDevice, IUserLogger* const logger) +{ #ifdef CL_VERSION_1_1 - m_oclDevice = (cl_device_id*)oclDevice; - cl_int error; - m_oclContext = clCreateContext(NULL, 1, m_oclDevice, NULL, NULL, &error); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create context\n"); + m_oclDevice = (cl_device_id*)oclDevice; + cl_int error; + m_oclContext = clCreateContext(NULL, 1, m_oclDevice, NULL, NULL, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create context\n"); + } + return false; } - return false; - } #ifdef OCL_SOURCE_FROM_FILE - std::string cl_files = OPENCL_CL_FILES; + std::string cl_files = OPENCL_CL_FILES; // read kernal from file #ifdef _WIN32 - std::replace(cl_files.begin(), cl_files.end(), '/', '\\'); -#endif // _WIN32 - - FILE* program_handle = fopen(cl_files.c_str(), "rb"); - fseek(program_handle, 0, SEEK_END); - size_t program_size = ftell(program_handle); - rewind(program_handle); - char* program_buffer = new char[program_size + 1]; - program_buffer[program_size] = '\0'; - fread(program_buffer, sizeof(char), program_size, program_handle); - fclose(program_handle); - // create program - m_oclProgram = clCreateProgramWithSource( - m_oclContext, 1, (const char**)&program_buffer, &program_size, &error); - delete[] program_buffer; + std::replace(cl_files.begin(), cl_files.end(), '/', '\\'); +#endif // _WIN32 + + FILE* program_handle = fopen(cl_files.c_str(), "rb"); + fseek(program_handle, 0, SEEK_END); + size_t program_size = ftell(program_handle); + rewind(program_handle); + char* program_buffer = new char[program_size + 1]; + program_buffer[program_size] = '\0'; + fread(program_buffer, sizeof(char), program_size, program_handle); + fclose(program_handle); + // create program + m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&program_buffer, &program_size, &error); + delete[] program_buffer; #else - size_t program_size = strlen(oclProgramSource); - m_oclProgram = clCreateProgramWithSource( - m_oclContext, 1, (const char**)&oclProgramSource, &program_size, &error); + size_t program_size = strlen(oclProgramSource); + m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&oclProgramSource, &program_size, &error); #endif - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create program\n"); - } - return false; - } - - /* Build program */ - error = clBuildProgram(m_oclProgram, 1, m_oclDevice, "-cl-denorms-are-zero", - NULL, NULL); - if (error != CL_SUCCESS) { - size_t log_size; - /* Find Size of log and print to std output */ - clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, 0, - NULL, &log_size); - char* program_log = new char[log_size + 2]; - program_log[log_size] = '\n'; - program_log[log_size + 1] = '\0'; - clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, - log_size + 1, program_log, NULL); - if (logger) { - logger->Log("Couldn't build program\n"); - logger->Log(program_log); - } - delete[] program_log; - return false; - } - - delete[] m_oclQueue; - delete[] m_oclKernelComputePartialVolumes; - delete[] m_oclKernelComputeSum; - m_oclQueue = new cl_command_queue[m_ompNumProcessors]; - m_oclKernelComputePartialVolumes = new cl_kernel[m_ompNumProcessors]; - m_oclKernelComputeSum = new cl_kernel[m_ompNumProcessors]; - - const char nameKernelComputePartialVolumes[] = "ComputePartialVolumes"; - const char nameKernelComputeSum[] = "ComputePartialSums"; - for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - m_oclKernelComputePartialVolumes[k] = - clCreateKernel(m_oclProgram, nameKernelComputePartialVolumes, &error); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create kernel\n"); - } - return false; - } - m_oclKernelComputeSum[k] = - clCreateKernel(m_oclProgram, nameKernelComputeSum, &error); if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create kernel\n"); - } - return false; - } - } - - error = clGetKernelWorkGroupInfo(m_oclKernelComputePartialVolumes[0], - *m_oclDevice, CL_KERNEL_WORK_GROUP_SIZE, - sizeof(size_t), &m_oclWorkGroupSize, NULL); - size_t workGroupSize = 0; - error = clGetKernelWorkGroupInfo(m_oclKernelComputeSum[0], *m_oclDevice, - CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), - &workGroupSize, NULL); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't query work group info\n"); + if (logger) { + logger->Log("Couldn't create program\n"); + } + return false; } - return false; - } - if (workGroupSize < m_oclWorkGroupSize) { - m_oclWorkGroupSize = workGroupSize; - } - - for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - m_oclQueue[k] = clCreateCommandQueue( - m_oclContext, *m_oclDevice, 0 /*CL_QUEUE_PROFILING_ENABLE*/, &error); + /* Build program */ + error = clBuildProgram(m_oclProgram, 1, m_oclDevice, "-cl-denorms-are-zero", NULL, NULL); if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't create queue\n"); - } - return false; - } - } - return true; -#else // CL_VERSION_1_1 - return false; -#endif // CL_VERSION_1_1 -} -bool VHACD::OCLRelease(IUserLogger* const logger) { -#ifdef CL_VERSION_1_1 - cl_int error; - if (m_oclKernelComputePartialVolumes) { - for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - error = clReleaseKernel(m_oclKernelComputePartialVolumes[k]); - if (error != CL_SUCCESS) { + size_t log_size; + /* Find Size of log and print to std output */ + clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size); + char* program_log = new char[log_size + 2]; + program_log[log_size] = '\n'; + program_log[log_size + 1] = '\0'; + clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, log_size + 1, program_log, NULL); if (logger) { - logger->Log("Couldn't release kernal\n"); + logger->Log("Couldn't build program\n"); + logger->Log(program_log); } + delete[] program_log; return false; - } } + + delete[] m_oclQueue; delete[] m_oclKernelComputePartialVolumes; - } - if (m_oclKernelComputeSum) { + delete[] m_oclKernelComputeSum; + m_oclQueue = new cl_command_queue[m_ompNumProcessors]; + m_oclKernelComputePartialVolumes = new cl_kernel[m_ompNumProcessors]; + m_oclKernelComputeSum = new cl_kernel[m_ompNumProcessors]; + + const char nameKernelComputePartialVolumes[] = "ComputePartialVolumes"; + const char nameKernelComputeSum[] = "ComputePartialSums"; for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - error = clReleaseKernel(m_oclKernelComputeSum[k]); - if (error != CL_SUCCESS) { + m_oclKernelComputePartialVolumes[k] = clCreateKernel(m_oclProgram, nameKernelComputePartialVolumes, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create kernel\n"); + } + return false; + } + m_oclKernelComputeSum[k] = clCreateKernel(m_oclProgram, nameKernelComputeSum, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create kernel\n"); + } + return false; + } + } + + error = clGetKernelWorkGroupInfo(m_oclKernelComputePartialVolumes[0], + *m_oclDevice, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), + &m_oclWorkGroupSize, + NULL); + size_t workGroupSize = 0; + error = clGetKernelWorkGroupInfo(m_oclKernelComputeSum[0], + *m_oclDevice, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), + &workGroupSize, + NULL); + if (error != CL_SUCCESS) { if (logger) { - logger->Log("Couldn't release kernal\n"); + logger->Log("Couldn't query work group info\n"); } return false; - } } - delete[] m_oclKernelComputeSum; - } - if (m_oclQueue) { + + if (workGroupSize < m_oclWorkGroupSize) { + m_oclWorkGroupSize = workGroupSize; + } + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { - error = clReleaseCommandQueue(m_oclQueue[k]); - if (error != CL_SUCCESS) { + m_oclQueue[k] = clCreateCommandQueue(m_oclContext, *m_oclDevice, 0 /*CL_QUEUE_PROFILING_ENABLE*/, &error); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't create queue\n"); + } + return false; + } + } + return true; +#else //CL_VERSION_1_1 + return false; +#endif //CL_VERSION_1_1 +} +bool VHACD::OCLRelease(IUserLogger* const logger) +{ +#ifdef CL_VERSION_1_1 + cl_int error; + if (m_oclKernelComputePartialVolumes) { + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseKernel(m_oclKernelComputePartialVolumes[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release kernal\n"); + } + return false; + } + } + delete[] m_oclKernelComputePartialVolumes; + } + if (m_oclKernelComputeSum) { + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseKernel(m_oclKernelComputeSum[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release kernal\n"); + } + return false; + } + } + delete[] m_oclKernelComputeSum; + } + if (m_oclQueue) { + for (int32_t k = 0; k < m_ompNumProcessors; ++k) { + error = clReleaseCommandQueue(m_oclQueue[k]); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release queue\n"); + } + return false; + } + } + delete[] m_oclQueue; + } + error = clReleaseProgram(m_oclProgram); + if (error != CL_SUCCESS) { if (logger) { - logger->Log("Couldn't release queue\n"); + logger->Log("Couldn't release program\n"); } return false; - } } - delete[] m_oclQueue; - } - error = clReleaseProgram(m_oclProgram); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't release program\n"); + error = clReleaseContext(m_oclContext); + if (error != CL_SUCCESS) { + if (logger) { + logger->Log("Couldn't release context\n"); + } + return false; } + + return true; +#else //CL_VERSION_1_1 return false; - } - error = clReleaseContext(m_oclContext); - if (error != CL_SUCCESS) { - if (logger) { - logger->Log("Couldn't release context\n"); +#endif //CL_VERSION_1_1 +} +void VHACD::ComputePrimitiveSet(const Parameters& params) +{ + if (GetCancel()) { + return; } - return false; - } + m_timer.Tic(); - return true; -#else // CL_VERSION_1_1 - return false; -#endif // CL_VERSION_1_1 -} -void VHACD::ComputePrimitiveSet(const Parameters& params) { - if (GetCancel()) { - return; - } - m_timer.Tic(); - - m_stage = "Compute primitive set"; - m_operation = "Convert volume to pset"; - - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - Update(0.0, 0.0, params); - if (params.m_mode == 0) { - VoxelSet* vset = new VoxelSet; - m_volume->Convert(*vset); - m_pset = vset; - } else { - TetrahedronSet* tset = new TetrahedronSet; - m_volume->Convert(*tset); - m_pset = tset; - } - - delete m_volume; - m_volume = 0; - - if (params.m_logger) { - msg.str(""); - msg << "\t # primitives " << m_pset->GetNPrimitives() - << std::endl; - msg << "\t # inside surface " - << m_pset->GetNPrimitivesInsideSurf() << std::endl; - msg << "\t # on surface " << m_pset->GetNPrimitivesOnSurf() - << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - m_overallProgress = 15.0; - Update(100.0, 100.0, params); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } -} -bool VHACD::Compute(const double* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params) { - return ComputeACD(points, nPoints, triangles, nTriangles, params); -} -bool VHACD::Compute(const float* const points, - const uint32_t nPoints, - const uint32_t* const triangles, - const uint32_t nTriangles, - const Parameters& params) { - return ComputeACD(points, nPoints, triangles, nTriangles, params); -} -bool VHACD::computeVoxelField(const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) { - return computeVoxelFieldHelper(points, countPoints, triangles, countTriangles, - params); -} -bool VHACD::computeVoxelField(const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const Parameters& params) { - return computeVoxelFieldHelper(points, countPoints, triangles, countTriangles, - params); -} + m_stage = "Compute primitive set"; + m_operation = "Convert volume to pset"; -Volume* VHACD::getVoxelField() { - return (Volume*)m_volume; // VoxelSet* vset = (VoxelSet*)pset -} + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + if (params.m_mode == 0) { + VoxelSet* vset = new VoxelSet; + m_volume->Convert(*vset); + m_pset = vset; + } + else { + TetrahedronSet* tset = new TetrahedronSet; + m_volume->Convert(*tset); + m_pset = tset; + } + + delete m_volume; + m_volume = 0; + + if (params.m_logger) { + msg.str(""); + msg << "\t # primitives " << m_pset->GetNPrimitives() << std::endl; + msg << "\t # inside surface " << m_pset->GetNPrimitivesInsideSurf() << std::endl; + msg << "\t # on surface " << m_pset->GetNPrimitivesOnSurf() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } -double ComputePreferredCuttingDirection(const PrimitiveSet* const tset, - Vec3& dir) { - double ex = tset->GetEigenValue(AXIS_X); - double ey = tset->GetEigenValue(AXIS_Y); - double ez = tset->GetEigenValue(AXIS_Z); - double vx = (ey - ez) * (ey - ez); - double vy = (ex - ez) * (ex - ez); - double vz = (ex - ey) * (ex - ey); - if (vx < vy && vx < vz) { - double e = ey * ey + ez * ez; - dir[0] = 1.0; - dir[1] = 0.0; - dir[2] = 0.0; - return (e == 0.0) ? 0.0 : 1.0 - vx / e; - } else if (vy < vx && vy < vz) { - double e = ex * ex + ez * ez; - dir[0] = 0.0; - dir[1] = 1.0; - dir[2] = 0.0; - return (e == 0.0) ? 0.0 : 1.0 - vy / e; - } else { - double e = ex * ex + ey * ey; - dir[0] = 0.0; - dir[1] = 0.0; - dir[2] = 1.0; - return (e == 0.0) ? 0.0 : 1.0 - vz / e; - } + m_overallProgress = 15.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } } -void ComputeAxesAlignedClippingPlanes(const VoxelSet& vset, - const short downsampling, - SArray& planes) { - const Vec3 minV = vset.GetMinBBVoxels(); - const Vec3 maxV = vset.GetMaxBBVoxels(); - Vec3 pt; - Plane plane; - const short i0 = minV[0]; - const short i1 = maxV[0]; - plane.m_a = 1.0; - plane.m_b = 0.0; - plane.m_c = 0.0; - plane.m_axis = AXIS_X; - for (short i = i0; i <= i1; i += downsampling) { - pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); - plane.m_d = -pt[0]; - plane.m_index = i; - planes.PushBack(plane); - } - const short j0 = minV[1]; - const short j1 = maxV[1]; - plane.m_a = 0.0; - plane.m_b = 1.0; - plane.m_c = 0.0; - plane.m_axis = AXIS_Y; - for (short j = j0; j <= j1; j += downsampling) { - pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); - plane.m_d = -pt[1]; - plane.m_index = j; - planes.PushBack(plane); - } - const short k0 = minV[2]; - const short k1 = maxV[2]; - plane.m_a = 0.0; - plane.m_b = 0.0; - plane.m_c = 1.0; - plane.m_axis = AXIS_Z; - for (short k = k0; k <= k1; k += downsampling) { - pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); - plane.m_d = -pt[2]; - plane.m_index = k; - planes.PushBack(plane); - } +bool VHACD::Compute(const double* const points, const uint32_t nPoints, + const uint32_t* const triangles,const uint32_t nTriangles, const Parameters& params) +{ + return ComputeACD(points, nPoints, triangles, nTriangles, params); } -void ComputeAxesAlignedClippingPlanes(const TetrahedronSet& tset, - const short downsampling, - SArray& planes) { - const Vec3 minV = tset.GetMinBB(); - const Vec3 maxV = tset.GetMaxBB(); - const double scale = tset.GetSacle(); - const short i0 = 0; - const short j0 = 0; - const short k0 = 0; - const short i1 = static_cast((maxV[0] - minV[0]) / scale + 0.5); - const short j1 = static_cast((maxV[1] - minV[1]) / scale + 0.5); - const short k1 = static_cast((maxV[2] - minV[2]) / scale + 0.5); - - Plane plane; - plane.m_a = 1.0; - plane.m_b = 0.0; - plane.m_c = 0.0; - plane.m_axis = AXIS_X; - for (short i = i0; i <= i1; i += downsampling) { - double x = minV[0] + scale * i; - plane.m_d = -x; - plane.m_index = i; - planes.PushBack(plane); - } - plane.m_a = 0.0; - plane.m_b = 1.0; - plane.m_c = 0.0; - plane.m_axis = AXIS_Y; - for (short j = j0; j <= j1; j += downsampling) { - double y = minV[1] + scale * j; - plane.m_d = -y; - plane.m_index = j; - planes.PushBack(plane); - } - plane.m_a = 0.0; - plane.m_b = 0.0; - plane.m_c = 1.0; - plane.m_axis = AXIS_Z; - for (short k = k0; k <= k1; k += downsampling) { - double z = minV[2] + scale * k; - plane.m_d = -z; - plane.m_index = k; - planes.PushBack(plane); - } +bool VHACD::Compute(const float* const points,const uint32_t nPoints, + const uint32_t* const triangles,const uint32_t nTriangles, const Parameters& params) +{ + return ComputeACD(points, nPoints, triangles, nTriangles, params); } -void RefineAxesAlignedClippingPlanes(const VoxelSet& vset, - const Plane& bestPlane, - const short downsampling, - SArray& planes) { - const Vec3 minV = vset.GetMinBBVoxels(); - const Vec3 maxV = vset.GetMaxBBVoxels(); - Vec3 pt; - Plane plane; - - if (bestPlane.m_axis == AXIS_X) { - const short i0 = MAX(minV[0], bestPlane.m_index - downsampling); - const short i1 = MIN(maxV[0], bestPlane.m_index + downsampling); +double ComputePreferredCuttingDirection(const PrimitiveSet* const tset, Vec3& dir) +{ + double ex = tset->GetEigenValue(AXIS_X); + double ey = tset->GetEigenValue(AXIS_Y); + double ez = tset->GetEigenValue(AXIS_Z); + double vx = (ey - ez) * (ey - ez); + double vy = (ex - ez) * (ex - ez); + double vz = (ex - ey) * (ex - ey); + if (vx < vy && vx < vz) { + double e = ey * ey + ez * ez; + dir[0] = 1.0; + dir[1] = 0.0; + dir[2] = 0.0; + return (e == 0.0) ? 0.0 : 1.0 - vx / e; + } + else if (vy < vx && vy < vz) { + double e = ex * ex + ez * ez; + dir[0] = 0.0; + dir[1] = 1.0; + dir[2] = 0.0; + return (e == 0.0) ? 0.0 : 1.0 - vy / e; + } + else { + double e = ex * ex + ey * ey; + dir[0] = 0.0; + dir[1] = 0.0; + dir[2] = 1.0; + return (e == 0.0) ? 0.0 : 1.0 - vz / e; + } +} +void ComputeAxesAlignedClippingPlanes(const VoxelSet& vset, const short downsampling, SArray& planes) +{ + const Vec3 minV = vset.GetMinBBVoxels(); + const Vec3 maxV = vset.GetMaxBBVoxels(); + Vec3 pt; + Plane plane; + const short i0 = minV[0]; + const short i1 = maxV[0]; plane.m_a = 1.0; plane.m_b = 0.0; plane.m_c = 0.0; plane.m_axis = AXIS_X; - for (short i = i0; i <= i1; ++i) { - pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); - plane.m_d = -pt[0]; - plane.m_index = i; - planes.PushBack(plane); - } - } else if (bestPlane.m_axis == AXIS_Y) { - const short j0 = MAX(minV[1], bestPlane.m_index - downsampling); - const short j1 = MIN(maxV[1], bestPlane.m_index + downsampling); + for (short i = i0; i <= i1; i += downsampling) { + pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); + plane.m_d = -pt[0]; + plane.m_index = i; + planes.PushBack(plane); + } + const short j0 = minV[1]; + const short j1 = maxV[1]; plane.m_a = 0.0; plane.m_b = 1.0; plane.m_c = 0.0; plane.m_axis = AXIS_Y; - for (short j = j0; j <= j1; ++j) { - pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); - plane.m_d = -pt[1]; - plane.m_index = j; - planes.PushBack(plane); - } - } else { - const short k0 = MAX(minV[2], bestPlane.m_index - downsampling); - const short k1 = MIN(maxV[2], bestPlane.m_index + downsampling); + for (short j = j0; j <= j1; j += downsampling) { + pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); + plane.m_d = -pt[1]; + plane.m_index = j; + planes.PushBack(plane); + } + const short k0 = minV[2]; + const short k1 = maxV[2]; plane.m_a = 0.0; plane.m_b = 0.0; plane.m_c = 1.0; plane.m_axis = AXIS_Z; - for (short k = k0; k <= k1; ++k) { - pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); - plane.m_d = -pt[2]; - plane.m_index = k; - planes.PushBack(plane); + for (short k = k0; k <= k1; k += downsampling) { + pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); + plane.m_d = -pt[2]; + plane.m_index = k; + planes.PushBack(plane); } - } } -void RefineAxesAlignedClippingPlanes(const TetrahedronSet& tset, - const Plane& bestPlane, - const short downsampling, - SArray& planes) { - const Vec3 minV = tset.GetMinBB(); - const Vec3 maxV = tset.GetMaxBB(); - const double scale = tset.GetSacle(); - Plane plane; - - if (bestPlane.m_axis == AXIS_X) { - const short i0 = MAX(0, bestPlane.m_index - downsampling); - const short i1 = static_cast(MIN((maxV[0] - minV[0]) / scale + 0.5, - bestPlane.m_index + downsampling)); +void ComputeAxesAlignedClippingPlanes(const TetrahedronSet& tset, const short downsampling, SArray& planes) +{ + const Vec3 minV = tset.GetMinBB(); + const Vec3 maxV = tset.GetMaxBB(); + const double scale = tset.GetSacle(); + const short i0 = 0; + const short j0 = 0; + const short k0 = 0; + const short i1 = static_cast((maxV[0] - minV[0]) / scale + 0.5); + const short j1 = static_cast((maxV[1] - minV[1]) / scale + 0.5); + const short k1 = static_cast((maxV[2] - minV[2]) / scale + 0.5); + + Plane plane; plane.m_a = 1.0; plane.m_b = 0.0; plane.m_c = 0.0; plane.m_axis = AXIS_X; - for (short i = i0; i <= i1; ++i) { - double x = minV[0] + scale * i; - plane.m_d = -x; - plane.m_index = i; - planes.PushBack(plane); - } - } else if (bestPlane.m_axis == AXIS_Y) { - const short j0 = MAX(0, bestPlane.m_index - downsampling); - const short j1 = static_cast(MIN((maxV[1] - minV[1]) / scale + 0.5, - bestPlane.m_index + downsampling)); + for (short i = i0; i <= i1; i += downsampling) { + double x = minV[0] + scale * i; + plane.m_d = -x; + plane.m_index = i; + planes.PushBack(plane); + } plane.m_a = 0.0; plane.m_b = 1.0; plane.m_c = 0.0; plane.m_axis = AXIS_Y; - for (short j = j0; j <= j1; ++j) { - double y = minV[1] + scale * j; - plane.m_d = -y; - plane.m_index = j; - planes.PushBack(plane); - } - } else { - const short k0 = MAX(0, bestPlane.m_index - downsampling); - const short k1 = static_cast(MIN((maxV[2] - minV[2]) / scale + 0.5, - bestPlane.m_index + downsampling)); + for (short j = j0; j <= j1; j += downsampling) { + double y = minV[1] + scale * j; + plane.m_d = -y; + plane.m_index = j; + planes.PushBack(plane); + } plane.m_a = 0.0; plane.m_b = 0.0; plane.m_c = 1.0; plane.m_axis = AXIS_Z; - for (short k = k0; k <= k1; ++k) { - double z = minV[2] + scale * k; - plane.m_d = -z; - plane.m_index = k; - planes.PushBack(plane); + for (short k = k0; k <= k1; k += downsampling) { + double z = minV[2] + scale * k; + plane.m_d = -z; + plane.m_index = k; + planes.PushBack(plane); } - } } -inline double ComputeLocalConcavity(const double volume, - const double volumeCH) { - return fabs(volumeCH - volume) / volumeCH; +void RefineAxesAlignedClippingPlanes(const VoxelSet& vset, const Plane& bestPlane, const short downsampling, + SArray& planes) +{ + const Vec3 minV = vset.GetMinBBVoxels(); + const Vec3 maxV = vset.GetMaxBBVoxels(); + Vec3 pt; + Plane plane; + + if (bestPlane.m_axis == AXIS_X) { + const short i0 = MAX(minV[0], bestPlane.m_index - downsampling); + const short i1 = MIN(maxV[0], bestPlane.m_index + downsampling); + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; ++i) { + pt = vset.GetPoint(Vec3(i + 0.5, 0.0, 0.0)); + plane.m_d = -pt[0]; + plane.m_index = i; + planes.PushBack(plane); + } + } + else if (bestPlane.m_axis == AXIS_Y) { + const short j0 = MAX(minV[1], bestPlane.m_index - downsampling); + const short j1 = MIN(maxV[1], bestPlane.m_index + downsampling); + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; ++j) { + pt = vset.GetPoint(Vec3(0.0, j + 0.5, 0.0)); + plane.m_d = -pt[1]; + plane.m_index = j; + planes.PushBack(plane); + } + } + else { + const short k0 = MAX(minV[2], bestPlane.m_index - downsampling); + const short k1 = MIN(maxV[2], bestPlane.m_index + downsampling); + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; ++k) { + pt = vset.GetPoint(Vec3(0.0, 0.0, k + 0.5)); + plane.m_d = -pt[2]; + plane.m_index = k; + planes.PushBack(plane); + } + } } -inline double ComputeConcavity(const double volume, - const double volumeCH, - const double volume0) { - return fabs(volumeCH - volume) / volume0; +void RefineAxesAlignedClippingPlanes(const TetrahedronSet& tset, const Plane& bestPlane, const short downsampling, + SArray& planes) +{ + const Vec3 minV = tset.GetMinBB(); + const Vec3 maxV = tset.GetMaxBB(); + const double scale = tset.GetSacle(); + Plane plane; + + if (bestPlane.m_axis == AXIS_X) { + const short i0 = MAX(0, bestPlane.m_index - downsampling); + const short i1 = static_cast(MIN((maxV[0] - minV[0]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 1.0; + plane.m_b = 0.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_X; + for (short i = i0; i <= i1; ++i) { + double x = minV[0] + scale * i; + plane.m_d = -x; + plane.m_index = i; + planes.PushBack(plane); + } + } + else if (bestPlane.m_axis == AXIS_Y) { + const short j0 = MAX(0, bestPlane.m_index - downsampling); + const short j1 = static_cast(MIN((maxV[1] - minV[1]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 0.0; + plane.m_b = 1.0; + plane.m_c = 0.0; + plane.m_axis = AXIS_Y; + for (short j = j0; j <= j1; ++j) { + double y = minV[1] + scale * j; + plane.m_d = -y; + plane.m_index = j; + planes.PushBack(plane); + } + } + else { + const short k0 = MAX(0, bestPlane.m_index - downsampling); + const short k1 = static_cast(MIN((maxV[2] - minV[2]) / scale + 0.5, bestPlane.m_index + downsampling)); + plane.m_a = 0.0; + plane.m_b = 0.0; + plane.m_c = 1.0; + plane.m_axis = AXIS_Z; + for (short k = k0; k <= k1; ++k) { + double z = minV[2] + scale * k; + plane.m_d = -z; + plane.m_index = k; + planes.PushBack(plane); + } + } +} +inline double ComputeLocalConcavity(const double volume, const double volumeCH) +{ + return fabs(volumeCH - volume) / volumeCH; +} +inline double ComputeConcavity(const double volume, const double volumeCH, const double volume0) +{ + return fabs(volumeCH - volume) / volume0; } //#define DEBUG_TEMP -void VHACD::ComputeBestClippingPlane( - const PrimitiveSet* inputPSet, - const double volume, - const SArray& planes, - const Vec3& preferredCuttingDirection, - const double w, - const double alpha, - const double beta, - const int32_t convexhullDownsampling, - const double progress0, - const double progress1, - Plane& bestPlane, - double& minConcavity, - const Parameters& params) { - if (GetCancel()) { - return; - } - char msg[256]; - size_t nPrimitives = inputPSet->GetNPrimitives(); - bool oclAcceleration = (nPrimitives > OCL_MIN_NUM_PRIMITIVES && - params.m_oclAcceleration && params.m_mode == 0) - ? true - : false; - int32_t iBest = -1; - int32_t nPlanes = static_cast(planes.Size()); - bool cancel = false; - int32_t done = 0; - double minTotal = MAX_DOUBLE; - double minBalance = MAX_DOUBLE; - double minSymmetry = MAX_DOUBLE; - minConcavity = MAX_DOUBLE; - - SArray >* chPts = - new SArray >[2 * m_ompNumProcessors]; - Mesh* chs = new Mesh[2 * m_ompNumProcessors]; - PrimitiveSet* onSurfacePSet = inputPSet->Create(); - inputPSet->SelectOnSurface(onSurfacePSet); - - PrimitiveSet** psets = 0; - if (!params.m_convexhullApproximation) { - psets = new PrimitiveSet*[2 * m_ompNumProcessors]; - for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { - psets[i] = inputPSet->Create(); - } - } +void VHACD::ComputeBestClippingPlane(const PrimitiveSet* inputPSet, const double volume, const SArray& planes, + const Vec3& preferredCuttingDirection, const double w, const double alpha, const double beta, + const int32_t convexhullDownsampling, const double progress0, const double progress1, Plane& bestPlane, + double& minConcavity, const Parameters& params) +{ + if (GetCancel()) { + return; + } + char msg[256]; + size_t nPrimitives = inputPSet->GetNPrimitives(); + bool oclAcceleration = (nPrimitives > OCL_MIN_NUM_PRIMITIVES && params.m_oclAcceleration && params.m_mode == 0) ? true : false; + int32_t iBest = -1; + int32_t nPlanes = static_cast(planes.Size()); + bool cancel = false; + int32_t done = 0; + double minTotal = MAX_DOUBLE; + double minBalance = MAX_DOUBLE; + double minSymmetry = MAX_DOUBLE; + minConcavity = MAX_DOUBLE; + + SArray >* chPts = new SArray >[2 * m_ompNumProcessors]; + Mesh* chs = new Mesh[2 * m_ompNumProcessors]; + PrimitiveSet* onSurfacePSet = inputPSet->Create(); + inputPSet->SelectOnSurface(onSurfacePSet); + + PrimitiveSet** psets = 0; + if (!params.m_convexhullApproximation) { + psets = new PrimitiveSet*[2 * m_ompNumProcessors]; + for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { + psets[i] = inputPSet->Create(); + } + } #ifdef CL_VERSION_1_1 - // allocate OpenCL data structures - cl_mem voxels; - cl_mem* partialVolumes = 0; - size_t globalSize = 0; - size_t nWorkGroups = 0; - double unitVolume = 0.0; - if (oclAcceleration) { - VoxelSet* vset = (VoxelSet*)inputPSet; - const Vec3 minBB = vset->GetMinBB(); - const float fMinBB[4] = {(float)minBB[0], (float)minBB[1], (float)minBB[2], - 1.0f}; - const float fSclae[4] = {(float)vset->GetScale(), (float)vset->GetScale(), - (float)vset->GetScale(), 0.0f}; - const int32_t nVoxels = (int32_t)nPrimitives; - unitVolume = vset->GetUnitVolume(); - nWorkGroups = - (nPrimitives + 4 * m_oclWorkGroupSize - 1) / (4 * m_oclWorkGroupSize); - globalSize = nWorkGroups * m_oclWorkGroupSize; - cl_int error; - voxels = - clCreateBuffer(m_oclContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, - sizeof(Voxel) * nPrimitives, vset->GetVoxels(), &error); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't create buffer\n"); - } - SetCancel(true); - } - - partialVolumes = new cl_mem[m_ompNumProcessors]; - for (int32_t i = 0; i < m_ompNumProcessors; ++i) { - partialVolumes[i] = - clCreateBuffer(m_oclContext, CL_MEM_WRITE_ONLY, - sizeof(uint32_t) * 4 * nWorkGroups, NULL, &error); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't create buffer\n"); + // allocate OpenCL data structures + cl_mem voxels; + cl_mem* partialVolumes = 0; + size_t globalSize = 0; + size_t nWorkGroups = 0; + double unitVolume = 0.0; + if (oclAcceleration) { + VoxelSet* vset = (VoxelSet*)inputPSet; + const Vec3 minBB = vset->GetMinBB(); + const float fMinBB[4] = { (float)minBB[0], (float)minBB[1], (float)minBB[2], 1.0f }; + const float fSclae[4] = { (float)vset->GetScale(), (float)vset->GetScale(), (float)vset->GetScale(), 0.0f }; + const int32_t nVoxels = (int32_t)nPrimitives; + unitVolume = vset->GetUnitVolume(); + nWorkGroups = (nPrimitives + 4 * m_oclWorkGroupSize - 1) / (4 * m_oclWorkGroupSize); + globalSize = nWorkGroups * m_oclWorkGroupSize; + cl_int error; + voxels = clCreateBuffer(m_oclContext, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(Voxel) * nPrimitives, + vset->GetVoxels(), + &error); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't create buffer\n"); + } + SetCancel(true); } - SetCancel(true); - break; - } - error = clSetKernelArg(m_oclKernelComputePartialVolumes[i], 0, - sizeof(cl_mem), &voxels); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 1, - sizeof(uint32_t), &nVoxels); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 3, - sizeof(float) * 4, fMinBB); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 4, - sizeof(float) * 4, &fSclae); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 5, - sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); - error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 6, - sizeof(cl_mem), &(partialVolumes[i])); - error |= clSetKernelArg(m_oclKernelComputeSum[i], 0, sizeof(cl_mem), - &(partialVolumes[i])); - error |= clSetKernelArg(m_oclKernelComputeSum[i], 2, - sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't kernel arguments \n"); + + partialVolumes = new cl_mem[m_ompNumProcessors]; + for (int32_t i = 0; i < m_ompNumProcessors; ++i) { + partialVolumes[i] = clCreateBuffer(m_oclContext, + CL_MEM_WRITE_ONLY, + sizeof(uint32_t) * 4 * nWorkGroups, + NULL, + &error); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't create buffer\n"); + } + SetCancel(true); + break; + } + error = clSetKernelArg(m_oclKernelComputePartialVolumes[i], 0, sizeof(cl_mem), &voxels); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 1, sizeof(uint32_t), &nVoxels); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 3, sizeof(float) * 4, fMinBB); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 4, sizeof(float) * 4, &fSclae); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 5, sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); + error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 6, sizeof(cl_mem), &(partialVolumes[i])); + error |= clSetKernelArg(m_oclKernelComputeSum[i], 0, sizeof(cl_mem), &(partialVolumes[i])); + error |= clSetKernelArg(m_oclKernelComputeSum[i], 2, sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel arguments \n"); + } + SetCancel(true); + } } - SetCancel(true); - } } - } -#else // CL_VERSION_1_1 - oclAcceleration = false; -#endif // CL_VERSION_1_1 +#else // CL_VERSION_1_1 + oclAcceleration = false; +#endif // CL_VERSION_1_1 #ifdef DEBUG_TEMP - Timer timerComputeCost; - timerComputeCost.Tic(); -#endif // DEBUG_TEMP + Timer timerComputeCost; + timerComputeCost.Tic(); +#endif // DEBUG_TEMP #if USE_THREAD == 1 && _OPENMP #pragma omp parallel for #endif - for (int32_t x = 0; x < nPlanes; ++x) { - int32_t threadID = 0; + for (int32_t x = 0; x < nPlanes; ++x) { + int32_t threadID = 0; #if USE_THREAD == 1 && _OPENMP - threadID = omp_get_thread_num(); + threadID = omp_get_thread_num(); #pragma omp flush(cancel) #endif - if (!cancel) { - // Update progress - if (GetCancel()) { - cancel = true; + if (!cancel) { + //Update progress + if (GetCancel()) { + cancel = true; #if USE_THREAD == 1 && _OPENMP #pragma omp flush(cancel) #endif - } - Plane plane = planes[x]; + } + Plane plane = planes[x]; - if (oclAcceleration) { + if (oclAcceleration) { #ifdef CL_VERSION_1_1 - const float fPlane[4] = {(float)plane.m_a, (float)plane.m_b, - (float)plane.m_c, (float)plane.m_d}; - cl_int error = - clSetKernelArg(m_oclKernelComputePartialVolumes[threadID], 2, - sizeof(float) * 4, fPlane); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't kernel atguments \n"); - } - SetCancel(true); - } - - error = clEnqueueNDRangeKernel( - m_oclQueue[threadID], m_oclKernelComputePartialVolumes[threadID], 1, - NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't run kernel \n"); - } - SetCancel(true); - } - int32_t nValues = (int32_t)nWorkGroups; - while (nValues > 1) { - error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, - sizeof(int32_t), &nValues); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't kernel atguments \n"); + const float fPlane[4] = { (float)plane.m_a, (float)plane.m_b, (float)plane.m_c, (float)plane.m_d }; + cl_int error = clSetKernelArg(m_oclKernelComputePartialVolumes[threadID], 2, sizeof(float) * 4, fPlane); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); + } + SetCancel(true); + } + + error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputePartialVolumes[threadID], + 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't run kernel \n"); + } + SetCancel(true); + } + int32_t nValues = (int32_t)nWorkGroups; + while (nValues > 1) { + error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, sizeof(int32_t), &nValues); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \n"); + } + SetCancel(true); + } + size_t nWorkGroups = (nValues + m_oclWorkGroupSize - 1) / m_oclWorkGroupSize; + size_t globalSize = nWorkGroups * m_oclWorkGroupSize; + error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputeSum[threadID], + 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't run kernel \n"); + } + SetCancel(true); + } + nValues = (int32_t)nWorkGroups; + } +#endif // CL_VERSION_1_1 } - SetCancel(true); - } - size_t nWorkGroups = - (nValues + m_oclWorkGroupSize - 1) / m_oclWorkGroupSize; - size_t globalSize = nWorkGroups * m_oclWorkGroupSize; - error = clEnqueueNDRangeKernel( - m_oclQueue[threadID], m_oclKernelComputeSum[threadID], 1, NULL, - &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL); - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't run kernel \n"); - } - SetCancel(true); - } - nValues = (int32_t)nWorkGroups; - } -#endif // CL_VERSION_1_1 - } - Mesh& leftCH = chs[threadID]; - Mesh& rightCH = chs[threadID + m_ompNumProcessors]; - rightCH.ResizePoints(0); - leftCH.ResizePoints(0); - rightCH.ResizeTriangles(0); - leftCH.ResizeTriangles(0); + Mesh& leftCH = chs[threadID]; + Mesh& rightCH = chs[threadID + m_ompNumProcessors]; + rightCH.ResizePoints(0); + leftCH.ResizePoints(0); + rightCH.ResizeTriangles(0); + leftCH.ResizeTriangles(0); // compute convex-hulls #ifdef TEST_APPROX_CH - double volumeLeftCH1; - double volumeRightCH1; -#endif // TEST_APPROX_CH - if (params.m_convexhullApproximation) { - SArray >& leftCHPts = chPts[threadID]; - SArray >& rightCHPts = - chPts[threadID + m_ompNumProcessors]; - rightCHPts.Resize(0); - leftCHPts.Resize(0); - onSurfacePSet->Intersect(plane, &rightCHPts, &leftCHPts, - convexhullDownsampling * 32); - inputPSet->GetConvexHull().Clip(plane, rightCHPts, leftCHPts); - rightCH.ComputeConvexHull((double*)rightCHPts.Data(), - rightCHPts.Size()); - leftCH.ComputeConvexHull((double*)leftCHPts.Data(), leftCHPts.Size()); + double volumeLeftCH1; + double volumeRightCH1; +#endif //TEST_APPROX_CH + if (params.m_convexhullApproximation) { + SArray >& leftCHPts = chPts[threadID]; + SArray >& rightCHPts = chPts[threadID + m_ompNumProcessors]; + rightCHPts.Resize(0); + leftCHPts.Resize(0); + onSurfacePSet->Intersect(plane, &rightCHPts, &leftCHPts, convexhullDownsampling * 32); + inputPSet->GetConvexHull().Clip(plane, rightCHPts, leftCHPts); + rightCH.ComputeConvexHull((double*)rightCHPts.Data(), rightCHPts.Size()); + leftCH.ComputeConvexHull((double*)leftCHPts.Data(), leftCHPts.Size()); #ifdef TEST_APPROX_CH - Mesh leftCH1; - Mesh rightCH1; - VoxelSet right; - VoxelSet left; - onSurfacePSet->Clip(plane, &right, &left); - right.ComputeConvexHull(rightCH1, convexhullDownsampling); - left.ComputeConvexHull(leftCH1, convexhullDownsampling); - - volumeLeftCH1 = leftCH1.ComputeVolume(); - volumeRightCH1 = rightCH1.ComputeVolume(); -#endif // TEST_APPROX_CH - } else { - PrimitiveSet* const right = psets[threadID]; - PrimitiveSet* const left = psets[threadID + m_ompNumProcessors]; - onSurfacePSet->Clip(plane, right, left); - right->ComputeConvexHull(rightCH, convexhullDownsampling); - left->ComputeConvexHull(leftCH, convexhullDownsampling); - } - double volumeLeftCH = leftCH.ComputeVolume(); - double volumeRightCH = rightCH.ComputeVolume(); - - // compute clipped volumes - double volumeLeft = 0.0; - double volumeRight = 0.0; - if (oclAcceleration) { + Mesh leftCH1; + Mesh rightCH1; + VoxelSet right; + VoxelSet left; + onSurfacePSet->Clip(plane, &right, &left); + right.ComputeConvexHull(rightCH1, convexhullDownsampling); + left.ComputeConvexHull(leftCH1, convexhullDownsampling); + + volumeLeftCH1 = leftCH1.ComputeVolume(); + volumeRightCH1 = rightCH1.ComputeVolume(); +#endif //TEST_APPROX_CH + } + else { + PrimitiveSet* const right = psets[threadID]; + PrimitiveSet* const left = psets[threadID + m_ompNumProcessors]; + onSurfacePSet->Clip(plane, right, left); + right->ComputeConvexHull(rightCH, convexhullDownsampling); + left->ComputeConvexHull(leftCH, convexhullDownsampling); + } + double volumeLeftCH = leftCH.ComputeVolume(); + double volumeRightCH = rightCH.ComputeVolume(); + + // compute clipped volumes + double volumeLeft = 0.0; + double volumeRight = 0.0; + if (oclAcceleration) { #ifdef CL_VERSION_1_1 - uint32_t volumes[4]; - cl_int error = clEnqueueReadBuffer( - m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE, 0, - sizeof(uint32_t) * 4, volumes, 0, NULL, NULL); - size_t nPrimitivesRight = - volumes[0] + volumes[1] + volumes[2] + volumes[3]; - size_t nPrimitivesLeft = nPrimitives - nPrimitivesRight; - volumeRight = nPrimitivesRight * unitVolume; - volumeLeft = nPrimitivesLeft * unitVolume; - if (error != CL_SUCCESS) { - if (params.m_logger) { - params.m_logger->Log("Couldn't read buffer \n"); - } - SetCancel(true); - } -#endif // CL_VERSION_1_1 - } else { - inputPSet->ComputeClippedVolumes(plane, volumeRight, volumeLeft); - } - double concavityLeft = - ComputeConcavity(volumeLeft, volumeLeftCH, m_volumeCH0); - double concavityRight = - ComputeConcavity(volumeRight, volumeRightCH, m_volumeCH0); - double concavity = (concavityLeft + concavityRight); - - // compute cost - double balance = alpha * fabs(volumeLeft - volumeRight) / m_volumeCH0; - double d = w * (preferredCuttingDirection[0] * plane.m_a + - preferredCuttingDirection[1] * plane.m_b + - preferredCuttingDirection[2] * plane.m_c); - double symmetry = beta * d; - double total = concavity + balance + symmetry; + uint32_t volumes[4]; + cl_int error = clEnqueueReadBuffer(m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE, + 0, sizeof(uint32_t) * 4, volumes, 0, NULL, NULL); + size_t nPrimitivesRight = volumes[0] + volumes[1] + volumes[2] + volumes[3]; + size_t nPrimitivesLeft = nPrimitives - nPrimitivesRight; + volumeRight = nPrimitivesRight * unitVolume; + volumeLeft = nPrimitivesLeft * unitVolume; + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't read buffer \n"); + } + SetCancel(true); + } +#endif // CL_VERSION_1_1 + } + else { + inputPSet->ComputeClippedVolumes(plane, volumeRight, volumeLeft); + } + double concavityLeft = ComputeConcavity(volumeLeft, volumeLeftCH, m_volumeCH0); + double concavityRight = ComputeConcavity(volumeRight, volumeRightCH, m_volumeCH0); + double concavity = (concavityLeft + concavityRight); + + // compute cost + double balance = alpha * fabs(volumeLeft - volumeRight) / m_volumeCH0; + double d = w * (preferredCuttingDirection[0] * plane.m_a + preferredCuttingDirection[1] * plane.m_b + preferredCuttingDirection[2] * plane.m_c); + double symmetry = beta * d; + double total = concavity + balance + symmetry; #if USE_THREAD == 1 && _OPENMP #pragma omp critical #endif - { - if (total < minTotal || (total == minTotal && x < iBest)) { - minConcavity = concavity; - minBalance = balance; - minSymmetry = symmetry; - bestPlane = plane; - minTotal = total; - iBest = x; - } - ++done; - if (!(done & 127)) // reduce update frequency - { - double progress = - done * (progress1 - progress0) / nPlanes + progress0; - Update(m_stageProgress, progress, params); + { + if (total < minTotal || (total == minTotal && x < iBest)) { + minConcavity = concavity; + minBalance = balance; + minSymmetry = symmetry; + bestPlane = plane; + minTotal = total; + iBest = x; + } + ++done; + if (!(done & 127)) // reduce update frequency + { + double progress = done * (progress1 - progress0) / nPlanes + progress0; + Update(m_stageProgress, progress, params); + } + } } - } } - } #ifdef DEBUG_TEMP - timerComputeCost.Toc(); - printf_s("Cost[%i] = %f\n", nPlanes, timerComputeCost.GetElapsedTime()); -#endif // DEBUG_TEMP + timerComputeCost.Toc(); + printf_s("Cost[%i] = %f\n", nPlanes, timerComputeCost.GetElapsedTime()); +#endif // DEBUG_TEMP #ifdef CL_VERSION_1_1 - if (oclAcceleration) { - clReleaseMemObject(voxels); - for (int32_t i = 0; i < m_ompNumProcessors; ++i) { - clReleaseMemObject(partialVolumes[i]); - } - delete[] partialVolumes; - } -#endif // CL_VERSION_1_1 - - if (psets) { - for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { - delete psets[i]; - } - delete[] psets; - } - delete onSurfacePSet; - delete[] chPts; - delete[] chs; - if (params.m_logger) { - sprintf(msg, - "\n\t\t\t Best %04i T=%2.6f C=%2.6f B=%2.6f S=%2.6f (%1.1f, " - "%1.1f, %1.1f, %3.3f)\n\n", - iBest, minTotal, minConcavity, minBalance, minSymmetry, - bestPlane.m_a, bestPlane.m_b, bestPlane.m_c, bestPlane.m_d); - params.m_logger->Log(msg); - } + if (oclAcceleration) { + clReleaseMemObject(voxels); + for (int32_t i = 0; i < m_ompNumProcessors; ++i) { + clReleaseMemObject(partialVolumes[i]); + } + delete[] partialVolumes; + } +#endif // CL_VERSION_1_1 + + if (psets) { + for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) { + delete psets[i]; + } + delete[] psets; + } + delete onSurfacePSet; + delete[] chPts; + delete[] chs; + if (params.m_logger) { + sprintf(msg, "\n\t\t\t Best %04i T=%2.6f C=%2.6f B=%2.6f S=%2.6f (%1.1f, %1.1f, %1.1f, %3.3f)\n\n", iBest, minTotal, minConcavity, minBalance, minSymmetry, bestPlane.m_a, bestPlane.m_b, bestPlane.m_c, bestPlane.m_d); + params.m_logger->Log(msg); + } } -void VHACD::ComputeACD(const Parameters& params) { - if (GetCancel()) { - return; - } - std::cout << "@@@@ COMPUTE_ACD ====" << '\n'; - m_timer.Tic(); - - m_stage = "Approximate Convex Decomposition"; - m_stageProgress = 0.0; - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - SArray parts; - SArray inputParts; - SArray temp; - inputParts.PushBack(m_pset); - m_pset = 0; - SArray planes; - SArray planesRef; - uint32_t sub = 0; - bool firstIteration = true; - m_volumeCH0 = 1.0; - - // Compute the decomposition depth based on the number of convex hulls being - // requested.. - uint32_t hullCount = 2; - uint32_t depth = 1; - while (params.m_maxConvexHulls > hullCount) { - depth++; - hullCount *= 2; - } - // We must always increment the decomposition depth one higher than the - // maximum number of hulls requested. The reason for this is as follows. Say, - // for example, the user requests 32 convex hulls exactly. This would be a - // decomposition depth of 5. However, when we do that, we do *not* necessarily - // get 32 hulls as a result. This is because, during the recursive descent of - // the binary tree, one or more of the leaf nodes may have no concavity and - // will not be split. So, in this way, even with a decomposition depth of 5, - // you can produce fewer than 32 hulls. So, in this case, we would set the - // decomposition depth to 6 (producing up to as high as 64 convex hulls). - // Then, the merge step which combines over-described hulls down to the user - // requested amount, we will end up getting exactly 32 convex hulls as a - // result. We could just allow the artist to directly control the - // decomposition depth directly, but this would be a bit too complex and the - // preference is simply to let them specify how many hulls they want and - // derive the solution from that. - depth++; - - while (sub++ < depth && inputParts.Size() > 0 && !m_cancel) { - msg.str(""); - msg << "Subdivision level " << sub; - m_operation = msg.str(); +void VHACD::ComputeACD(const Parameters& params) +{ + if (GetCancel()) { + return; + } + m_timer.Tic(); + m_stage = "Approximate Convex Decomposition"; + m_stageProgress = 0.0; + std::ostringstream msg; if (params.m_logger) { - msg.str(""); - msg << "\t Subdivision level " << sub << std::endl; - params.m_logger->Log(msg.str().c_str()); + msg << "+ " << m_stage << std::endl; + params.m_logger->Log(msg.str().c_str()); } - double maxConcavity = 0.0; - const size_t nInputParts = inputParts.Size(); - Update(m_stageProgress, 0.0, params); - for (size_t p = 0; p < nInputParts && !m_cancel; ++p) { - const double progress0 = p * 100.0 / nInputParts; - const double progress1 = (p + 0.75) * 100.0 / nInputParts; - const double progress2 = (p + 1.00) * 100.0 / nInputParts; - - Update(m_stageProgress, progress0, params); - - PrimitiveSet* pset = inputParts[p]; - inputParts[p] = 0; - double volume = pset->ComputeVolume(); - pset->ComputeBB(); - pset->ComputePrincipalAxes(); - if (params.m_pca) { - pset->AlignToPrincipalAxes(); - } - - pset->ComputeConvexHull(pset->GetConvexHull()); - double volumeCH = fabs(pset->GetConvexHull().ComputeVolume()); - if (firstIteration) { - m_volumeCH0 = volumeCH; - } - - double concavity = ComputeConcavity(volume, volumeCH, m_volumeCH0); - double error = 1.01 * pset->ComputeMaxVolumeError() / m_volumeCH0; - - if (firstIteration) { - firstIteration = false; - } - - if (params.m_logger) { + SArray parts; + SArray inputParts; + SArray temp; + inputParts.PushBack(m_pset); + m_pset = 0; + SArray planes; + SArray planesRef; + uint32_t sub = 0; + bool firstIteration = true; + m_volumeCH0 = 1.0; + + // Compute the decomposition depth based on the number of convex hulls being requested.. + uint32_t hullCount = 2; + uint32_t depth = 1; + while (params.m_maxConvexHulls > hullCount) + { + depth++; + hullCount *= 2; + } + // We must always increment the decomposition depth one higher than the maximum number of hulls requested. + // The reason for this is as follows. + // Say, for example, the user requests 32 convex hulls exactly. This would be a decomposition depth of 5. + // However, when we do that, we do *not* necessarily get 32 hulls as a result. This is because, during + // the recursive descent of the binary tree, one or more of the leaf nodes may have no concavity and + // will not be split. So, in this way, even with a decomposition depth of 5, you can produce fewer than + // 32 hulls. So, in this case, we would set the decomposition depth to 6 (producing up to as high as 64 convex hulls). + // Then, the merge step which combines over-described hulls down to the user requested amount, we will end up + // getting exactly 32 convex hulls as a result. + // We could just allow the artist to directly control the decomposition depth directly, but this would be a bit + // too complex and the preference is simply to let them specify how many hulls they want and derive the solution + // from that. + depth++; + + + while (sub++ < depth && inputParts.Size() > 0 && !m_cancel) { msg.str(""); - msg << "\t -> Part[" << p << "] C = " << concavity - << ", E = " << error << ", VS = " << pset->GetNPrimitivesOnSurf() - << ", VI = " << pset->GetNPrimitivesInsideSurf() << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - if (concavity > params.m_concavity && concavity > error) { - Vec3 preferredCuttingDirection; - double w = - ComputePreferredCuttingDirection(pset, preferredCuttingDirection); - planes.Resize(0); - if (params.m_mode == 0) { - VoxelSet* vset = (VoxelSet*)pset; - ComputeAxesAlignedClippingPlanes(*vset, params.m_planeDownsampling, - planes); - } else { - TetrahedronSet* tset = (TetrahedronSet*)pset; - ComputeAxesAlignedClippingPlanes(*tset, params.m_planeDownsampling, - planes); - } + msg << "Subdivision level " << sub; + m_operation = msg.str(); if (params.m_logger) { - msg.str(""); - msg << "\t\t [Regular sampling] Number of clipping planes " - << planes.Size() << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - Plane bestPlane; - double minConcavity = MAX_DOUBLE; - ComputeBestClippingPlane( - pset, volume, planes, preferredCuttingDirection, w, - concavity * params.m_alpha, concavity * params.m_beta, - params.m_convexhullDownsampling, progress0, progress1, bestPlane, - minConcavity, params); - if (!m_cancel && (params.m_planeDownsampling > 1 || - params.m_convexhullDownsampling > 1)) { - planesRef.Resize(0); - - if (params.m_mode == 0) { - VoxelSet* vset = (VoxelSet*)pset; - RefineAxesAlignedClippingPlanes( - *vset, bestPlane, params.m_planeDownsampling, planesRef); - } else { - TetrahedronSet* tset = (TetrahedronSet*)pset; - RefineAxesAlignedClippingPlanes( - *tset, bestPlane, params.m_planeDownsampling, planesRef); - } - - if (params.m_logger) { msg.str(""); - msg << "\t\t [Refining] Number of clipping planes " - << planesRef.Size() << std::endl; + msg << "\t Subdivision level " << sub << std::endl; params.m_logger->Log(msg.str().c_str()); - } - ComputeBestClippingPlane( - pset, volume, planesRef, preferredCuttingDirection, w, - concavity * params.m_alpha, concavity * params.m_beta, - 1, // convexhullDownsampling = 1 - progress1, progress2, bestPlane, minConcavity, params); } + + double maxConcavity = 0.0; + const size_t nInputParts = inputParts.Size(); + Update(m_stageProgress, 0.0, params); + for (size_t p = 0; p < nInputParts && !m_cancel; ++p) { + const double progress0 = p * 100.0 / nInputParts; + const double progress1 = (p + 0.75) * 100.0 / nInputParts; + const double progress2 = (p + 1.00) * 100.0 / nInputParts; + + Update(m_stageProgress, progress0, params); + + PrimitiveSet* pset = inputParts[p]; + inputParts[p] = 0; + double volume = pset->ComputeVolume(); + pset->ComputeBB(); + pset->ComputePrincipalAxes(); + if (params.m_pca) { + pset->AlignToPrincipalAxes(); + } + + pset->ComputeConvexHull(pset->GetConvexHull()); + double volumeCH = fabs(pset->GetConvexHull().ComputeVolume()); + if (firstIteration) { + m_volumeCH0 = volumeCH; + } + + double concavity = ComputeConcavity(volume, volumeCH, m_volumeCH0); + double error = 1.01 * pset->ComputeMaxVolumeError() / m_volumeCH0; + + if (firstIteration) { + firstIteration = false; + } + + if (params.m_logger) { + msg.str(""); + msg << "\t -> Part[" << p + << "] C = " << concavity + << ", E = " << error + << ", VS = " << pset->GetNPrimitivesOnSurf() + << ", VI = " << pset->GetNPrimitivesInsideSurf() + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + if (concavity > params.m_concavity && concavity > error) { + Vec3 preferredCuttingDirection; + double w = ComputePreferredCuttingDirection(pset, preferredCuttingDirection); + planes.Resize(0); + if (params.m_mode == 0) { + VoxelSet* vset = (VoxelSet*)pset; + ComputeAxesAlignedClippingPlanes(*vset, params.m_planeDownsampling, planes); + } + else { + TetrahedronSet* tset = (TetrahedronSet*)pset; + ComputeAxesAlignedClippingPlanes(*tset, params.m_planeDownsampling, planes); + } + + if (params.m_logger) { + msg.str(""); + msg << "\t\t [Regular sampling] Number of clipping planes " << planes.Size() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Plane bestPlane; + double minConcavity = MAX_DOUBLE; + ComputeBestClippingPlane(pset, + volume, + planes, + preferredCuttingDirection, + w, + concavity * params.m_alpha, + concavity * params.m_beta, + params.m_convexhullDownsampling, + progress0, + progress1, + bestPlane, + minConcavity, + params); + if (!m_cancel && (params.m_planeDownsampling > 1 || params.m_convexhullDownsampling > 1)) { + planesRef.Resize(0); + + if (params.m_mode == 0) { + VoxelSet* vset = (VoxelSet*)pset; + RefineAxesAlignedClippingPlanes(*vset, bestPlane, params.m_planeDownsampling, planesRef); + } + else { + TetrahedronSet* tset = (TetrahedronSet*)pset; + RefineAxesAlignedClippingPlanes(*tset, bestPlane, params.m_planeDownsampling, planesRef); + } + + if (params.m_logger) { + msg.str(""); + msg << "\t\t [Refining] Number of clipping planes " << planesRef.Size() << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + ComputeBestClippingPlane(pset, + volume, + planesRef, + preferredCuttingDirection, + w, + concavity * params.m_alpha, + concavity * params.m_beta, + 1, // convexhullDownsampling = 1 + progress1, + progress2, + bestPlane, + minConcavity, + params); + } + if (GetCancel()) { + delete pset; // clean up + break; + } + else { + if (maxConcavity < minConcavity) { + maxConcavity = minConcavity; + } + PrimitiveSet* bestLeft = pset->Create(); + PrimitiveSet* bestRight = pset->Create(); + temp.PushBack(bestLeft); + temp.PushBack(bestRight); + pset->Clip(bestPlane, bestRight, bestLeft); + if (params.m_pca) { + bestRight->RevertAlignToPrincipalAxes(); + bestLeft->RevertAlignToPrincipalAxes(); + } + delete pset; + } + } + else { + if (params.m_pca) { + pset->RevertAlignToPrincipalAxes(); + } + parts.PushBack(pset); + } + } + + Update(95.0 * (1.0 - maxConcavity) / (1.0 - params.m_concavity), 100.0, params); if (GetCancel()) { - delete pset; // clean up - break; - } else { - if (maxConcavity < minConcavity) { - maxConcavity = minConcavity; - } - PrimitiveSet* bestLeft = pset->Create(); - PrimitiveSet* bestRight = pset->Create(); - temp.PushBack(bestLeft); - temp.PushBack(bestRight); - pset->Clip(bestPlane, bestRight, bestLeft); - if (params.m_pca) { - bestRight->RevertAlignToPrincipalAxes(); - bestLeft->RevertAlignToPrincipalAxes(); - } - delete pset; + const size_t nTempParts = temp.Size(); + for (size_t p = 0; p < nTempParts; ++p) { + delete temp[p]; + } + temp.Resize(0); } - } else { - if (params.m_pca) { - pset->RevertAlignToPrincipalAxes(); + else { + inputParts = temp; + temp.Resize(0); } - parts.PushBack(pset); - } + } + const size_t nInputParts = inputParts.Size(); + for (size_t p = 0; p < nInputParts; ++p) { + parts.PushBack(inputParts[p]); } - Update(95.0 * (1.0 - maxConcavity) / (1.0 - params.m_concavity), 100.0, - params); if (GetCancel()) { - const size_t nTempParts = temp.Size(); - for (size_t p = 0; p < nTempParts; ++p) { - delete temp[p]; - } - temp.Resize(0); - } else { - inputParts = temp; - temp.Resize(0); - } - } - const size_t nInputParts = inputParts.Size(); - for (size_t p = 0; p < nInputParts; ++p) { - parts.PushBack(inputParts[p]); - } - - if (GetCancel()) { - const size_t nParts = parts.Size(); - for (size_t p = 0; p < nParts; ++p) { - delete parts[p]; + const size_t nParts = parts.Size(); + for (size_t p = 0; p < nParts; ++p) { + delete parts[p]; + } + return; } - return; - } - m_overallProgress = 90.0; - Update(m_stageProgress, 100.0, params); + m_overallProgress = 90.0; + Update(m_stageProgress, 100.0, params); - msg.str(""); - msg << "Generate convex-hulls"; - m_operation = msg.str(); - size_t nConvexHulls = parts.Size(); - if (params.m_logger) { msg.str(""); - msg << "+ Generate " << nConvexHulls << " convex-hulls " << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - Update(m_stageProgress, 0.0, params); - m_convexHulls.Resize(0); - for (size_t p = 0; p < nConvexHulls && !m_cancel; ++p) { - Update(m_stageProgress, p * 100.0 / nConvexHulls, params); - m_convexHulls.PushBack(new Mesh); - parts[p]->ComputeConvexHull(*m_convexHulls[p]); - size_t nv = m_convexHulls[p]->GetNPoints(); - double x, y, z; - for (size_t i = 0; i < nv; ++i) { - Vec3& pt = m_convexHulls[p]->GetPoint(i); - x = pt[0]; - y = pt[1]; - z = pt[2]; - pt[0] = - m_rot[0][0] * x + m_rot[0][1] * y + m_rot[0][2] * z + m_barycenter[0]; - pt[1] = - m_rot[1][0] * x + m_rot[1][1] * y + m_rot[1][2] * z + m_barycenter[1]; - pt[2] = - m_rot[2][0] * x + m_rot[2][1] * y + m_rot[2][2] * z + m_barycenter[2]; - } - } - - const size_t nParts = parts.Size(); - for (size_t p = 0; p < nParts; ++p) { - delete parts[p]; - parts[p] = 0; - } - parts.Resize(0); - - if (GetCancel()) { - const size_t nConvexHulls = m_convexHulls.Size(); - for (size_t p = 0; p < nConvexHulls; ++p) { - delete m_convexHulls[p]; + msg << "Generate convex-hulls"; + m_operation = msg.str(); + size_t nConvexHulls = parts.Size(); + if (params.m_logger) { + msg.str(""); + msg << "+ Generate " << nConvexHulls << " convex-hulls " << std::endl; + params.m_logger->Log(msg.str().c_str()); } - m_convexHulls.Clear(); - return; - } - m_overallProgress = 95.0; - Update(100.0, 100.0, params); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + Update(m_stageProgress, 0.0, params); + m_convexHulls.Resize(0); + for (size_t p = 0; p < nConvexHulls && !m_cancel; ++p) { + Update(m_stageProgress, p * 100.0 / nConvexHulls, params); + m_convexHulls.PushBack(new Mesh); + parts[p]->ComputeConvexHull(*m_convexHulls[p]); + size_t nv = m_convexHulls[p]->GetNPoints(); + double x, y, z; + for (size_t i = 0; i < nv; ++i) { + Vec3& pt = m_convexHulls[p]->GetPoint(i); + x = pt[0]; + y = pt[1]; + z = pt[2]; + pt[0] = m_rot[0][0] * x + m_rot[0][1] * y + m_rot[0][2] * z + m_barycenter[0]; + pt[1] = m_rot[1][0] * x + m_rot[1][1] * y + m_rot[1][2] * z + m_barycenter[1]; + pt[2] = m_rot[2][0] * x + m_rot[2][1] * y + m_rot[2][2] * z + m_barycenter[2]; + } + } + + const size_t nParts = parts.Size(); + for (size_t p = 0; p < nParts; ++p) { + delete parts[p]; + parts[p] = 0; + } + parts.Resize(0); + + if (GetCancel()) { + const size_t nConvexHulls = m_convexHulls.Size(); + for (size_t p = 0; p < nConvexHulls; ++p) { + delete m_convexHulls[p]; + } + m_convexHulls.Clear(); + return; + } + + m_overallProgress = 95.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } } -void AddPoints(const Mesh* const mesh, SArray >& pts) { - const int32_t n = (int32_t)mesh->GetNPoints(); - for (int32_t i = 0; i < n; ++i) { - pts.PushBack(mesh->GetPoint(i)); - } +void AddPoints(const Mesh* const mesh, SArray >& pts) +{ + const int32_t n = (int32_t)mesh->GetNPoints(); + for (int32_t i = 0; i < n; ++i) { + pts.PushBack(mesh->GetPoint(i)); + } } -void ComputeConvexHull(const Mesh* const ch1, - const Mesh* const ch2, - SArray >& pts, - Mesh* const combinedCH) { - pts.Resize(0); - AddPoints(ch1, pts); - AddPoints(ch2, pts); - - btConvexHullComputer ch; - ch.compute((double*)pts.Data(), 3 * sizeof(double), (int32_t)pts.Size(), -1.0, - -1.0); - combinedCH->ResizePoints(0); - combinedCH->ResizeTriangles(0); - for (int32_t v = 0; v < ch.vertices.size(); v++) { - combinedCH->AddPoint(Vec3( - ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); - } - const int32_t nt = ch.faces.size(); - for (int32_t t = 0; t < nt; ++t) { - const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); - int32_t a = sourceEdge->getSourceVertex(); - int32_t b = sourceEdge->getTargetVertex(); - const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); - int32_t c = edge->getTargetVertex(); - while (c != a) { - combinedCH->AddTriangle(Vec3(a, b, c)); - edge = edge->getNextEdgeOfFace(); - b = c; - c = edge->getTargetVertex(); - } - } +void ComputeConvexHull(const Mesh* const ch1, const Mesh* const ch2, SArray >& pts, Mesh* const combinedCH) +{ + pts.Resize(0); + AddPoints(ch1, pts); + AddPoints(ch2, pts); + + btConvexHullComputer ch; + ch.compute((double*)pts.Data(), 3 * sizeof(double), (int32_t)pts.Size(), -1.0, -1.0); + combinedCH->ResizePoints(0); + combinedCH->ResizeTriangles(0); + for (int32_t v = 0; v < ch.vertices.size(); v++) { + combinedCH->AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int32_t nt = ch.faces.size(); + for (int32_t t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int32_t a = sourceEdge->getSourceVertex(); + int32_t b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int32_t c = edge->getTargetVertex(); + while (c != a) { + combinedCH->AddTriangle(Vec3(a, b, c)); + edge = edge->getNextEdgeOfFace(); + b = c; + c = edge->getTargetVertex(); + } + } } -void VHACD::MergeConvexHulls(const Parameters& params) { - if (GetCancel()) { - return; - } - m_timer.Tic(); - - m_stage = "Merge Convex Hulls"; - - std::ostringstream msg; - if (params.m_logger) { - msg << "+ " << m_stage << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - // Get the current number of convex hulls - size_t nConvexHulls = m_convexHulls.Size(); - // Iteration counter - int32_t iteration = 0; - // While we have more than at least one convex hull and the user has not asked - // us to cancel the operation - if (nConvexHulls > 1 && !m_cancel) { - // Get the gamma error threshold for when to exit - SArray > pts; - Mesh combinedCH; - - // Populate the cost matrix - size_t idx = 0; - SArray costMatrix; - costMatrix.Resize(((nConvexHulls * nConvexHulls) - nConvexHulls) >> 1); - for (size_t p1 = 1; p1 < nConvexHulls; ++p1) { - const float volume1 = m_convexHulls[p1]->ComputeVolume(); - for (size_t p2 = 0; p2 < p1; ++p2) { - ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, - &combinedCH); - costMatrix[idx++] = - ComputeConcavity(volume1 + m_convexHulls[p2]->ComputeVolume(), - combinedCH.ComputeVolume(), m_volumeCH0); - } - } - - // Until we cant merge below the maximum cost - size_t costSize = m_convexHulls.Size(); - while (!m_cancel) { - msg.str(""); - msg << "Iteration " << iteration++; - m_operation = msg.str(); - - // Search for lowest cost - float bestCost = (std::numeric_limits::max)(); - const size_t addr = FindMinimumElement(costMatrix.Data(), &bestCost, 0, - costMatrix.Size()); - if ((costSize - 1) < params.m_maxConvexHulls) { - break; - } - const size_t addrI = - (static_cast(sqrt(1 + (8 * addr))) - 1) >> 1; - const size_t p1 = addrI + 1; - const size_t p2 = addr - ((addrI * (addrI + 1)) >> 1); - assert(p1 >= 0); - assert(p2 >= 0); - assert(p1 < costSize); - assert(p2 < costSize); - - if (params.m_logger) { - msg.str(""); - msg << "\t\t Merging (" << p1 << ", " << p2 << ") " << bestCost - << std::endl - << std::endl; +void VHACD::MergeConvexHulls(const Parameters& params) +{ + if (GetCancel()) { + return; + } + m_timer.Tic(); + + m_stage = "Merge Convex Hulls"; + + std::ostringstream msg; + if (params.m_logger) { + msg << "+ " << m_stage << std::endl; params.m_logger->Log(msg.str().c_str()); - } - - // Make the lowest cost row and column into a new hull - Mesh* cch = new Mesh; - ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, cch); - delete m_convexHulls[p2]; - m_convexHulls[p2] = cch; - - delete m_convexHulls[p1]; - std::swap(m_convexHulls[p1], m_convexHulls[m_convexHulls.Size() - 1]); - m_convexHulls.PopBack(); - - costSize = costSize - 1; - - // Calculate costs versus the new hull - size_t rowIdx = ((p2 - 1) * p2) >> 1; - const float volume1 = m_convexHulls[p2]->ComputeVolume(); - for (size_t i = 0; (i < p2) && (!m_cancel); ++i) { - ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, - &combinedCH); - costMatrix[rowIdx++] = - ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), - combinedCH.ComputeVolume(), m_volumeCH0); - } - - rowIdx += p2; - for (size_t i = p2 + 1; (i < costSize) && (!m_cancel); ++i) { - ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, - &combinedCH); - costMatrix[rowIdx] = - ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), - combinedCH.ComputeVolume(), m_volumeCH0); - rowIdx += i; - assert(rowIdx >= 0); - } - - // Move the top column in to replace its space - const size_t erase_idx = ((costSize - 1) * costSize) >> 1; - if (p1 < costSize) { - rowIdx = (addrI * p1) >> 1; - size_t top_row = erase_idx; - for (size_t i = 0; i < p1; ++i) { - if (i != p2) { - costMatrix[rowIdx] = costMatrix[top_row]; - } - ++rowIdx; - ++top_row; + } + + // Get the current number of convex hulls + size_t nConvexHulls = m_convexHulls.Size(); + // Iteration counter + int32_t iteration = 0; + // While we have more than at least one convex hull and the user has not asked us to cancel the operation + if (nConvexHulls > 1 && !m_cancel) + { + // Get the gamma error threshold for when to exit + SArray > pts; + Mesh combinedCH; + + // Populate the cost matrix + size_t idx = 0; + SArray costMatrix; + costMatrix.Resize(((nConvexHulls * nConvexHulls) - nConvexHulls) >> 1); + for (size_t p1 = 1; p1 < nConvexHulls; ++p1) + { + const float volume1 = m_convexHulls[p1]->ComputeVolume(); + for (size_t p2 = 0; p2 < p1; ++p2) + { + ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, &combinedCH); + costMatrix[idx++] = ComputeConcavity(volume1 + m_convexHulls[p2]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + } } - ++top_row; - rowIdx += p1; - for (size_t i = p1 + 1; i < (costSize + 1); ++i) { - costMatrix[rowIdx] = costMatrix[top_row++]; - rowIdx += i; - assert(rowIdx >= 0); + // Until we cant merge below the maximum cost + size_t costSize = m_convexHulls.Size(); + while (!m_cancel) + { + msg.str(""); + msg << "Iteration " << iteration++; + m_operation = msg.str(); + + // Search for lowest cost + float bestCost = (std::numeric_limits::max)(); + const size_t addr = FindMinimumElement(costMatrix.Data(), &bestCost, 0, costMatrix.Size()); + if ( (costSize-1) < params.m_maxConvexHulls) + { + break; + } + const size_t addrI = (static_cast(sqrt(1 + (8 * addr))) - 1) >> 1; + const size_t p1 = addrI + 1; + const size_t p2 = addr - ((addrI * (addrI + 1)) >> 1); + assert(p1 >= 0); + assert(p2 >= 0); + assert(p1 < costSize); + assert(p2 < costSize); + + if (params.m_logger) + { + msg.str(""); + msg << "\t\t Merging (" << p1 << ", " << p2 << ") " << bestCost << std::endl + << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + // Make the lowest cost row and column into a new hull + Mesh* cch = new Mesh; + ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, cch); + delete m_convexHulls[p2]; + m_convexHulls[p2] = cch; + + delete m_convexHulls[p1]; + std::swap(m_convexHulls[p1], m_convexHulls[m_convexHulls.Size() - 1]); + m_convexHulls.PopBack(); + + costSize = costSize - 1; + + // Calculate costs versus the new hull + size_t rowIdx = ((p2 - 1) * p2) >> 1; + const float volume1 = m_convexHulls[p2]->ComputeVolume(); + for (size_t i = 0; (i < p2) && (!m_cancel); ++i) + { + ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH); + costMatrix[rowIdx++] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + } + + rowIdx += p2; + for (size_t i = p2 + 1; (i < costSize) && (!m_cancel); ++i) + { + ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH); + costMatrix[rowIdx] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0); + rowIdx += i; + assert(rowIdx >= 0); + } + + // Move the top column in to replace its space + const size_t erase_idx = ((costSize - 1) * costSize) >> 1; + if (p1 < costSize) { + rowIdx = (addrI * p1) >> 1; + size_t top_row = erase_idx; + for (size_t i = 0; i < p1; ++i) { + if (i != p2) { + costMatrix[rowIdx] = costMatrix[top_row]; + } + ++rowIdx; + ++top_row; + } + + ++top_row; + rowIdx += p1; + for (size_t i = p1 + 1; i < (costSize + 1); ++i) { + costMatrix[rowIdx] = costMatrix[top_row++]; + rowIdx += i; + assert(rowIdx >= 0); + } + } + costMatrix.Resize(erase_idx); } - } - costMatrix.Resize(erase_idx); - } - } - m_overallProgress = 99.0; - Update(100.0, 100.0, params); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + } + m_overallProgress = 99.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } } -void VHACD::SimplifyConvexHull(Mesh* const ch, - const size_t nvertices, - const double minVolume) { - if (nvertices <= 4) { - return; - } - ICHull icHull; - if (mRaycastMesh) { - // We project these points onto the original source mesh to increase - // precision The voxelization process drops floating point precision so - // returned data points are not exactly lying on the surface of the original - // source mesh. The first step is we need to compute the bounding box of the - // mesh we are trying to build a convex hull for. From this bounding box, we - // compute the length of the diagonal to get a relative size and center for - // point projection - uint32_t nPoints = ch->GetNPoints(); - Vec3* inputPoints = ch->GetPointsBuffer(); - Vec3 bmin(inputPoints[0]); - Vec3 bmax(inputPoints[1]); - for (uint32_t i = 1; i < nPoints; i++) { - const Vec3& p = inputPoints[i]; - p.UpdateMinMax(bmin, bmax); - } - Vec3 center; - double diagonalLength = - center.GetCenter(bmin, bmax); // Get the center of the bounding box - // This is the error threshold for determining if we should use the raycast - // result data point vs. the voxelized result. - double pointDistanceThreshold = diagonalLength * 0.05; - // If a new point is within 1/100th the diagonal length of the bounding - // volume we do not add it. To do so would create a thin sliver in the - // resulting convex hull - double snapDistanceThreshold = diagonalLength * 0.01; - double snapDistanceThresholdSquared = - snapDistanceThreshold * snapDistanceThreshold; - - // Allocate buffer for projected vertices - Vec3* outputPoints = new Vec3[nPoints]; - uint32_t outCount = 0; - for (uint32_t i = 0; i < nPoints; i++) { - Vec3& inputPoint = inputPoints[i]; - Vec3& outputPoint = outputPoints[outCount]; - // Compute the direction vector from the center of this mesh to the vertex - Vec3 dir = inputPoint - center; - // Normalize the direction vector. - dir.Normalize(); - // Multiply times the diagonal length of the mesh - dir *= diagonalLength; - // Add the center back in again to get the destination point - dir += center; - // By default the output point is equal to the input point - outputPoint = inputPoint; - double pointDistance; - if (mRaycastMesh->raycast(center.GetData(), dir.GetData(), - inputPoint.GetData(), outputPoint.GetData(), - &pointDistance)) { - // If the nearest intersection point is too far away, we keep the - // original source data point. Not all points lie directly on the - // original mesh surface - if (pointDistance > pointDistanceThreshold) { - outputPoint = inputPoint; +void VHACD::SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume) +{ + if (nvertices <= 4) { + return; + } + ICHull icHull; + if (mRaycastMesh) + { + // We project these points onto the original source mesh to increase precision + // The voxelization process drops floating point precision so returned data points are not exactly lying on the + // surface of the original source mesh. + // The first step is we need to compute the bounding box of the mesh we are trying to build a convex hull for. + // From this bounding box, we compute the length of the diagonal to get a relative size and center for point projection + uint32_t nPoints = ch->GetNPoints(); + Vec3 *inputPoints = ch->GetPointsBuffer(); + Vec3 bmin(inputPoints[0]); + Vec3 bmax(inputPoints[1]); + for (uint32_t i = 1; i < nPoints; i++) + { + const Vec3 &p = inputPoints[i]; + p.UpdateMinMax(bmin, bmax); } - } - // Ok, before we add this point, we do not want to create points which are - // extremely close to each other. This will result in tiny sliver - // triangles which are really bad for collision detection. - bool foundNearbyPoint = false; - for (uint32_t j = 0; j < outCount; j++) { - // If this new point is extremely close to an existing point, we do not - // add it! - double squaredDistance = - outputPoints[j].GetDistanceSquared(outputPoint); - if (squaredDistance < snapDistanceThresholdSquared) { - foundNearbyPoint = true; - break; + Vec3 center; + double diagonalLength = center.GetCenter(bmin, bmax); // Get the center of the bounding box + // This is the error threshold for determining if we should use the raycast result data point vs. the voxelized result. + double pointDistanceThreshold = diagonalLength * 0.05; + // If a new point is within 1/100th the diagonal length of the bounding volume we do not add it. To do so would create a + // thin sliver in the resulting convex hull + double snapDistanceThreshold = diagonalLength * 0.01; + double snapDistanceThresholdSquared = snapDistanceThreshold*snapDistanceThreshold; + + // Allocate buffer for projected vertices + Vec3 *outputPoints = new Vec3[nPoints]; + uint32_t outCount = 0; + for (uint32_t i = 0; i < nPoints; i++) + { + Vec3 &inputPoint = inputPoints[i]; + Vec3 &outputPoint = outputPoints[outCount]; + // Compute the direction vector from the center of this mesh to the vertex + Vec3 dir = inputPoint - center; + // Normalize the direction vector. + dir.Normalize(); + // Multiply times the diagonal length of the mesh + dir *= diagonalLength; + // Add the center back in again to get the destination point + dir += center; + // By default the output point is equal to the input point + outputPoint = inputPoint; + double pointDistance; + if (mRaycastMesh->raycast(center.GetData(), dir.GetData(), inputPoint.GetData(), outputPoint.GetData(),&pointDistance) ) + { + // If the nearest intersection point is too far away, we keep the original source data point. + // Not all points lie directly on the original mesh surface + if (pointDistance > pointDistanceThreshold) + { + outputPoint = inputPoint; + } + } + // Ok, before we add this point, we do not want to create points which are extremely close to each other. + // This will result in tiny sliver triangles which are really bad for collision detection. + bool foundNearbyPoint = false; + for (uint32_t j = 0; j < outCount; j++) + { + // If this new point is extremely close to an existing point, we do not add it! + double squaredDistance = outputPoints[j].GetDistanceSquared(outputPoint); + if (squaredDistance < snapDistanceThresholdSquared ) + { + foundNearbyPoint = true; + break; + } + } + if (!foundNearbyPoint) + { + outCount++; + } } - } - if (!foundNearbyPoint) { - outCount++; - } - } - icHull.AddPoints(outputPoints, outCount); - delete[] outputPoints; - } else { - icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints()); - } - icHull.Process((uint32_t)nvertices, minVolume); - TMMesh& mesh = icHull.GetMesh(); - const size_t nT = mesh.GetNTriangles(); - const size_t nV = mesh.GetNVertices(); - ch->ResizePoints(nV); - ch->ResizeTriangles(nT); - mesh.GetIFS(ch->GetPointsBuffer(), ch->GetTrianglesBuffer()); + icHull.AddPoints(outputPoints, outCount); + delete[]outputPoints; + } + else + { + icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints()); + } + icHull.Process((uint32_t)nvertices, minVolume); + TMMesh& mesh = icHull.GetMesh(); + const size_t nT = mesh.GetNTriangles(); + const size_t nV = mesh.GetNVertices(); + ch->ResizePoints(nV); + ch->ResizeTriangles(nT); + mesh.GetIFS(ch->GetPointsBuffer(), ch->GetTrianglesBuffer()); } -void VHACD::SimplifyConvexHulls(const Parameters& params) { - if (m_cancel || params.m_maxNumVerticesPerCH < 4) { - return; - } - m_timer.Tic(); - - m_stage = "Simplify convex-hulls"; - m_operation = "Simplify convex-hulls"; - - std::ostringstream msg; - const size_t nConvexHulls = m_convexHulls.Size(); - if (params.m_logger) { - msg << "+ Simplify " << nConvexHulls << " convex-hulls " << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - - Update(0.0, 0.0, params); - for (size_t i = 0; i < nConvexHulls && !m_cancel; ++i) { +void VHACD::SimplifyConvexHulls(const Parameters& params) +{ + if (m_cancel || params.m_maxNumVerticesPerCH < 4) { + return; + } + m_timer.Tic(); + + m_stage = "Simplify convex-hulls"; + m_operation = "Simplify convex-hulls"; + + std::ostringstream msg; + const size_t nConvexHulls = m_convexHulls.Size(); if (params.m_logger) { - msg.str(""); - msg << "\t\t Simplify CH[" << std::setfill('0') << std::setw(5) << i - << "] " << m_convexHulls[i]->GetNPoints() << " V, " - << m_convexHulls[i]->GetNTriangles() << " T" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } - SimplifyConvexHull(m_convexHulls[i], params.m_maxNumVerticesPerCH, - m_volumeCH0 * params.m_minVolumePerCH); - } - - m_overallProgress = 100.0; - Update(100.0, 100.0, params); - m_timer.Toc(); - if (params.m_logger) { - msg.str(""); - msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; - params.m_logger->Log(msg.str().c_str()); - } + msg << "+ Simplify " << nConvexHulls << " convex-hulls " << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + + Update(0.0, 0.0, params); + for (size_t i = 0; i < nConvexHulls && !m_cancel; ++i) { + if (params.m_logger) { + msg.str(""); + msg << "\t\t Simplify CH[" << std::setfill('0') << std::setw(5) << i << "] " << m_convexHulls[i]->GetNPoints() << " V, " << m_convexHulls[i]->GetNTriangles() << " T" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } + SimplifyConvexHull(m_convexHulls[i], params.m_maxNumVerticesPerCH, m_volumeCH0 * params.m_minVolumePerCH); + } + + m_overallProgress = 100.0; + Update(100.0, 100.0, params); + m_timer.Toc(); + if (params.m_logger) { + msg.str(""); + msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl; + params.m_logger->Log(msg.str().c_str()); + } } -bool VHACD::ComputeCenterOfMass(double centerOfMass[3]) const { - bool ret = false; - - centerOfMass[0] = 0; - centerOfMass[1] = 0; - centerOfMass[2] = 0; - // Get number of convex hulls in the result - uint32_t hullCount = GetNConvexHulls(); - if (hullCount) // if we have results - { - ret = true; - double totalVolume = 0; - // Initialize the center of mass to zero - centerOfMass[0] = 0; - centerOfMass[1] = 0; - centerOfMass[2] = 0; - // Compute the total volume of all convex hulls - for (uint32_t i = 0; i < hullCount; i++) { - ConvexHull ch; - GetConvexHull(i, ch); - totalVolume += ch.m_volume; - } - // compute the reciprocal of the total volume - double recipVolume = 1.0 / totalVolume; - // Add in the weighted by volume average of the center point of each convex - // hull - for (uint32_t i = 0; i < hullCount; i++) { - ConvexHull ch; - GetConvexHull(i, ch); - double ratio = ch.m_volume * recipVolume; - centerOfMass[0] += ch.m_center[0] * ratio; - centerOfMass[1] += ch.m_center[1] * ratio; - centerOfMass[2] += ch.m_center[2] * ratio; - } - } - return ret; +bool VHACD::ComputeCenterOfMass(double centerOfMass[3]) const +{ + bool ret = false; + + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + // Get number of convex hulls in the result + uint32_t hullCount = GetNConvexHulls(); + if (hullCount) // if we have results + { + ret = true; + double totalVolume = 0; + // Initialize the center of mass to zero + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + // Compute the total volume of all convex hulls + for (uint32_t i = 0; i < hullCount; i++) + { + ConvexHull ch; + GetConvexHull(i, ch); + totalVolume += ch.m_volume; + } + // compute the reciprocal of the total volume + double recipVolume = 1.0 / totalVolume; + // Add in the weighted by volume average of the center point of each convex hull + for (uint32_t i = 0; i < hullCount; i++) + { + ConvexHull ch; + GetConvexHull(i, ch); + double ratio = ch.m_volume*recipVolume; + centerOfMass[0] += ch.m_center[0] * ratio; + centerOfMass[1] += ch.m_center[1] * ratio; + centerOfMass[2] += ch.m_center[2] * ratio; + } + } + return ret; } -} // namespace VHACD +} // end of VHACD namespace