diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index bbca74e1..a63cdbd4 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -10,7 +10,7 @@ jobs: platform: [ubuntu-latest, macos-latest, windows-latest] include: - platform: windows-latest - cmake_type: "Visual Studio 16 2019" + cmake_type: "Visual Studio 17 2022" dll_name: "Release/libvhacd.dll" target_dll_name: "libvhacd.dll" - platform: ubuntu-latest @@ -32,14 +32,14 @@ jobs: run: | mkdir build cd build - cmake -G "${{ matrix.cmake_type }}" ../src + cmake -G "${{ matrix.cmake_type }}" -DCMAKE_BUILD_TYPE=Release ../src - name: build run: cmake --build build --config Release - uses: actions/upload-artifact@v1 with: name: ${{ matrix.platform }} - path: build/dll/${{ matrix.dll_name }} + path: build/${{ matrix.dll_name }} create_release: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 79606f65..86a54792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ All notable changes to this repository will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.0.2-preview] - 2023 + +### Upgrade Notes + +Updated to v-hacd 4.1 + +### Known Issues + +### Added + +### Changed + +v-hacd is now up-to-date with the latest version 4.1 + +### Deprecated + +### Removed + +### Fixed + ## Unreleased ### Upgrade Notes @@ -38,4 +58,4 @@ Created VHACD Unity package ### Removed -### Fixed \ No newline at end of file +### Fixed diff --git a/bin-no-ocl-omp/osx/testVHACD b/bin-no-ocl-omp/osx/testVHACD deleted file mode 100755 index 43e93bee..00000000 Binary files a/bin-no-ocl-omp/osx/testVHACD and /dev/null differ diff --git a/bin-no-ocl/osx/testVHACD b/bin-no-ocl/osx/testVHACD deleted file mode 100755 index c518a0e9..00000000 Binary files a/bin-no-ocl/osx/testVHACD and /dev/null differ diff --git a/bin-no-ocl/win32/testVHACD.exe b/bin-no-ocl/win32/testVHACD.exe deleted file mode 100644 index 6314cb71..00000000 Binary files a/bin-no-ocl/win32/testVHACD.exe and /dev/null differ diff --git a/bin-no-ocl/win64/testVHACD.exe b/bin-no-ocl/win64/testVHACD.exe deleted file mode 100644 index ff67ae33..00000000 Binary files a/bin-no-ocl/win64/testVHACD.exe and /dev/null differ diff --git a/bin/.DS_Store b/bin/.DS_Store deleted file mode 100644 index db2462a8..00000000 Binary files a/bin/.DS_Store and /dev/null differ diff --git a/bin/osx/testVHACD b/bin/osx/testVHACD deleted file mode 100755 index 1824faca..00000000 Binary files a/bin/osx/testVHACD and /dev/null differ diff --git a/bin/win32/testVHACD.exe b/bin/win32/testVHACD.exe deleted file mode 100644 index b91f0f13..00000000 Binary files a/bin/win32/testVHACD.exe and /dev/null differ diff --git a/bin/win64/testVHACD.exe b/bin/win64/testVHACD.exe deleted file mode 100644 index a20dd776..00000000 Binary files a/bin/win64/testVHACD.exe and /dev/null differ diff --git a/com.unity.robotics.vhacd/Runtime/VHACD.cs b/com.unity.robotics.vhacd/Runtime/VHACD.cs index 4c36099e..5902cfc0 100644 --- a/com.unity.robotics.vhacd/Runtime/VHACD.cs +++ b/com.unity.robotics.vhacd/Runtime/VHACD.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -8,92 +8,86 @@ namespace MeshProcess { public class VHACD : MonoBehaviour { - [System.Serializable] + // Should match `enum class FillMode` in VHACD.h + [Serializable] + public enum FillMode + { + FLOOD_FILL, // 0 + SURFACE_ONLY, // 1 + RAYCAST_FILL // 2 + } + + [Serializable] + [StructLayout(LayoutKind.Sequential)] public unsafe struct Parameters { public void Init() { - 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 = null; m_logger = null; - m_convexhullApproximation = 1; - m_oclAcceleration = 0; - 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 + m_taskRunner = null; + m_maxConvexHulls = 32; + m_resolution = 100000; + m_minimumVolumePercentErrorAllowed = 1; + m_maxRecursionDepth = 10; + m_shrinkWrap = true; + m_fillMode = FillMode.FLOOD_FILL; + m_maxNumVerticesPerCH = 64; + m_asyncACD = true; + m_minEdgeLength = 2; + m_findBestPlane = false; } - [Tooltip("maximum concavity")] - [Range(0, 1)] - public double m_concavity; - - [Tooltip("controls the bias toward clipping along symmetry planes")] - [Range(0, 1)] - public double m_alpha; - - [Tooltip("controls the bias toward clipping along revolution axes")] - [Range(0, 1)] - public double m_beta; - - [Tooltip("controls the adaptive sampling of the generated convex-hulls")] - [Range(0, 0.01f)] - public double m_minVolumePerCH; - public void* m_callback; public void* m_logger; + public void* m_taskRunner; - [Tooltip("maximum number of voxels generated during the voxelization stage")] + [Tooltip("The maximum number of convex hulls to produce. Performance sensitive: adding more MeshColliders slows down Unity at runtime.")] + [Range(1, 2048)] + public uint m_maxConvexHulls; + + [Tooltip("Maximum number of voxels generated during the voxelization stage. Higher value increases generation time.")] [Range(10000, 64000000)] public uint m_resolution; - [Tooltip("controls the maximum number of triangles per convex-hull")] - [Range(4, 1024)] - public uint m_maxNumVerticesPerCH; - - [Tooltip("controls the granularity of the search for the \"best\" clipping plane")] - [Range(1, 16)] - public uint m_planeDownsampling; + [Tooltip( + "If the voxels are within X% of the volume of the hull, we consider this a close enough approximation.")] + [Range(0.001f, 10)] + public double m_minimumVolumePercentErrorAllowed; - [Tooltip("controls the precision of the convex-hull generation process during the clipping plane selection stage")] - [Range(1, 16)] - public uint m_convexhullDownsampling; + [Tooltip("Maximum recursion depth. Default value is 10.")] + [Range(1, 15)] + public uint m_maxRecursionDepth; - [Tooltip("enable/disable normalizing the mesh before applying the convex decomposition")] - [Range(0, 1)] - public uint m_pca; + [Tooltip( + "This will project the output convex hull vertices onto the original source mesh to increase the floating point accuracy of the results. Default is true.")] + public bool m_shrinkWrap; - [Tooltip("0: voxel-based (recommended), 1: tetrahedron-based")] - [Range(0, 1)] - public uint m_mode; + [Tooltip("How to fill the interior of the voxelized mesh")] + public FillMode m_fillMode; - [Range(0, 1)] - public uint m_convexhullApproximation; + [Tooltip("Controls the maximum number of triangles per convex-hull")] + [Range(4, 1024)] + public uint m_maxNumVerticesPerCH; - [Range(0, 1)] - public uint m_oclAcceleration; + [Tooltip("Whether or not to run asynchronously, taking advantage of additional cores")] + public bool m_asyncACD; - public uint m_maxConvexHulls; + [Tooltip("Minimum size of a voxel edge. Default value is 2 voxels.")] + [Range(1, 16)] + public uint m_minEdgeLength; - [Tooltip("This will project the output convex hull vertices onto the original source mesh to increase the floating point accuracy of the results")] - public bool m_projectHullVertices; - }; + [Tooltip("If false, splits hulls in the middle. If true, tries to find optimal split plane location. False by default.")] + public bool m_findBestPlane; + } + [StructLayout(LayoutKind.Sequential)] unsafe struct ConvexHull { public double* m_points; public uint* m_triangles; public uint m_nPoints; public uint m_nTriangles; - public double m_volume; - public fixed double m_center[3]; }; [DllImport("libvhacd")] static extern unsafe void* CreateVHACD(); @@ -124,7 +118,11 @@ static extern unsafe bool ComputeDouble( static extern unsafe void GetConvexHull( void* pVHACD, uint index, - ConvexHull* ch); + ConvexHull* convexHull); + + [DllImport("libvhacd")] + static extern unsafe void FreeConvexHull( + ConvexHull* convexHull); public Parameters m_parameters; @@ -183,8 +181,10 @@ public unsafe List GenerateConvexMeshes(Mesh mesh = null) Marshal.Copy((System.IntPtr)hull.m_triangles, indices, 0, indices.Length); hullMesh.SetTriangles(indices, 0); - + convexMesh.Add(hullMesh); + + FreeConvexHull(&hull); } DestroyVHACD(vhacd); diff --git a/com.unity.robotics.vhacd/Runtime/liblibvhacd.dylib b/com.unity.robotics.vhacd/Runtime/liblibvhacd.dylib index ba519998..3d29bab7 100644 Binary files a/com.unity.robotics.vhacd/Runtime/liblibvhacd.dylib and b/com.unity.robotics.vhacd/Runtime/liblibvhacd.dylib differ diff --git a/com.unity.robotics.vhacd/Runtime/liblibvhacd.so b/com.unity.robotics.vhacd/Runtime/liblibvhacd.so index 102ac30a..02f5b3eb 100644 Binary files a/com.unity.robotics.vhacd/Runtime/liblibvhacd.so and b/com.unity.robotics.vhacd/Runtime/liblibvhacd.so differ diff --git a/com.unity.robotics.vhacd/Runtime/libvhacd.dll b/com.unity.robotics.vhacd/Runtime/libvhacd.dll index 5e8e2521..b1552eb6 100644 Binary files a/com.unity.robotics.vhacd/Runtime/libvhacd.dll and b/com.unity.robotics.vhacd/Runtime/libvhacd.dll differ diff --git a/com.unity.robotics.vhacd/package.json b/com.unity.robotics.vhacd/package.json index f943333c..adb44799 100644 --- a/com.unity.robotics.vhacd/package.json +++ b/com.unity.robotics.vhacd/package.json @@ -1,6 +1,6 @@ { "name": "com.unity.robotics.vhacd", - "version": "0.0.1-preview", + "version": "0.0.2-preview", "displayName": "VHACD", "description": "", "unity": "2020.3", diff --git a/scripts/cmake_common.cmake b/scripts/cmake_common.cmake deleted file mode 100644 index 579f76d3..00000000 --- a/scripts/cmake_common.cmake +++ /dev/null @@ -1,18 +0,0 @@ -message("[VHACD] Generating " ${PROJECT_NAME} "...") -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_CL_FILES "cl/*.cl") -source_group (Inc FILES ${PROJECT_INC_FILES}) -source_group (Inl FILES ${PROJECT_INL_FILES}) -source_group (Src FILES ${PROJECT_CPP_FILES} ) -source_group (SrcC FILES ${PROJECT_C_FILES} ) -source_group (CL FILES ${PROJECT_CL_FILES}) - -#include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/inc) -message("[VHACD] \t INC_FILES: ${PROJECT_INC_FILES}") -message("[VHACD] \t INL_FILES: ${PROJECT_INL_FILES}") -message("[VHACD] \t CPP_FILES: ${PROJECT_CPP_FILES}") -message("[VHACD] \t C_FILES: ${PROJECT_C_FILES}") -message("[VHACD] \t CL_FILES: ${PROJECT_CL_FILES}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b9358fbc..74b44b9e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,14 +1,13 @@ cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) project(VHACD) -option(NO_OPENCL "NO_OPENCL" OFF) -option(NO_OPENMP "NO_OPENMP" OFF) -message("NO_OPENCL " ${NO_OPENCL}) -message("NO_OPENMP " ${NO_OPENMP}) +# This project requires C++11. +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -#set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") set(CompilerFlags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG @@ -20,15 +19,17 @@ set(CompilerFlags CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - ) +) + foreach(CompilerFlag ${CompilerFlags}) string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") set(${CompilerFlag} "${${CompilerFlag}}" CACHE STRING "msvc compiler flags" FORCE) message("MSVC flags: ${CompilerFlag}:${${CompilerFlag}}") endforeach() -#set(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/output" CACHE PATH "project install prefix" FORCE) -set(CMAKE_COMMON_INC "${CMAKE_SOURCE_DIR}/../scripts/cmake_common.cmake") -add_subdirectory ("${CMAKE_SOURCE_DIR}/VHACD_Lib") -add_subdirectory ("${CMAKE_SOURCE_DIR}/test") -add_subdirectory ("${CMAKE_SOURCE_DIR}/dll") +include_directories(${CMAKE_SOURCE_DIR}/inc) + +add_library(libvhacd SHARED ${CMAKE_SOURCE_DIR}/dll/dll.cpp) + +# VHACD.h is a header-only library. Implementation needs this define flag. +target_compile_definitions(libvhacd PRIVATE ENABLE_VHACD_IMPLEMENTATION=1) diff --git a/src/VHACD_Lib/CMakeLists.txt b/src/VHACD_Lib/CMakeLists.txt deleted file mode 100644 index 46fc1b16..00000000 --- a/src/VHACD_Lib/CMakeLists.txt +++ /dev/null @@ -1,77 +0,0 @@ -project(VHACD_LIB CXX C) -include(${CMAKE_COMMON_INC}) - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - -if (NOT NO_OPENCL) - #include(FindOpenCL OPTIONAL) - find_package(OpenCL) -endif() -if (NOT NO_OPENMP) - #include(FindOpenMP OPTIONAL) - find_package(OpenMP) -endif() -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") -endif() -set(LIB_TYPE "STATIC" CACHE STRING "STATIC, SHARED or MODULE?") -message("[VHACD] \t LIB_TYPE " ${LIB_TYPE}) -add_library(vhacd ${LIB_TYPE} ${PROJECT_CPP_FILES} ${PROJECT_C_FILES} ${PROJECT_INC_FILES} ${PROJECT_INL_FILES} ${PROJECT_CL_FILES}) - -if (OpenCL_FOUND) - target_include_directories(vhacd PRIVATE "${OpenCL_INCLUDE_DIR}") - target_link_libraries(vhacd PRIVATE "${OpenCL_LIBRARY}") - target_compile_definitions(vhacd PRIVATE -DOPENCL_FOUND=1 ) - target_compile_definitions(vhacd PRIVATE -DOPENCL_CL_FILES="${PROJECT_CL_FILES}" ) -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) -install(FILES ${PROJECT_INL_FILES} DESTINATION include) - - -set(VHACD_LIB_VERSION 3.2.0) -include(CMakePackageConfigHelpers) -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/vhacd/vhacd-config-version.cmake" - VERSION ${VHACD_LIB_VERSION} - COMPATIBILITY AnyNewerVersion -) - -export(EXPORT vhacd-targets NAMESPACE :: - FILE "${CMAKE_CURRENT_BINARY_DIR}/vhacd/vhacd-targets.cmake" -) - -configure_file(cmake/vhacd-config.cmake - "${CMAKE_CURRENT_BINARY_DIR}/vhacd/vhacd-config.cmake" - COPYONLY -) - -set(ConfigPackageLocation lib/cmake/vhacd) -install(EXPORT vhacd-targets - FILE - vhacd-targets.cmake - DESTINATION - ${ConfigPackageLocation} - NAMESPACE :: -) -install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/vhacd/vhacd-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/vhacd/vhacd-config-version.cmake" - DESTINATION - ${ConfigPackageLocation} - COMPONENT - Devel -) - diff --git a/src/VHACD_Lib/cl/vhacdKernels.cl b/src/VHACD_Lib/cl/vhacdKernels.cl deleted file mode 100644 index 21c2da0b..00000000 --- a/src/VHACD_Lib/cl/vhacdKernels.cl +++ /dev/null @@ -1,71 +0,0 @@ -__kernel void ComputePartialVolumes(__global short4 * voxels, - const int numVoxels, - const float4 plane, - const float4 minBB, - const float4 scale, - __local uint4 * localPartialVolumes, - __global uint4 * partialVolumes) -{ - int localId = get_local_id(0); - int groupSize = get_local_size(0); - int i0 = get_global_id(0) << 2; - float4 voxel; - uint4 v; - - voxel = convert_float4(voxels[i0]); - v.s0 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 < numVoxels); - voxel = convert_float4(voxels[i0 + 1]); - v.s1 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 1 < numVoxels); - voxel = convert_float4(voxels[i0 + 2]); - v.s2 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 2 < numVoxels); - voxel = convert_float4(voxels[i0 + 3]); - v.s3 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 3 < numVoxels); - - localPartialVolumes[localId] = v; - barrier(CLK_LOCAL_MEM_FENCE); - - for (int i = groupSize >> 1; i > 0; i >>= 1) - { - if (localId < i) - { - localPartialVolumes[localId] += localPartialVolumes[localId + i]; - } - barrier(CLK_LOCAL_MEM_FENCE); - } - if (localId == 0) - { - partialVolumes[get_group_id(0)] = localPartialVolumes[0]; - } -} -__kernel void ComputePartialSums(__global uint4 * data, - const int dataSize, - __local uint4 * partialSums) -{ - - int globalId = get_global_id(0); - int localId = get_local_id(0); - int groupSize = get_local_size(0); - int i; - if (globalId < dataSize) - { - partialSums[localId] = data[globalId]; - } - else - { - partialSums[localId] = (0, 0, 0, 0); - } - barrier(CLK_LOCAL_MEM_FENCE); - for (i = groupSize >> 1; i > 0; i >>= 1) - { - if (localId < i) - { - partialSums[localId] += partialSums[localId + i]; - } - barrier(CLK_LOCAL_MEM_FENCE); - } - if (localId == 0) - { - data[get_group_id(0)] = partialSums[0]; - } -} - diff --git a/src/VHACD_Lib/cmake/vhacd-config.cmake b/src/VHACD_Lib/cmake/vhacd-config.cmake deleted file mode 100644 index 8fc5c583..00000000 --- a/src/VHACD_Lib/cmake/vhacd-config.cmake +++ /dev/null @@ -1,4 +0,0 @@ - -find_package(OpenCL) -find_package(OpenMP) -include("${CMAKE_CURRENT_LIST_DIR}/vhacd-targets.cmake") \ No newline at end of file diff --git a/src/VHACD_Lib/inc/FloatMath.h b/src/VHACD_Lib/inc/FloatMath.h deleted file mode 100644 index 37b07cb6..00000000 --- a/src/VHACD_Lib/inc/FloatMath.h +++ /dev/null @@ -1,525 +0,0 @@ -#ifndef FLOAT_MATH_LIB_H - -#define FLOAT_MATH_LIB_H - - -#include -#include - -namespace FLOAT_MATH -{ - -enum FM_ClipState -{ - FMCS_XMIN = (1<<0), - FMCS_XMAX = (1<<1), - FMCS_YMIN = (1<<2), - FMCS_YMAX = (1<<3), - FMCS_ZMIN = (1<<4), - FMCS_ZMAX = (1<<5), -}; - -enum FM_Axis -{ - FM_XAXIS = (1<<0), - FM_YAXIS = (1<<1), - FM_ZAXIS = (1<<2) -}; - -enum LineSegmentType -{ - LS_START, - LS_MIDDLE, - LS_END -}; - - -const float FM_PI = 3.1415926535897932384626433832795028841971693993751f; -const float FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f); -const float FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI)); - -//***************** Float versions -//*** -//*** vectors are assumed to be 3 floats or 3 doubles representing X, Y, Z -//*** quaternions are assumed to be 4 floats or 4 doubles representing X,Y,Z,W -//*** matrices are assumed to be 16 floats or 16 doubles representing a standard D3D or OpenGL style 4x4 matrix -//*** bounding volumes are expressed as two sets of 3 floats/double representing bmin(x,y,z) and bmax(x,y,z) -//*** Plane equations are assumed to be 4 floats or 4 doubles representing Ax,By,Cz,D - -FM_Axis fm_getDominantAxis(const float normal[3]); -FM_Axis fm_getDominantAxis(const double normal[3]); - -void fm_decomposeTransform(const float local_transform[16],float trans[3],float rot[4],float scale[3]); -void fm_decomposeTransform(const double local_transform[16],double trans[3],double rot[4],double scale[3]); - -void fm_multiplyTransform(const float *pA,const float *pB,float *pM); -void fm_multiplyTransform(const double *pA,const double *pB,double *pM); - -void fm_inverseTransform(const float matrix[16],float inverse_matrix[16]); -void fm_inverseTransform(const double matrix[16],double inverse_matrix[16]); - -void fm_identity(float matrix[16]); // set 4x4 matrix to identity. -void fm_identity(double matrix[16]); // set 4x4 matrix to identity. - -void fm_inverseRT(const float matrix[16], const float pos[3], float t[3]); // inverse rotate translate the point. -void fm_inverseRT(const double matrix[16],const double pos[3],double t[3]); // inverse rotate translate the point. - -void fm_transform(const float matrix[16], const float pos[3], float t[3]); // rotate and translate this point. -void fm_transform(const double matrix[16],const double pos[3],double t[3]); // rotate and translate this point. - -float fm_getDeterminant(const float matrix[16]); -double fm_getDeterminant(const double matrix[16]); - -void fm_getSubMatrix(int32_t ki,int32_t kj,float pDst[16],const float matrix[16]); -void fm_getSubMatrix(int32_t ki,int32_t kj,double pDst[16],const float matrix[16]); - -void fm_rotate(const float matrix[16],const float pos[3],float t[3]); // only rotate the point by a 4x4 matrix, don't translate. -void fm_rotate(const double matri[16],const double pos[3],double t[3]); // only rotate the point by a 4x4 matrix, don't translate. - -void fm_eulerToMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) -void fm_eulerToMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) - -void fm_getAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]); -void fm_getAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]); - -void fm_getAABBCenter(const float bmin[3],const float bmax[3],float center[3]); -void fm_getAABBCenter(const double bmin[3],const double bmax[3],double center[3]); - -void fm_transformAABB(const float bmin[3],const float bmax[3],const float matrix[16],float tbmin[3],float tbmax[3]); -void fm_transformAABB(const double bmin[3],const double bmax[3],const double matrix[16],double tbmin[3],double tbmax[3]); - -void fm_eulerToQuat(float x,float y,float z,float quat[4]); // convert euler angles to quaternion. -void fm_eulerToQuat(double x,double y,double z,double quat[4]); // convert euler angles to quaternion. - -void fm_quatToEuler(const float quat[4],float &ax,float &ay,float &az); -void fm_quatToEuler(const double quat[4],double &ax,double &ay,double &az); - -void fm_eulerToQuat(const float euler[3],float quat[4]); // convert euler angles to quaternion. Angles must be radians not degrees! -void fm_eulerToQuat(const double euler[3],double quat[4]); // convert euler angles to quaternion. - -void fm_scale(float x,float y,float z,float matrix[16]); // apply scale to the matrix. -void fm_scale(double x,double y,double z,double matrix[16]); // apply scale to the matrix. - -void fm_eulerToQuatDX(float x,float y,float z,float quat[4]); // convert euler angles to quaternion using the fucked up DirectX method -void fm_eulerToQuatDX(double x,double y,double z,double quat[4]); // convert euler angles to quaternion using the fucked up DirectX method - -void fm_eulerToMatrixDX(float x,float y,float z,float matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method. -void fm_eulerToMatrixDX(double x,double y,double z,double matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method. - -void fm_quatToMatrix(const float quat[4],float matrix[16]); // convert quaterinion rotation to matrix, translation set to zero. -void fm_quatToMatrix(const double quat[4],double matrix[16]); // convert quaterinion rotation to matrix, translation set to zero. - -void fm_quatRotate(const float quat[4],const float v[3],float r[3]); // rotate a vector directly by a quaternion. -void fm_quatRotate(const double quat[4],const double v[3],double r[3]); // rotate a vector directly by a quaternion. - -void fm_getTranslation(const float matrix[16],float t[3]); -void fm_getTranslation(const double matrix[16],double t[3]); - -void fm_setTranslation(const float *translation,float matrix[16]); -void fm_setTranslation(const double *translation,double matrix[16]); - -void fm_multiplyQuat(const float *qa,const float *qb,float *quat); -void fm_multiplyQuat(const double *qa,const double *qb,double *quat); - -void fm_matrixToQuat(const float matrix[16],float quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w -void fm_matrixToQuat(const double matrix[16],double quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w - -float fm_sphereVolume(float radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed ) -double fm_sphereVolume(double radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed ) - -float fm_cylinderVolume(float radius,float h); -double fm_cylinderVolume(double radius,double h); - -float fm_capsuleVolume(float radius,float h); -double fm_capsuleVolume(double radius,double h); - -float fm_distance(const float p1[3],const float p2[3]); -double fm_distance(const double p1[3],const double p2[3]); - -float fm_distanceSquared(const float p1[3],const float p2[3]); -double fm_distanceSquared(const double p1[3],const double p2[3]); - -float fm_distanceSquaredXZ(const float p1[3],const float p2[3]); -double fm_distanceSquaredXZ(const double p1[3],const double p2[3]); - -float fm_computePlane(const float p1[3],const float p2[3],const float p3[3],float *n); // return D -double fm_computePlane(const double p1[3],const double p2[3],const double p3[3],double *n); // return D - -float fm_distToPlane(const float plane[4],const float pos[3]); // computes the distance of this point from the plane. -double fm_distToPlane(const double plane[4],const double pos[3]); // computes the distance of this point from the plane. - -float fm_dot(const float p1[3],const float p2[3]); -double fm_dot(const double p1[3],const double p2[3]); - -void fm_cross(float cross[3],const float a[3],const float b[3]); -void fm_cross(double cross[3],const double a[3],const double b[3]); - -float fm_computeNormalVector(float n[3],const float p1[3],const float p2[3]); // as P2-P1 normalized. -double fm_computeNormalVector(double n[3],const double p1[3],const double p2[3]); // as P2-P1 normalized. - -bool fm_computeWindingOrder(const float p1[3],const float p2[3],const float p3[3]); // returns true if the triangle is clockwise. -bool fm_computeWindingOrder(const double p1[3],const double p2[3],const double p3[3]); // returns true if the triangle is clockwise. - -float fm_normalize(float n[3]); // normalize this vector and return the distance -double fm_normalize(double n[3]); // normalize this vector and return the distance - -float fm_normalizeQuat(float n[4]); // normalize this quat -double fm_normalizeQuat(double n[4]); // normalize this quat - -void fm_matrixMultiply(const float A[16],const float B[16],float dest[16]); -void fm_matrixMultiply(const double A[16],const double B[16],double dest[16]); - -void fm_composeTransform(const float position[3],const float quat[4],const float scale[3],float matrix[16]); -void fm_composeTransform(const double position[3],const double quat[4],const double scale[3],double matrix[16]); - -float fm_computeArea(const float p1[3],const float p2[3],const float p3[3]); -double fm_computeArea(const double p1[3],const double p2[3],const double p3[3]); - -void fm_lerp(const float p1[3],const float p2[3],float dest[3],float lerpValue); -void fm_lerp(const double p1[3],const double p2[3],double dest[3],double lerpValue); - -bool fm_insideTriangleXZ(const float test[3],const float p1[3],const float p2[3],const float p3[3]); -bool fm_insideTriangleXZ(const double test[3],const double p1[3],const double p2[3],const double p3[3]); - -bool fm_insideAABB(const float pos[3],const float bmin[3],const float bmax[3]); -bool fm_insideAABB(const double pos[3],const double bmin[3],const double bmax[3]); - -bool fm_insideAABB(const float obmin[3],const float obmax[3],const float tbmin[3],const float tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax -bool fm_insideAABB(const double obmin[3],const double obmax[3],const double tbmin[3],const double tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax - -uint32_t fm_clipTestPoint(const float bmin[3],const float bmax[3],const float pos[3]); -uint32_t fm_clipTestPoint(const double bmin[3],const double bmax[3],const double pos[3]); - -uint32_t fm_clipTestPointXZ(const float bmin[3],const float bmax[3],const float pos[3]); // only tests X and Z, not Y -uint32_t fm_clipTestPointXZ(const double bmin[3],const double bmax[3],const double pos[3]); // only tests X and Z, not Y - - -uint32_t fm_clipTestAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],const float p3[3],uint32_t &andCode); -uint32_t fm_clipTestAABB(const double bmin[3],const double bmax[3],const double p1[3],const double p2[3],const double p3[3],uint32_t &andCode); - - -bool fm_lineTestAABBXZ(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time); -bool fm_lineTestAABBXZ(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time); - -bool fm_lineTestAABB(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time); -bool fm_lineTestAABB(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time); - - -void fm_initMinMax(const float p[3],float bmin[3],float bmax[3]); -void fm_initMinMax(const double p[3],double bmin[3],double bmax[3]); - -void fm_initMinMax(float bmin[3],float bmax[3]); -void fm_initMinMax(double bmin[3],double bmax[3]); - -void fm_minmax(const float p[3],float bmin[3],float bmax[3]); // accumulate to a min-max value -void fm_minmax(const double p[3],double bmin[3],double bmax[3]); // accumulate to a min-max value - -// Computes the diagonal length of the bounding box and then inflates the bounding box on all sides -// by the ratio provided. -void fm_inflateMinMax(float bmin[3], float bmax[3], float ratio); -void fm_inflateMinMax(double bmin[3], double bmax[3], double ratio); - -float fm_solveX(const float plane[4],float y,float z); // solve for X given this plane equation and the other two components. -double fm_solveX(const double plane[4],double y,double z); // solve for X given this plane equation and the other two components. - -float fm_solveY(const float plane[4],float x,float z); // solve for Y given this plane equation and the other two components. -double fm_solveY(const double plane[4],double x,double z); // solve for Y given this plane equation and the other two components. - -float fm_solveZ(const float plane[4],float x,float y); // solve for Z given this plane equation and the other two components. -double fm_solveZ(const double plane[4],double x,double y); // solve for Z given this plane equation and the other two components. - -bool fm_computeBestFitPlane(uint32_t vcount, // number of input data points - const float *points, // starting address of points array. - uint32_t vstride, // stride between input points. - const float *weights, // *optional point weighting values. - uint32_t wstride, // weight stride for each vertex. - float plane[4], // Best fit plane equation - float center[3]); // Best fit weighted center of input points - -bool fm_computeBestFitPlane(uint32_t vcount, // number of input data points - const double *points, // starting address of points array. - uint32_t vstride, // stride between input points. - const double *weights, // *optional point weighting values. - uint32_t wstride, // weight stride for each vertex. - double plane[4], - double center[3]); - -// Computes the average center of a set of data points -bool fm_computeCentroid(uint32_t vcount, // number of input data points - const float *points, // starting address of points array. - float *center); - -bool fm_computeCentroid(uint32_t vcount, // number of input data points - const double *points, // starting address of points array. - double *center); - -// Compute centroid of a triangle mesh; takes area of each triangle into account -// weighted average -bool fm_computeCentroid(uint32_t vcount, // number of input data points - const float *points, // starting address of points array. - uint32_t triangleCount, - const uint32_t *indices, - float *center); - -// Compute centroid of a triangle mesh; takes area of each triangle into account -// weighted average -bool fm_computeCentroid(uint32_t vcount, // number of input data points - const double *points, // starting address of points array. - uint32_t triangleCount, - const uint32_t *indices, - double *center); - - -float fm_computeBestFitAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]); // returns the diagonal distance -double fm_computeBestFitAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]); // returns the diagonal distance - -float fm_computeBestFitSphere(uint32_t vcount,const float *points,uint32_t pstride,float center[3]); -double fm_computeBestFitSphere(uint32_t vcount,const double *points,uint32_t pstride,double center[3]); - -bool fm_lineSphereIntersect(const float center[3],float radius,const float p1[3],const float p2[3],float intersect[3]); -bool fm_lineSphereIntersect(const double center[3],double radius,const double p1[3],const double p2[3],double intersect[3]); - -bool fm_intersectRayAABB(const float bmin[3],const float bmax[3],const float pos[3],const float dir[3],float intersect[3]); -bool fm_intersectLineSegmentAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],float intersect[3]); - -bool fm_lineIntersectsTriangle(const float rayStart[3],const float rayEnd[3],const float p1[3],const float p2[3],const float p3[3],float sect[3]); -bool fm_lineIntersectsTriangle(const double rayStart[3],const double rayEnd[3],const double p1[3],const double p2[3],const double p3[3],double sect[3]); - -bool fm_rayIntersectsTriangle(const float origin[3],const float dir[3],const float v0[3],const float v1[3],const float v2[3],float &t); -bool fm_rayIntersectsTriangle(const double origin[3],const double dir[3],const double v0[3],const double v1[3],const double v2[3],double &t); - -bool fm_raySphereIntersect(const float center[3],float radius,const float pos[3],const float dir[3],float distance,float intersect[3]); -bool fm_raySphereIntersect(const double center[3],double radius,const double pos[3],const double dir[3],double distance,double intersect[3]); - -void fm_catmullRom(float out_vector[3],const float p1[3],const float p2[3],const float p3[3],const float *p4, const float s); -void fm_catmullRom(double out_vector[3],const double p1[3],const double p2[3],const double p3[3],const double *p4, const double s); - -bool fm_intersectAABB(const float bmin1[3],const float bmax1[3],const float bmin2[3],const float bmax2[3]); -bool fm_intersectAABB(const double bmin1[3],const double bmax1[3],const double bmin2[3],const double bmax2[3]); - - -// computes the rotation quaternion to go from unit-vector v0 to unit-vector v1 -void fm_rotationArc(const float v0[3],const float v1[3],float quat[4]); -void fm_rotationArc(const double v0[3],const double v1[3],double quat[4]); - -float fm_distancePointLineSegment(const float Point[3],const float LineStart[3],const float LineEnd[3],float intersection[3],LineSegmentType &type,float epsilon); -double fm_distancePointLineSegment(const double Point[3],const double LineStart[3],const double LineEnd[3],double intersection[3],LineSegmentType &type,double epsilon); - - -bool fm_colinear(const double p1[3],const double p2[3],const double p3[3],double epsilon=0.999); // true if these three points in a row are co-linear -bool fm_colinear(const float p1[3],const float p2[3],const float p3[3],float epsilon=0.999f); - -bool fm_colinear(const float a1[3],const float a2[3],const float b1[3],const float b2[3],float epsilon=0.999f); // true if these two line segments are co-linear. -bool fm_colinear(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double epsilon=0.999); // true if these two line segments are co-linear. - -enum IntersectResult -{ - IR_DONT_INTERSECT, - IR_DO_INTERSECT, - IR_COINCIDENT, - IR_PARALLEL, -}; - -IntersectResult fm_intersectLineSegments2d(const float a1[3], const float a2[3], const float b1[3], const float b2[3], float intersectionPoint[3]); -IntersectResult fm_intersectLineSegments2d(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double intersectionPoint[3]); - -IntersectResult fm_intersectLineSegments2dTime(const float a1[3], const float a2[3], const float b1[3], const float b2[3],float &t1,float &t2); -IntersectResult fm_intersectLineSegments2dTime(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double &t1,double &t2); - -// Plane-Triangle splitting - -enum PlaneTriResult -{ - PTR_ON_PLANE, - PTR_FRONT, - PTR_BACK, - PTR_SPLIT, -}; - -PlaneTriResult fm_planeTriIntersection(const float plane[4], // the plane equation in Ax+By+Cz+D format - const float *triangle, // the source triangle. - uint32_t tstride, // stride in bytes of the input and output *vertices* - float epsilon, // the co-planer epsilon value. - float *front, // the triangle in front of the - uint32_t &fcount, // number of vertices in the 'front' triangle - float *back, // the triangle in back of the plane - uint32_t &bcount); // the number of vertices in the 'back' triangle. - - -PlaneTriResult fm_planeTriIntersection(const double plane[4], // the plane equation in Ax+By+Cz+D format - const double *triangle, // the source triangle. - uint32_t tstride, // stride in bytes of the input and output *vertices* - double epsilon, // the co-planer epsilon value. - double *front, // the triangle in front of the - uint32_t &fcount, // number of vertices in the 'front' triangle - double *back, // the triangle in back of the plane - uint32_t &bcount); // the number of vertices in the 'back' triangle. - - -bool fm_intersectPointPlane(const float p1[3],const float p2[3],float *split,const float plane[4]); -bool fm_intersectPointPlane(const double p1[3],const double p2[3],double *split,const double plane[4]); - -PlaneTriResult fm_getSidePlane(const float p[3],const float plane[4],float epsilon); -PlaneTriResult fm_getSidePlane(const double p[3],const double plane[4],double epsilon); - - -void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float matrix[16],bool bruteForce=true); -void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double matrix[16],bool bruteForce=true); - -void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3],float quat[4],bool bruteForce=true); -void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3],double quat[4],bool bruteForce=true); - -void fm_computeBestFitABB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3]); -void fm_computeBestFitABB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3]); - - -//** Note, if the returned capsule height is less than zero, then you must represent it is a sphere of size radius. -void fm_computeBestFitCapsule(uint32_t vcount,const float *points,uint32_t pstride,float &radius,float &height,float matrix[16],bool bruteForce=true); -void fm_computeBestFitCapsule(uint32_t vcount,const double *points,uint32_t pstride,float &radius,float &height,double matrix[16],bool bruteForce=true); - - -void fm_planeToMatrix(const float plane[4],float matrix[16]); // convert a plane equation to a 4x4 rotation matrix. Reference vector is 0,1,0 -void fm_planeToQuat(const float plane[4],float quat[4],float pos[3]); // convert a plane equation to a quaternion and translation - -void fm_planeToMatrix(const double plane[4],double matrix[16]); // convert a plane equation to a 4x4 rotation matrix -void fm_planeToQuat(const double plane[4],double quat[4],double pos[3]); // convert a plane equation to a quaternion and translation - -inline void fm_doubleToFloat3(const double p[3],float t[3]) { t[0] = (float) p[0]; t[1] = (float)p[1]; t[2] = (float)p[2]; }; -inline void fm_floatToDouble3(const float p[3],double t[3]) { t[0] = (double)p[0]; t[1] = (double)p[1]; t[2] = (double)p[2]; }; - - -void fm_eulerMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) -void fm_eulerMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) - - -float fm_computeMeshVolume(const float *vertices,uint32_t tcount,const uint32_t *indices); -double fm_computeMeshVolume(const double *vertices,uint32_t tcount,const uint32_t *indices); - - -#define FM_DEFAULT_GRANULARITY 0.001f // 1 millimeter is the default granularity - -class fm_VertexIndex -{ -public: - virtual uint32_t getIndex(const float pos[3],bool &newPos) = 0; // get welded index for this float vector[3] - virtual uint32_t getIndex(const double pos[3],bool &newPos) = 0; // get welded index for this double vector[3] - virtual const float * getVerticesFloat(void) const = 0; - virtual const double * getVerticesDouble(void) const = 0; - virtual const float * getVertexFloat(uint32_t index) const = 0; - virtual const double * getVertexDouble(uint32_t index) const = 0; - virtual uint32_t getVcount(void) const = 0; - virtual bool isDouble(void) const = 0; - virtual bool saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) = 0; -}; - -fm_VertexIndex * fm_createVertexIndex(double granularity,bool snapToGrid); // create an indexed vertex system for doubles -fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid); // create an indexed vertext system for floats -void fm_releaseVertexIndex(fm_VertexIndex *vindex); - - -class fm_Triangulate -{ -public: - virtual const double * triangulate3d(uint32_t pcount, - const double *points, - uint32_t vstride, - uint32_t &tcount, - bool consolidate, - double epsilon) = 0; - - virtual const float * triangulate3d(uint32_t pcount, - const float *points, - uint32_t vstride, - uint32_t &tcount, - bool consolidate, - float epsilon) = 0; -}; - -fm_Triangulate * fm_createTriangulate(void); -void fm_releaseTriangulate(fm_Triangulate *t); - - -const float * fm_getPoint(const float *points,uint32_t pstride,uint32_t index); -const double * fm_getPoint(const double *points,uint32_t pstride,uint32_t index); - -bool fm_insideTriangle(float Ax, float Ay,float Bx, float By,float Cx, float Cy,float Px, float Py); -bool fm_insideTriangle(double Ax, double Ay,double Bx, double By,double Cx, double Cy,double Px, double Py); -float fm_areaPolygon2d(uint32_t pcount,const float *points,uint32_t pstride); -double fm_areaPolygon2d(uint32_t pcount,const double *points,uint32_t pstride); - -bool fm_pointInsidePolygon2d(uint32_t pcount,const float *points,uint32_t pstride,const float *point,uint32_t xindex=0,uint32_t yindex=1); -bool fm_pointInsidePolygon2d(uint32_t pcount,const double *points,uint32_t pstride,const double *point,uint32_t xindex=0,uint32_t yindex=1); - -uint32_t fm_consolidatePolygon(uint32_t pcount,const float *points,uint32_t pstride,float *dest,float epsilon=0.999999f); // collapses co-linear edges. -uint32_t fm_consolidatePolygon(uint32_t pcount,const double *points,uint32_t pstride,double *dest,double epsilon=0.999999); // collapses co-linear edges. - - -bool fm_computeSplitPlane(uint32_t vcount,const double *vertices,uint32_t tcount,const uint32_t *indices,double *plane); -bool fm_computeSplitPlane(uint32_t vcount,const float *vertices,uint32_t tcount,const uint32_t *indices,float *plane); - -void fm_nearestPointInTriangle(const float *pos,const float *p1,const float *p2,const float *p3,float *nearest); -void fm_nearestPointInTriangle(const double *pos,const double *p1,const double *p2,const double *p3,double *nearest); - -float fm_areaTriangle(const float *p1,const float *p2,const float *p3); -double fm_areaTriangle(const double *p1,const double *p2,const double *p3); - -void fm_subtract(const float *A,const float *B,float *diff); // compute A-B and store the result in 'diff' -void fm_subtract(const double *A,const double *B,double *diff); // compute A-B and store the result in 'diff' - -void fm_multiply(float *A,float scaler); -void fm_multiply(double *A,double scaler); - -void fm_add(const float *A,const float *B,float *sum); -void fm_add(const double *A,const double *B,double *sum); - -void fm_copy3(const float *source,float *dest); -void fm_copy3(const double *source,double *dest); - -// re-indexes an indexed triangle mesh but drops unused vertices. The output_indices can be the same pointer as the input indices. -// the output_vertices can point to the input vertices if you desire. The output_vertices buffer should be at least the same size -// is the input buffer. The routine returns the new vertex count after re-indexing. -uint32_t fm_copyUniqueVertices(uint32_t vcount,const float *input_vertices,float *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices); -uint32_t fm_copyUniqueVertices(uint32_t vcount,const double *input_vertices,double *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices); - -bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const float *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar! -bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const double *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar! - -bool fm_samePlane(const float p1[4],const float p2[4],float normalEpsilon=0.01f,float dEpsilon=0.001f,bool doubleSided=false); // returns true if these two plane equations are identical within an epsilon -bool fm_samePlane(const double p1[4],const double p2[4],double normalEpsilon=0.01,double dEpsilon=0.001,bool doubleSided=false); - -void fm_OBBtoAABB(const float obmin[3],const float obmax[3],const float matrix[16],float abmin[3],float abmax[3]); - -// a utility class that will tessellate a mesh. -class fm_Tesselate -{ -public: - virtual const uint32_t * tesselate(fm_VertexIndex *vindex,uint32_t tcount,const uint32_t *indices,float longEdge,uint32_t maxDepth,uint32_t &outcount) = 0; -}; - -fm_Tesselate * fm_createTesselate(void); -void fm_releaseTesselate(fm_Tesselate *t); - -void fm_computeMeanNormals(uint32_t vcount, // the number of vertices - const float *vertices, // the base address of the vertex position data. - uint32_t vstride, // the stride between position data. - float *normals, // the base address of the destination for mean vector normals - uint32_t nstride, // the stride between normals - uint32_t tcount, // the number of triangles - const uint32_t *indices); // the triangle indices - -void fm_computeMeanNormals(uint32_t vcount, // the number of vertices - const double *vertices, // the base address of the vertex position data. - uint32_t vstride, // the stride between position data. - double *normals, // the base address of the destination for mean vector normals - uint32_t nstride, // the stride between normals - uint32_t tcount, // the number of triangles - const uint32_t *indices); // the triangle indices - - -bool fm_isValidTriangle(const float *p1,const float *p2,const float *p3,float epsilon=0.00001f); -bool fm_isValidTriangle(const double *p1,const double *p2,const double *p3,double epsilon=0.00001f); - - -}; // end of namespace - -#endif diff --git a/src/VHACD_Lib/inc/btAlignedAllocator.h b/src/VHACD_Lib/inc/btAlignedAllocator.h deleted file mode 100644 index 11f6e12d..00000000 --- a/src/VHACD_Lib/inc/btAlignedAllocator.h +++ /dev/null @@ -1,104 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_ALIGNED_ALLOCATOR -#define BT_ALIGNED_ALLOCATOR - -///we probably replace this with our own aligned memory allocator -///so we replace _aligned_malloc and _aligned_free with our own -///that is better portable and more predictable - -#include "btScalar.h" -//#define BT_DEBUG_MEMORY_ALLOCATIONS 1 -#ifdef BT_DEBUG_MEMORY_ALLOCATIONS - -#define btAlignedAlloc(a, b) \ - btAlignedAllocInternal(a, b, __LINE__, __FILE__) - -#define btAlignedFree(ptr) \ - btAlignedFreeInternal(ptr, __LINE__, __FILE__) - -void* btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char* filename); - -void btAlignedFreeInternal(void* ptr, int32_t line, char* filename); - -#else -void* btAlignedAllocInternal(size_t size, int32_t alignment); -void btAlignedFreeInternal(void* ptr); - -#define btAlignedAlloc(size, alignment) btAlignedAllocInternal(size, alignment) -#define btAlignedFree(ptr) btAlignedFreeInternal(ptr) - -#endif -typedef int32_t size_type; - -typedef void*(btAlignedAllocFunc)(size_t size, int32_t alignment); -typedef void(btAlignedFreeFunc)(void* memblock); -typedef void*(btAllocFunc)(size_t size); -typedef void(btFreeFunc)(void* memblock); - -///The developer can let all Bullet memory allocations go through a custom memory allocator, using btAlignedAllocSetCustom -void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc); -///If the developer has already an custom aligned allocator, then btAlignedAllocSetCustomAligned can be used. The default aligned allocator pre-allocates extra memory using the non-aligned allocator, and instruments it. -void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc); - -///The btAlignedAllocator is a portable class for aligned memory allocations. -///Default implementations for unaligned and aligned allocations can be overridden by a custom allocator using btAlignedAllocSetCustom and btAlignedAllocSetCustomAligned. -template -class btAlignedAllocator { - - typedef btAlignedAllocator self_type; - -public: - //just going down a list: - btAlignedAllocator() {} - /* - btAlignedAllocator( const self_type & ) {} - */ - - template - btAlignedAllocator(const btAlignedAllocator&) {} - - typedef const T* const_pointer; - typedef const T& const_reference; - typedef T* pointer; - typedef T& reference; - typedef T value_type; - - pointer address(reference ref) const { return &ref; } - const_pointer address(const_reference ref) const { return &ref; } - pointer allocate(size_type n, const_pointer* hint = 0) - { - (void)hint; - return reinterpret_cast(btAlignedAlloc(sizeof(value_type) * n, Alignment)); - } - void construct(pointer ptr, const value_type& value) { new (ptr) value_type(value); } - void deallocate(pointer ptr) - { - btAlignedFree(reinterpret_cast(ptr)); - } - void destroy(pointer ptr) { ptr->~value_type(); } - - template - struct rebind { - typedef btAlignedAllocator other; - }; - template - self_type& operator=(const btAlignedAllocator&) { return *this; } - - friend bool operator==(const self_type&, const self_type&) { return true; } -}; - -#endif //BT_ALIGNED_ALLOCATOR diff --git a/src/VHACD_Lib/inc/btAlignedObjectArray.h b/src/VHACD_Lib/inc/btAlignedObjectArray.h deleted file mode 100644 index e6620adf..00000000 --- a/src/VHACD_Lib/inc/btAlignedObjectArray.h +++ /dev/null @@ -1,448 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_OBJECT_ARRAY__ -#define BT_OBJECT_ARRAY__ - -#include "btAlignedAllocator.h" -#include "btScalar.h" // has definitions like SIMD_FORCE_INLINE - -///If the platform doesn't support placement new, you can disable BT_USE_PLACEMENT_NEW -///then the btAlignedObjectArray doesn't support objects with virtual methods, and non-trivial constructors/destructors -///You can enable BT_USE_MEMCPY, then swapping elements in the array will use memcpy instead of operator= -///see discussion here: http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1231 and -///http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1240 - -#define BT_USE_PLACEMENT_NEW 1 -//#define BT_USE_MEMCPY 1 //disable, because it is cumbersome to find out for each platform where memcpy is defined. It can be in or or otherwise... -#define BT_ALLOW_ARRAY_COPY_OPERATOR // enabling this can accidently perform deep copies of data if you are not careful - -#ifdef BT_USE_MEMCPY -#include -#include -#endif //BT_USE_MEMCPY - -#ifdef BT_USE_PLACEMENT_NEW -#include //for placement new -#endif //BT_USE_PLACEMENT_NEW - -///The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods -///It is developed to replace stl::vector to avoid portability issues, including STL alignment issues to add SIMD/SSE data -template -//template -class btAlignedObjectArray { - btAlignedAllocator m_allocator; - - int32_t m_size; - int32_t m_capacity; - T* m_data; - //PCK: added this line - bool m_ownsMemory; - -#ifdef BT_ALLOW_ARRAY_COPY_OPERATOR -public: - SIMD_FORCE_INLINE btAlignedObjectArray& operator=(const btAlignedObjectArray& other) - { - copyFromArray(other); - return *this; - } -#else //BT_ALLOW_ARRAY_COPY_OPERATOR -private: - SIMD_FORCE_INLINE btAlignedObjectArray& operator=(const btAlignedObjectArray& other); -#endif //BT_ALLOW_ARRAY_COPY_OPERATOR - -protected: - SIMD_FORCE_INLINE int32_t allocSize(int32_t size) - { - return (size ? size * 2 : 1); - } - SIMD_FORCE_INLINE void copy(int32_t start, int32_t end, T* dest) const - { - int32_t i; - for (i = start; i < end; ++i) -#ifdef BT_USE_PLACEMENT_NEW - new (&dest[i]) T(m_data[i]); -#else - dest[i] = m_data[i]; -#endif //BT_USE_PLACEMENT_NEW - } - - SIMD_FORCE_INLINE void init() - { - //PCK: added this line - m_ownsMemory = true; - m_data = 0; - m_size = 0; - m_capacity = 0; - } - SIMD_FORCE_INLINE void destroy(int32_t first, int32_t last) - { - int32_t i; - for (i = first; i < last; i++) { - m_data[i].~T(); - } - } - - SIMD_FORCE_INLINE void* allocate(int32_t size) - { - if (size) - return m_allocator.allocate(size); - return 0; - } - - SIMD_FORCE_INLINE void deallocate() - { - if (m_data) { - //PCK: enclosed the deallocation in this block - if (m_ownsMemory) { - m_allocator.deallocate(m_data); - } - m_data = 0; - } - } - -public: - btAlignedObjectArray() - { - init(); - } - - ~btAlignedObjectArray() - { - clear(); - } - - ///Generally it is best to avoid using the copy constructor of an btAlignedObjectArray, and use a (const) reference to the array instead. - btAlignedObjectArray(const btAlignedObjectArray& otherArray) - { - init(); - - int32_t otherSize = otherArray.size(); - resize(otherSize); - otherArray.copy(0, otherSize, m_data); - } - - /// return the number of elements in the array - SIMD_FORCE_INLINE int32_t size() const - { - return m_size; - } - - SIMD_FORCE_INLINE const T& at(int32_t n) const - { - btAssert(n >= 0); - btAssert(n < size()); - return m_data[n]; - } - - SIMD_FORCE_INLINE T& at(int32_t n) - { - btAssert(n >= 0); - btAssert(n < size()); - return m_data[n]; - } - - SIMD_FORCE_INLINE const T& operator[](int32_t n) const - { - btAssert(n >= 0); - btAssert(n < size()); - return m_data[n]; - } - - SIMD_FORCE_INLINE T& operator[](int32_t n) - { - btAssert(n >= 0); - btAssert(n < size()); - return m_data[n]; - } - - ///clear the array, deallocated memory. Generally it is better to use array.resize(0), to reduce performance overhead of run-time memory (de)allocations. - SIMD_FORCE_INLINE void clear() - { - destroy(0, size()); - - deallocate(); - - init(); - } - - SIMD_FORCE_INLINE void pop_back() - { - btAssert(m_size > 0); - m_size--; - m_data[m_size].~T(); - } - - ///resize changes the number of elements in the array. If the new size is larger, the new elements will be constructed using the optional second argument. - ///when the new number of elements is smaller, the destructor will be called, but memory will not be freed, to reduce performance overhead of run-time memory (de)allocations. - SIMD_FORCE_INLINE void resize(int32_t newsize, const T& fillData = T()) - { - int32_t curSize = size(); - - if (newsize < curSize) { - for (int32_t i = newsize; i < curSize; i++) { - m_data[i].~T(); - } - } - else { - if (newsize > size()) { - reserve(newsize); - } -#ifdef BT_USE_PLACEMENT_NEW - for (int32_t i = curSize; i < newsize; i++) { - new (&m_data[i]) T(fillData); - } -#endif //BT_USE_PLACEMENT_NEW - } - - m_size = newsize; - } - - SIMD_FORCE_INLINE T& expandNonInitializing() - { - int32_t sz = size(); - if (sz == capacity()) { - reserve(allocSize(size())); - } - m_size++; - - return m_data[sz]; - } - - SIMD_FORCE_INLINE T& expand(const T& fillValue = T()) - { - int32_t sz = size(); - if (sz == capacity()) { - reserve(allocSize(size())); - } - m_size++; -#ifdef BT_USE_PLACEMENT_NEW - new (&m_data[sz]) T(fillValue); //use the in-place new (not really allocating heap memory) -#endif - - return m_data[sz]; - } - - SIMD_FORCE_INLINE void push_back(const T& _Val) - { - int32_t sz = size(); - if (sz == capacity()) { - reserve(allocSize(size())); - } - -#ifdef BT_USE_PLACEMENT_NEW - new (&m_data[m_size]) T(_Val); -#else - m_data[size()] = _Val; -#endif //BT_USE_PLACEMENT_NEW - - m_size++; - } - - /// return the pre-allocated (reserved) elements, this is at least as large as the total number of elements,see size() and reserve() - SIMD_FORCE_INLINE int32_t capacity() const - { - return m_capacity; - } - - SIMD_FORCE_INLINE void reserve(int32_t _Count) - { // determine new minimum length of allocated storage - if (capacity() < _Count) { // not enough room, reallocate - T* s = (T*)allocate(_Count); - - copy(0, size(), s); - - destroy(0, size()); - - deallocate(); - - //PCK: added this line - m_ownsMemory = true; - - m_data = s; - - m_capacity = _Count; - } - } - - class less { - public: - bool operator()(const T& a, const T& b) - { - return (a < b); - } - }; - - template - void quickSortInternal(const L& CompareFunc, int32_t lo, int32_t hi) - { - // lo is the lower index, hi is the upper index - // of the region of array a that is to be sorted - int32_t i = lo, j = hi; - T x = m_data[(lo + hi) / 2]; - - // partition - do { - while (CompareFunc(m_data[i], x)) - i++; - while (CompareFunc(x, m_data[j])) - j--; - if (i <= j) { - swap(i, j); - i++; - j--; - } - } while (i <= j); - - // recursion - if (lo < j) - quickSortInternal(CompareFunc, lo, j); - if (i < hi) - quickSortInternal(CompareFunc, i, hi); - } - - template - void quickSort(const L& CompareFunc) - { - //don't sort 0 or 1 elements - if (size() > 1) { - quickSortInternal(CompareFunc, 0, size() - 1); - } - } - - ///heap sort from http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/ - template - void downHeap(T* pArr, int32_t k, int32_t n, const L& CompareFunc) - { - /* PRE: a[k+1..N] is a heap */ - /* POST: a[k..N] is a heap */ - - T temp = pArr[k - 1]; - /* k has child(s) */ - while (k <= n / 2) { - int32_t child = 2 * k; - - if ((child < n) && CompareFunc(pArr[child - 1], pArr[child])) { - child++; - } - /* pick larger child */ - if (CompareFunc(temp, pArr[child - 1])) { - /* move child up */ - pArr[k - 1] = pArr[child - 1]; - k = child; - } - else { - break; - } - } - pArr[k - 1] = temp; - } /*downHeap*/ - - void swap(int32_t index0, int32_t index1) - { -#ifdef BT_USE_MEMCPY - char temp[sizeof(T)]; - memcpy(temp, &m_data[index0], sizeof(T)); - memcpy(&m_data[index0], &m_data[index1], sizeof(T)); - memcpy(&m_data[index1], temp, sizeof(T)); -#else - T temp = m_data[index0]; - m_data[index0] = m_data[index1]; - m_data[index1] = temp; -#endif //BT_USE_PLACEMENT_NEW - } - - template - void heapSort(const L& CompareFunc) - { - /* sort a[0..N-1], N.B. 0 to N-1 */ - int32_t k; - int32_t n = m_size; - for (k = n / 2; k > 0; k--) { - downHeap(m_data, k, n, CompareFunc); - } - - /* a[1..N] is now a heap */ - while (n >= 1) { - swap(0, n - 1); /* largest of a[0..n-1] */ - - n = n - 1; - /* restore a[1..i-1] heap */ - downHeap(m_data, 1, n, CompareFunc); - } - } - - ///non-recursive binary search, assumes sorted array - int32_t findBinarySearch(const T& key) const - { - int32_t first = 0; - int32_t last = size() - 1; - - //assume sorted array - while (first <= last) { - int32_t mid = (first + last) / 2; // compute mid point. - if (key > m_data[mid]) - first = mid + 1; // repeat search in top half. - else if (key < m_data[mid]) - last = mid - 1; // repeat search in bottom half. - else - return mid; // found it. return position ///// - } - return size(); // failed to find key - } - - int32_t findLinearSearch(const T& key) const - { - int32_t index = size(); - int32_t i; - - for (i = 0; i < size(); i++) { - if (m_data[i] == key) { - index = i; - break; - } - } - return index; - } - - void remove(const T& key) - { - - int32_t findIndex = findLinearSearch(key); - if (findIndex < size()) { - swap(findIndex, size() - 1); - pop_back(); - } - } - - //PCK: whole function - void initializeFromBuffer(void* buffer, int32_t size, int32_t capacity) - { - clear(); - m_ownsMemory = false; - m_data = (T*)buffer; - m_size = size; - m_capacity = capacity; - } - - void copyFromArray(const btAlignedObjectArray& otherArray) - { - int32_t otherSize = otherArray.size(); - resize(otherSize); - otherArray.copy(0, otherSize, m_data); - } -}; - -#endif //BT_OBJECT_ARRAY__ diff --git a/src/VHACD_Lib/inc/btConvexHullComputer.h b/src/VHACD_Lib/inc/btConvexHullComputer.h deleted file mode 100644 index 3c5075c2..00000000 --- a/src/VHACD_Lib/inc/btConvexHullComputer.h +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright (c) 2011 Ole Kniemeyer, MAXON, www.maxon.net - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_CONVEX_HULL_COMPUTER_H -#define BT_CONVEX_HULL_COMPUTER_H - -#include "btAlignedObjectArray.h" -#include "btVector3.h" - -/// Convex hull implementation based on Preparata and Hong -/// See http://code.google.com/p/bullet/issues/detail?id=275 -/// Ole Kniemeyer, MAXON Computer GmbH -class btConvexHullComputer { -private: - btScalar compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp); - -public: - class Edge { - private: - int32_t next; - int32_t reverse; - int32_t targetVertex; - - friend class btConvexHullComputer; - - public: - int32_t getSourceVertex() const - { - return (this + reverse)->targetVertex; - } - - int32_t getTargetVertex() const - { - return targetVertex; - } - - const Edge* getNextEdgeOfVertex() const // clockwise list of all edges of a vertex - { - return this + next; - } - - const Edge* getNextEdgeOfFace() const // counter-clockwise list of all edges of a face - { - return (this + reverse)->getNextEdgeOfVertex(); - } - - const Edge* getReverseEdge() const - { - return this + reverse; - } - }; - - // Vertices of the output hull - btAlignedObjectArray vertices; - - // Edges of the output hull - btAlignedObjectArray edges; - - // Faces of the convex hull. Each entry is an index into the "edges" array pointing to an edge of the face. Faces are planar n-gons - btAlignedObjectArray faces; - - /* - Compute convex hull of "count" vertices stored in "coords". "stride" is the difference in bytes - between the addresses of consecutive vertices. If "shrink" is positive, the convex hull is shrunken - by that amount (each face is moved by "shrink" length units towards the center along its normal). - If "shrinkClamp" is positive, "shrink" is clamped to not exceed "shrinkClamp * innerRadius", where "innerRadius" - is the minimum distance of a face to the center of the convex hull. - - The returned value is the amount by which the hull has been shrunken. If it is negative, the amount was so large - that the resulting convex hull is empty. - - The output convex hull can be found in the member variables "vertices", "edges", "faces". - */ - btScalar compute(const float* coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) - { - return compute(coords, false, stride, count, shrink, shrinkClamp); - } - - // same as above, but double precision - btScalar compute(const double* coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) - { - return compute(coords, true, stride, count, shrink, shrinkClamp); - } -}; - -#endif //BT_CONVEX_HULL_COMPUTER_H diff --git a/src/VHACD_Lib/inc/btMinMax.h b/src/VHACD_Lib/inc/btMinMax.h deleted file mode 100644 index 40b0ceb6..00000000 --- a/src/VHACD_Lib/inc/btMinMax.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_GEN_MINMAX_H -#define BT_GEN_MINMAX_H - -#include "btScalar.h" - -template -SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b) -{ - return a < b ? a : b; -} - -template -SIMD_FORCE_INLINE const T& btMax(const T& a, const T& b) -{ - return a > b ? a : b; -} - -template -SIMD_FORCE_INLINE const T& btClamped(const T& a, const T& lb, const T& ub) -{ - return a < lb ? lb : (ub < a ? ub : a); -} - -template -SIMD_FORCE_INLINE void btSetMin(T& a, const T& b) -{ - if (b < a) { - a = b; - } -} - -template -SIMD_FORCE_INLINE void btSetMax(T& a, const T& b) -{ - if (a < b) { - a = b; - } -} - -template -SIMD_FORCE_INLINE void btClamp(T& a, const T& lb, const T& ub) -{ - if (a < lb) { - a = lb; - } - else if (ub < a) { - a = ub; - } -} - -#endif //BT_GEN_MINMAX_H diff --git a/src/VHACD_Lib/inc/btScalar.h b/src/VHACD_Lib/inc/btScalar.h deleted file mode 100644 index b814474b..00000000 --- a/src/VHACD_Lib/inc/btScalar.h +++ /dev/null @@ -1,533 +0,0 @@ -/* -Copyright (c) 2003-2009 Erwin Coumans http://bullet.googlecode.com - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_SCALAR_H -#define BT_SCALAR_H - -#ifdef BT_MANAGED_CODE -//Aligned data types not supported in managed code -#pragma unmanaged -#endif - -#include -#include -#include //size_t for MSVC 6.0 -#include - -/* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ -#define BT_BULLET_VERSION 279 - -inline int32_t btGetVersion() -{ - return BT_BULLET_VERSION; -} - -#if defined(DEBUG) || defined(_DEBUG) -#define BT_DEBUG -#endif - -#ifdef _WIN32 - -#if defined(__MINGW32__) || defined(__CYGWIN__) || (defined(_MSC_VER) && _MSC_VER < 1300) - -#define SIMD_FORCE_INLINE inline -#define ATTRIBUTE_ALIGNED16(a) a -#define ATTRIBUTE_ALIGNED64(a) a -#define ATTRIBUTE_ALIGNED128(a) a -#else -//#define BT_HAS_ALIGNED_ALLOCATOR -#pragma warning(disable : 4324) // disable padding warning -// #pragma warning(disable:4530) // Disable the exception disable but used in MSCV Stl warning. -// #pragma warning(disable:4996) //Turn off warnings about deprecated C routines -// #pragma warning(disable:4786) // Disable the "debug name too long" warning - -#define SIMD_FORCE_INLINE __forceinline -#define ATTRIBUTE_ALIGNED16(a) __declspec(align(16)) a -#define ATTRIBUTE_ALIGNED64(a) __declspec(align(64)) a -#define ATTRIBUTE_ALIGNED128(a) __declspec(align(128)) a -#ifdef _XBOX -#define BT_USE_VMX128 - -#include -#define BT_HAVE_NATIVE_FSEL -#define btFsel(a, b, c) __fsel((a), (b), (c)) -#else - -#if (defined(_WIN32) && (_MSC_VER) && _MSC_VER >= 1400) && (!defined(BT_USE_DOUBLE_PRECISION)) -#define BT_USE_SSE -#include -#endif - -#endif //_XBOX - -#endif //__MINGW32__ - -#include -#ifdef BT_DEBUG -#define btAssert assert -#else -#define btAssert(x) -#endif -//btFullAssert is optional, slows down a lot -#define btFullAssert(x) - -#define btLikely(_c) _c -#define btUnlikely(_c) _c - -#else - -#if defined(__CELLOS_LV2__) -#define SIMD_FORCE_INLINE inline __attribute__((always_inline)) -#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) -#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) -#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) -#ifndef assert -#include -#endif -#ifdef BT_DEBUG -#ifdef __SPU__ -#include -#define printf spu_printf -#define btAssert(x) \ - { \ - if (!(x)) { \ - printf("Assert " __FILE__ ":%u (" #x ")\n", __LINE__); \ - spu_hcmpeq(0, 0); \ - } \ - } -#else -#define btAssert assert -#endif - -#else -#define btAssert(x) -#endif -//btFullAssert is optional, slows down a lot -#define btFullAssert(x) - -#define btLikely(_c) _c -#define btUnlikely(_c) _c - -#else - -#ifdef USE_LIBSPE2 - -#define SIMD_FORCE_INLINE __inline -#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) -#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) -#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) -#ifndef assert -#include -#endif -#ifdef BT_DEBUG -#define btAssert assert -#else -#define btAssert(x) -#endif -//btFullAssert is optional, slows down a lot -#define btFullAssert(x) - -#define btLikely(_c) __builtin_expect((_c), 1) -#define btUnlikely(_c) __builtin_expect((_c), 0) - -#else -//non-windows systems - -#if (defined(__APPLE__) && defined(__i386__) && (!defined(BT_USE_DOUBLE_PRECISION))) -#define BT_USE_SSE -#include - -#define SIMD_FORCE_INLINE inline -///@todo: check out alignment methods for other platforms/compilers -#define ATTRIBUTE_ALIGNED16(a) a __attribute__((aligned(16))) -#define ATTRIBUTE_ALIGNED64(a) a __attribute__((aligned(64))) -#define ATTRIBUTE_ALIGNED128(a) a __attribute__((aligned(128))) -#ifndef assert -#include -#endif - -#if defined(DEBUG) || defined(_DEBUG) -#define btAssert assert -#else -#define btAssert(x) -#endif - -//btFullAssert is optional, slows down a lot -#define btFullAssert(x) -#define btLikely(_c) _c -#define btUnlikely(_c) _c - -#else - -#define SIMD_FORCE_INLINE inline -///@todo: check out alignment methods for other platforms/compilers -///#define ATTRIBUTE_ALIGNED16(a) a __attribute__ ((aligned (16))) -///#define ATTRIBUTE_ALIGNED64(a) a __attribute__ ((aligned (64))) -///#define ATTRIBUTE_ALIGNED128(a) a __attribute__ ((aligned (128))) -#define ATTRIBUTE_ALIGNED16(a) a -#define ATTRIBUTE_ALIGNED64(a) a -#define ATTRIBUTE_ALIGNED128(a) a -#ifndef assert -#include -#endif - -#if defined(DEBUG) || defined(_DEBUG) -#define btAssert assert -#else -#define btAssert(x) -#endif - -//btFullAssert is optional, slows down a lot -#define btFullAssert(x) -#define btLikely(_c) _c -#define btUnlikely(_c) _c -#endif //__APPLE__ - -#endif // LIBSPE2 - -#endif //__CELLOS_LV2__ -#endif - -///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision. -#if defined(BT_USE_DOUBLE_PRECISION) -typedef double btScalar; -//this number could be bigger in double precision -#define BT_LARGE_FLOAT 1e30 -#else -typedef float btScalar; -//keep BT_LARGE_FLOAT*BT_LARGE_FLOAT < FLT_MAX -#define BT_LARGE_FLOAT 1e18f -#endif - -#define BT_DECLARE_ALIGNED_ALLOCATOR() \ - SIMD_FORCE_INLINE void* operator new(size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ - SIMD_FORCE_INLINE void operator delete(void* ptr) { btAlignedFree(ptr); } \ - SIMD_FORCE_INLINE void* operator new(size_t, void* ptr) { return ptr; } \ - SIMD_FORCE_INLINE void operator delete(void*, void*) {} \ - SIMD_FORCE_INLINE void* operator new[](size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ - SIMD_FORCE_INLINE void operator delete[](void* ptr) { btAlignedFree(ptr); } \ - SIMD_FORCE_INLINE void* operator new[](size_t, void* ptr) { return ptr; } \ - SIMD_FORCE_INLINE void operator delete[](void*, void*) {} - -#if defined(BT_USE_DOUBLE_PRECISION) || defined(BT_FORCE_DOUBLE_FUNCTIONS) - -SIMD_FORCE_INLINE btScalar btSqrt(btScalar x) -{ - return sqrt(x); -} -SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabs(x); } -SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cos(x); } -SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sin(x); } -SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tan(x); } -SIMD_FORCE_INLINE btScalar btAcos(btScalar x) -{ - if (x < btScalar(-1)) - x = btScalar(-1); - if (x > btScalar(1)) - x = btScalar(1); - return acos(x); -} -SIMD_FORCE_INLINE btScalar btAsin(btScalar x) -{ - if (x < btScalar(-1)) - x = btScalar(-1); - if (x > btScalar(1)) - x = btScalar(1); - return asin(x); -} -SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atan(x); } -SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2(x, y); } -SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return exp(x); } -SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return log(x); } -SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return pow(x, y); } -SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmod(x, y); } - -#else - -SIMD_FORCE_INLINE btScalar btSqrt(btScalar y) -{ -#ifdef USE_APPROXIMATION - double x, z, tempf; - unsigned long* tfptr = ((unsigned long*)&tempf) + 1; - - tempf = y; - *tfptr = (0xbfcdd90a - *tfptr) >> 1; /* estimate of 1/sqrt(y) */ - x = tempf; - z = y * btScalar(0.5); - x = (btScalar(1.5) * x) - (x * x) * (x * z); /* iteration formula */ - x = (btScalar(1.5) * x) - (x * x) * (x * z); - x = (btScalar(1.5) * x) - (x * x) * (x * z); - x = (btScalar(1.5) * x) - (x * x) * (x * z); - x = (btScalar(1.5) * x) - (x * x) * (x * z); - return x * y; -#else - return sqrtf(y); -#endif -} -SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabsf(x); } -SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cosf(x); } -SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sinf(x); } -SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tanf(x); } -SIMD_FORCE_INLINE btScalar btAcos(btScalar x) -{ - if (x < btScalar(-1)) - x = btScalar(-1); - if (x > btScalar(1)) - x = btScalar(1); - return acosf(x); -} -SIMD_FORCE_INLINE btScalar btAsin(btScalar x) -{ - if (x < btScalar(-1)) - x = btScalar(-1); - if (x > btScalar(1)) - x = btScalar(1); - return asinf(x); -} -SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atanf(x); } -SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2f(x, y); } -SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return expf(x); } -SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return logf(x); } -SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return powf(x, y); } -SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmodf(x, y); } - -#endif - -#define SIMD_2_PI btScalar(6.283185307179586232) -#define SIMD_PI (SIMD_2_PI * btScalar(0.5)) -#define SIMD_HALF_PI (SIMD_2_PI * btScalar(0.25)) -#define SIMD_RADS_PER_DEG (SIMD_2_PI / btScalar(360.0)) -#define SIMD_DEGS_PER_RAD (btScalar(360.0) / SIMD_2_PI) -#define SIMDSQRT12 btScalar(0.7071067811865475244008443621048490) - -#define btRecipSqrt(x) ((btScalar)(btScalar(1.0) / btSqrt(btScalar(x)))) /* reciprocal square root */ - -#ifdef BT_USE_DOUBLE_PRECISION -#define SIMD_EPSILON DBL_EPSILON -#define SIMD_INFINITY DBL_MAX -#else -#define SIMD_EPSILON FLT_EPSILON -#define SIMD_INFINITY FLT_MAX -#endif - -SIMD_FORCE_INLINE btScalar btAtan2Fast(btScalar y, btScalar x) -{ - btScalar coeff_1 = SIMD_PI / 4.0f; - btScalar coeff_2 = 3.0f * coeff_1; - btScalar abs_y = btFabs(y); - btScalar angle; - if (x >= 0.0f) { - btScalar r = (x - abs_y) / (x + abs_y); - angle = coeff_1 - coeff_1 * r; - } - else { - btScalar r = (x + abs_y) / (abs_y - x); - angle = coeff_2 - coeff_1 * r; - } - return (y < 0.0f) ? -angle : angle; -} - -SIMD_FORCE_INLINE bool btFuzzyZero(btScalar x) { return btFabs(x) < SIMD_EPSILON; } - -SIMD_FORCE_INLINE bool btEqual(btScalar a, btScalar eps) -{ - return (((a) <= eps) && !((a) < -eps)); -} -SIMD_FORCE_INLINE bool btGreaterEqual(btScalar a, btScalar eps) -{ - return (!((a) <= eps)); -} - -SIMD_FORCE_INLINE int32_t btIsNegative(btScalar x) -{ - return x < btScalar(0.0) ? 1 : 0; -} - -SIMD_FORCE_INLINE btScalar btRadians(btScalar x) { return x * SIMD_RADS_PER_DEG; } -SIMD_FORCE_INLINE btScalar btDegrees(btScalar x) { return x * SIMD_DEGS_PER_RAD; } - -#define BT_DECLARE_HANDLE(name) \ - typedef struct name##__ { \ - int32_t unused; \ - } * name - -#ifndef btFsel -SIMD_FORCE_INLINE btScalar btFsel(btScalar a, btScalar b, btScalar c) -{ - return a >= 0 ? b : c; -} -#endif -#define btFsels(a, b, c) (btScalar) btFsel(a, b, c) - -SIMD_FORCE_INLINE bool btMachineIsLittleEndian() -{ - long int i = 1; - const char* p = (const char*)&i; - if (p[0] == 1) // Lowest address contains the least significant byte - return true; - else - return false; -} - -///btSelect avoids branches, which makes performance much better for consoles like Playstation 3 and XBox 360 -///Thanks Phil Knight. See also http://www.cellperformance.com/articles/2006/04/more_techniques_for_eliminatin_1.html -SIMD_FORCE_INLINE unsigned btSelect(unsigned condition, unsigned valueIfConditionNonZero, unsigned valueIfConditionZero) -{ - // Set testNz to 0xFFFFFFFF if condition is nonzero, 0x00000000 if condition is zero - // Rely on positive value or'ed with its negative having sign bit on - // and zero value or'ed with its negative (which is still zero) having sign bit off - // Use arithmetic shift right, shifting the sign bit through all 32 bits - unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31); - unsigned testEqz = ~testNz; - return ((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); -} -SIMD_FORCE_INLINE int32_t btSelect(unsigned condition, int32_t valueIfConditionNonZero, int32_t valueIfConditionZero) -{ - unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31); - unsigned testEqz = ~testNz; - return static_cast((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); -} -SIMD_FORCE_INLINE float btSelect(unsigned condition, float valueIfConditionNonZero, float valueIfConditionZero) -{ -#ifdef BT_HAVE_NATIVE_FSEL - return (float)btFsel((btScalar)condition - btScalar(1.0f), valueIfConditionNonZero, valueIfConditionZero); -#else - return (condition != 0) ? valueIfConditionNonZero : valueIfConditionZero; -#endif -} - -template -SIMD_FORCE_INLINE void btSwap(T& a, T& b) -{ - T tmp = a; - a = b; - b = tmp; -} - -//PCK: endian swapping functions -SIMD_FORCE_INLINE unsigned btSwapEndian(unsigned val) -{ - return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); -} - -SIMD_FORCE_INLINE unsigned short btSwapEndian(unsigned short val) -{ - return static_cast(((val & 0xff00) >> 8) | ((val & 0x00ff) << 8)); -} - -SIMD_FORCE_INLINE unsigned btSwapEndian(int32_t val) -{ - return btSwapEndian((unsigned)val); -} - -SIMD_FORCE_INLINE unsigned short btSwapEndian(short val) -{ - return btSwapEndian((unsigned short)val); -} - -///btSwapFloat uses using char pointers to swap the endianness -////btSwapFloat/btSwapDouble will NOT return a float, because the machine might 'correct' invalid floating point values -///Not all values of sign/exponent/mantissa are valid floating point numbers according to IEEE 754. -///When a floating point unit is faced with an invalid value, it may actually change the value, or worse, throw an exception. -///In most systems, running user mode code, you wouldn't get an exception, but instead the hardware/os/runtime will 'fix' the number for you. -///so instead of returning a float/double, we return integer/long long integer -SIMD_FORCE_INLINE uint32_t btSwapEndianFloat(float d) -{ - uint32_t a = 0; - unsigned char* dst = (unsigned char*)&a; - unsigned char* src = (unsigned char*)&d; - - dst[0] = src[3]; - dst[1] = src[2]; - dst[2] = src[1]; - dst[3] = src[0]; - return a; -} - -// unswap using char pointers -SIMD_FORCE_INLINE float btUnswapEndianFloat(uint32_t a) -{ - float d = 0.0f; - unsigned char* src = (unsigned char*)&a; - unsigned char* dst = (unsigned char*)&d; - - dst[0] = src[3]; - dst[1] = src[2]; - dst[2] = src[1]; - dst[3] = src[0]; - - return d; -} - -// swap using char pointers -SIMD_FORCE_INLINE void btSwapEndianDouble(double d, unsigned char* dst) -{ - unsigned char* src = (unsigned char*)&d; - - dst[0] = src[7]; - dst[1] = src[6]; - dst[2] = src[5]; - dst[3] = src[4]; - dst[4] = src[3]; - dst[5] = src[2]; - dst[6] = src[1]; - dst[7] = src[0]; -} - -// unswap using char pointers -SIMD_FORCE_INLINE double btUnswapEndianDouble(const unsigned char* src) -{ - double d = 0.0; - unsigned char* dst = (unsigned char*)&d; - - dst[0] = src[7]; - dst[1] = src[6]; - dst[2] = src[5]; - dst[3] = src[4]; - dst[4] = src[3]; - dst[5] = src[2]; - dst[6] = src[1]; - dst[7] = src[0]; - - return d; -} - -// returns normalized value in range [-SIMD_PI, SIMD_PI] -SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians) -{ - angleInRadians = btFmod(angleInRadians, SIMD_2_PI); - if (angleInRadians < -SIMD_PI) { - return angleInRadians + SIMD_2_PI; - } - else if (angleInRadians > SIMD_PI) { - return angleInRadians - SIMD_2_PI; - } - else { - return angleInRadians; - } -} - -///rudimentary class to provide type info -struct btTypedObject { - btTypedObject(int32_t objectType) - : m_objectType(objectType) - { - } - int32_t m_objectType; - inline int32_t getObjectType() const - { - return m_objectType; - } -}; -#endif //BT_SCALAR_H diff --git a/src/VHACD_Lib/inc/btVector3.h b/src/VHACD_Lib/inc/btVector3.h deleted file mode 100644 index 0f2fefbb..00000000 --- a/src/VHACD_Lib/inc/btVector3.h +++ /dev/null @@ -1,715 +0,0 @@ -/* -Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_VECTOR3_H -#define BT_VECTOR3_H - -#include "btMinMax.h" -#include "btScalar.h" - -#ifdef BT_USE_DOUBLE_PRECISION -#define btVector3Data btVector3DoubleData -#define btVector3DataName "btVector3DoubleData" -#else -#define btVector3Data btVector3FloatData -#define btVector3DataName "btVector3FloatData" -#endif //BT_USE_DOUBLE_PRECISION - -/**@brief btVector3 can be used to represent 3D points and vectors. - * It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user - * Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers - */ -ATTRIBUTE_ALIGNED16(class) -btVector3 -{ -public: -#if defined(__SPU__) && defined(__CELLOS_LV2__) - btScalar m_floats[4]; - -public: - SIMD_FORCE_INLINE const vec_float4& get128() const - { - return *((const vec_float4*)&m_floats[0]); - } - -public: -#else //__CELLOS_LV2__ __SPU__ -#ifdef BT_USE_SSE // _WIN32 - union { - __m128 mVec128; - btScalar m_floats[4]; - }; - SIMD_FORCE_INLINE __m128 get128() const - { - return mVec128; - } - SIMD_FORCE_INLINE void set128(__m128 v128) - { - mVec128 = v128; - } -#else - btScalar m_floats[4]; -#endif -#endif //__CELLOS_LV2__ __SPU__ - -public: - /**@brief No initialization constructor */ - SIMD_FORCE_INLINE btVector3() {} - - /**@brief Constructor from scalars - * @param x X value - * @param y Y value - * @param z Z value - */ - SIMD_FORCE_INLINE btVector3(const btScalar& x, const btScalar& y, const btScalar& z) - { - m_floats[0] = x; - m_floats[1] = y; - m_floats[2] = z; - m_floats[3] = btScalar(0.); - } - - /**@brief Add a vector to this one - * @param The vector to add to this one */ - SIMD_FORCE_INLINE btVector3& operator+=(const btVector3& v) - { - - m_floats[0] += v.m_floats[0]; - m_floats[1] += v.m_floats[1]; - m_floats[2] += v.m_floats[2]; - return *this; - } - - /**@brief Subtract a vector from this one - * @param The vector to subtract */ - SIMD_FORCE_INLINE btVector3& operator-=(const btVector3& v) - { - m_floats[0] -= v.m_floats[0]; - m_floats[1] -= v.m_floats[1]; - m_floats[2] -= v.m_floats[2]; - return *this; - } - /**@brief Scale the vector - * @param s Scale factor */ - SIMD_FORCE_INLINE btVector3& operator*=(const btScalar& s) - { - m_floats[0] *= s; - m_floats[1] *= s; - m_floats[2] *= s; - return *this; - } - - /**@brief Inversely scale the vector - * @param s Scale factor to divide by */ - SIMD_FORCE_INLINE btVector3& operator/=(const btScalar& s) - { - btFullAssert(s != btScalar(0.0)); - return * this *= btScalar(1.0) / s; - } - - /**@brief Return the dot product - * @param v The other vector in the dot product */ - SIMD_FORCE_INLINE btScalar dot(const btVector3& v) const - { - return m_floats[0] * v.m_floats[0] + m_floats[1] * v.m_floats[1] + m_floats[2] * v.m_floats[2]; - } - - /**@brief Return the length of the vector squared */ - SIMD_FORCE_INLINE btScalar length2() const - { - return dot(*this); - } - - /**@brief Return the length of the vector */ - SIMD_FORCE_INLINE btScalar length() const - { - return btSqrt(length2()); - } - - /**@brief Return the distance squared between the ends of this and another vector - * This is symantically treating the vector like a point */ - SIMD_FORCE_INLINE btScalar distance2(const btVector3& v) const; - - /**@brief Return the distance between the ends of this and another vector - * This is symantically treating the vector like a point */ - SIMD_FORCE_INLINE btScalar distance(const btVector3& v) const; - - SIMD_FORCE_INLINE btVector3& safeNormalize() - { - btVector3 absVec = this->absolute(); - int32_t maxIndex = absVec.maxAxis(); - if (absVec[maxIndex] > 0) { - *this /= absVec[maxIndex]; - return * this /= length(); - } - setValue(1, 0, 0); - return *this; - } - - /**@brief Normalize this vector - * x^2 + y^2 + z^2 = 1 */ - SIMD_FORCE_INLINE btVector3& normalize() - { - return * this /= length(); - } - - /**@brief Return a normalized version of this vector */ - SIMD_FORCE_INLINE btVector3 normalized() const; - - /**@brief Return a rotated version of this vector - * @param wAxis The axis to rotate about - * @param angle The angle to rotate by */ - SIMD_FORCE_INLINE btVector3 rotate(const btVector3& wAxis, const btScalar angle) const; - - /**@brief Return the angle between this and another vector - * @param v The other vector */ - SIMD_FORCE_INLINE btScalar angle(const btVector3& v) const - { - btScalar s = btSqrt(length2() * v.length2()); - btFullAssert(s != btScalar(0.0)); - return btAcos(dot(v) / s); - } - /**@brief Return a vector will the absolute values of each element */ - SIMD_FORCE_INLINE btVector3 absolute() const - { - return btVector3( - btFabs(m_floats[0]), - btFabs(m_floats[1]), - btFabs(m_floats[2])); - } - /**@brief Return the cross product between this and another vector - * @param v The other vector */ - SIMD_FORCE_INLINE btVector3 cross(const btVector3& v) const - { - return btVector3( - m_floats[1] * v.m_floats[2] - m_floats[2] * v.m_floats[1], - m_floats[2] * v.m_floats[0] - m_floats[0] * v.m_floats[2], - m_floats[0] * v.m_floats[1] - m_floats[1] * v.m_floats[0]); - } - - SIMD_FORCE_INLINE btScalar triple(const btVector3& v1, const btVector3& v2) const - { - return m_floats[0] * (v1.m_floats[1] * v2.m_floats[2] - v1.m_floats[2] * v2.m_floats[1]) + m_floats[1] * (v1.m_floats[2] * v2.m_floats[0] - v1.m_floats[0] * v2.m_floats[2]) + m_floats[2] * (v1.m_floats[0] * v2.m_floats[1] - v1.m_floats[1] * v2.m_floats[0]); - } - - /**@brief Return the axis with the smallest value - * Note return values are 0,1,2 for x, y, or z */ - SIMD_FORCE_INLINE int32_t minAxis() const - { - return m_floats[0] < m_floats[1] ? (m_floats[0] < m_floats[2] ? 0 : 2) : (m_floats[1] < m_floats[2] ? 1 : 2); - } - - /**@brief Return the axis with the largest value - * Note return values are 0,1,2 for x, y, or z */ - SIMD_FORCE_INLINE int32_t maxAxis() const - { - return m_floats[0] < m_floats[1] ? (m_floats[1] < m_floats[2] ? 2 : 1) : (m_floats[0] < m_floats[2] ? 2 : 0); - } - - SIMD_FORCE_INLINE int32_t furthestAxis() const - { - return absolute().minAxis(); - } - - SIMD_FORCE_INLINE int32_t closestAxis() const - { - return absolute().maxAxis(); - } - - SIMD_FORCE_INLINE void setInterpolate3(const btVector3& v0, const btVector3& v1, btScalar rt) - { - btScalar s = btScalar(1.0) - rt; - m_floats[0] = s * v0.m_floats[0] + rt * v1.m_floats[0]; - m_floats[1] = s * v0.m_floats[1] + rt * v1.m_floats[1]; - m_floats[2] = s * v0.m_floats[2] + rt * v1.m_floats[2]; - //don't do the unused w component - // m_co[3] = s * v0[3] + rt * v1[3]; - } - - /**@brief Return the linear interpolation between this and another vector - * @param v The other vector - * @param t The ration of this to v (t = 0 => return this, t=1 => return other) */ - SIMD_FORCE_INLINE btVector3 lerp(const btVector3& v, const btScalar& t) const - { - return btVector3(m_floats[0] + (v.m_floats[0] - m_floats[0]) * t, - m_floats[1] + (v.m_floats[1] - m_floats[1]) * t, - m_floats[2] + (v.m_floats[2] - m_floats[2]) * t); - } - - /**@brief Elementwise multiply this vector by the other - * @param v The other vector */ - SIMD_FORCE_INLINE btVector3& operator*=(const btVector3& v) - { - m_floats[0] *= v.m_floats[0]; - m_floats[1] *= v.m_floats[1]; - m_floats[2] *= v.m_floats[2]; - return *this; - } - - /**@brief Return the x value */ - SIMD_FORCE_INLINE const btScalar& getX() const { return m_floats[0]; } - /**@brief Return the y value */ - SIMD_FORCE_INLINE const btScalar& getY() const { return m_floats[1]; } - /**@brief Return the z value */ - SIMD_FORCE_INLINE const btScalar& getZ() const { return m_floats[2]; } - /**@brief Set the x value */ - SIMD_FORCE_INLINE void setX(btScalar x) { m_floats[0] = x; }; - /**@brief Set the y value */ - SIMD_FORCE_INLINE void setY(btScalar y) { m_floats[1] = y; }; - /**@brief Set the z value */ - SIMD_FORCE_INLINE void setZ(btScalar z) { m_floats[2] = z; }; - /**@brief Set the w value */ - SIMD_FORCE_INLINE void setW(btScalar w) { m_floats[3] = w; }; - /**@brief Return the x value */ - SIMD_FORCE_INLINE const btScalar& x() const { return m_floats[0]; } - /**@brief Return the y value */ - SIMD_FORCE_INLINE const btScalar& y() const { return m_floats[1]; } - /**@brief Return the z value */ - SIMD_FORCE_INLINE const btScalar& z() const { return m_floats[2]; } - /**@brief Return the w value */ - SIMD_FORCE_INLINE const btScalar& w() const { return m_floats[3]; } - - //SIMD_FORCE_INLINE btScalar& operator[](int32_t i) { return (&m_floats[0])[i]; } - //SIMD_FORCE_INLINE const btScalar& operator[](int32_t i) const { return (&m_floats[0])[i]; } - ///operator btScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons. - SIMD_FORCE_INLINE operator btScalar*() { return &m_floats[0]; } - SIMD_FORCE_INLINE operator const btScalar*() const { return &m_floats[0]; } - - SIMD_FORCE_INLINE bool operator==(const btVector3& other) const - { - return ((m_floats[3] == other.m_floats[3]) && (m_floats[2] == other.m_floats[2]) && (m_floats[1] == other.m_floats[1]) && (m_floats[0] == other.m_floats[0])); - } - - SIMD_FORCE_INLINE bool operator!=(const btVector3& other) const - { - return !(*this == other); - } - - /**@brief Set each element to the max of the current values and the values of another btVector3 - * @param other The other btVector3 to compare with - */ - SIMD_FORCE_INLINE void setMax(const btVector3& other) - { - btSetMax(m_floats[0], other.m_floats[0]); - btSetMax(m_floats[1], other.m_floats[1]); - btSetMax(m_floats[2], other.m_floats[2]); - btSetMax(m_floats[3], other.w()); - } - /**@brief Set each element to the min of the current values and the values of another btVector3 - * @param other The other btVector3 to compare with - */ - SIMD_FORCE_INLINE void setMin(const btVector3& other) - { - btSetMin(m_floats[0], other.m_floats[0]); - btSetMin(m_floats[1], other.m_floats[1]); - btSetMin(m_floats[2], other.m_floats[2]); - btSetMin(m_floats[3], other.w()); - } - - SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z) - { - m_floats[0] = x; - m_floats[1] = y; - m_floats[2] = z; - m_floats[3] = btScalar(0.); - } - - void getSkewSymmetricMatrix(btVector3 * v0, btVector3 * v1, btVector3 * v2) const - { - v0->setValue(0., -z(), y()); - v1->setValue(z(), 0., -x()); - v2->setValue(-y(), x(), 0.); - } - - void setZero() - { - setValue(btScalar(0.), btScalar(0.), btScalar(0.)); - } - - SIMD_FORCE_INLINE bool isZero() const - { - return m_floats[0] == btScalar(0) && m_floats[1] == btScalar(0) && m_floats[2] == btScalar(0); - } - - SIMD_FORCE_INLINE bool fuzzyZero() const - { - return length2() < SIMD_EPSILON; - } - - SIMD_FORCE_INLINE void serialize(struct btVector3Data & dataOut) const; - - SIMD_FORCE_INLINE void deSerialize(const struct btVector3Data& dataIn); - - SIMD_FORCE_INLINE void serializeFloat(struct btVector3FloatData & dataOut) const; - - SIMD_FORCE_INLINE void deSerializeFloat(const struct btVector3FloatData& dataIn); - - SIMD_FORCE_INLINE void serializeDouble(struct btVector3DoubleData & dataOut) const; - - SIMD_FORCE_INLINE void deSerializeDouble(const struct btVector3DoubleData& dataIn); -}; - -/**@brief Return the sum of two vectors (Point symantics)*/ -SIMD_FORCE_INLINE btVector3 -operator+(const btVector3& v1, const btVector3& v2) -{ - return btVector3(v1.m_floats[0] + v2.m_floats[0], v1.m_floats[1] + v2.m_floats[1], v1.m_floats[2] + v2.m_floats[2]); -} - -/**@brief Return the elementwise product of two vectors */ -SIMD_FORCE_INLINE btVector3 -operator*(const btVector3& v1, const btVector3& v2) -{ - return btVector3(v1.m_floats[0] * v2.m_floats[0], v1.m_floats[1] * v2.m_floats[1], v1.m_floats[2] * v2.m_floats[2]); -} - -/**@brief Return the difference between two vectors */ -SIMD_FORCE_INLINE btVector3 -operator-(const btVector3& v1, const btVector3& v2) -{ - return btVector3(v1.m_floats[0] - v2.m_floats[0], v1.m_floats[1] - v2.m_floats[1], v1.m_floats[2] - v2.m_floats[2]); -} -/**@brief Return the negative of the vector */ -SIMD_FORCE_INLINE btVector3 -operator-(const btVector3& v) -{ - return btVector3(-v.m_floats[0], -v.m_floats[1], -v.m_floats[2]); -} - -/**@brief Return the vector scaled by s */ -SIMD_FORCE_INLINE btVector3 -operator*(const btVector3& v, const btScalar& s) -{ - return btVector3(v.m_floats[0] * s, v.m_floats[1] * s, v.m_floats[2] * s); -} - -/**@brief Return the vector scaled by s */ -SIMD_FORCE_INLINE btVector3 -operator*(const btScalar& s, const btVector3& v) -{ - return v * s; -} - -/**@brief Return the vector inversely scaled by s */ -SIMD_FORCE_INLINE btVector3 -operator/(const btVector3& v, const btScalar& s) -{ - btFullAssert(s != btScalar(0.0)); - return v * (btScalar(1.0) / s); -} - -/**@brief Return the vector inversely scaled by s */ -SIMD_FORCE_INLINE btVector3 -operator/(const btVector3& v1, const btVector3& v2) -{ - return btVector3(v1.m_floats[0] / v2.m_floats[0], v1.m_floats[1] / v2.m_floats[1], v1.m_floats[2] / v2.m_floats[2]); -} - -/**@brief Return the dot product between two vectors */ -SIMD_FORCE_INLINE btScalar -btDot(const btVector3& v1, const btVector3& v2) -{ - return v1.dot(v2); -} - -/**@brief Return the distance squared between two vectors */ -SIMD_FORCE_INLINE btScalar -btDistance2(const btVector3& v1, const btVector3& v2) -{ - return v1.distance2(v2); -} - -/**@brief Return the distance between two vectors */ -SIMD_FORCE_INLINE btScalar -btDistance(const btVector3& v1, const btVector3& v2) -{ - return v1.distance(v2); -} - -/**@brief Return the angle between two vectors */ -SIMD_FORCE_INLINE btScalar -btAngle(const btVector3& v1, const btVector3& v2) -{ - return v1.angle(v2); -} - -/**@brief Return the cross product of two vectors */ -SIMD_FORCE_INLINE btVector3 -btCross(const btVector3& v1, const btVector3& v2) -{ - return v1.cross(v2); -} - -SIMD_FORCE_INLINE btScalar -btTriple(const btVector3& v1, const btVector3& v2, const btVector3& v3) -{ - return v1.triple(v2, v3); -} - -/**@brief Return the linear interpolation between two vectors - * @param v1 One vector - * @param v2 The other vector - * @param t The ration of this to v (t = 0 => return v1, t=1 => return v2) */ -SIMD_FORCE_INLINE btVector3 -lerp(const btVector3& v1, const btVector3& v2, const btScalar& t) -{ - return v1.lerp(v2, t); -} - -SIMD_FORCE_INLINE btScalar btVector3::distance2(const btVector3& v) const -{ - return (v - *this).length2(); -} - -SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3& v) const -{ - return (v - *this).length(); -} - -SIMD_FORCE_INLINE btVector3 btVector3::normalized() const -{ - return *this / length(); -} - -SIMD_FORCE_INLINE btVector3 btVector3::rotate(const btVector3& wAxis, const btScalar angle) const -{ - // wAxis must be a unit lenght vector - - btVector3 o = wAxis * wAxis.dot(*this); - btVector3 x = *this - o; - btVector3 y; - - y = wAxis.cross(*this); - - return (o + x * btCos(angle) + y * btSin(angle)); -} - -class btVector4 : public btVector3 { -public: - SIMD_FORCE_INLINE btVector4() {} - - SIMD_FORCE_INLINE btVector4(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) - : btVector3(x, y, z) - { - m_floats[3] = w; - } - - SIMD_FORCE_INLINE btVector4 absolute4() const - { - return btVector4( - btFabs(m_floats[0]), - btFabs(m_floats[1]), - btFabs(m_floats[2]), - btFabs(m_floats[3])); - } - - btScalar getW() const { return m_floats[3]; } - - SIMD_FORCE_INLINE int32_t maxAxis4() const - { - int32_t maxIndex = -1; - btScalar maxVal = btScalar(-BT_LARGE_FLOAT); - if (m_floats[0] > maxVal) { - maxIndex = 0; - maxVal = m_floats[0]; - } - if (m_floats[1] > maxVal) { - maxIndex = 1; - maxVal = m_floats[1]; - } - if (m_floats[2] > maxVal) { - maxIndex = 2; - maxVal = m_floats[2]; - } - if (m_floats[3] > maxVal) { - maxIndex = 3; - } - return maxIndex; - } - - SIMD_FORCE_INLINE int32_t minAxis4() const - { - int32_t minIndex = -1; - btScalar minVal = btScalar(BT_LARGE_FLOAT); - if (m_floats[0] < minVal) { - minIndex = 0; - minVal = m_floats[0]; - } - if (m_floats[1] < minVal) { - minIndex = 1; - minVal = m_floats[1]; - } - if (m_floats[2] < minVal) { - minIndex = 2; - minVal = m_floats[2]; - } - if (m_floats[3] < minVal) { - minIndex = 3; - } - - return minIndex; - } - - SIMD_FORCE_INLINE int32_t closestAxis4() const - { - return absolute4().maxAxis4(); - } - - /**@brief Set x,y,z and zero w - * @param x Value of x - * @param y Value of y - * @param z Value of z - */ - - /* void getValue(btScalar *m) const - { - m[0] = m_floats[0]; - m[1] = m_floats[1]; - m[2] =m_floats[2]; - } -*/ - /**@brief Set the values - * @param x Value of x - * @param y Value of y - * @param z Value of z - * @param w Value of w - */ - SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) - { - m_floats[0] = x; - m_floats[1] = y; - m_floats[2] = z; - m_floats[3] = w; - } -}; - -///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization -SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar& sourceVal, btScalar& destVal) -{ -#ifdef BT_USE_DOUBLE_PRECISION - unsigned char* dest = (unsigned char*)&destVal; - unsigned char* src = (unsigned char*)&sourceVal; - dest[0] = src[7]; - dest[1] = src[6]; - dest[2] = src[5]; - dest[3] = src[4]; - dest[4] = src[3]; - dest[5] = src[2]; - dest[6] = src[1]; - dest[7] = src[0]; -#else - unsigned char* dest = (unsigned char*)&destVal; - unsigned char* src = (unsigned char*)&sourceVal; - dest[0] = src[3]; - dest[1] = src[2]; - dest[2] = src[1]; - dest[3] = src[0]; -#endif //BT_USE_DOUBLE_PRECISION -} -///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization -SIMD_FORCE_INLINE void btSwapVector3Endian(const btVector3& sourceVec, btVector3& destVec) -{ - for (int32_t i = 0; i < 4; i++) { - btSwapScalarEndian(sourceVec[i], destVec[i]); - } -} - -///btUnSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization -SIMD_FORCE_INLINE void btUnSwapVector3Endian(btVector3& vector) -{ - - btVector3 swappedVec; - for (int32_t i = 0; i < 4; i++) { - btSwapScalarEndian(vector[i], swappedVec[i]); - } - vector = swappedVec; -} - -template -SIMD_FORCE_INLINE void btPlaneSpace1(const T& n, T& p, T& q) -{ - if (btFabs(n[2]) > SIMDSQRT12) { - // choose p in y-z plane - btScalar a = n[1] * n[1] + n[2] * n[2]; - btScalar k = btRecipSqrt(a); - p[0] = 0; - p[1] = -n[2] * k; - p[2] = n[1] * k; - // set q = n x p - q[0] = a * k; - q[1] = -n[0] * p[2]; - q[2] = n[0] * p[1]; - } - else { - // choose p in x-y plane - btScalar a = n[0] * n[0] + n[1] * n[1]; - btScalar k = btRecipSqrt(a); - p[0] = -n[1] * k; - p[1] = n[0] * k; - p[2] = 0; - // set q = n x p - q[0] = -n[2] * p[1]; - q[1] = n[2] * p[0]; - q[2] = a * k; - } -} - -struct btVector3FloatData { - float m_floats[4]; -}; - -struct btVector3DoubleData { - double m_floats[4]; -}; - -SIMD_FORCE_INLINE void btVector3::serializeFloat(struct btVector3FloatData& dataOut) const -{ - ///could also do a memcpy, check if it is worth it - for (int32_t i = 0; i < 4; i++) - dataOut.m_floats[i] = float(m_floats[i]); -} - -SIMD_FORCE_INLINE void btVector3::deSerializeFloat(const struct btVector3FloatData& dataIn) -{ - for (int32_t i = 0; i < 4; i++) - m_floats[i] = btScalar(dataIn.m_floats[i]); -} - -SIMD_FORCE_INLINE void btVector3::serializeDouble(struct btVector3DoubleData& dataOut) const -{ - ///could also do a memcpy, check if it is worth it - for (int32_t i = 0; i < 4; i++) - dataOut.m_floats[i] = double(m_floats[i]); -} - -SIMD_FORCE_INLINE void btVector3::deSerializeDouble(const struct btVector3DoubleData& dataIn) -{ - for (int32_t i = 0; i < 4; i++) - m_floats[i] = btScalar(dataIn.m_floats[i]); -} - -SIMD_FORCE_INLINE void btVector3::serialize(struct btVector3Data& dataOut) const -{ - ///could also do a memcpy, check if it is worth it - for (int32_t i = 0; i < 4; i++) - dataOut.m_floats[i] = m_floats[i]; -} - -SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn) -{ - for (int32_t i = 0; i < 4; i++) - m_floats[i] = dataIn.m_floats[i]; -} - -#endif //BT_VECTOR3_H diff --git a/src/VHACD_Lib/inc/vhacdCircularList.h b/src/VHACD_Lib/inc/vhacdCircularList.h deleted file mode 100644 index 0f5ddf9e..00000000 --- a/src/VHACD_Lib/inc/vhacdCircularList.h +++ /dev/null @@ -1,79 +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_CIRCULAR_LIST_H -#define VHACD_CIRCULAR_LIST_H -#include -namespace VHACD { -//! CircularListElement class. -template -class CircularListElement { -public: - T& GetData() { return m_data; } - const T& GetData() const { return m_data; } - CircularListElement*& GetNext() { return m_next; } - CircularListElement*& GetPrev() { return m_prev; } - const CircularListElement*& GetNext() const { return m_next; } - const CircularListElement*& GetPrev() const { return m_prev; } - //! Constructor - CircularListElement(const T& data) { m_data = data; } - CircularListElement(void) {} - //! Destructor - ~CircularListElement(void) {} -private: - T m_data; - CircularListElement* m_next; - CircularListElement* m_prev; - - CircularListElement(const CircularListElement& rhs); -}; -//! CircularList class. -template -class CircularList { -public: - CircularListElement*& GetHead() { return m_head; } - const CircularListElement* GetHead() const { return m_head; } - bool IsEmpty() const { return (m_size == 0); } - size_t GetSize() const { return m_size; } - const T& GetData() const { return m_head->GetData(); } - T& GetData() { return m_head->GetData(); } - bool Delete(); - bool Delete(CircularListElement* element); - CircularListElement* Add(const T* data = 0); - CircularListElement* Add(const T& data); - bool Next(); - bool Prev(); - void Clear() - { - while (Delete()) - ; - }; - const CircularList& operator=(const CircularList& rhs); - //! Constructor - CircularList() - { - m_head = 0; - m_size = 0; - } - CircularList(const CircularList& rhs); - //! Destructor - ~CircularList(void) { Clear(); }; -private: - CircularListElement* m_head; //!< a pointer to the head of the circular list - size_t m_size; //!< number of element in the circular list -}; -} -#include "vhacdCircularList.inl" -#endif // VHACD_CIRCULAR_LIST_H \ No newline at end of file diff --git a/src/VHACD_Lib/inc/vhacdCircularList.inl b/src/VHACD_Lib/inc/vhacdCircularList.inl deleted file mode 100644 index 2be51805..00000000 --- a/src/VHACD_Lib/inc/vhacdCircularList.inl +++ /dev/null @@ -1,161 +0,0 @@ -#pragma once -#ifndef HACD_CIRCULAR_LIST_INL -#define HACD_CIRCULAR_LIST_INL -namespace VHACD -{ - template < typename T > - inline bool CircularList::Delete(CircularListElement * element) - { - if (!element) - { - return false; - } - if (m_size > 1) - { - CircularListElement * next = element->GetNext(); - CircularListElement * prev = element->GetPrev(); - delete element; - m_size--; - if (element == m_head) - { - m_head = next; - } - next->GetPrev() = prev; - prev->GetNext() = next; - return true; - } - else if (m_size == 1) - { - delete m_head; - m_size--; - m_head = 0; - return true; - } - else - { - return false; - } - } - - template < typename T > - inline bool CircularList::Delete() - { - if (m_size > 1) - { - CircularListElement * next = m_head->GetNext(); - CircularListElement * prev = m_head->GetPrev(); - delete m_head; - m_size--; - m_head = next; - next->GetPrev() = prev; - prev->GetNext() = next; - return true; - } - else if (m_size == 1) - { - delete m_head; - m_size--; - m_head = 0; - return true; - } - else - { - return false; - } - } - template < typename T > - inline CircularListElement * CircularList::Add(const T * data) - { - if (m_size == 0) - { - if (data) - { - m_head = new CircularListElement(*data); - } - else - { - m_head = new CircularListElement(); - } - m_head->GetNext() = m_head->GetPrev() = m_head; - } - else - { - CircularListElement * next = m_head->GetNext(); - CircularListElement * element = m_head; - if (data) - { - m_head = new CircularListElement(*data); - } - else - { - m_head = new CircularListElement(); - } - m_head->GetNext() = next; - m_head->GetPrev() = element; - element->GetNext() = m_head; - next->GetPrev() = m_head; - } - m_size++; - return m_head; - } - template < typename T > - inline CircularListElement * CircularList::Add(const T & data) - { - const T * pData = &data; - return Add(pData); - } - template < typename T > - inline bool CircularList::Next() - { - if (m_size == 0) - { - return false; - } - m_head = m_head->GetNext(); - return true; - } - template < typename T > - inline bool CircularList::Prev() - { - if (m_size == 0) - { - return false; - } - m_head = m_head->GetPrev(); - return true; - } - template < typename T > - inline CircularList::CircularList(const CircularList& rhs) - { - if (rhs.m_size > 0) - { - CircularListElement * current = rhs.m_head; - do - { - current = current->GetNext(); - Add(current->GetData()); - } - while ( current != rhs.m_head ); - } - } - template < typename T > - inline const CircularList& CircularList::operator=(const CircularList& rhs) - { - if (&rhs != this) - { - Clear(); - if (rhs.m_size > 0) - { - CircularListElement * current = rhs.m_head; - do - { - current = current->GetNext(); - Add(current->GetData()); - } - while ( current != rhs.m_head ); - } - } - return (*this); - } -} -#endif \ No newline at end of file diff --git a/src/VHACD_Lib/inc/vhacdICHull.h b/src/VHACD_Lib/inc/vhacdICHull.h deleted file mode 100644 index 132bdcfb..00000000 --- a/src/VHACD_Lib/inc/vhacdICHull.h +++ /dev/null @@ -1,98 +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_ICHULL_H -#define VHACD_ICHULL_H -#include "vhacdManifoldMesh.h" -#include "vhacdVector.h" - -namespace VHACD { -//! Incremental Convex Hull algorithm (cf. http://cs.smith.edu/~orourke/books/ftp.html ). -enum ICHullError { - ICHullErrorOK = 0, - ICHullErrorCoplanarPoints, - ICHullErrorNoVolume, - ICHullErrorInconsistent, - ICHullErrorNotEnoughPoints -}; -class ICHull { -public: - static const double sc_eps; - //! - bool IsFlat() { return m_isFlat; } - //! Returns the computed mesh - TMMesh& GetMesh() { return m_mesh; } - //! Add one point to the convex-hull - bool AddPoint(const Vec3& point) { return AddPoints(&point, 1); } - //! Add one point to the convex-hull - bool AddPoint(const Vec3& point, int32_t id); - //! Add points to the convex-hull - bool AddPoints(const Vec3* points, size_t nPoints); - //! - ICHullError Process(); - //! - ICHullError Process(const uint32_t nPointsCH, const double minVolume = 0.0); - //! - bool IsInside(const Vec3& pt0, const double eps = 0.0); - //! - const ICHull& operator=(ICHull& rhs); - - //! Constructor - ICHull(); - //! Destructor - ~ICHull(void){}; - -private: - //! DoubleTriangle builds the initial double triangle. It first finds 3 noncollinear points and makes two faces out of them, in opposite order. It then finds a fourth point that is not coplanar with that face. The vertices are stored in the face structure in counterclockwise order so that the volume between the face and the point is negative. Lastly, the 3 newfaces to the fourth point are constructed and the data structures are cleaned up. - ICHullError DoubleTriangle(); - //! MakeFace creates a new face structure from three vertices (in ccw order). It returns a pointer to the face. - CircularListElement* MakeFace(CircularListElement* v0, - CircularListElement* v1, - CircularListElement* v2, - CircularListElement* fold); - //! - CircularListElement* MakeConeFace(CircularListElement* e, CircularListElement* v); - //! - bool ProcessPoint(); - //! - bool ComputePointVolume(double& totalVolume, bool markVisibleFaces); - //! - bool FindMaxVolumePoint(const double minVolume = 0.0); - //! - bool CleanEdges(); - //! - bool CleanVertices(uint32_t& addedPoints); - //! - bool CleanTriangles(); - //! - bool CleanUp(uint32_t& addedPoints); - //! - bool MakeCCW(CircularListElement* f, - CircularListElement* e, - CircularListElement* v); - void Clear(); - -private: - static const int32_t sc_dummyIndex; - TMMesh m_mesh; - SArray*> m_edgesToDelete; - SArray*> m_edgesToUpdate; - SArray*> m_trianglesToDelete; - Vec3 m_normal; - bool m_isFlat; - ICHull(const ICHull& rhs); -}; -} -#endif // VHACD_ICHULL_H diff --git a/src/VHACD_Lib/inc/vhacdManifoldMesh.h b/src/VHACD_Lib/inc/vhacdManifoldMesh.h deleted file mode 100644 index a48f53c5..00000000 --- a/src/VHACD_Lib/inc/vhacdManifoldMesh.h +++ /dev/null @@ -1,142 +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_MANIFOLD_MESH_H -#define VHACD_MANIFOLD_MESH_H -#include "vhacdCircularList.h" -#include "vhacdSArray.h" -#include "vhacdVector.h" -namespace VHACD { -class TMMTriangle; -class TMMEdge; -class TMMesh; -class ICHull; - -//! Vertex data structure used in a triangular manifold mesh (TMM). -class TMMVertex { -public: - void Initialize(); - TMMVertex(void); - ~TMMVertex(void); - -private: - Vec3 m_pos; - int32_t m_name; - size_t m_id; - CircularListElement* m_duplicate; // pointer to incident cone edge (or NULL) - bool m_onHull; - bool m_tag; - TMMVertex(const TMMVertex& rhs); - friend class ICHull; - friend class TMMesh; - friend class TMMTriangle; - friend class TMMEdge; -}; - -//! Edge data structure used in a triangular manifold mesh (TMM). -class TMMEdge { -public: - void Initialize(); - TMMEdge(void); - ~TMMEdge(void); - -private: - size_t m_id; - CircularListElement* m_triangles[2]; - CircularListElement* m_vertices[2]; - CircularListElement* m_newFace; - TMMEdge(const TMMEdge& rhs); - friend class ICHull; - friend class TMMTriangle; - friend class TMMVertex; - friend class TMMesh; -}; - -//! Triangle data structure used in a triangular manifold mesh (TMM). -class TMMTriangle { -public: - void Initialize(); - TMMTriangle(void); - ~TMMTriangle(void); - -private: - size_t m_id; - CircularListElement* m_edges[3]; - CircularListElement* m_vertices[3]; - bool m_visible; - - TMMTriangle(const TMMTriangle& rhs); - friend class ICHull; - friend class TMMesh; - friend class TMMVertex; - friend class TMMEdge; -}; -//! triangular manifold mesh data structure. -class TMMesh { -public: - //! Returns the number of vertices> - inline size_t GetNVertices() const { return m_vertices.GetSize(); } - //! Returns the number of edges - inline size_t GetNEdges() const { return m_edges.GetSize(); } - //! Returns the number of triangles - inline size_t GetNTriangles() const { return m_triangles.GetSize(); } - //! Returns the vertices circular list - inline const CircularList& GetVertices() const { return m_vertices; } - //! Returns the edges circular list - inline const CircularList& GetEdges() const { return m_edges; } - //! Returns the triangles circular list - inline const CircularList& GetTriangles() const { return m_triangles; } - //! Returns the vertices circular list - inline CircularList& GetVertices() { return m_vertices; } - //! Returns the edges circular list - inline CircularList& GetEdges() { return m_edges; } - //! Returns the triangles circular list - inline CircularList& GetTriangles() { return m_triangles; } - //! Add vertex to the mesh - CircularListElement* AddVertex() { return m_vertices.Add(); } - //! Add vertex to the mesh - CircularListElement* AddEdge() { return m_edges.Add(); } - //! Add vertex to the mesh - CircularListElement* AddTriangle() { return m_triangles.Add(); } - //! Print mesh information - void Print(); - //! - void GetIFS(Vec3* const points, Vec3* const triangles); - //! - void Clear(); - //! - void Copy(TMMesh& mesh); - //! - bool CheckConsistancy(); - //! - bool Normalize(); - //! - bool Denormalize(); - //! Constructor - TMMesh(); - //! Destructor - virtual ~TMMesh(void); - -private: - CircularList m_vertices; - CircularList m_edges; - CircularList m_triangles; - - // not defined - TMMesh(const TMMesh& rhs); - friend class ICHull; -}; -} -#endif // VHACD_MANIFOLD_MESH_H \ No newline at end of file diff --git a/src/VHACD_Lib/inc/vhacdMesh.h b/src/VHACD_Lib/inc/vhacdMesh.h deleted file mode 100644 index a282e2b4..00000000 --- a/src/VHACD_Lib/inc/vhacdMesh.h +++ /dev/null @@ -1,130 +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_MESH_H -#define VHACD_MESH_H -#include "vhacdSArray.h" -#include "vhacdVector.h" - -#define VHACD_DEBUG_MESH - -namespace VHACD { -enum AXIS { - AXIS_X = 0, - AXIS_Y = 1, - AXIS_Z = 2 -}; -struct Plane { - double m_a; - double m_b; - double m_c; - double m_d; - AXIS m_axis; - short m_index; -}; -#ifdef VHACD_DEBUG_MESH -struct Material { - - Vec3 m_diffuseColor; - double m_ambientIntensity; - Vec3 m_specularColor; - Vec3 m_emissiveColor; - double m_shininess; - double m_transparency; - Material(void) - { - m_diffuseColor.X() = 0.5; - m_diffuseColor.Y() = 0.5; - m_diffuseColor.Z() = 0.5; - m_specularColor.X() = 0.5; - m_specularColor.Y() = 0.5; - m_specularColor.Z() = 0.5; - m_ambientIntensity = 0.4; - m_emissiveColor.X() = 0.0; - m_emissiveColor.Y() = 0.0; - m_emissiveColor.Z() = 0.0; - m_shininess = 0.4; - m_transparency = 0.0; - }; -}; -#endif // VHACD_DEBUG_MESH - -//! Triangular mesh data structure -class Mesh { -public: - void AddPoint(const Vec3& pt) { m_points.PushBack(pt); }; - void SetPoint(size_t index, const Vec3& pt) { m_points[index] = pt; }; - const Vec3& GetPoint(size_t index) const { return m_points[index]; }; - Vec3& GetPoint(size_t index) { return m_points[index]; }; - size_t GetNPoints() const { return m_points.Size(); }; - double* GetPoints() { return (double*)m_points.Data(); } // ugly - const double* const GetPoints() const { return (double*)m_points.Data(); } // ugly - const Vec3* const GetPointsBuffer() const { return m_points.Data(); } // - Vec3* const GetPointsBuffer() { return m_points.Data(); } // - void AddTriangle(const Vec3& tri) { m_triangles.PushBack(tri); }; - void SetTriangle(size_t index, const Vec3& tri) { m_triangles[index] = tri; }; - const Vec3& GetTriangle(size_t index) const { return m_triangles[index]; }; - Vec3& GetTriangle(size_t index) { return m_triangles[index]; }; - size_t GetNTriangles() const { return m_triangles.Size(); }; - int32_t* GetTriangles() { return (int32_t*)m_triangles.Data(); } // ugly - const int32_t* const GetTriangles() const { return (int32_t*)m_triangles.Data(); } // ugly - const Vec3* const GetTrianglesBuffer() const { return m_triangles.Data(); } - Vec3* const GetTrianglesBuffer() { return m_triangles.Data(); } - const Vec3& GetCenter() const { return m_center; } - const Vec3& GetMinBB() const { return m_minBB; } - const Vec3& GetMaxBB() const { return m_maxBB; } - void ClearPoints() { m_points.Clear(); } - void ClearTriangles() { m_triangles.Clear(); } - void Clear() - { - ClearPoints(); - ClearTriangles(); - } - void ResizePoints(size_t nPts) { m_points.Resize(nPts); } - void ResizeTriangles(size_t nTri) { m_triangles.Resize(nTri); } - void CopyPoints(SArray >& points) const { points = m_points; } - double GetDiagBB() const { return m_diag; } - double ComputeVolume() const; - void ComputeConvexHull(const double* const pts, - const size_t nPts); - void Clip(const Plane& plane, - SArray >& positivePart, - SArray >& negativePart) const; - bool IsInside(const Vec3& pt) const; - double ComputeDiagBB(); - Vec3 &ComputeCenter(void); - -#ifdef VHACD_DEBUG_MESH - bool LoadOFF(const std::string& fileName, bool invert); - bool SaveVRML2(const std::string& fileName) const; - bool SaveVRML2(std::ofstream& fout, const Material& material) const; - bool SaveOFF(const std::string& fileName) const; -#endif // VHACD_DEBUG_MESH - - //! Constructor. - Mesh(); - //! Destructor. - ~Mesh(void); - -private: - SArray > m_points; - SArray > m_triangles; - Vec3 m_minBB; - Vec3 m_maxBB; - Vec3 m_center; - double m_diag; -}; -} -#endif \ No newline at end of file diff --git a/src/VHACD_Lib/inc/vhacdMutex.h b/src/VHACD_Lib/inc/vhacdMutex.h deleted file mode 100644 index 6b092592..00000000 --- a/src/VHACD_Lib/inc/vhacdMutex.h +++ /dev/null @@ -1,148 +0,0 @@ -/*! -** -** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com -** -** Portions of this source has been released with the PhysXViewer application, as well as -** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. -** -** If you find this code useful or you are feeling particularily generous I would -** ask that you please go to http://www.amillionpixels.us and make a donation -** to Troy DeMolay. -** -** DeMolay is a youth group for young men between the ages of 12 and 21. -** It teaches strong moral principles, as well as leadership skills and -** public speaking. The donations page uses the 'pay for pixels' paradigm -** where, in this case, a pixel is only a single penny. Donations can be -** made for as small as $4 or as high as a $100 block. Each person who donates -** will get a link to their own site as well as acknowledgement on the -** donations blog located here http://www.amillionpixels.blogspot.com/ -** -** If you wish to contact me you can use the following methods: -** -** Skype ID: jratcliff63367 -** Yahoo: jratcliff63367 -** AOL: jratcliff1961 -** email: jratcliffscarab@gmail.com -** -** -** The MIT license: -** -** Permission is hereby granted, free of charge, to any person obtaining a copy -** of this software and associated documentation files (the "Software"), to deal -** in the Software without restriction, including without limitation the rights -** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -** copies of the Software, and to permit persons to whom the Software is furnished -** to do so, subject to the following conditions: -** -** The above copyright notice and this permission notice shall be included in all -** copies or substantial portions of the Software. - -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -#pragma once -#ifndef VHACD_MUTEX_H -#define VHACD_MUTEX_H - -#if defined(WIN32) - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x400 -#endif -#include -#pragma comment(lib, "winmm.lib") -#endif - -#if defined(__linux__) -//#include -#include -#include -#include -#define __stdcall -#endif - -#if defined(__APPLE__) || defined(__linux__) -#include -#endif - -#if defined(__APPLE__) -#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE -#endif - -#define VHACD_DEBUG - -//#define VHACD_NDEBUG -#ifdef VHACD_NDEBUG -#define VHACD_VERIFY(x) (x) -#else -#define VHACD_VERIFY(x) assert((x)) -#endif - -namespace VHACD { -class Mutex { -public: - Mutex(void) - { -#if defined(WIN32) || defined(_XBOX) - InitializeCriticalSection(&m_mutex); -#elif defined(__APPLE__) || defined(__linux__) - pthread_mutexattr_t mutexAttr; // Mutex Attribute - VHACD_VERIFY(pthread_mutexattr_init(&mutexAttr) == 0); - VHACD_VERIFY(pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0); - VHACD_VERIFY(pthread_mutex_init(&m_mutex, &mutexAttr) == 0); - VHACD_VERIFY(pthread_mutexattr_destroy(&mutexAttr) == 0); -#endif - } - ~Mutex(void) - { -#if defined(WIN32) || defined(_XBOX) - DeleteCriticalSection(&m_mutex); -#elif defined(__APPLE__) || defined(__linux__) - VHACD_VERIFY(pthread_mutex_destroy(&m_mutex) == 0); -#endif - } - void Lock(void) - { -#if defined(WIN32) || defined(_XBOX) - EnterCriticalSection(&m_mutex); -#elif defined(__APPLE__) || defined(__linux__) - VHACD_VERIFY(pthread_mutex_lock(&m_mutex) == 0); -#endif - } - bool TryLock(void) - { -#if defined(WIN32) || defined(_XBOX) - bool bRet = false; - //assert(("TryEnterCriticalSection seems to not work on XP???", 0)); - bRet = TryEnterCriticalSection(&m_mutex) ? true : false; - return bRet; -#elif defined(__APPLE__) || defined(__linux__) - int32_t result = pthread_mutex_trylock(&m_mutex); - return (result == 0); -#endif - } - - void Unlock(void) - { -#if defined(WIN32) || defined(_XBOX) - LeaveCriticalSection(&m_mutex); -#elif defined(__APPLE__) || defined(__linux__) - VHACD_VERIFY(pthread_mutex_unlock(&m_mutex) == 0); -#endif - } - -private: -#if defined(WIN32) || defined(_XBOX) - CRITICAL_SECTION m_mutex; -#elif defined(__APPLE__) || defined(__linux__) - pthread_mutex_t m_mutex; -#endif -}; -} -#endif // VHACD_MUTEX_H diff --git a/src/VHACD_Lib/inc/vhacdRaycastMesh.h b/src/VHACD_Lib/inc/vhacdRaycastMesh.h deleted file mode 100644 index 93bf299c..00000000 --- a/src/VHACD_Lib/inc/vhacdRaycastMesh.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef RAYCAST_MESH_H - -#define RAYCAST_MESH_H - -#include - -namespace VHACD -{ - - // Very simple brute force raycast against a triangle mesh. Tests every triangle; no hierachy. - // Does a deep copy, always does calculations with full double float precision - class RaycastMesh - { - public: - static RaycastMesh * createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh - const double *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc. - uint32_t tcount, // The number of triangles in the source triangle mesh - const uint32_t *indices); // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ... - - static RaycastMesh * createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh - const float *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc. - uint32_t tcount, // The number of triangles in the source triangle mesh - const uint32_t *indices); // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ... - - - virtual bool raycast(const double *from, // The starting point of the raycast - const double *to, // The ending point of the raycast - const double *closestToPoint, // The point to match the nearest hit location (can just be the 'from' location of no specific point) - double *hitLocation, // The point where the ray hit nearest to the 'closestToPoint' location - double *hitDistance) = 0; // The distance the ray traveled to the hit location - - virtual void release(void) = 0; - protected: - virtual ~RaycastMesh(void) { }; - }; - -} // end of VHACD namespace - -#endif diff --git a/src/VHACD_Lib/inc/vhacdSArray.h b/src/VHACD_Lib/inc/vhacdSArray.h deleted file mode 100644 index 4d84d1ae..00000000 --- a/src/VHACD_Lib/inc/vhacdSArray.h +++ /dev/null @@ -1,158 +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_SARRAY_H -#define VHACD_SARRAY_H -#include -#include -#include - -#define SARRAY_DEFAULT_MIN_SIZE 16 - -namespace VHACD { -//! SArray. -template -class SArray { -public: - T& operator[](size_t i) - { - T* const data = Data(); - return data[i]; - } - const T& operator[](size_t i) const - { - const T* const data = Data(); - return data[i]; - } - size_t Size() const - { - return m_size; - } - T* const Data() - { - return (m_maxSize == N) ? m_data0 : m_data; - } - const T* const Data() const - { - return (m_maxSize == N) ? m_data0 : m_data; - } - void Clear() - { - m_size = 0; - delete[] m_data; - m_data = 0; - m_maxSize = N; - } - void PopBack() - { - --m_size; - } - void Allocate(size_t size) - { - if (size > m_maxSize) { - T* temp = new T[size]; - memcpy(temp, Data(), m_size * sizeof(T)); - delete[] m_data; - m_data = temp; - m_maxSize = size; - } - } - void Resize(size_t size) - { - Allocate(size); - m_size = size; - } - - void PushBack(const T& value) - { - if (m_size == m_maxSize) { - size_t maxSize = (m_maxSize << 1); - T* temp = new T[maxSize]; - memcpy(temp, Data(), m_maxSize * sizeof(T)); - delete[] m_data; - m_data = temp; - m_maxSize = maxSize; - } - T* const data = Data(); - data[m_size++] = value; - } - bool Find(const T& value, size_t& pos) - { - T* const data = Data(); - for (pos = 0; pos < m_size; ++pos) - if (value == data[pos]) - return true; - return false; - } - bool Insert(const T& value) - { - size_t pos; - if (Find(value, pos)) - return false; - PushBack(value); - return true; - } - bool Erase(const T& value) - { - size_t pos; - T* const data = Data(); - if (Find(value, pos)) { - for (size_t j = pos + 1; j < m_size; ++j) - data[j - 1] = data[j]; - --m_size; - return true; - } - return false; - } - void operator=(const SArray& rhs) - { - if (m_maxSize < rhs.m_size) { - delete[] m_data; - m_maxSize = rhs.m_maxSize; - m_data = new T[m_maxSize]; - } - m_size = rhs.m_size; - memcpy(Data(), rhs.Data(), m_size * sizeof(T)); - } - void Initialize() - { - m_data = 0; - m_size = 0; - m_maxSize = N; - } - SArray(const SArray& rhs) - { - m_data = 0; - m_size = 0; - m_maxSize = N; - *this = rhs; - } - SArray() - { - Initialize(); - } - ~SArray() - { - delete[] m_data; - } - -private: - T m_data0[N]; - T* m_data; - size_t m_size; - size_t m_maxSize; -}; -} -#endif \ No newline at end of file diff --git a/src/VHACD_Lib/inc/vhacdTimer.h b/src/VHACD_Lib/inc/vhacdTimer.h deleted file mode 100644 index ba0e2c33..00000000 --- a/src/VHACD_Lib/inc/vhacdTimer.h +++ /dev/null @@ -1,121 +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_TIMER_H -#define VHACD_TIMER_H - -#ifdef _WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#endif -#include -#elif __MACH__ -#include -#include -#else -#include -#include -#endif - -namespace VHACD { -#ifdef _WIN32 -class Timer { -public: - Timer(void) - { - m_start.QuadPart = 0; - m_stop.QuadPart = 0; - QueryPerformanceFrequency(&m_freq); - }; - ~Timer(void){}; - void Tic() - { - QueryPerformanceCounter(&m_start); - } - void Toc() - { - QueryPerformanceCounter(&m_stop); - } - double GetElapsedTime() // in ms - { - LARGE_INTEGER delta; - delta.QuadPart = m_stop.QuadPart - m_start.QuadPart; - return (1000.0 * delta.QuadPart) / (double)m_freq.QuadPart; - } - -private: - LARGE_INTEGER m_start; - LARGE_INTEGER m_stop; - LARGE_INTEGER m_freq; -}; - -#elif __MACH__ -class Timer { -public: - Timer(void) - { - memset(this, 0, sizeof(Timer)); - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &m_cclock); - }; - ~Timer(void) - { - mach_port_deallocate(mach_task_self(), m_cclock); - }; - void Tic() - { - clock_get_time(m_cclock, &m_start); - } - void Toc() - { - clock_get_time(m_cclock, &m_stop); - } - double GetElapsedTime() // in ms - { - return 1000.0 * (m_stop.tv_sec - m_start.tv_sec + (1.0E-9) * (m_stop.tv_nsec - m_start.tv_nsec)); - } - -private: - clock_serv_t m_cclock; - mach_timespec_t m_start; - mach_timespec_t m_stop; -}; -#else -class Timer { -public: - Timer(void) - { - memset(this, 0, sizeof(Timer)); - }; - ~Timer(void){}; - void Tic() - { - clock_gettime(CLOCK_REALTIME, &m_start); - } - void Toc() - { - clock_gettime(CLOCK_REALTIME, &m_stop); - } - double GetElapsedTime() // in ms - { - return 1000.0 * (m_stop.tv_sec - m_start.tv_sec + (1.0E-9) * (m_stop.tv_nsec - m_start.tv_nsec)); - } - -private: - struct timespec m_start; - struct timespec m_stop; -}; -#endif -} -#endif // VHACD_TIMER_H diff --git a/src/VHACD_Lib/inc/vhacdVHACD.h b/src/VHACD_Lib/inc/vhacdVHACD.h deleted file mode 100644 index d67b1076..00000000 --- a/src/VHACD_Lib/inc/vhacdVHACD.h +++ /dev/null @@ -1,371 +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_VHACD_H -#define VHACD_VHACD_H - -#ifdef OPENCL_FOUND -#ifdef __MACH__ -#include -#else -#include -#endif -#endif //OPENCL_FOUND - -#include "vhacdMutex.h" -#include "vhacdVolume.h" -#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() - { -#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 -#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 ¢er = 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; - } - 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 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 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 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_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()); - } - - 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); - - 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.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; - } - - m_timer.Tic(); - m_stage = "Voxelization"; - - 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(); - - 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); - - 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; - } - -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 -}; -} -#endif // VHACD_VHACD_H diff --git a/src/VHACD_Lib/inc/vhacdVector.h b/src/VHACD_Lib/inc/vhacdVector.h deleted file mode 100644 index efcfcf6a..00000000 --- a/src/VHACD_Lib/inc/vhacdVector.h +++ /dev/null @@ -1,168 +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_VECTOR_H -#define VHACD_VECTOR_H -#include -#include - -namespace VHACD { -//! Vector dim 3. -template -class Vec3 { -public: - T& operator[](size_t i) { return m_data[i]; } - const T& operator[](size_t i) const { return m_data[i]; } - T& X(); - T& Y(); - T& Z(); - const T& X() const; - const T& Y() const; - const T& Z() const; - void Normalize(); - T GetNorm() const; - void operator=(const Vec3& rhs); - void operator+=(const Vec3& rhs); - void operator-=(const Vec3& rhs); - void operator-=(T a); - void operator+=(T a); - void operator/=(T a); - void operator*=(T a); - Vec3 operator^(const Vec3& rhs) const; - T operator*(const Vec3& rhs) const; - Vec3 operator+(const Vec3& rhs) const; - Vec3 operator-(const Vec3& rhs) const; - Vec3 operator-() const; - Vec3 operator*(T rhs) const; - Vec3 operator/(T rhs) const; - bool operator<(const Vec3& rhs) const; - bool operator>(const Vec3& rhs) const; - Vec3(); - Vec3(T a); - Vec3(T x, T y, T z); - Vec3(const Vec3& rhs); - /*virtual*/ ~Vec3(void); - - // Compute the center of this bounding box and return the diagonal length - T GetCenter(const Vec3 &bmin, const Vec3 &bmax) - { - X() = (bmin.X() + bmax.X())*0.5; - Y() = (bmin.Y() + bmax.Y())*0.5; - Z() = (bmin.Z() + bmax.Z())*0.5; - T dx = bmax.X() - bmin.X(); - T dy = bmax.Y() - bmin.Y(); - T dz = bmax.Z() - bmin.Z(); - T diagonal = T(sqrt(dx*dx + dy*dy + dz*dz)); - return diagonal; - } - - // Update the min/max values relative to this point - void UpdateMinMax(Vec3 &bmin,Vec3 &bmax) const - { - if (X() < bmin.X()) - { - bmin.X() = X(); - } - if (Y() < bmin.Y()) - { - bmin.Y() = Y(); - } - if (Z() < bmin.Z()) - { - bmin.Z() = Z(); - } - if (X() > bmax.X()) - { - bmax.X() = X(); - } - if (X() > bmax.X()) - { - bmax.X() = X(); - } - if (Y() > bmax.Y()) - { - bmax.Y() = Y(); - } - if (Z() > bmax.Z()) - { - bmax.Z() = Z(); - } - } - - // Returns the squared distance between these two points - T GetDistanceSquared(const Vec3 &p) const - { - T dx = X() - p.X(); - T dy = Y() - p.Y(); - T dz = Z() - p.Z(); - return dx*dx + dy*dy + dz*dz; - } - - T GetDistance(const Vec3 &p) const - { - return sqrt(GetDistanceSquared(p)); - } - - // Returns the raw vector data as a pointer - T* GetData(void) - { - return m_data; - } -private: - T m_data[3]; -}; -//! Vector dim 2. -template -class Vec2 { -public: - T& operator[](size_t i) { return m_data[i]; } - const T& operator[](size_t i) const { return m_data[i]; } - T& X(); - T& Y(); - const T& X() const; - const T& Y() const; - void Normalize(); - T GetNorm() const; - void operator=(const Vec2& rhs); - void operator+=(const Vec2& rhs); - void operator-=(const Vec2& rhs); - void operator-=(T a); - void operator+=(T a); - void operator/=(T a); - void operator*=(T a); - T operator^(const Vec2& rhs) const; - T operator*(const Vec2& rhs) const; - Vec2 operator+(const Vec2& rhs) const; - Vec2 operator-(const Vec2& rhs) const; - Vec2 operator-() const; - Vec2 operator*(T rhs) const; - Vec2 operator/(T rhs) const; - Vec2(); - Vec2(T a); - Vec2(T x, T y); - Vec2(const Vec2& rhs); - /*virtual*/ ~Vec2(void); - -private: - T m_data[2]; -}; - -template -const bool Colinear(const Vec3& a, const Vec3& b, const Vec3& c); -template -const T ComputeVolume4(const Vec3& a, const Vec3& b, const Vec3& c, const Vec3& d); -} -#include "vhacdVector.inl" // template implementation -#endif \ No newline at end of file diff --git a/src/VHACD_Lib/inc/vhacdVector.inl b/src/VHACD_Lib/inc/vhacdVector.inl deleted file mode 100644 index 223c2ef1..00000000 --- a/src/VHACD_Lib/inc/vhacdVector.inl +++ /dev/null @@ -1,362 +0,0 @@ -#pragma once -#ifndef VHACD_VECTOR_INL -#define VHACD_VECTOR_INL -namespace VHACD -{ - template - inline Vec3 operator*(T lhs, const Vec3 & rhs) - { - return Vec3(lhs * rhs.X(), lhs * rhs.Y(), lhs * rhs.Z()); - } - template - inline T & Vec3::X() - { - return m_data[0]; - } - template - inline T & Vec3::Y() - { - return m_data[1]; - } - template - inline T & Vec3::Z() - { - return m_data[2]; - } - template - inline const T & Vec3::X() const - { - return m_data[0]; - } - template - inline const T & Vec3::Y() const - { - return m_data[1]; - } - template - inline const T & Vec3::Z() const - { - return m_data[2]; - } - template - inline void Vec3::Normalize() - { - T n = sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]); - if (n != 0.0) (*this) /= n; - } - template - inline T Vec3::GetNorm() const - { - return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]+m_data[2]*m_data[2]); - } - template - inline void Vec3::operator= (const Vec3 & rhs) - { - this->m_data[0] = rhs.m_data[0]; - this->m_data[1] = rhs.m_data[1]; - this->m_data[2] = rhs.m_data[2]; - } - template - inline void Vec3::operator+=(const Vec3 & rhs) - { - this->m_data[0] += rhs.m_data[0]; - this->m_data[1] += rhs.m_data[1]; - this->m_data[2] += rhs.m_data[2]; - } - template - inline void Vec3::operator-=(const Vec3 & rhs) - { - this->m_data[0] -= rhs.m_data[0]; - this->m_data[1] -= rhs.m_data[1]; - this->m_data[2] -= rhs.m_data[2]; - } - template - inline void Vec3::operator-=(T a) - { - this->m_data[0] -= a; - this->m_data[1] -= a; - this->m_data[2] -= a; - } - template - inline void Vec3::operator+=(T a) - { - this->m_data[0] += a; - this->m_data[1] += a; - this->m_data[2] += a; - } - template - inline void Vec3::operator/=(T a) - { - this->m_data[0] /= a; - this->m_data[1] /= a; - this->m_data[2] /= a; - } - template - inline void Vec3::operator*=(T a) - { - this->m_data[0] *= a; - this->m_data[1] *= a; - this->m_data[2] *= a; - } - template - inline Vec3 Vec3::operator^ (const Vec3 & rhs) const - { - return Vec3(m_data[1] * rhs.m_data[2] - m_data[2] * rhs.m_data[1], - m_data[2] * rhs.m_data[0] - m_data[0] * rhs.m_data[2], - m_data[0] * rhs.m_data[1] - m_data[1] * rhs.m_data[0]); - } - template - inline T Vec3::operator*(const Vec3 & rhs) const - { - return (m_data[0] * rhs.m_data[0] + m_data[1] * rhs.m_data[1] + m_data[2] * rhs.m_data[2]); - } - template - inline Vec3 Vec3::operator+(const Vec3 & rhs) const - { - return Vec3(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1],m_data[2] + rhs.m_data[2]); - } - template - inline Vec3 Vec3::operator-(const Vec3 & rhs) const - { - return Vec3(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1],m_data[2] - rhs.m_data[2]) ; - } - template - inline Vec3 Vec3::operator-() const - { - return Vec3(-m_data[0],-m_data[1],-m_data[2]) ; - } - - template - inline Vec3 Vec3::operator*(T rhs) const - { - return Vec3(rhs * this->m_data[0], rhs * this->m_data[1], rhs * this->m_data[2]); - } - template - inline Vec3 Vec3::operator/ (T rhs) const - { - return Vec3(m_data[0] / rhs, m_data[1] / rhs, m_data[2] / rhs); - } - template - inline Vec3::Vec3(T a) - { - m_data[0] = m_data[1] = m_data[2] = a; - } - template - inline Vec3::Vec3(T x, T y, T z) - { - m_data[0] = x; - m_data[1] = y; - m_data[2] = z; - } - template - inline Vec3::Vec3(const Vec3 & rhs) - { - m_data[0] = rhs.m_data[0]; - m_data[1] = rhs.m_data[1]; - m_data[2] = rhs.m_data[2]; - } - template - inline Vec3::~Vec3(void){}; - - template - inline Vec3::Vec3() {} - - template - inline const bool Colinear(const Vec3 & a, const Vec3 & b, const Vec3 & c) - { - return ((c.Z() - a.Z()) * (b.Y() - a.Y()) - (b.Z() - a.Z()) * (c.Y() - a.Y()) == 0.0 /*EPS*/) && - ((b.Z() - a.Z()) * (c.X() - a.X()) - (b.X() - a.X()) * (c.Z() - a.Z()) == 0.0 /*EPS*/) && - ((b.X() - a.X()) * (c.Y() - a.Y()) - (b.Y() - a.Y()) * (c.X() - a.X()) == 0.0 /*EPS*/); - } - - template - inline const T ComputeVolume4(const Vec3 & a, const Vec3 & b, const Vec3 & c, const Vec3 & d) - { - return (a-d) * ((b-d) ^ (c-d)); - } - - template - inline bool Vec3::operator<(const Vec3 & rhs) const - { - if (X() == rhs[0]) - { - if (Y() == rhs[1]) - { - return (Z() - inline bool Vec3::operator>(const Vec3 & rhs) const - { - if (X() == rhs[0]) - { - if (Y() == rhs[1]) - { - return (Z()>rhs[2]); - } - return (Y()>rhs[1]); - } - return (X()>rhs[0]); - } - template - inline Vec2 operator*(T lhs, const Vec2 & rhs) - { - return Vec2(lhs * rhs.X(), lhs * rhs.Y()); - } - template - inline T & Vec2::X() - { - return m_data[0]; - } - template - inline T & Vec2::Y() - { - return m_data[1]; - } - template - inline const T & Vec2::X() const - { - return m_data[0]; - } - template - inline const T & Vec2::Y() const - { - return m_data[1]; - } - template - inline void Vec2::Normalize() - { - T n = sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]); - if (n != 0.0) (*this) /= n; - } - template - inline T Vec2::GetNorm() const - { - return sqrt(m_data[0]*m_data[0]+m_data[1]*m_data[1]); - } - template - inline void Vec2::operator= (const Vec2 & rhs) - { - this->m_data[0] = rhs.m_data[0]; - this->m_data[1] = rhs.m_data[1]; - } - template - inline void Vec2::operator+=(const Vec2 & rhs) - { - this->m_data[0] += rhs.m_data[0]; - this->m_data[1] += rhs.m_data[1]; - } - template - inline void Vec2::operator-=(const Vec2 & rhs) - { - this->m_data[0] -= rhs.m_data[0]; - this->m_data[1] -= rhs.m_data[1]; - } - template - inline void Vec2::operator-=(T a) - { - this->m_data[0] -= a; - this->m_data[1] -= a; - } - template - inline void Vec2::operator+=(T a) - { - this->m_data[0] += a; - this->m_data[1] += a; - } - template - inline void Vec2::operator/=(T a) - { - this->m_data[0] /= a; - this->m_data[1] /= a; - } - template - inline void Vec2::operator*=(T a) - { - this->m_data[0] *= a; - this->m_data[1] *= a; - } - template - inline T Vec2::operator^ (const Vec2 & rhs) const - { - return m_data[0] * rhs.m_data[1] - m_data[1] * rhs.m_data[0]; - } - template - inline T Vec2::operator*(const Vec2 & rhs) const - { - return (m_data[0] * rhs.m_data[0] + m_data[1] * rhs.m_data[1]); - } - template - inline Vec2 Vec2::operator+(const Vec2 & rhs) const - { - return Vec2(m_data[0] + rhs.m_data[0],m_data[1] + rhs.m_data[1]); - } - template - inline Vec2 Vec2::operator-(const Vec2 & rhs) const - { - return Vec2(m_data[0] - rhs.m_data[0],m_data[1] - rhs.m_data[1]); - } - template - inline Vec2 Vec2::operator-() const - { - return Vec2(-m_data[0],-m_data[1]) ; - } - - template - inline Vec2 Vec2::operator*(T rhs) const - { - return Vec2(rhs * this->m_data[0], rhs * this->m_data[1]); - } - template - inline Vec2 Vec2::operator/ (T rhs) const - { - return Vec2(m_data[0] / rhs, m_data[1] / rhs); - } - template - inline Vec2::Vec2(T a) - { - m_data[0] = m_data[1] = a; - } - template - inline Vec2::Vec2(T x, T y) - { - m_data[0] = x; - m_data[1] = y; - } - template - inline Vec2::Vec2(const Vec2 & rhs) - { - m_data[0] = rhs.m_data[0]; - m_data[1] = rhs.m_data[1]; - } - template - inline Vec2::~Vec2(void){}; - - template - inline Vec2::Vec2() {} - - /* - InsideTriangle decides if a point P is Inside of the triangle - defined by A, B, C. - */ - template - inline const bool InsideTriangle(const Vec2 & a, const Vec2 & b, const Vec2 & c, const Vec2 & p) - { - T ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; - T cCROSSap, bCROSScp, aCROSSbp; - ax = c.X() - b.X(); ay = c.Y() - b.Y(); - bx = a.X() - c.X(); by = a.Y() - c.Y(); - cx = b.X() - a.X(); cy = b.Y() - a.Y(); - apx= p.X() - a.X(); apy= p.Y() - a.Y(); - bpx= p.X() - b.X(); bpy= p.Y() - b.Y(); - cpx= p.X() - c.X(); cpy= p.Y() - c.Y(); - aCROSSbp = ax*bpy - ay*bpx; - cCROSSap = cx*apy - cy*apx; - bCROSScp = bx*cpy - by*cpx; - return ((aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0)); - } -} -#endif //VHACD_VECTOR_INL \ No newline at end of file diff --git a/src/VHACD_Lib/inc/vhacdVolume.h b/src/VHACD_Lib/inc/vhacdVolume.h deleted file mode 100644 index 8c47fa1e..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 deleted file mode 100644 index 863aeda9..00000000 --- a/src/VHACD_Lib/public/VHACD.h +++ /dev/null @@ -1,153 +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_H -#define VHACD_H - -#define VHACD_VERSION_MAJOR 2 -#define VHACD_VERSION_MINOR 3 - -// 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'. -// -// 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. -// -// 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 - -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) {} -}; -IVHACD* CreateVHACD(void); -IVHACD* CreateVHACD_ASYNC(void); -} -#endif // VHACD_H diff --git a/src/VHACD_Lib/src/FloatMath.cpp b/src/VHACD_Lib/src/FloatMath.cpp deleted file mode 100644 index 481e8751..00000000 --- a/src/VHACD_Lib/src/FloatMath.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "FloatMath.h" -#include - -#define REAL float - -#include "FloatMath.inl" - -#undef REAL -#define REAL double - -#include "FloatMath.inl" diff --git a/src/VHACD_Lib/src/FloatMath.inl b/src/VHACD_Lib/src/FloatMath.inl deleted file mode 100644 index a30deba4..00000000 --- a/src/VHACD_Lib/src/FloatMath.inl +++ /dev/null @@ -1,5280 +0,0 @@ -// a set of routines that let you do common 3d math -// operations without any vector, matrix, or quaternion -// classes or templates. -// -// a vector (or point) is a 'float *' to 3 floating point numbers. -// a matrix is a 'float *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL -// a quaternion is a 'float *' to 4 floats representing a quaternion x,y,z,w -// - -#ifdef _MSC_VER -#pragma warning(disable:4996) -#endif - -namespace FLOAT_MATH -{ - -void fm_inverseRT(const REAL matrix[16],const REAL pos[3],REAL t[3]) // inverse rotate translate the point. -{ - - REAL _x = pos[0] - matrix[3*4+0]; - REAL _y = pos[1] - matrix[3*4+1]; - REAL _z = pos[2] - matrix[3*4+2]; - - // Multiply inverse-translated source vector by inverted rotation transform - - t[0] = (matrix[0*4+0] * _x) + (matrix[0*4+1] * _y) + (matrix[0*4+2] * _z); - t[1] = (matrix[1*4+0] * _x) + (matrix[1*4+1] * _y) + (matrix[1*4+2] * _z); - t[2] = (matrix[2*4+0] * _x) + (matrix[2*4+1] * _y) + (matrix[2*4+2] * _z); - -} - -REAL fm_getDeterminant(const REAL matrix[16]) -{ - REAL tempv[3]; - REAL p0[3]; - REAL p1[3]; - REAL p2[3]; - - - p0[0] = matrix[0*4+0]; - p0[1] = matrix[0*4+1]; - p0[2] = matrix[0*4+2]; - - p1[0] = matrix[1*4+0]; - p1[1] = matrix[1*4+1]; - p1[2] = matrix[1*4+2]; - - p2[0] = matrix[2*4+0]; - p2[1] = matrix[2*4+1]; - p2[2] = matrix[2*4+2]; - - fm_cross(tempv,p1,p2); - - return fm_dot(p0,tempv); - -} - -REAL fm_squared(REAL x) { return x*x; }; - -void fm_decomposeTransform(const REAL local_transform[16],REAL trans[3],REAL rot[4],REAL scale[3]) -{ - - trans[0] = local_transform[12]; - trans[1] = local_transform[13]; - trans[2] = local_transform[14]; - - scale[0] = (REAL)sqrt(fm_squared(local_transform[0*4+0]) + fm_squared(local_transform[0*4+1]) + fm_squared(local_transform[0*4+2])); - scale[1] = (REAL)sqrt(fm_squared(local_transform[1*4+0]) + fm_squared(local_transform[1*4+1]) + fm_squared(local_transform[1*4+2])); - scale[2] = (REAL)sqrt(fm_squared(local_transform[2*4+0]) + fm_squared(local_transform[2*4+1]) + fm_squared(local_transform[2*4+2])); - - REAL m[16]; - memcpy(m,local_transform,sizeof(REAL)*16); - - REAL sx = 1.0f / scale[0]; - REAL sy = 1.0f / scale[1]; - REAL sz = 1.0f / scale[2]; - - m[0*4+0]*=sx; - m[0*4+1]*=sx; - m[0*4+2]*=sx; - - m[1*4+0]*=sy; - m[1*4+1]*=sy; - m[1*4+2]*=sy; - - m[2*4+0]*=sz; - m[2*4+1]*=sz; - m[2*4+2]*=sz; - - fm_matrixToQuat(m,rot); - -} - -void fm_getSubMatrix(int32_t ki,int32_t kj,REAL pDst[16],const REAL matrix[16]) -{ - int32_t row, col; - int32_t dstCol = 0, dstRow = 0; - - for ( col = 0; col < 4; col++ ) - { - if ( col == kj ) - { - continue; - } - for ( dstRow = 0, row = 0; row < 4; row++ ) - { - if ( row == ki ) - { - continue; - } - pDst[dstCol*4+dstRow] = matrix[col*4+row]; - dstRow++; - } - dstCol++; - } -} - -void fm_inverseTransform(const REAL matrix[16],REAL inverse_matrix[16]) -{ - REAL determinant = fm_getDeterminant(matrix); - determinant = 1.0f / determinant; - for (int32_t i = 0; i < 4; i++ ) - { - for (int32_t j = 0; j < 4; j++ ) - { - int32_t sign = 1 - ( ( i + j ) % 2 ) * 2; - REAL subMat[16]; - fm_identity(subMat); - fm_getSubMatrix( i, j, subMat, matrix ); - REAL subDeterminant = fm_getDeterminant(subMat); - inverse_matrix[i*4+j] = ( subDeterminant * sign ) * determinant; - } - } -} - -void fm_identity(REAL matrix[16]) // set 4x4 matrix to identity. -{ - matrix[0*4+0] = 1; - matrix[1*4+1] = 1; - matrix[2*4+2] = 1; - matrix[3*4+3] = 1; - - matrix[1*4+0] = 0; - matrix[2*4+0] = 0; - matrix[3*4+0] = 0; - - matrix[0*4+1] = 0; - matrix[2*4+1] = 0; - matrix[3*4+1] = 0; - - matrix[0*4+2] = 0; - matrix[1*4+2] = 0; - matrix[3*4+2] = 0; - - matrix[0*4+3] = 0; - matrix[1*4+3] = 0; - matrix[2*4+3] = 0; - -} - -void fm_quatToEuler(const REAL quat[4],REAL &ax,REAL &ay,REAL &az) -{ - REAL x = quat[0]; - REAL y = quat[1]; - REAL z = quat[2]; - REAL w = quat[3]; - - REAL sint = (2.0f * w * y) - (2.0f * x * z); - REAL cost_temp = 1.0f - (sint * sint); - REAL cost = 0; - - if ( (REAL)fabs(cost_temp) > 0.001f ) - { - cost = (REAL)sqrt( cost_temp ); - } - - REAL sinv, cosv, sinf, cosf; - if ( (REAL)fabs(cost) > 0.001f ) - { - cost = 1.0f / cost; - sinv = ((2.0f * y * z) + (2.0f * w * x)) * cost; - cosv = (1.0f - (2.0f * x * x) - (2.0f * y * y)) * cost; - sinf = ((2.0f * x * y) + (2.0f * w * z)) * cost; - cosf = (1.0f - (2.0f * y * y) - (2.0f * z * z)) * cost; - } - else - { - sinv = (2.0f * w * x) - (2.0f * y * z); - cosv = 1.0f - (2.0f * x * x) - (2.0f * z * z); - sinf = 0; - cosf = 1.0f; - } - - // compute output rotations - ax = (REAL)atan2( sinv, cosv ); - ay = (REAL)atan2( sint, cost ); - az = (REAL)atan2( sinf, cosf ); - -} - -void fm_eulerToMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) -{ - REAL quat[4]; - fm_eulerToQuat(ax,ay,az,quat); - fm_quatToMatrix(quat,matrix); -} - -void fm_getAABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *bmin,REAL *bmax) -{ - - const uint8_t *source = (const uint8_t *) points; - - bmin[0] = points[0]; - bmin[1] = points[1]; - bmin[2] = points[2]; - - bmax[0] = points[0]; - bmax[1] = points[1]; - bmax[2] = points[2]; - - - for (uint32_t i=1; i bmax[0] ) bmax[0] = p[0]; - if ( p[1] > bmax[1] ) bmax[1] = p[1]; - if ( p[2] > bmax[2] ) bmax[2] = p[2]; - - } -} - -void fm_eulerToQuat(const REAL *euler,REAL *quat) // convert euler angles to quaternion. -{ - fm_eulerToQuat(euler[0],euler[1],euler[2],quat); -} - -void fm_eulerToQuat(REAL roll,REAL pitch,REAL yaw,REAL *quat) // convert euler angles to quaternion. -{ - roll *= 0.5f; - pitch *= 0.5f; - yaw *= 0.5f; - - REAL cr = (REAL)cos(roll); - REAL cp = (REAL)cos(pitch); - REAL cy = (REAL)cos(yaw); - - REAL sr = (REAL)sin(roll); - REAL sp = (REAL)sin(pitch); - REAL sy = (REAL)sin(yaw); - - REAL cpcy = cp * cy; - REAL spsy = sp * sy; - REAL spcy = sp * cy; - REAL cpsy = cp * sy; - - quat[0] = ( sr * cpcy - cr * spsy); - quat[1] = ( cr * spcy + sr * cpsy); - quat[2] = ( cr * cpsy - sr * spcy); - quat[3] = cr * cpcy + sr * spsy; -} - -void fm_quatToMatrix(const REAL *quat,REAL *matrix) // convert quaterinion rotation to matrix, zeros out the translation component. -{ - - REAL xx = quat[0]*quat[0]; - REAL yy = quat[1]*quat[1]; - REAL zz = quat[2]*quat[2]; - REAL xy = quat[0]*quat[1]; - REAL xz = quat[0]*quat[2]; - REAL yz = quat[1]*quat[2]; - REAL wx = quat[3]*quat[0]; - REAL wy = quat[3]*quat[1]; - REAL wz = quat[3]*quat[2]; - - matrix[0*4+0] = 1 - 2 * ( yy + zz ); - matrix[1*4+0] = 2 * ( xy - wz ); - matrix[2*4+0] = 2 * ( xz + wy ); - - matrix[0*4+1] = 2 * ( xy + wz ); - matrix[1*4+1] = 1 - 2 * ( xx + zz ); - matrix[2*4+1] = 2 * ( yz - wx ); - - matrix[0*4+2] = 2 * ( xz - wy ); - matrix[1*4+2] = 2 * ( yz + wx ); - matrix[2*4+2] = 1 - 2 * ( xx + yy ); - - matrix[3*4+0] = matrix[3*4+1] = matrix[3*4+2] = (REAL) 0.0f; - matrix[0*4+3] = matrix[1*4+3] = matrix[2*4+3] = (REAL) 0.0f; - matrix[3*4+3] =(REAL) 1.0f; - -} - - -void fm_quatRotate(const REAL *quat,const REAL *v,REAL *r) // rotate a vector directly by a quaternion. -{ - REAL left[4]; - - left[0] = quat[3]*v[0] + quat[1]*v[2] - v[1]*quat[2]; - left[1] = quat[3]*v[1] + quat[2]*v[0] - v[2]*quat[0]; - left[2] = quat[3]*v[2] + quat[0]*v[1] - v[0]*quat[1]; - left[3] = - quat[0]*v[0] - quat[1]*v[1] - quat[2]*v[2]; - - r[0] = (left[3]*-quat[0]) + (quat[3]*left[0]) + (left[1]*-quat[2]) - (-quat[1]*left[2]); - r[1] = (left[3]*-quat[1]) + (quat[3]*left[1]) + (left[2]*-quat[0]) - (-quat[2]*left[0]); - r[2] = (left[3]*-quat[2]) + (quat[3]*left[2]) + (left[0]*-quat[1]) - (-quat[0]*left[1]); - -} - - -void fm_getTranslation(const REAL *matrix,REAL *t) -{ - t[0] = matrix[3*4+0]; - t[1] = matrix[3*4+1]; - t[2] = matrix[3*4+2]; -} - -void fm_matrixToQuat(const REAL *matrix,REAL *quat) // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w -{ - - REAL tr = matrix[0*4+0] + matrix[1*4+1] + matrix[2*4+2]; - - // check the diagonal - - if (tr > 0.0f ) - { - REAL s = (REAL) sqrt ( (double) (tr + 1.0f) ); - quat[3] = s * 0.5f; - s = 0.5f / s; - quat[0] = (matrix[1*4+2] - matrix[2*4+1]) * s; - quat[1] = (matrix[2*4+0] - matrix[0*4+2]) * s; - quat[2] = (matrix[0*4+1] - matrix[1*4+0]) * s; - - } - else - { - // diagonal is negative - int32_t nxt[3] = {1, 2, 0}; - REAL qa[4]; - - int32_t i = 0; - - if (matrix[1*4+1] > matrix[0*4+0]) i = 1; - if (matrix[2*4+2] > matrix[i*4+i]) i = 2; - - int32_t j = nxt[i]; - int32_t k = nxt[j]; - - REAL s = (REAL)sqrt ( ((matrix[i*4+i] - (matrix[j*4+j] + matrix[k*4+k])) + 1.0f) ); - - qa[i] = s * 0.5f; - - if (s != 0.0f ) s = 0.5f / s; - - qa[3] = (matrix[j*4+k] - matrix[k*4+j]) * s; - qa[j] = (matrix[i*4+j] + matrix[j*4+i]) * s; - qa[k] = (matrix[i*4+k] + matrix[k*4+i]) * s; - - quat[0] = qa[0]; - quat[1] = qa[1]; - quat[2] = qa[2]; - quat[3] = qa[3]; - } -// fm_normalizeQuat(quat); -} - - -REAL fm_sphereVolume(REAL radius) // return's the volume of a sphere of this radius (4/3 PI * R cubed ) -{ - return (4.0f / 3.0f ) * FM_PI * radius * radius * radius; -} - - -REAL fm_cylinderVolume(REAL radius,REAL h) -{ - return FM_PI * radius * radius *h; -} - -REAL fm_capsuleVolume(REAL radius,REAL h) -{ - REAL volume = fm_sphereVolume(radius); // volume of the sphere portion. - REAL ch = h-radius*2; // this is the cylinder length - if ( ch > 0 ) - { - volume+=fm_cylinderVolume(radius,ch); - } - return volume; -} - -void fm_transform(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point -{ - if ( matrix ) - { - REAL tx = (matrix[0*4+0] * v[0]) + (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]) + matrix[3*4+0]; - REAL ty = (matrix[0*4+1] * v[0]) + (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]) + matrix[3*4+1]; - REAL tz = (matrix[0*4+2] * v[0]) + (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]) + matrix[3*4+2]; - t[0] = tx; - t[1] = ty; - t[2] = tz; - } - else - { - t[0] = v[0]; - t[1] = v[1]; - t[2] = v[2]; - } -} - -void fm_rotate(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point -{ - if ( matrix ) - { - REAL tx = (matrix[0*4+0] * v[0]) + (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]); - REAL ty = (matrix[0*4+1] * v[0]) + (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]); - REAL tz = (matrix[0*4+2] * v[0]) + (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]); - t[0] = tx; - t[1] = ty; - t[2] = tz; - } - else - { - t[0] = v[0]; - t[1] = v[1]; - t[2] = v[2]; - } -} - - -REAL fm_distance(const REAL *p1,const REAL *p2) -{ - REAL dx = p1[0] - p2[0]; - REAL dy = p1[1] - p2[1]; - REAL dz = p1[2] - p2[2]; - - return (REAL)sqrt( dx*dx + dy*dy + dz *dz ); -} - -REAL fm_distanceSquared(const REAL *p1,const REAL *p2) -{ - REAL dx = p1[0] - p2[0]; - REAL dy = p1[1] - p2[1]; - REAL dz = p1[2] - p2[2]; - - return dx*dx + dy*dy + dz *dz; -} - - -REAL fm_distanceSquaredXZ(const REAL *p1,const REAL *p2) -{ - REAL dx = p1[0] - p2[0]; - REAL dz = p1[2] - p2[2]; - - return dx*dx + dz *dz; -} - - -REAL fm_computePlane(const REAL *A,const REAL *B,const REAL *C,REAL *n) // returns D -{ - REAL vx = (B[0] - C[0]); - REAL vy = (B[1] - C[1]); - REAL vz = (B[2] - C[2]); - - REAL wx = (A[0] - B[0]); - REAL wy = (A[1] - B[1]); - REAL wz = (A[2] - B[2]); - - REAL vw_x = vy * wz - vz * wy; - REAL vw_y = vz * wx - vx * wz; - REAL vw_z = vx * wy - vy * wx; - - REAL mag = (REAL)sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z)); - - if ( mag < 0.000001f ) - { - mag = 0; - } - else - { - mag = 1.0f/mag; - } - - REAL x = vw_x * mag; - REAL y = vw_y * mag; - REAL z = vw_z * mag; - - - REAL D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2])); - - n[0] = x; - n[1] = y; - n[2] = z; - - return D; -} - -REAL fm_distToPlane(const REAL *plane,const REAL *p) // computes the distance of this point from the plane. -{ - return p[0]*plane[0]+p[1]*plane[1]+p[2]*plane[2]+plane[3]; -} - -REAL fm_dot(const REAL *p1,const REAL *p2) -{ - return p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2]; -} - -void fm_cross(REAL *cross,const REAL *a,const REAL *b) -{ - cross[0] = a[1]*b[2] - a[2]*b[1]; - cross[1] = a[2]*b[0] - a[0]*b[2]; - cross[2] = a[0]*b[1] - a[1]*b[0]; -} - -REAL fm_computeNormalVector(REAL *n,const REAL *p1,const REAL *p2) -{ - n[0] = p2[0] - p1[0]; - n[1] = p2[1] - p1[1]; - n[2] = p2[2] - p1[2]; - return fm_normalize(n); -} - -bool fm_computeWindingOrder(const REAL *p1,const REAL *p2,const REAL *p3) // returns true if the triangle is clockwise. -{ - bool ret = false; - - REAL v1[3]; - REAL v2[3]; - - fm_computeNormalVector(v1,p1,p2); // p2-p1 (as vector) and then normalized - fm_computeNormalVector(v2,p1,p3); // p3-p1 (as vector) and then normalized - - REAL cross[3]; - - fm_cross(cross, v1, v2 ); - REAL ref[3] = { 1, 0, 0 }; - - REAL d = fm_dot( cross, ref ); - - - if ( d <= 0 ) - ret = false; - else - ret = true; - - return ret; -} - -REAL fm_normalize(REAL *n) // normalize this vector -{ - REAL dist = (REAL)sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]); - if ( dist > 0.0000001f ) - { - REAL mag = 1.0f / dist; - n[0]*=mag; - n[1]*=mag; - n[2]*=mag; - } - else - { - n[0] = 1; - n[1] = 0; - n[2] = 0; - } - - return dist; -} - - -void fm_matrixMultiply(const REAL *pA,const REAL *pB,REAL *pM) -{ -#if 1 - - REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0]; - REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1]; - REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2]; - REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3]; - - REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0]; - REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1]; - REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2]; - REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3]; - - REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0]; - REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1]; - REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2]; - REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3]; - - REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0]; - REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1]; - REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2]; - REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3]; - - pM[0] = a; - pM[1] = b; - pM[2] = c; - pM[3] = d; - - pM[4] = e; - pM[5] = f; - pM[6] = g; - pM[7] = h; - - pM[8] = i; - pM[9] = j; - pM[10] = k; - pM[11] = l; - - pM[12] = m; - pM[13] = n; - pM[14] = o; - pM[15] = p; - - -#else - memset(pM, 0, sizeof(REAL)*16); - for(int32_t i=0; i<4; i++ ) - for(int32_t j=0; j<4; j++ ) - for(int32_t k=0; k<4; k++ ) - pM[4*i+j] += pA[4*i+k] * pB[4*k+j]; -#endif -} - - -void fm_eulerToQuatDX(REAL x,REAL y,REAL z,REAL *quat) // convert euler angles to quaternion using the fucked up DirectX method -{ - REAL matrix[16]; - fm_eulerToMatrix(x,y,z,matrix); - fm_matrixToQuat(matrix,quat); -} - -// implementation copied from: http://blogs.msdn.com/mikepelton/archive/2004/10/29/249501.aspx -void fm_eulerToMatrixDX(REAL x,REAL y,REAL z,REAL *matrix) // convert euler angles to quaternion using the fucked up DirectX method. -{ - fm_identity(matrix); - matrix[0*4+0] = (REAL)(cos(z)*cos(y) + sin(z)*sin(x)*sin(y)); - matrix[0*4+1] = (REAL)(sin(z)*cos(x)); - matrix[0*4+2] = (REAL)(cos(z)*-sin(y) + sin(z)*sin(x)*cos(y)); - - matrix[1*4+0] = (REAL)(-sin(z)*cos(y)+cos(z)*sin(x)*sin(y)); - matrix[1*4+1] = (REAL)(cos(z)*cos(x)); - matrix[1*4+2] = (REAL)(sin(z)*sin(y) +cos(z)*sin(x)*cos(y)); - - matrix[2*4+0] = (REAL)(cos(x)*sin(y)); - matrix[2*4+1] = (REAL)(-sin(x)); - matrix[2*4+2] = (REAL)(cos(x)*cos(y)); -} - - -void fm_scale(REAL x,REAL y,REAL z,REAL *fscale) // apply scale to the matrix. -{ - fscale[0*4+0] = x; - fscale[1*4+1] = y; - fscale[2*4+2] = z; -} - - -void fm_composeTransform(const REAL *position,const REAL *quat,const REAL *scale,REAL *matrix) -{ - fm_identity(matrix); - fm_quatToMatrix(quat,matrix); - - if ( scale && ( scale[0] != 1 || scale[1] != 1 || scale[2] != 1 ) ) - { - REAL work[16]; - memcpy(work,matrix,sizeof(REAL)*16); - REAL mscale[16]; - fm_identity(mscale); - fm_scale(scale[0],scale[1],scale[2],mscale); - fm_matrixMultiply(work,mscale,matrix); - } - - matrix[12] = position[0]; - matrix[13] = position[1]; - matrix[14] = position[2]; -} - - -void fm_setTranslation(const REAL *translation,REAL *matrix) -{ - matrix[12] = translation[0]; - matrix[13] = translation[1]; - matrix[14] = translation[2]; -} - -static REAL enorm0_3d ( REAL x0, REAL y0, REAL z0, REAL x1, REAL y1, REAL z1 ) - -/**********************************************************************/ - -/* -Purpose: - -ENORM0_3D computes the Euclidean norm of (P1-P0) in 3D. - -Modified: - -18 April 1999 - -Author: - -John Burkardt - -Parameters: - -Input, REAL X0, Y0, Z0, X1, Y1, Z1, the coordinates of the points -P0 and P1. - -Output, REAL ENORM0_3D, the Euclidean norm of (P1-P0). -*/ -{ - REAL value; - - value = (REAL)sqrt ( - ( x1 - x0 ) * ( x1 - x0 ) + - ( y1 - y0 ) * ( y1 - y0 ) + - ( z1 - z0 ) * ( z1 - z0 ) ); - - return value; -} - - -static REAL triangle_area_3d ( REAL x1, REAL y1, REAL z1, REAL x2,REAL y2, REAL z2, REAL x3, REAL y3, REAL z3 ) - - /**********************************************************************/ - - /* - Purpose: - - TRIANGLE_AREA_3D computes the area of a triangle in 3D. - - Modified: - - 22 April 1999 - - Author: - - John Burkardt - - Parameters: - - Input, REAL X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, the (X,Y,Z) - coordinates of the corners of the triangle. - - Output, REAL TRIANGLE_AREA_3D, the area of the triangle. - */ -{ - REAL a; - REAL alpha; - REAL area; - REAL b; - REAL base; - REAL c; - REAL dot; - REAL height; - /* - Find the projection of (P3-P1) onto (P2-P1). - */ - dot = - ( x2 - x1 ) * ( x3 - x1 ) + - ( y2 - y1 ) * ( y3 - y1 ) + - ( z2 - z1 ) * ( z3 - z1 ); - - base = enorm0_3d ( x1, y1, z1, x2, y2, z2 ); - /* - The height of the triangle is the length of (P3-P1) after its - projection onto (P2-P1) has been subtracted. - */ - if ( base == 0.0 ) { - - height = 0.0; - - } - else { - - alpha = dot / ( base * base ); - - a = x3 - x1 - alpha * ( x2 - x1 ); - b = y3 - y1 - alpha * ( y2 - y1 ); - c = z3 - z1 - alpha * ( z2 - z1 ); - - height = (REAL)sqrt ( a * a + b * b + c * c ); - - } - - area = 0.5f * base * height; - - return area; -} - - -REAL fm_computeArea(const REAL *p1,const REAL *p2,const REAL *p3) -{ - REAL ret = 0; - - ret = triangle_area_3d(p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],p3[0],p3[1],p3[2]); - - return ret; -} - - -void fm_lerp(const REAL *p1,const REAL *p2,REAL *dest,REAL lerpValue) -{ - dest[0] = ((p2[0] - p1[0])*lerpValue) + p1[0]; - dest[1] = ((p2[1] - p1[1])*lerpValue) + p1[1]; - dest[2] = ((p2[2] - p1[2])*lerpValue) + p1[2]; -} - -bool fm_pointTestXZ(const REAL *p,const REAL *i,const REAL *j) -{ - bool ret = false; - - if (((( i[2] <= p[2] ) && ( p[2] < j[2] )) || (( j[2] <= p[2] ) && ( p[2] < i[2] ))) && ( p[0] < (j[0] - i[0]) * (p[2] - i[2]) / (j[2] - i[2]) + i[0])) - ret = true; - - return ret; -}; - - -bool fm_insideTriangleXZ(const REAL *p,const REAL *p1,const REAL *p2,const REAL *p3) -{ - bool ret = false; - - int32_t c = 0; - if ( fm_pointTestXZ(p,p1,p2) ) c = !c; - if ( fm_pointTestXZ(p,p2,p3) ) c = !c; - if ( fm_pointTestXZ(p,p3,p1) ) c = !c; - if ( c ) ret = true; - - return ret; -} - -bool fm_insideAABB(const REAL *pos,const REAL *bmin,const REAL *bmax) -{ - bool ret = false; - - if ( pos[0] >= bmin[0] && pos[0] <= bmax[0] && - pos[1] >= bmin[1] && pos[1] <= bmax[1] && - pos[2] >= bmin[2] && pos[2] <= bmax[2] ) - ret = true; - - return ret; -} - - -uint32_t fm_clipTestPoint(const REAL *bmin,const REAL *bmax,const REAL *pos) -{ - uint32_t ret = 0; - - if ( pos[0] < bmin[0] ) - ret|=FMCS_XMIN; - else if ( pos[0] > bmax[0] ) - ret|=FMCS_XMAX; - - if ( pos[1] < bmin[1] ) - ret|=FMCS_YMIN; - else if ( pos[1] > bmax[1] ) - ret|=FMCS_YMAX; - - if ( pos[2] < bmin[2] ) - ret|=FMCS_ZMIN; - else if ( pos[2] > bmax[2] ) - ret|=FMCS_ZMAX; - - return ret; -} - -uint32_t fm_clipTestPointXZ(const REAL *bmin,const REAL *bmax,const REAL *pos) // only tests X and Z, not Y -{ - uint32_t ret = 0; - - if ( pos[0] < bmin[0] ) - ret|=FMCS_XMIN; - else if ( pos[0] > bmax[0] ) - ret|=FMCS_XMAX; - - if ( pos[2] < bmin[2] ) - ret|=FMCS_ZMIN; - else if ( pos[2] > bmax[2] ) - ret|=FMCS_ZMAX; - - return ret; -} - -uint32_t fm_clipTestAABB(const REAL *bmin,const REAL *bmax,const REAL *p1,const REAL *p2,const REAL *p3,uint32_t &andCode) -{ - uint32_t orCode = 0; - - andCode = FMCS_XMIN | FMCS_XMAX | FMCS_YMIN | FMCS_YMAX | FMCS_ZMIN | FMCS_ZMAX; - - uint32_t c = fm_clipTestPoint(bmin,bmax,p1); - orCode|=c; - andCode&=c; - - c = fm_clipTestPoint(bmin,bmax,p2); - orCode|=c; - andCode&=c; - - c = fm_clipTestPoint(bmin,bmax,p3); - orCode|=c; - andCode&=c; - - return orCode; -} - -bool intersect(const REAL *si,const REAL *ei,const REAL *bmin,const REAL *bmax,REAL *time) -{ - REAL st,et,fst = 0,fet = 1; - - for (int32_t i = 0; i < 3; i++) - { - if (*si < *ei) - { - if (*si > *bmax || *ei < *bmin) - return false; - REAL di = *ei - *si; - st = (*si < *bmin)? (*bmin - *si) / di: 0; - et = (*ei > *bmax)? (*bmax - *si) / di: 1; - } - else - { - if (*ei > *bmax || *si < *bmin) - return false; - REAL di = *ei - *si; - st = (*si > *bmax)? (*bmax - *si) / di: 0; - et = (*ei < *bmin)? (*bmin - *si) / di: 1; - } - - if (st > fst) fst = st; - if (et < fet) fet = et; - if (fet < fst) - return false; - bmin++; bmax++; - si++; ei++; - } - - *time = fst; - return true; -} - - - -bool fm_lineTestAABB(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time) -{ - bool sect = intersect(p1,p2,bmin,bmax,&time); - return sect; -} - - -bool fm_lineTestAABBXZ(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time) -{ - REAL _bmin[3]; - REAL _bmax[3]; - - _bmin[0] = bmin[0]; - _bmin[1] = -1e9; - _bmin[2] = bmin[2]; - - _bmax[0] = bmax[0]; - _bmax[1] = 1e9; - _bmax[2] = bmax[2]; - - bool sect = intersect(p1,p2,_bmin,_bmax,&time); - - return sect; -} - -void fm_minmax(const REAL *p,REAL *bmin,REAL *bmax) // accmulate to a min-max value -{ - - if ( p[0] < bmin[0] ) bmin[0] = p[0]; - if ( p[1] < bmin[1] ) bmin[1] = p[1]; - if ( p[2] < bmin[2] ) bmin[2] = p[2]; - - if ( p[0] > bmax[0] ) bmax[0] = p[0]; - if ( p[1] > bmax[1] ) bmax[1] = p[1]; - if ( p[2] > bmax[2] ) bmax[2] = p[2]; - -} - -REAL fm_solveX(const REAL *plane,REAL y,REAL z) // solve for X given this plane equation and the other two components. -{ - REAL x = (y*plane[1]+z*plane[2]+plane[3]) / -plane[0]; - return x; -} - -REAL fm_solveY(const REAL *plane,REAL x,REAL z) // solve for Y given this plane equation and the other two components. -{ - REAL y = (x*plane[0]+z*plane[2]+plane[3]) / -plane[1]; - return y; -} - - -REAL fm_solveZ(const REAL *plane,REAL x,REAL y) // solve for Y given this plane equation and the other two components. -{ - REAL z = (x*plane[0]+y*plane[1]+plane[3]) / -plane[2]; - return z; -} - - -void fm_getAABBCenter(const REAL *bmin,const REAL *bmax,REAL *center) -{ - center[0] = (bmax[0]-bmin[0])*0.5f+bmin[0]; - center[1] = (bmax[1]-bmin[1])*0.5f+bmin[1]; - center[2] = (bmax[2]-bmin[2])*0.5f+bmin[2]; -} - -FM_Axis fm_getDominantAxis(const REAL normal[3]) -{ - FM_Axis ret = FM_XAXIS; - - REAL x = (REAL)fabs(normal[0]); - REAL y = (REAL)fabs(normal[1]); - REAL z = (REAL)fabs(normal[2]); - - if ( y > x && y > z ) - ret = FM_YAXIS; - else if ( z > x && z > y ) - ret = FM_ZAXIS; - - return ret; -} - - -bool fm_lineSphereIntersect(const REAL *center,REAL radius,const REAL *p1,const REAL *p2,REAL *intersect) -{ - bool ret = false; - - REAL dir[3]; - - dir[0] = p2[0]-p1[0]; - dir[1] = p2[1]-p1[1]; - dir[2] = p2[2]-p1[2]; - - REAL distance = (REAL)sqrt( dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]); - - if ( distance > 0 ) - { - REAL recip = 1.0f / distance; - dir[0]*=recip; - dir[1]*=recip; - dir[2]*=recip; - ret = fm_raySphereIntersect(center,radius,p1,dir,distance,intersect); - } - else - { - dir[0] = center[0]-p1[0]; - dir[1] = center[1]-p1[1]; - dir[2] = center[2]-p1[2]; - REAL d2 = dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]; - REAL r2 = radius*radius; - if ( d2 < r2 ) - { - ret = true; - if ( intersect ) - { - intersect[0] = p1[0]; - intersect[1] = p1[1]; - intersect[2] = p1[2]; - } - } - } - return ret; -} - -#define DOT(p1,p2) (p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2]) - -bool fm_raySphereIntersect(const REAL *center,REAL radius,const REAL *pos,const REAL *dir,REAL distance,REAL *intersect) -{ - bool ret = false; - - REAL E0[3]; - - E0[0] = center[0] - pos[0]; - E0[1] = center[1] - pos[1]; - E0[2] = center[2] - pos[2]; - - REAL V[3]; - - V[0] = dir[0]; - V[1] = dir[1]; - V[2] = dir[2]; - - - REAL dist2 = E0[0]*E0[0] + E0[1]*E0[1] + E0[2] * E0[2]; - REAL radius2 = radius*radius; // radius squared.. - - // Bug Fix For Gem, if origin is *inside* the sphere, invert the - // direction vector so that we get a valid intersection location. - if ( dist2 < radius2 ) - { - V[0]*=-1; - V[1]*=-1; - V[2]*=-1; - } - - - REAL v = DOT(E0,V); - - REAL disc = radius2 - (dist2 - v*v); - - if (disc > 0.0f) - { - if ( intersect ) - { - REAL d = (REAL)sqrt(disc); - REAL diff = v-d; - if ( diff < distance ) - { - intersect[0] = pos[0]+V[0]*diff; - intersect[1] = pos[1]+V[1]*diff; - intersect[2] = pos[2]+V[2]*diff; - ret = true; - } - } - } - - return ret; -} - - -void fm_catmullRom(REAL *out_vector,const REAL *p1,const REAL *p2,const REAL *p3,const REAL *p4, const REAL s) -{ - REAL s_squared = s * s; - REAL s_cubed = s_squared * s; - - REAL coefficient_p1 = -s_cubed + 2*s_squared - s; - REAL coefficient_p2 = 3 * s_cubed - 5 * s_squared + 2; - REAL coefficient_p3 = -3 * s_cubed +4 * s_squared + s; - REAL coefficient_p4 = s_cubed - s_squared; - - out_vector[0] = (coefficient_p1 * p1[0] + coefficient_p2 * p2[0] + coefficient_p3 * p3[0] + coefficient_p4 * p4[0])*0.5f; - out_vector[1] = (coefficient_p1 * p1[1] + coefficient_p2 * p2[1] + coefficient_p3 * p3[1] + coefficient_p4 * p4[1])*0.5f; - out_vector[2] = (coefficient_p1 * p1[2] + coefficient_p2 * p2[2] + coefficient_p3 * p3[2] + coefficient_p4 * p4[2])*0.5f; -} - -bool fm_intersectAABB(const REAL *bmin1,const REAL *bmax1,const REAL *bmin2,const REAL *bmax2) -{ - if ((bmin1[0] > bmax2[0]) || (bmin2[0] > bmax1[0])) return false; - if ((bmin1[1] > bmax2[1]) || (bmin2[1] > bmax1[1])) return false; - if ((bmin1[2] > bmax2[2]) || (bmin2[2] > bmax1[2])) return false; - return true; - -} - -bool fm_insideAABB(const REAL *obmin,const REAL *obmax,const REAL *tbmin,const REAL *tbmax) // test if bounding box tbmin/tmbax is fully inside obmin/obmax -{ - bool ret = false; - - if ( tbmax[0] <= obmax[0] && - tbmax[1] <= obmax[1] && - tbmax[2] <= obmax[2] && - tbmin[0] >= obmin[0] && - tbmin[1] >= obmin[1] && - tbmin[2] >= obmin[2] ) ret = true; - - return ret; -} - - -// Reference, from Stan Melax in Game Gems I -// Quaternion q; -// vector3 c = CrossProduct(v0,v1); -// REAL d = DotProduct(v0,v1); -// REAL s = (REAL)sqrt((1+d)*2); -// q.x = c.x / s; -// q.y = c.y / s; -// q.z = c.z / s; -// q.w = s /2.0f; -// return q; -void fm_rotationArc(const REAL *v0,const REAL *v1,REAL *quat) -{ - REAL cross[3]; - - fm_cross(cross,v0,v1); - REAL d = fm_dot(v0,v1); - - if( d<= -0.99999f ) // 180 about x axis - { - if ( fabsf((float)v0[0]) < 0.1f ) - { - quat[0] = 0; - quat[1] = v0[2]; - quat[2] = -v0[1]; - quat[3] = 0; - } - else - { - quat[0] = v0[1]; - quat[1] = -v0[0]; - quat[2] = 0; - quat[3] = 0; - } - REAL magnitudeSquared = quat[0]*quat[0] + quat[1]*quat[1] + quat[2]*quat[2] + quat[3]*quat[3]; - REAL magnitude = sqrtf((float)magnitudeSquared); - REAL recip = 1.0f / magnitude; - quat[0]*=recip; - quat[1]*=recip; - quat[2]*=recip; - quat[3]*=recip; - } - else - { - REAL s = (REAL)sqrt((1+d)*2); - REAL recip = 1.0f / s; - - quat[0] = cross[0] * recip; - quat[1] = cross[1] * recip; - quat[2] = cross[2] * recip; - quat[3] = s * 0.5f; - } -} - - -REAL fm_distancePointLineSegment(const REAL *Point,const REAL *LineStart,const REAL *LineEnd,REAL *intersection,LineSegmentType &type,REAL epsilon) -{ - REAL ret; - - REAL LineMag = fm_distance( LineEnd, LineStart ); - - if ( LineMag > 0 ) - { - REAL U = ( ( ( Point[0] - LineStart[0] ) * ( LineEnd[0] - LineStart[0] ) ) + ( ( Point[1] - LineStart[1] ) * ( LineEnd[1] - LineStart[1] ) ) + ( ( Point[2] - LineStart[2] ) * ( LineEnd[2] - LineStart[2] ) ) ) / ( LineMag * LineMag ); - if( U < 0.0f || U > 1.0f ) - { - REAL d1 = fm_distanceSquared(Point,LineStart); - REAL d2 = fm_distanceSquared(Point,LineEnd); - if ( d1 <= d2 ) - { - ret = (REAL)sqrt(d1); - intersection[0] = LineStart[0]; - intersection[1] = LineStart[1]; - intersection[2] = LineStart[2]; - type = LS_START; - } - else - { - ret = (REAL)sqrt(d2); - intersection[0] = LineEnd[0]; - intersection[1] = LineEnd[1]; - intersection[2] = LineEnd[2]; - type = LS_END; - } - } - else - { - intersection[0] = LineStart[0] + U * ( LineEnd[0] - LineStart[0] ); - intersection[1] = LineStart[1] + U * ( LineEnd[1] - LineStart[1] ); - intersection[2] = LineStart[2] + U * ( LineEnd[2] - LineStart[2] ); - - ret = fm_distance(Point,intersection); - - REAL d1 = fm_distanceSquared(intersection,LineStart); - REAL d2 = fm_distanceSquared(intersection,LineEnd); - REAL mag = (epsilon*2)*(epsilon*2); - - if ( d1 < mag ) // if less than 1/100th the total distance, treat is as the 'start' - { - type = LS_START; - } - else if ( d2 < mag ) - { - type = LS_END; - } - else - { - type = LS_MIDDLE; - } - - } - } - else - { - ret = LineMag; - intersection[0] = LineEnd[0]; - intersection[1] = LineEnd[1]; - intersection[2] = LineEnd[2]; - type = LS_END; - } - - return ret; -} - - -#ifndef BEST_FIT_PLANE_H - -#define BEST_FIT_PLANE_H - -template class Eigen -{ -public: - - - void DecrSortEigenStuff(void) - { - Tridiagonal(); //diagonalize the matrix. - QLAlgorithm(); // - DecreasingSort(); - GuaranteeRotation(); - } - - void Tridiagonal(void) - { - Type fM00 = mElement[0][0]; - Type fM01 = mElement[0][1]; - Type fM02 = mElement[0][2]; - Type fM11 = mElement[1][1]; - Type fM12 = mElement[1][2]; - Type fM22 = mElement[2][2]; - - m_afDiag[0] = fM00; - m_afSubd[2] = 0; - if (fM02 != (Type)0.0) - { - Type fLength = (REAL)sqrt(fM01*fM01+fM02*fM02); - Type fInvLength = ((Type)1.0)/fLength; - fM01 *= fInvLength; - fM02 *= fInvLength; - Type fQ = ((Type)2.0)*fM01*fM12+fM02*(fM22-fM11); - m_afDiag[1] = fM11+fM02*fQ; - m_afDiag[2] = fM22-fM02*fQ; - m_afSubd[0] = fLength; - m_afSubd[1] = fM12-fM01*fQ; - mElement[0][0] = (Type)1.0; - mElement[0][1] = (Type)0.0; - mElement[0][2] = (Type)0.0; - mElement[1][0] = (Type)0.0; - mElement[1][1] = fM01; - mElement[1][2] = fM02; - mElement[2][0] = (Type)0.0; - mElement[2][1] = fM02; - mElement[2][2] = -fM01; - m_bIsRotation = false; - } - else - { - m_afDiag[1] = fM11; - m_afDiag[2] = fM22; - m_afSubd[0] = fM01; - m_afSubd[1] = fM12; - mElement[0][0] = (Type)1.0; - mElement[0][1] = (Type)0.0; - mElement[0][2] = (Type)0.0; - mElement[1][0] = (Type)0.0; - mElement[1][1] = (Type)1.0; - mElement[1][2] = (Type)0.0; - mElement[2][0] = (Type)0.0; - mElement[2][1] = (Type)0.0; - mElement[2][2] = (Type)1.0; - m_bIsRotation = true; - } - } - - bool QLAlgorithm(void) - { - const int32_t iMaxIter = 32; - - for (int32_t i0 = 0; i0 <3; i0++) - { - int32_t i1; - for (i1 = 0; i1 < iMaxIter; i1++) - { - int32_t i2; - for (i2 = i0; i2 <= (3-2); i2++) - { - Type fTmp = fabs(m_afDiag[i2]) + fabs(m_afDiag[i2+1]); - if ( fabs(m_afSubd[i2]) + fTmp == fTmp ) - break; - } - if (i2 == i0) - { - break; - } - - Type fG = (m_afDiag[i0+1] - m_afDiag[i0])/(((Type)2.0) * m_afSubd[i0]); - Type fR = (REAL)sqrt(fG*fG+(Type)1.0); - if (fG < (Type)0.0) - { - fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG-fR); - } - else - { - fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG+fR); - } - Type fSin = (Type)1.0, fCos = (Type)1.0, fP = (Type)0.0; - for (int32_t i3 = i2-1; i3 >= i0; i3--) - { - Type fF = fSin*m_afSubd[i3]; - Type fB = fCos*m_afSubd[i3]; - if (fabs(fF) >= fabs(fG)) - { - fCos = fG/fF; - fR = (REAL)sqrt(fCos*fCos+(Type)1.0); - m_afSubd[i3+1] = fF*fR; - fSin = ((Type)1.0)/fR; - fCos *= fSin; - } - else - { - fSin = fF/fG; - fR = (REAL)sqrt(fSin*fSin+(Type)1.0); - m_afSubd[i3+1] = fG*fR; - fCos = ((Type)1.0)/fR; - fSin *= fCos; - } - fG = m_afDiag[i3+1]-fP; - fR = (m_afDiag[i3]-fG)*fSin+((Type)2.0)*fB*fCos; - fP = fSin*fR; - m_afDiag[i3+1] = fG+fP; - fG = fCos*fR-fB; - for (int32_t i4 = 0; i4 < 3; i4++) - { - fF = mElement[i4][i3+1]; - mElement[i4][i3+1] = fSin*mElement[i4][i3]+fCos*fF; - mElement[i4][i3] = fCos*mElement[i4][i3]-fSin*fF; - } - } - m_afDiag[i0] -= fP; - m_afSubd[i0] = fG; - m_afSubd[i2] = (Type)0.0; - } - if (i1 == iMaxIter) - { - return false; - } - } - return true; - } - - void DecreasingSort(void) - { - //sort eigenvalues in decreasing order, e[0] >= ... >= e[iSize-1] - for (int32_t i0 = 0, i1; i0 <= 3-2; i0++) - { - // locate maximum eigenvalue - i1 = i0; - Type fMax = m_afDiag[i1]; - int32_t i2; - for (i2 = i0+1; i2 < 3; i2++) - { - if (m_afDiag[i2] > fMax) - { - i1 = i2; - fMax = m_afDiag[i1]; - } - } - - if (i1 != i0) - { - // swap eigenvalues - m_afDiag[i1] = m_afDiag[i0]; - m_afDiag[i0] = fMax; - // swap eigenvectors - for (i2 = 0; i2 < 3; i2++) - { - Type fTmp = mElement[i2][i0]; - mElement[i2][i0] = mElement[i2][i1]; - mElement[i2][i1] = fTmp; - m_bIsRotation = !m_bIsRotation; - } - } - } - } - - - void GuaranteeRotation(void) - { - if (!m_bIsRotation) - { - // change sign on the first column - for (int32_t iRow = 0; iRow <3; iRow++) - { - mElement[iRow][0] = -mElement[iRow][0]; - } - } - } - - Type mElement[3][3]; - Type m_afDiag[3]; - Type m_afSubd[3]; - bool m_bIsRotation; -}; - -#endif - -bool fm_computeBestFitPlane(uint32_t vcount, - const REAL *points, - uint32_t vstride, - const REAL *weights, - uint32_t wstride, - REAL *plane, - REAL *center) -{ - bool ret = false; - - REAL kOrigin[3] = { 0, 0, 0 }; - - REAL wtotal = 0; - - { - const char *source = (const char *) points; - const char *wsource = (const char *) weights; - - for (uint32_t i=0; i kES; - - kES.mElement[0][0] = fSumXX; - kES.mElement[0][1] = fSumXY; - kES.mElement[0][2] = fSumXZ; - - kES.mElement[1][0] = fSumXY; - kES.mElement[1][1] = fSumYY; - kES.mElement[1][2] = fSumYZ; - - kES.mElement[2][0] = fSumXZ; - kES.mElement[2][1] = fSumYZ; - kES.mElement[2][2] = fSumZZ; - - // compute eigenstuff, smallest eigenvalue is in last position - kES.DecrSortEigenStuff(); - - REAL kNormal[3]; - - kNormal[0] = kES.mElement[0][2]; - kNormal[1] = kES.mElement[1][2]; - kNormal[2] = kES.mElement[2][2]; - - // the minimum energy - plane[0] = kNormal[0]; - plane[1] = kNormal[1]; - plane[2] = kNormal[2]; - - plane[3] = 0 - fm_dot(kNormal,kOrigin); - - ret = true; - - return ret; -} - - -bool fm_colinear(const REAL a1[3],const REAL a2[3],const REAL b1[3],const REAL b2[3],REAL epsilon) // true if these two line segments are co-linear. -{ - bool ret = false; - - REAL dir1[3]; - REAL dir2[3]; - - dir1[0] = (a2[0] - a1[0]); - dir1[1] = (a2[1] - a1[1]); - dir1[2] = (a2[2] - a1[2]); - - dir2[0] = (b2[0]-a1[0]) - (b1[0]-a1[0]); - dir2[1] = (b2[1]-a1[1]) - (b1[1]-a1[1]); - dir2[2] = (b2[2]-a2[2]) - (b1[2]-a2[2]); - - fm_normalize(dir1); - fm_normalize(dir2); - - REAL dot = fm_dot(dir1,dir2); - - if ( dot >= epsilon ) - { - ret = true; - } - - - return ret; -} - -bool fm_colinear(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon) -{ - bool ret = false; - - REAL dir1[3]; - REAL dir2[3]; - - dir1[0] = p2[0] - p1[0]; - dir1[1] = p2[1] - p1[1]; - dir1[2] = p2[2] - p1[2]; - - dir2[0] = p3[0] - p2[0]; - dir2[1] = p3[1] - p2[1]; - dir2[2] = p3[2] - p2[2]; - - fm_normalize(dir1); - fm_normalize(dir2); - - REAL dot = fm_dot(dir1,dir2); - - if ( dot >= epsilon ) - { - ret = true; - } - - - return ret; -} - -void fm_initMinMax(const REAL *p,REAL *bmin,REAL *bmax) -{ - bmax[0] = bmin[0] = p[0]; - bmax[1] = bmin[1] = p[1]; - bmax[2] = bmin[2] = p[2]; -} - -IntersectResult fm_intersectLineSegments2d(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL *intersection) -{ - IntersectResult ret; - - REAL denom = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1])); - REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0])); - REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0])); - if (denom == 0 ) - { - if(nume_a == 0 && nume_b == 0) - { - ret = IR_COINCIDENT; - } - else - { - ret = IR_PARALLEL; - } - } - else - { - - REAL recip = 1 / denom; - REAL ua = nume_a * recip; - REAL ub = nume_b * recip; - - if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 ) - { - // Get the intersection point. - intersection[0] = a1[0] + ua*(a2[0] - a1[0]); - intersection[1] = a1[1] + ua*(a2[1] - a1[1]); - ret = IR_DO_INTERSECT; - } - else - { - ret = IR_DONT_INTERSECT; - } - } - return ret; -} - -IntersectResult fm_intersectLineSegments2dTime(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL &t1,REAL &t2) -{ - IntersectResult ret; - - REAL denom = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1])); - REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0])); - REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0])); - if (denom == 0 ) - { - if(nume_a == 0 && nume_b == 0) - { - ret = IR_COINCIDENT; - } - else - { - ret = IR_PARALLEL; - } - } - else - { - - REAL recip = 1 / denom; - REAL ua = nume_a * recip; - REAL ub = nume_b * recip; - - if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 ) - { - t1 = ua; - t2 = ub; - ret = IR_DO_INTERSECT; - } - else - { - ret = IR_DONT_INTERSECT; - } - } - return ret; -} - -//**** Plane Triangle Intersection - - - - - -// assumes that the points are on opposite sides of the plane! -bool fm_intersectPointPlane(const REAL *p1,const REAL *p2,REAL *split,const REAL *plane) -{ - - REAL dp1 = fm_distToPlane(plane,p1); - REAL dp2 = fm_distToPlane(plane, p2); - if (dp1 <= 0 && dp2 <= 0) - { - return false; - } - if (dp1 >= 0 && dp2 >= 0) - { - return false; - } - - REAL dir[3]; - - dir[0] = p2[0] - p1[0]; - dir[1] = p2[1] - p1[1]; - dir[2] = p2[2] - p1[2]; - - REAL dot1 = dir[0]*plane[0] + dir[1]*plane[1] + dir[2]*plane[2]; - REAL dot2 = dp1 - plane[3]; - - REAL t = -(plane[3] + dot2 ) / dot1; - - split[0] = (dir[0]*t)+p1[0]; - split[1] = (dir[1]*t)+p1[1]; - split[2] = (dir[2]*t)+p1[2]; - - return true; -} - -PlaneTriResult fm_getSidePlane(const REAL *p,const REAL *plane,REAL epsilon) -{ - PlaneTriResult ret = PTR_ON_PLANE; - - REAL d = fm_distToPlane(plane,p); - - if ( d < -epsilon || d > epsilon ) - { - if ( d > 0 ) - ret = PTR_FRONT; // it is 'in front' within the provided epsilon value. - else - ret = PTR_BACK; - } - - return ret; -} - - - -#ifndef PLANE_TRIANGLE_INTERSECTION_H - -#define PLANE_TRIANGLE_INTERSECTION_H - -#define MAXPTS 256 - -template class point -{ -public: - - void set(const Type *p) - { - x = p[0]; - y = p[1]; - z = p[2]; - } - - Type x; - Type y; - Type z; -}; - -template class plane -{ -public: - plane(const Type *p) - { - normal.x = p[0]; - normal.y = p[1]; - normal.z = p[2]; - D = p[3]; - } - - Type Classify_Point(const point &p) - { - return p.x*normal.x + p.y*normal.y + p.z*normal.z + D; - } - - point normal; - Type D; -}; - -template class polygon -{ -public: - polygon(void) - { - mVcount = 0; - } - - polygon(const Type *p1,const Type *p2,const Type *p3) - { - mVcount = 3; - mVertices[0].set(p1); - mVertices[1].set(p2); - mVertices[2].set(p3); - } - - - int32_t NumVertices(void) const { return mVcount; }; - - const point& Vertex(int32_t index) - { - if ( index < 0 ) index+=mVcount; - return mVertices[index]; - }; - - - void set(const point *pts,int32_t count) - { - for (int32_t i=0; i *poly,plane *part, polygon &front, polygon &back) - { - int32_t count = poly->NumVertices (); - int32_t out_c = 0, in_c = 0; - point ptA, ptB,outpts[MAXPTS],inpts[MAXPTS]; - Type sideA, sideB; - ptA = poly->Vertex (count - 1); - sideA = part->Classify_Point (ptA); - for (int32_t i = -1; ++i < count;) - { - ptB = poly->Vertex(i); - sideB = part->Classify_Point(ptB); - if (sideB > 0) - { - if (sideA < 0) - { - point v; - fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x ); - outpts[out_c++] = inpts[in_c++] = v; - } - outpts[out_c++] = ptB; - } - else if (sideB < 0) - { - if (sideA > 0) - { - point v; - fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x ); - outpts[out_c++] = inpts[in_c++] = v; - } - inpts[in_c++] = ptB; - } - else - outpts[out_c++] = inpts[in_c++] = ptB; - ptA = ptB; - sideA = sideB; - } - - front.set(&outpts[0], out_c); - back.set(&inpts[0], in_c); - } - - int32_t mVcount; - point mVertices[MAXPTS]; -}; - - - -#endif - -static inline void add(const REAL *p,REAL *dest,uint32_t tstride,uint32_t &pcount) -{ - char *d = (char *) dest; - d = d + pcount*tstride; - dest = (REAL *) d; - dest[0] = p[0]; - dest[1] = p[1]; - dest[2] = p[2]; - pcount++; - assert( pcount <= 4 ); -} - - -PlaneTriResult fm_planeTriIntersection(const REAL *_plane, // the plane equation in Ax+By+Cz+D format - const REAL *triangle, // the source triangle. - uint32_t tstride, // stride in bytes of the input and output *vertices* - REAL epsilon, // the co-planar epsilon value. - REAL *front, // the triangle in front of the - uint32_t &fcount, // number of vertices in the 'front' triangle - REAL *back, // the triangle in back of the plane - uint32_t &bcount) // the number of vertices in the 'back' triangle. -{ - - fcount = 0; - bcount = 0; - - const char *tsource = (const char *) triangle; - - // get the three vertices of the triangle. - const REAL *p1 = (const REAL *) (tsource); - const REAL *p2 = (const REAL *) (tsource+tstride); - const REAL *p3 = (const REAL *) (tsource+tstride*2); - - - PlaneTriResult r1 = fm_getSidePlane(p1,_plane,epsilon); // compute the side of the plane each vertex is on - PlaneTriResult r2 = fm_getSidePlane(p2,_plane,epsilon); - PlaneTriResult r3 = fm_getSidePlane(p3,_plane,epsilon); - - // If any of the points lay right *on* the plane.... - if ( r1 == PTR_ON_PLANE || r2 == PTR_ON_PLANE || r3 == PTR_ON_PLANE ) - { - // If the triangle is completely co-planar, then just treat it as 'front' and return! - if ( r1 == PTR_ON_PLANE && r2 == PTR_ON_PLANE && r3 == PTR_ON_PLANE ) - { - add(p1,front,tstride,fcount); - add(p2,front,tstride,fcount); - add(p3,front,tstride,fcount); - return PTR_FRONT; - } - // Decide to place the co-planar points on the same side as the co-planar point. - PlaneTriResult r= PTR_ON_PLANE; - if ( r1 != PTR_ON_PLANE ) - r = r1; - else if ( r2 != PTR_ON_PLANE ) - r = r2; - else if ( r3 != PTR_ON_PLANE ) - r = r3; - - if ( r1 == PTR_ON_PLANE ) r1 = r; - if ( r2 == PTR_ON_PLANE ) r2 = r; - if ( r3 == PTR_ON_PLANE ) r3 = r; - - } - - if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane. - { - if ( r1 == PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle. - { - add(p1,front,tstride,fcount); - add(p2,front,tstride,fcount); - add(p3,front,tstride,fcount); - } - else - { - add(p1,back,tstride,bcount); // if all three are in 'back' then copy to the 'back' output triangle. - add(p2,back,tstride,bcount); - add(p3,back,tstride,bcount); - } - return r1; // if all three points are on the same side of the plane return result - } - - - polygon pi(p1,p2,p3); - polygon pfront,pback; - - plane part(_plane); - - pi.Split_Polygon(&pi,&part,pfront,pback); - - for (int32_t i=0; i bmax[0] ) bmax[0] = t[0]; - if ( t[1] > bmax[1] ) bmax[1] = t[1]; - if ( t[2] > bmax[2] ) bmax[2] = t[2]; - - src+=pstride; - } - - REAL center[3]; - - sides[0] = bmax[0]-bmin[0]; - sides[1] = bmax[1]-bmin[1]; - sides[2] = bmax[2]-bmin[2]; - - center[0] = sides[0]*0.5f+bmin[0]; - center[1] = sides[1]*0.5f+bmin[1]; - center[2] = sides[2]*0.5f+bmin[2]; - - REAL ocenter[3]; - - fm_rotate(matrix,center,ocenter); - - matrix[12]+=ocenter[0]; - matrix[13]+=ocenter[1]; - matrix[14]+=ocenter[2]; - -} - -void fm_computeBestFitOBB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *matrix,bool bruteForce) -{ - REAL plane[4]; - REAL center[3]; - fm_computeBestFitPlane(vcount,points,pstride,0,0,plane,center); - fm_planeToMatrix(plane,matrix); - computeOBB( vcount, points, pstride, sides, matrix ); - - REAL refmatrix[16]; - memcpy(refmatrix,matrix,16*sizeof(REAL)); - - REAL volume = sides[0]*sides[1]*sides[2]; - if ( bruteForce ) - { - for (REAL a=10; a<180; a+=10) - { - REAL quat[4]; - fm_eulerToQuat(0,a*FM_DEG_TO_RAD,0,quat); - REAL temp[16]; - REAL pmatrix[16]; - fm_quatToMatrix(quat,temp); - fm_matrixMultiply(temp,refmatrix,pmatrix); - REAL psides[3]; - computeOBB( vcount, points, pstride, psides, pmatrix ); - REAL v = psides[0]*psides[1]*psides[2]; - if ( v < volume ) - { - volume = v; - memcpy(matrix,pmatrix,sizeof(REAL)*16); - sides[0] = psides[0]; - sides[1] = psides[1]; - sides[2] = psides[2]; - } - } - } -} - -void fm_computeBestFitOBB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *pos,REAL *quat,bool bruteForce) -{ - REAL matrix[16]; - fm_computeBestFitOBB(vcount,points,pstride,sides,matrix,bruteForce); - fm_getTranslation(matrix,pos); - fm_matrixToQuat(matrix,quat); -} - -void fm_computeBestFitABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *sides,REAL *pos) -{ - REAL bmin[3]; - REAL bmax[3]; - - bmin[0] = points[0]; - bmin[1] = points[1]; - bmin[2] = points[2]; - - bmax[0] = points[0]; - bmax[1] = points[1]; - bmax[2] = points[2]; - - const char *cp = (const char *) points; - for (uint32_t i=0; i bmax[0] ) bmax[0] = p[0]; - if ( p[1] > bmax[1] ) bmax[1] = p[1]; - if ( p[2] > bmax[2] ) bmax[2] = p[2]; - - cp+=pstride; - } - - - sides[0] = bmax[0] - bmin[0]; - sides[1] = bmax[1] - bmin[1]; - sides[2] = bmax[2] - bmin[2]; - - pos[0] = bmin[0]+sides[0]*0.5f; - pos[1] = bmin[1]+sides[1]*0.5f; - pos[2] = bmin[2]+sides[2]*0.5f; - -} - - -void fm_planeToMatrix(const REAL *plane,REAL *matrix) // convert a plane equation to a 4x4 rotation matrix -{ - REAL ref[3] = { 0, 1, 0 }; - REAL quat[4]; - fm_rotationArc(ref,plane,quat); - fm_quatToMatrix(quat,matrix); - REAL origin[3] = { 0, -plane[3], 0 }; - REAL center[3]; - fm_transform(matrix,origin,center); - fm_setTranslation(center,matrix); -} - -void fm_planeToQuat(const REAL *plane,REAL *quat,REAL *pos) // convert a plane equation to a quaternion and translation -{ - REAL ref[3] = { 0, 1, 0 }; - REAL matrix[16]; - fm_rotationArc(ref,plane,quat); - fm_quatToMatrix(quat,matrix); - REAL origin[3] = { 0, plane[3], 0 }; - fm_transform(matrix,origin,pos); -} - -void fm_eulerMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) -{ - REAL quat[4]; - fm_eulerToQuat(ax,ay,az,quat); - fm_quatToMatrix(quat,matrix); -} - - -//********************************************************** -//********************************************************** -//**** Vertex Welding -//********************************************************** -//********************************************************** - -#ifndef VERTEX_INDEX_H - -#define VERTEX_INDEX_H - -namespace VERTEX_INDEX -{ - -class KdTreeNode; - -typedef std::vector< KdTreeNode * > KdTreeNodeVector; - -enum Axes -{ - X_AXIS = 0, - Y_AXIS = 1, - Z_AXIS = 2 -}; - -class KdTreeFindNode -{ -public: - KdTreeFindNode(void) - { - mNode = 0; - mDistance = 0; - } - KdTreeNode *mNode; - double mDistance; -}; - -class KdTreeInterface -{ -public: - virtual const double * getPositionDouble(uint32_t index) const = 0; - virtual const float * getPositionFloat(uint32_t index) const = 0; -}; - -class KdTreeNode -{ -public: - KdTreeNode(void) - { - mIndex = 0; - mLeft = 0; - mRight = 0; - } - - KdTreeNode(uint32_t index) - { - mIndex = index; - mLeft = 0; - mRight = 0; - }; - - ~KdTreeNode(void) - { - } - - - void addDouble(KdTreeNode *node,Axes dim,const KdTreeInterface *iface) - { - const double *nodePosition = iface->getPositionDouble( node->mIndex ); - const double *position = iface->getPositionDouble( mIndex ); - switch ( dim ) - { - case X_AXIS: - if ( nodePosition[0] <= position[0] ) - { - if ( mLeft ) - mLeft->addDouble(node,Y_AXIS,iface); - else - mLeft = node; - } - else - { - if ( mRight ) - mRight->addDouble(node,Y_AXIS,iface); - else - mRight = node; - } - break; - case Y_AXIS: - if ( nodePosition[1] <= position[1] ) - { - if ( mLeft ) - mLeft->addDouble(node,Z_AXIS,iface); - else - mLeft = node; - } - else - { - if ( mRight ) - mRight->addDouble(node,Z_AXIS,iface); - else - mRight = node; - } - break; - case Z_AXIS: - if ( nodePosition[2] <= position[2] ) - { - if ( mLeft ) - mLeft->addDouble(node,X_AXIS,iface); - else - mLeft = node; - } - else - { - if ( mRight ) - mRight->addDouble(node,X_AXIS,iface); - else - mRight = node; - } - break; - } - - } - - - void addFloat(KdTreeNode *node,Axes dim,const KdTreeInterface *iface) - { - const float *nodePosition = iface->getPositionFloat( node->mIndex ); - const float *position = iface->getPositionFloat( mIndex ); - switch ( dim ) - { - case X_AXIS: - if ( nodePosition[0] <= position[0] ) - { - if ( mLeft ) - mLeft->addFloat(node,Y_AXIS,iface); - else - mLeft = node; - } - else - { - if ( mRight ) - mRight->addFloat(node,Y_AXIS,iface); - else - mRight = node; - } - break; - case Y_AXIS: - if ( nodePosition[1] <= position[1] ) - { - if ( mLeft ) - mLeft->addFloat(node,Z_AXIS,iface); - else - mLeft = node; - } - else - { - if ( mRight ) - mRight->addFloat(node,Z_AXIS,iface); - else - mRight = node; - } - break; - case Z_AXIS: - if ( nodePosition[2] <= position[2] ) - { - if ( mLeft ) - mLeft->addFloat(node,X_AXIS,iface); - else - mLeft = node; - } - else - { - if ( mRight ) - mRight->addFloat(node,X_AXIS,iface); - else - mRight = node; - } - break; - } - - } - - - uint32_t getIndex(void) const { return mIndex; }; - - void search(Axes axis,const double *pos,double radius,uint32_t &count,uint32_t maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface) - { - - const double *position = iface->getPositionDouble(mIndex); - - double dx = pos[0] - position[0]; - double dy = pos[1] - position[1]; - double dz = pos[2] - position[2]; - - KdTreeNode *search1 = 0; - KdTreeNode *search2 = 0; - - switch ( axis ) - { - case X_AXIS: - if ( dx <= 0 ) // JWR if we are to the left - { - search1 = mLeft; // JWR then search to the left - if ( -dx < radius ) // JWR if distance to the right is less than our search radius, continue on the right as well. - search2 = mRight; - } - else - { - search1 = mRight; // JWR ok, we go down the left tree - if ( dx < radius ) // JWR if the distance from the right is less than our search radius - search2 = mLeft; - } - axis = Y_AXIS; - break; - case Y_AXIS: - if ( dy <= 0 ) - { - search1 = mLeft; - if ( -dy < radius ) - search2 = mRight; - } - else - { - search1 = mRight; - if ( dy < radius ) - search2 = mLeft; - } - axis = Z_AXIS; - break; - case Z_AXIS: - if ( dz <= 0 ) - { - search1 = mLeft; - if ( -dz < radius ) - search2 = mRight; - } - else - { - search1 = mRight; - if ( dz < radius ) - search2 = mLeft; - } - axis = X_AXIS; - break; - } - - double r2 = radius*radius; - double m = dx*dx+dy*dy+dz*dz; - - if ( m < r2 ) - { - switch ( count ) - { - case 0: - found[count].mNode = this; - found[count].mDistance = m; - break; - case 1: - if ( m < found[0].mDistance ) - { - if ( maxObjects == 1 ) - { - found[0].mNode = this; - found[0].mDistance = m; - } - else - { - found[1] = found[0]; - found[0].mNode = this; - found[0].mDistance = m; - } - } - else if ( maxObjects > 1) - { - found[1].mNode = this; - found[1].mDistance = m; - } - break; - default: - { - bool inserted = false; - - for (uint32_t i=0; i= maxObjects ) scan=maxObjects-1; - for (uint32_t j=scan; j>i; j--) - { - found[j] = found[j-1]; - } - found[i].mNode = this; - found[i].mDistance = m; - inserted = true; - break; - } - } - - if ( !inserted && count < maxObjects ) - { - found[count].mNode = this; - found[count].mDistance = m; - } - } - break; - } - count++; - if ( count > maxObjects ) - { - count = maxObjects; - } - } - - - if ( search1 ) - search1->search( axis, pos,radius, count, maxObjects, found, iface); - - if ( search2 ) - search2->search( axis, pos,radius, count, maxObjects, found, iface); - - } - - void search(Axes axis,const float *pos,float radius,uint32_t &count,uint32_t maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface) - { - - const float *position = iface->getPositionFloat(mIndex); - - float dx = pos[0] - position[0]; - float dy = pos[1] - position[1]; - float dz = pos[2] - position[2]; - - KdTreeNode *search1 = 0; - KdTreeNode *search2 = 0; - - switch ( axis ) - { - case X_AXIS: - if ( dx <= 0 ) // JWR if we are to the left - { - search1 = mLeft; // JWR then search to the left - if ( -dx < radius ) // JWR if distance to the right is less than our search radius, continue on the right as well. - search2 = mRight; - } - else - { - search1 = mRight; // JWR ok, we go down the left tree - if ( dx < radius ) // JWR if the distance from the right is less than our search radius - search2 = mLeft; - } - axis = Y_AXIS; - break; - case Y_AXIS: - if ( dy <= 0 ) - { - search1 = mLeft; - if ( -dy < radius ) - search2 = mRight; - } - else - { - search1 = mRight; - if ( dy < radius ) - search2 = mLeft; - } - axis = Z_AXIS; - break; - case Z_AXIS: - if ( dz <= 0 ) - { - search1 = mLeft; - if ( -dz < radius ) - search2 = mRight; - } - else - { - search1 = mRight; - if ( dz < radius ) - search2 = mLeft; - } - axis = X_AXIS; - break; - } - - float r2 = radius*radius; - float m = dx*dx+dy*dy+dz*dz; - - if ( m < r2 ) - { - switch ( count ) - { - case 0: - found[count].mNode = this; - found[count].mDistance = m; - break; - case 1: - if ( m < found[0].mDistance ) - { - if ( maxObjects == 1 ) - { - found[0].mNode = this; - found[0].mDistance = m; - } - else - { - found[1] = found[0]; - found[0].mNode = this; - found[0].mDistance = m; - } - } - else if ( maxObjects > 1) - { - found[1].mNode = this; - found[1].mDistance = m; - } - break; - default: - { - bool inserted = false; - - for (uint32_t i=0; i= maxObjects ) scan=maxObjects-1; - for (uint32_t j=scan; j>i; j--) - { - found[j] = found[j-1]; - } - found[i].mNode = this; - found[i].mDistance = m; - inserted = true; - break; - } - } - - if ( !inserted && count < maxObjects ) - { - found[count].mNode = this; - found[count].mDistance = m; - } - } - break; - } - count++; - if ( count > maxObjects ) - { - count = maxObjects; - } - } - - - if ( search1 ) - search1->search( axis, pos,radius, count, maxObjects, found, iface); - - if ( search2 ) - search2->search( axis, pos,radius, count, maxObjects, found, iface); - - } - -private: - - void setLeft(KdTreeNode *left) { mLeft = left; }; - void setRight(KdTreeNode *right) { mRight = right; }; - - KdTreeNode *getLeft(void) { return mLeft; } - KdTreeNode *getRight(void) { return mRight; } - - uint32_t mIndex; - KdTreeNode *mLeft; - KdTreeNode *mRight; -}; - - -#define MAX_BUNDLE_SIZE 1024 // 1024 nodes at a time, to minimize memory allocation and guarantee that pointers are persistent. - -class KdTreeNodeBundle -{ -public: - - KdTreeNodeBundle(void) - { - mNext = 0; - mIndex = 0; - } - - bool isFull(void) const - { - return (bool)( mIndex == MAX_BUNDLE_SIZE ); - } - - KdTreeNode * getNextNode(void) - { - assert(mIndex DoubleVector; -typedef std::vector< float > FloatVector; - -class KdTree : public KdTreeInterface -{ -public: - KdTree(void) - { - mRoot = 0; - mBundle = 0; - mVcount = 0; - mUseDouble = false; - } - - virtual ~KdTree(void) - { - reset(); - } - - const double * getPositionDouble(uint32_t index) const - { - assert( mUseDouble ); - assert ( index < mVcount ); - return &mVerticesDouble[index*3]; - } - - const float * getPositionFloat(uint32_t index) const - { - assert( !mUseDouble ); - assert ( index < mVcount ); - return &mVerticesFloat[index*3]; - } - - uint32_t search(const double *pos,double radius,uint32_t maxObjects,KdTreeFindNode *found) const - { - assert( mUseDouble ); - if ( !mRoot ) return 0; - uint32_t count = 0; - mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this); - return count; - } - - uint32_t search(const float *pos,float radius,uint32_t maxObjects,KdTreeFindNode *found) const - { - assert( !mUseDouble ); - if ( !mRoot ) return 0; - uint32_t count = 0; - mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this); - return count; - } - - void reset(void) - { - mRoot = 0; - mVerticesDouble.clear(); - mVerticesFloat.clear(); - KdTreeNodeBundle *bundle = mBundle; - while ( bundle ) - { - KdTreeNodeBundle *next = bundle->mNext; - delete bundle; - bundle = next; - } - mBundle = 0; - mVcount = 0; - } - - uint32_t add(double x,double y,double z) - { - assert(mUseDouble); - uint32_t ret = mVcount; - mVerticesDouble.push_back(x); - mVerticesDouble.push_back(y); - mVerticesDouble.push_back(z); - mVcount++; - KdTreeNode *node = getNewNode(ret); - if ( mRoot ) - { - mRoot->addDouble(node,X_AXIS,this); - } - else - { - mRoot = node; - } - return ret; - } - - uint32_t add(float x,float y,float z) - { - assert(!mUseDouble); - uint32_t ret = mVcount; - mVerticesFloat.push_back(x); - mVerticesFloat.push_back(y); - mVerticesFloat.push_back(z); - mVcount++; - KdTreeNode *node = getNewNode(ret); - if ( mRoot ) - { - mRoot->addFloat(node,X_AXIS,this); - } - else - { - mRoot = node; - } - return ret; - } - - KdTreeNode * getNewNode(uint32_t index) - { - if ( mBundle == 0 ) - { - mBundle = new KdTreeNodeBundle; - } - if ( mBundle->isFull() ) - { - KdTreeNodeBundle *bundle = new KdTreeNodeBundle; - mBundle->mNext = bundle; - mBundle = bundle; - } - KdTreeNode *node = mBundle->getNextNode(); - new ( node ) KdTreeNode(index); - return node; - } - - uint32_t getNearest(const double *pos,double radius,bool &_found) const // returns the nearest possible neighbor's index. - { - assert( mUseDouble ); - uint32_t ret = 0; - - _found = false; - KdTreeFindNode found[1]; - uint32_t count = search(pos,radius,1,found); - if ( count ) - { - KdTreeNode *node = found[0].mNode; - ret = node->getIndex(); - _found = true; - } - return ret; - } - - uint32_t getNearest(const float *pos,float radius,bool &_found) const // returns the nearest possible neighbor's index. - { - assert( !mUseDouble ); - uint32_t ret = 0; - - _found = false; - KdTreeFindNode found[1]; - uint32_t count = search(pos,radius,1,found); - if ( count ) - { - KdTreeNode *node = found[0].mNode; - ret = node->getIndex(); - _found = true; - } - return ret; - } - - const double * getVerticesDouble(void) const - { - assert( mUseDouble ); - const double *ret = 0; - if ( !mVerticesDouble.empty() ) - { - ret = &mVerticesDouble[0]; - } - return ret; - } - - const float * getVerticesFloat(void) const - { - assert( !mUseDouble ); - const float * ret = 0; - if ( !mVerticesFloat.empty() ) - { - ret = &mVerticesFloat[0]; - } - return ret; - } - - uint32_t getVcount(void) const { return mVcount; }; - - void setUseDouble(bool useDouble) - { - mUseDouble = useDouble; - } - -private: - bool mUseDouble; - KdTreeNode *mRoot; - KdTreeNodeBundle *mBundle; - uint32_t mVcount; - DoubleVector mVerticesDouble; - FloatVector mVerticesFloat; -}; - -}; // end of namespace VERTEX_INDEX - -class MyVertexIndex : public fm_VertexIndex -{ -public: - MyVertexIndex(double granularity,bool snapToGrid) - { - mDoubleGranularity = granularity; - mFloatGranularity = (float)granularity; - mSnapToGrid = snapToGrid; - mUseDouble = true; - mKdTree.setUseDouble(true); - } - - MyVertexIndex(float granularity,bool snapToGrid) - { - mDoubleGranularity = granularity; - mFloatGranularity = (float)granularity; - mSnapToGrid = snapToGrid; - mUseDouble = false; - mKdTree.setUseDouble(false); - } - - virtual ~MyVertexIndex(void) - { - - } - - - double snapToGrid(double p) - { - double m = fmod(p,mDoubleGranularity); - p-=m; - return p; - } - - float snapToGrid(float p) - { - float m = fmodf(p,mFloatGranularity); - p-=m; - return p; - } - - uint32_t getIndex(const float *_p,bool &newPos) // get index for a vector float - { - uint32_t ret; - - if ( mUseDouble ) - { - double p[3]; - p[0] = _p[0]; - p[1] = _p[1]; - p[2] = _p[2]; - return getIndex(p,newPos); - } - - newPos = false; - - float p[3]; - - if ( mSnapToGrid ) - { - p[0] = snapToGrid(_p[0]); - p[1] = snapToGrid(_p[1]); - p[2] = snapToGrid(_p[2]); - } - else - { - p[0] = _p[0]; - p[1] = _p[1]; - p[2] = _p[2]; - } - - bool found; - ret = mKdTree.getNearest(p,mFloatGranularity,found); - if ( !found ) - { - newPos = true; - ret = mKdTree.add(p[0],p[1],p[2]); - } - - - return ret; - } - - uint32_t getIndex(const double *_p,bool &newPos) // get index for a vector double - { - uint32_t ret; - - if ( !mUseDouble ) - { - float p[3]; - p[0] = (float)_p[0]; - p[1] = (float)_p[1]; - p[2] = (float)_p[2]; - return getIndex(p,newPos); - } - - newPos = false; - - double p[3]; - - if ( mSnapToGrid ) - { - p[0] = snapToGrid(_p[0]); - p[1] = snapToGrid(_p[1]); - p[2] = snapToGrid(_p[2]); - } - else - { - p[0] = _p[0]; - p[1] = _p[1]; - p[2] = _p[2]; - } - - bool found; - ret = mKdTree.getNearest(p,mDoubleGranularity,found); - if ( !found ) - { - newPos = true; - ret = mKdTree.add(p[0],p[1],p[2]); - } - - - return ret; - } - - const float * getVerticesFloat(void) const - { - const float * ret = 0; - - assert( !mUseDouble ); - - ret = mKdTree.getVerticesFloat(); - - return ret; - } - - const double * getVerticesDouble(void) const - { - const double * ret = 0; - - assert( mUseDouble ); - - ret = mKdTree.getVerticesDouble(); - - return ret; - } - - const float * getVertexFloat(uint32_t index) const - { - const float * ret = 0; - assert( !mUseDouble ); -#ifdef _DEBUG - uint32_t vcount = mKdTree.getVcount(); - assert( index < vcount ); -#endif - ret = mKdTree.getVerticesFloat(); - ret = &ret[index*3]; - return ret; - } - - const double * getVertexDouble(uint32_t index) const - { - const double * ret = 0; - assert( mUseDouble ); -#ifdef _DEBUG - uint32_t vcount = mKdTree.getVcount(); - assert( index < vcount ); -#endif - ret = mKdTree.getVerticesDouble(); - ret = &ret[index*3]; - - return ret; - } - - uint32_t getVcount(void) const - { - return mKdTree.getVcount(); - } - - bool isDouble(void) const - { - return mUseDouble; - } - - - bool saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) - { - bool ret = false; - - - FILE *fph = fopen(fname,"wb"); - if ( fph ) - { - ret = true; - - uint32_t vcount = getVcount(); - if ( mUseDouble ) - { - const double *v = getVerticesDouble(); - for (uint32_t i=0; i(ret); -} - -fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid) // create an indexed vertext system for floats -{ - MyVertexIndex *ret = new MyVertexIndex(granularity,snapToGrid); - return static_cast< fm_VertexIndex *>(ret); -} - -void fm_releaseVertexIndex(fm_VertexIndex *vindex) -{ - MyVertexIndex *m = static_cast< MyVertexIndex *>(vindex); - delete m; -} - -#endif // END OF VERTEX WELDING CODE - - -REAL fm_computeBestFitAABB(uint32_t vcount,const REAL *points,uint32_t pstride,REAL *bmin,REAL *bmax) // returns the diagonal distance -{ - - const uint8_t *source = (const uint8_t *) points; - - bmin[0] = points[0]; - bmin[1] = points[1]; - bmin[2] = points[2]; - - bmax[0] = points[0]; - bmax[1] = points[1]; - bmax[2] = points[2]; - - - for (uint32_t i=1; i bmax[0] ) bmax[0] = p[0]; - if ( p[1] > bmax[1] ) bmax[1] = p[1]; - if ( p[2] > bmax[2] ) bmax[2] = p[2]; - - } - - REAL dx = bmax[0] - bmin[0]; - REAL dy = bmax[1] - bmin[1]; - REAL dz = bmax[2] - bmin[2]; - - return (REAL) sqrt( dx*dx + dy*dy + dz*dz ); - -} - - - -/* a = b - c */ -#define vector(a,b,c) \ - (a)[0] = (b)[0] - (c)[0]; \ - (a)[1] = (b)[1] - (c)[1]; \ - (a)[2] = (b)[2] - (c)[2]; - - - -#define innerProduct(v,q) \ - ((v)[0] * (q)[0] + \ - (v)[1] * (q)[1] + \ - (v)[2] * (q)[2]) - -#define crossProduct(a,b,c) \ - (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \ - (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \ - (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1]; - - -bool fm_lineIntersectsTriangle(const REAL *rayStart,const REAL *rayEnd,const REAL *p1,const REAL *p2,const REAL *p3,REAL *sect) -{ - REAL dir[3]; - - dir[0] = rayEnd[0] - rayStart[0]; - dir[1] = rayEnd[1] - rayStart[1]; - dir[2] = rayEnd[2] - rayStart[2]; - - REAL d = (REAL)sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]); - REAL r = 1.0f / d; - - dir[0]*=r; - dir[1]*=r; - dir[2]*=r; - - - REAL t; - - bool ret = fm_rayIntersectsTriangle(rayStart, dir, p1, p2, p3, t ); - - if ( ret ) - { - if ( t > d ) - { - sect[0] = rayStart[0] + dir[0]*t; - sect[1] = rayStart[1] + dir[1]*t; - sect[2] = rayStart[2] + dir[2]*t; - } - else - { - ret = false; - } - } - - return ret; -} - - - -bool fm_rayIntersectsTriangle(const REAL *p,const REAL *d,const REAL *v0,const REAL *v1,const REAL *v2,REAL &t) -{ - REAL e1[3],e2[3],h[3],s[3],q[3]; - REAL a,f,u,v; - - vector(e1,v1,v0); - vector(e2,v2,v0); - crossProduct(h,d,e2); - a = innerProduct(e1,h); - - if (a > -0.00001 && a < 0.00001) - return(false); - - f = 1/a; - vector(s,p,v0); - u = f * (innerProduct(s,h)); - - if (u < 0.0 || u > 1.0) - return(false); - - crossProduct(q,s,e1); - v = f * innerProduct(d,q); - if (v < 0.0 || u + v > 1.0) - return(false); - // at this stage we can compute t to find out where - // the intersection point is on the line - t = f * innerProduct(e2,q); - if (t > 0) // ray intersection - return(true); - else // this means that there is a line intersection - // but not a ray intersection - return (false); -} - - -inline REAL det(const REAL *p1,const REAL *p2,const REAL *p3) -{ - return p1[0]*p2[1]*p3[2] + p2[0]*p3[1]*p1[2] + p3[0]*p1[1]*p2[2] -p1[0]*p3[1]*p2[2] - p2[0]*p1[1]*p3[2] - p3[0]*p2[1]*p1[2]; -} - - -REAL fm_computeMeshVolume(const REAL *vertices,uint32_t tcount,const uint32_t *indices) -{ - REAL volume = 0; - - for (uint32_t i=0; i= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); -} - - -REAL fm_areaPolygon2d(uint32_t pcount,const REAL *points,uint32_t pstride) -{ - int32_t n = (int32_t)pcount; - - REAL A=0.0f; - for(int32_t p=n-1,q=0; q= y) || (y2 < y && y1 >= y) ) - { - if (x1+(y-y1)/(y2-y1)*(x2-x1)= 3 ) - { - const REAL *prev = fm_getPoint(points,pstride,pcount-1); - const REAL *current = points; - const REAL *next = fm_getPoint(points,pstride,1); - REAL *dest = _dest; - - for (uint32_t i=0; i class Rect3d -{ -public: - Rect3d(void) { }; - - Rect3d(const T *bmin,const T *bmax) - { - - mMin[0] = bmin[0]; - mMin[1] = bmin[1]; - mMin[2] = bmin[2]; - - mMax[0] = bmax[0]; - mMax[1] = bmax[1]; - mMax[2] = bmax[2]; - - } - - void SetMin(const T *bmin) - { - mMin[0] = bmin[0]; - mMin[1] = bmin[1]; - mMin[2] = bmin[2]; - } - - void SetMax(const T *bmax) - { - mMax[0] = bmax[0]; - mMax[1] = bmax[1]; - mMax[2] = bmax[2]; - } - - void SetMin(T x,T y,T z) - { - mMin[0] = x; - mMin[1] = y; - mMin[2] = z; - } - - void SetMax(T x,T y,T z) - { - mMax[0] = x; - mMax[1] = y; - mMax[2] = z; - } - - T mMin[3]; - T mMax[3]; -}; - -#endif - -void splitRect(uint32_t axis, - const Rect3d &source, - Rect3d &b1, - Rect3d &b2, - const REAL *midpoint) -{ - switch ( axis ) - { - case 0: - b1.SetMin(source.mMin); - b1.SetMax( midpoint[0], source.mMax[1], source.mMax[2] ); - - b2.SetMin( midpoint[0], source.mMin[1], source.mMin[2] ); - b2.SetMax(source.mMax); - - break; - case 1: - b1.SetMin(source.mMin); - b1.SetMax( source.mMax[0], midpoint[1], source.mMax[2] ); - - b2.SetMin( source.mMin[0], midpoint[1], source.mMin[2] ); - b2.SetMax(source.mMax); - - break; - case 2: - b1.SetMin(source.mMin); - b1.SetMax( source.mMax[0], source.mMax[1], midpoint[2] ); - - b2.SetMin( source.mMin[0], source.mMin[1], midpoint[2] ); - b2.SetMax(source.mMax); - - break; - } -} - -bool fm_computeSplitPlane(uint32_t vcount, - const REAL *vertices, - uint32_t /* tcount */, - const uint32_t * /* indices */, - REAL *plane) -{ - - REAL sides[3]; - REAL matrix[16]; - - fm_computeBestFitOBB( vcount, vertices, sizeof(REAL)*3, sides, matrix ); - - REAL bmax[3]; - REAL bmin[3]; - - bmax[0] = sides[0]*0.5f; - bmax[1] = sides[1]*0.5f; - bmax[2] = sides[2]*0.5f; - - bmin[0] = -bmax[0]; - bmin[1] = -bmax[1]; - bmin[2] = -bmax[2]; - - - REAL dx = sides[0]; - REAL dy = sides[1]; - REAL dz = sides[2]; - - - uint32_t axis = 0; - - if ( dy > dx ) - { - axis = 1; - } - - if ( dz > dx && dz > dy ) - { - axis = 2; - } - - REAL p1[3]; - REAL p2[3]; - REAL p3[3]; - - p3[0] = p2[0] = p1[0] = bmin[0] + dx*0.5f; - p3[1] = p2[1] = p1[1] = bmin[1] + dy*0.5f; - p3[2] = p2[2] = p1[2] = bmin[2] + dz*0.5f; - - Rect3d b(bmin,bmax); - - Rect3d b1,b2; - - splitRect(axis,b,b1,b2,p1); - - - switch ( axis ) - { - case 0: - p2[1] = bmin[1]; - p2[2] = bmin[2]; - - if ( dz > dy ) - { - p3[1] = bmax[1]; - p3[2] = bmin[2]; - } - else - { - p3[1] = bmin[1]; - p3[2] = bmax[2]; - } - - break; - case 1: - p2[0] = bmin[0]; - p2[2] = bmin[2]; - - if ( dx > dz ) - { - p3[0] = bmax[0]; - p3[2] = bmin[2]; - } - else - { - p3[0] = bmin[0]; - p3[2] = bmax[2]; - } - - break; - case 2: - p2[0] = bmin[0]; - p2[1] = bmin[1]; - - if ( dx > dy ) - { - p3[0] = bmax[0]; - p3[1] = bmin[1]; - } - else - { - p3[0] = bmin[0]; - p3[1] = bmax[1]; - } - - break; - } - - REAL tp1[3]; - REAL tp2[3]; - REAL tp3[3]; - - fm_transform(matrix,p1,tp1); - fm_transform(matrix,p2,tp2); - fm_transform(matrix,p3,tp3); - - plane[3] = fm_computePlane(tp1,tp2,tp3,plane); - - return true; - -} - -#pragma warning(disable:4100) - -void fm_nearestPointInTriangle(const REAL * /*nearestPoint*/,const REAL * /*p1*/,const REAL * /*p2*/,const REAL * /*p3*/,REAL * /*nearest*/) -{ - -} - -static REAL Partial(const REAL *a,const REAL *p) -{ - return (a[0]*p[1]) - (p[0]*a[1]); -} - -REAL fm_areaTriangle(const REAL *p0,const REAL *p1,const REAL *p2) -{ - REAL A = Partial(p0,p1); - A+= Partial(p1,p2); - A+= Partial(p2,p0); - return A*0.5f; -} - -void fm_subtract(const REAL *A,const REAL *B,REAL *diff) // compute A-B and store the result in 'diff' -{ - diff[0] = A[0]-B[0]; - diff[1] = A[1]-B[1]; - diff[2] = A[2]-B[2]; -} - - -void fm_multiplyTransform(const REAL *pA,const REAL *pB,REAL *pM) -{ - - REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0]; - REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1]; - REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2]; - REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3]; - - REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0]; - REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1]; - REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2]; - REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3]; - - REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0]; - REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1]; - REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2]; - REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3]; - - REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0]; - REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1]; - REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2]; - REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3]; - - pM[0] = a; pM[1] = b; pM[2] = c; pM[3] = d; - - pM[4] = e; pM[5] = f; pM[6] = g; pM[7] = h; - - pM[8] = i; pM[9] = j; pM[10] = k; pM[11] = l; - - pM[12] = m; pM[13] = n; pM[14] = o; pM[15] = p; -} - -void fm_multiply(REAL *A,REAL scaler) -{ - A[0]*=scaler; - A[1]*=scaler; - A[2]*=scaler; -} - -void fm_add(const REAL *A,const REAL *B,REAL *sum) -{ - sum[0] = A[0]+B[0]; - sum[1] = A[1]+B[1]; - sum[2] = A[2]+B[2]; -} - -void fm_copy3(const REAL *source,REAL *dest) -{ - dest[0] = source[0]; - dest[1] = source[1]; - dest[2] = source[2]; -} - - -uint32_t fm_copyUniqueVertices(uint32_t vcount,const REAL *input_vertices,REAL *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices) -{ - uint32_t ret = 0; - - REAL *vertices = (REAL *)malloc(sizeof(REAL)*vcount*3); - memcpy(vertices,input_vertices,sizeof(REAL)*vcount*3); - REAL *dest = output_vertices; - - uint32_t *reindex = (uint32_t *)malloc(sizeof(uint32_t)*vcount); - memset(reindex,0xFF,sizeof(uint32_t)*vcount); - - uint32_t icount = tcount*3; - - for (uint32_t i=0; i 0 ) - { - uint32_t i1 = indices[0]; - uint32_t i2 = indices[1]; - uint32_t i3 = indices[2]; - const REAL *p1 = &vertices[i1*3]; - const REAL *p2 = &vertices[i2*3]; - const REAL *p3 = &vertices[i3*3]; - REAL plane[4]; - plane[3] = fm_computePlane(p1,p2,p3,plane); - const uint32_t *scan = &indices[3]; - for (uint32_t i=1; i= dmin && dot <= dmax ) - { - ret = true; // then the plane equation is for practical purposes identical. - } - } -#endif - return ret; -} - - -void fm_initMinMax(REAL bmin[3],REAL bmax[3]) -{ - bmin[0] = FLT_MAX; - bmin[1] = FLT_MAX; - bmin[2] = FLT_MAX; - - bmax[0] = -FLT_MAX; - bmax[1] = -FLT_MAX; - bmax[2] = -FLT_MAX; -} - -void fm_inflateMinMax(REAL bmin[3], REAL bmax[3], REAL ratio) -{ - REAL inflate = fm_distance(bmin, bmax)*0.5f*ratio; - - bmin[0] -= inflate; - bmin[1] -= inflate; - bmin[2] -= inflate; - - bmax[0] += inflate; - bmax[1] += inflate; - bmax[2] += inflate; -} - -#ifndef TESSELATE_H - -#define TESSELATE_H - -typedef std::vector< uint32_t > UintVector; - -class Myfm_Tesselate : public fm_Tesselate -{ -public: - virtual ~Myfm_Tesselate(void) - { - - } - - const uint32_t * tesselate(fm_VertexIndex *vindex,uint32_t tcount,const uint32_t *indices,float longEdge,uint32_t maxDepth,uint32_t &outcount) - { - const uint32_t *ret = 0; - - mMaxDepth = maxDepth; - mLongEdge = longEdge*longEdge; - mLongEdgeD = mLongEdge; - mVertices = vindex; - - if ( mVertices->isDouble() ) - { - uint32_t vcount = mVertices->getVcount(); - double *vertices = (double *)malloc(sizeof(double)*vcount*3); - memcpy(vertices,mVertices->getVerticesDouble(),sizeof(double)*vcount*3); - - for (uint32_t i=0; igetVcount(); - float *vertices = (float *)malloc(sizeof(float)*vcount*3); - memcpy(vertices,mVertices->getVerticesFloat(),sizeof(float)*vcount*3); - - - for (uint32_t i=0; i mLongEdge || l2 > mLongEdge || l3 > mLongEdge ) - split = true; - - } - - if ( split ) - { - uint32_t edge; - - if ( l1 >= l2 && l1 >= l3 ) - edge = 0; - else if ( l2 >= l1 && l2 >= l3 ) - edge = 1; - else - edge = 2; - - float splits[3]; - - switch ( edge ) - { - case 0: - { - fm_lerp(p1,p2,splits,0.5f); - tesselate(p1,splits,p3, recurse+1 ); - tesselate(splits,p2,p3, recurse+1 ); - } - break; - case 1: - { - fm_lerp(p2,p3,splits,0.5f); - tesselate(p1,p2,splits, recurse+1 ); - tesselate(p1,splits,p3, recurse+1 ); - } - break; - case 2: - { - fm_lerp(p3,p1,splits,0.5f); - tesselate(p1,p2,splits, recurse+1 ); - tesselate(splits,p2,p3, recurse+1 ); - } - break; - } - } - else - { - bool newp; - - uint32_t i1 = mVertices->getIndex(p1,newp); - uint32_t i2 = mVertices->getIndex(p2,newp); - uint32_t i3 = mVertices->getIndex(p3,newp); - - mIndices.push_back(i1); - mIndices.push_back(i2); - mIndices.push_back(i3); - } - - } - - void tesselate(const double *p1,const double *p2,const double *p3,uint32_t recurse) - { - bool split = false; - double l1,l2,l3; - - l1 = l2 = l3 = 0; - - if ( recurse < mMaxDepth ) - { - l1 = fm_distanceSquared(p1,p2); - l2 = fm_distanceSquared(p2,p3); - l3 = fm_distanceSquared(p3,p1); - - if ( l1 > mLongEdgeD || l2 > mLongEdgeD || l3 > mLongEdgeD ) - split = true; - - } - - if ( split ) - { - uint32_t edge; - - if ( l1 >= l2 && l1 >= l3 ) - edge = 0; - else if ( l2 >= l1 && l2 >= l3 ) - edge = 1; - else - edge = 2; - - double splits[3]; - - switch ( edge ) - { - case 0: - { - fm_lerp(p1,p2,splits,0.5); - tesselate(p1,splits,p3, recurse+1 ); - tesselate(splits,p2,p3, recurse+1 ); - } - break; - case 1: - { - fm_lerp(p2,p3,splits,0.5); - tesselate(p1,p2,splits, recurse+1 ); - tesselate(p1,splits,p3, recurse+1 ); - } - break; - case 2: - { - fm_lerp(p3,p1,splits,0.5); - tesselate(p1,p2,splits, recurse+1 ); - tesselate(splits,p2,p3, recurse+1 ); - } - break; - } - } - else - { - bool newp; - - uint32_t i1 = mVertices->getIndex(p1,newp); - uint32_t i2 = mVertices->getIndex(p2,newp); - uint32_t i3 = mVertices->getIndex(p3,newp); - - mIndices.push_back(i1); - mIndices.push_back(i2); - mIndices.push_back(i3); - } - - } - -private: - float mLongEdge; - double mLongEdgeD; - fm_VertexIndex *mVertices; - UintVector mIndices; - uint32_t mMaxDepth; -}; - -fm_Tesselate * fm_createTesselate(void) -{ - Myfm_Tesselate *m = new Myfm_Tesselate; - return static_cast< fm_Tesselate * >(m); -} - -void fm_releaseTesselate(fm_Tesselate *t) -{ - Myfm_Tesselate *m = static_cast< Myfm_Tesselate *>(t); - delete m; -} - -#endif - - -#ifndef RAY_ABB_INTERSECT - -#define RAY_ABB_INTERSECT - -//! Integer representation of a floating-point value. -#define IR(x) ((uint32_t&)x) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** -* A method to compute a ray-AABB intersection. -* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 -* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) -* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) -* -* Hence this version is faster as well as more robust than the original one. -* -* Should work provided: -* 1) the integer representation of 0.0f is 0x00000000 -* 2) the sign bit of the float is the most significant one -* -* Report bugs: p.terdiman@codercorner.com -* -* \param aabb [in] the axis-aligned bounding box -* \param origin [in] ray origin -* \param dir [in] ray direction -* \param coord [out] impact coordinates -* \return true if ray intersects AABB -*/ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#define RAYAABB_EPSILON 0.00001f -bool fm_intersectRayAABB(const float MinB[3],const float MaxB[3],const float origin[3],const float dir[3],float coord[3]) -{ - bool Inside = true; - float MaxT[3]; - MaxT[0]=MaxT[1]=MaxT[2]=-1.0f; - - // Find candidate planes. - for(uint32_t i=0;i<3;i++) - { - if(origin[i] < MinB[i]) - { - coord[i] = MinB[i]; - Inside = false; - - // Calculate T distances to candidate planes - if(IR(dir[i])) MaxT[i] = (MinB[i] - origin[i]) / dir[i]; - } - else if(origin[i] > MaxB[i]) - { - coord[i] = MaxB[i]; - Inside = false; - - // Calculate T distances to candidate planes - if(IR(dir[i])) MaxT[i] = (MaxB[i] - origin[i]) / dir[i]; - } - } - - // Ray origin inside bounding box - if(Inside) - { - coord[0] = origin[0]; - coord[1] = origin[1]; - coord[2] = origin[2]; - return true; - } - - // Get largest of the maxT's for final choice of intersection - uint32_t WhichPlane = 0; - if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; - if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; - - // Check final candidate actually inside box - if(IR(MaxT[WhichPlane])&0x80000000) return false; - - for(uint32_t i=0;i<3;i++) - { - if(i!=WhichPlane) - { - coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; -#ifdef RAYAABB_EPSILON - if(coord[i] < MinB[i] - RAYAABB_EPSILON || coord[i] > MaxB[i] + RAYAABB_EPSILON) return false; -#else - if(coord[i] < MinB[i] || coord[i] > MaxB[i]) return false; -#endif - } - } - return true; // ray hits box -} - -bool fm_intersectLineSegmentAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],float intersect[3]) -{ - bool ret = false; - - float dir[3]; - dir[0] = p2[0] - p1[0]; - dir[1] = p2[1] - p1[1]; - dir[2] = p2[2] - p1[2]; - float dist = fm_normalize(dir); - if ( dist > RAYAABB_EPSILON ) - { - ret = fm_intersectRayAABB(bmin,bmax,p1,dir,intersect); - if ( ret ) - { - float d = fm_distanceSquared(p1,intersect); - if ( d > (dist*dist) ) - { - ret = false; - } - } - } - return ret; -} - -#endif - -#ifndef OBB_TO_AABB - -#define OBB_TO_AABB - -#pragma warning(disable:4100) - -void fm_OBBtoAABB(const float /*obmin*/[3],const float /*obmax*/[3],const float /*matrix*/[16],float /*abmin*/[3],float /*abmax*/[3]) -{ - assert(0); // not yet implemented. -} - - -const REAL * computePos(uint32_t index,const REAL *vertices,uint32_t vstride) -{ - const char *tmp = (const char *)vertices; - tmp+=(index*vstride); - return (const REAL*)tmp; -} - -void computeNormal(uint32_t index,REAL *normals,uint32_t nstride,const REAL *normal) -{ - char *tmp = (char *)normals; - tmp+=(index*nstride); - REAL *dest = (REAL *)tmp; - dest[0]+=normal[0]; - dest[1]+=normal[1]; - dest[2]+=normal[2]; -} - -void fm_computeMeanNormals(uint32_t vcount, // the number of vertices - const REAL *vertices, // the base address of the vertex position data. - uint32_t vstride, // the stride between position data. - REAL *normals, // the base address of the destination for mean vector normals - uint32_t nstride, // the stride between normals - uint32_t tcount, // the number of triangles - const uint32_t *indices) // the triangle indices -{ - - // Step #1 : Zero out the vertex normals - char *dest = (char *)normals; - for (uint32_t i=0; ixmax[0]) - Copy(xmax,caller_p); - if (caller_p[1]ymax[1]) - Copy(ymax,caller_p); - if (caller_p[2]zmax[2]) - Copy(zmax,caller_p); - scan+=pstride; - } - } - - /* Set xspan = distance between the 2 points xmin & xmax (squared) */ - REAL dx = xmax[0] - xmin[0]; - REAL dy = xmax[1] - xmin[1]; - REAL dz = xmax[2] - xmin[2]; - REAL xspan = dx*dx + dy*dy + dz*dz; - -/* Same for y & z spans */ - dx = ymax[0] - ymin[0]; - dy = ymax[1] - ymin[1]; - dz = ymax[2] - ymin[2]; - REAL yspan = dx*dx + dy*dy + dz*dz; - - dx = zmax[0] - zmin[0]; - dy = zmax[1] - zmin[1]; - dz = zmax[2] - zmin[2]; - REAL zspan = dx*dx + dy*dy + dz*dz; - - /* Set points dia1 & dia2 to the maximally separated pair */ - Copy(dia1,xmin); - Copy(dia2,xmax); /* assume xspan biggest */ - REAL maxspan = xspan; - - if (yspan>maxspan) - { - maxspan = yspan; - Copy(dia1,ymin); - Copy(dia2,ymax); - } - - if (zspan>maxspan) - { - maxspan = zspan; - Copy(dia1,zmin); - Copy(dia2,zmax); - } - - - /* dia1,dia2 is a diameter of initial sphere */ - /* calc initial center */ - center[0] = (dia1[0]+dia2[0])*0.5f; - center[1] = (dia1[1]+dia2[1])*0.5f; - center[2] = (dia1[2]+dia2[2])*0.5f; - - /* calculate initial radius**2 and radius */ - - dx = dia2[0]-center[0]; /* x component of radius vector */ - dy = dia2[1]-center[1]; /* y component of radius vector */ - dz = dia2[2]-center[2]; /* z component of radius vector */ - - radius2 = dx*dx + dy*dy + dz*dz; - radius = REAL(sqrt(radius2)); - - /* SECOND PASS: increment current sphere */ - { - const char *scan = (const char *)points; - for (uint32_t i=0; i radius2) /* do r**2 test first */ - { /* this point is outside of current sphere */ - REAL old_to_p = REAL(sqrt(old_to_p_sq)); - /* calc radius of new sphere */ - radius = (radius + old_to_p) * 0.5f; - radius2 = radius*radius; /* for next r**2 compare */ - REAL old_to_new = old_to_p - radius; - /* calc center of new sphere */ - REAL recip = 1.0f /old_to_p; - REAL cx = (radius*center[0] + old_to_new*caller_p[0]) * recip; - REAL cy = (radius*center[1] + old_to_new*caller_p[1]) * recip; - REAL cz = (radius*center[2] + old_to_new*caller_p[2]) * recip; - Set(center,cx,cy,cz); - scan+=pstride; - } - } - } - return radius; -} - - -void fm_computeBestFitCapsule(uint32_t vcount,const REAL *points,uint32_t pstride,REAL &radius,REAL &height,REAL matrix[16],bool bruteForce) -{ - REAL sides[3]; - REAL omatrix[16]; - fm_computeBestFitOBB(vcount,points,pstride,sides,omatrix,bruteForce); - - int32_t axis = 0; - if ( sides[0] > sides[1] && sides[0] > sides[2] ) - axis = 0; - else if ( sides[1] > sides[0] && sides[1] > sides[2] ) - axis = 1; - else - axis = 2; - - REAL localTransform[16]; - - REAL maxDist = 0; - REAL maxLen = 0; - - switch ( axis ) - { - case 0: - { - fm_eulerMatrix(0,0,FM_PI/2,localTransform); - fm_matrixMultiply(localTransform,omatrix,matrix); - - const uint8_t *scan = (const uint8_t *)points; - for (uint32_t i=0; i maxDist ) - { - maxDist = dist; - } - REAL l = (REAL) fabs(t[0]); - if ( l > maxLen ) - { - maxLen = l; - } - scan+=pstride; - } - } - height = sides[0]; - break; - case 1: - { - fm_eulerMatrix(0,FM_PI/2,0,localTransform); - fm_matrixMultiply(localTransform,omatrix,matrix); - - const uint8_t *scan = (const uint8_t *)points; - for (uint32_t i=0; i maxDist ) - { - maxDist = dist; - } - REAL l = (REAL) fabs(t[1]); - if ( l > maxLen ) - { - maxLen = l; - } - scan+=pstride; - } - } - height = sides[1]; - break; - case 2: - { - fm_eulerMatrix(FM_PI/2,0,0,localTransform); - fm_matrixMultiply(localTransform,omatrix,matrix); - - const uint8_t *scan = (const uint8_t *)points; - for (uint32_t i=0; i maxDist ) - { - maxDist = dist; - } - REAL l = (REAL) fabs(t[2]); - if ( l > maxLen ) - { - maxLen = l; - } - scan+=pstride; - } - } - height = sides[2]; - break; - } - radius = (REAL)sqrt(maxDist); - height = (maxLen*2)-(radius*2); -} - - -//************* Triangulation - -#ifndef TRIANGULATE_H - -#define TRIANGULATE_H - -typedef uint32_t TU32; - -class TVec -{ -public: - TVec(double _x,double _y,double _z) { x = _x; y = _y; z = _z; }; - TVec(void) { }; - - double x; - double y; - double z; -}; - -typedef std::vector< TVec > TVecVector; -typedef std::vector< TU32 > TU32Vector; - -class CTriangulator -{ -public: - /// Default constructor - CTriangulator(); - - /// Default destructor - virtual ~CTriangulator(); - - /// Triangulates the contour - void triangulate(TU32Vector &indices); - - /// Returns the given point in the triangulator array - inline TVec get(const TU32 id) { return mPoints[id]; } - - virtual void reset(void) - { - mInputPoints.clear(); - mPoints.clear(); - mIndices.clear(); - } - - virtual void addPoint(double x,double y,double z) - { - TVec v(x,y,z); - // update bounding box... - if ( mInputPoints.empty() ) - { - mMin = v; - mMax = v; - } - else - { - if ( x < mMin.x ) mMin.x = x; - if ( y < mMin.y ) mMin.y = y; - if ( z < mMin.z ) mMin.z = z; - - if ( x > mMax.x ) mMax.x = x; - if ( y > mMax.y ) mMax.y = y; - if ( z > mMax.z ) mMax.z = z; - } - mInputPoints.push_back(v); - } - - // Triangulation happens in 2d. We could inverse transform the polygon around the normal direction, or we just use the two most signficant axes - // Here we find the two longest axes and use them to triangulate. Inverse transforming them would introduce more doubleing point error and isn't worth it. - virtual uint32_t * triangulate(uint32_t &tcount,double epsilon) - { - uint32_t *ret = 0; - tcount = 0; - mEpsilon = epsilon; - - if ( !mInputPoints.empty() ) - { - mPoints.clear(); - - double dx = mMax.x - mMin.x; // locate the first, second and third longest edges and store them in i1, i2, i3 - double dy = mMax.y - mMin.y; - double dz = mMax.z - mMin.z; - - uint32_t i1,i2,i3; - - if ( dx > dy && dx > dz ) - { - i1 = 0; - if ( dy > dz ) - { - i2 = 1; - i3 = 2; - } - else - { - i2 = 2; - i3 = 1; - } - } - else if ( dy > dx && dy > dz ) - { - i1 = 1; - if ( dx > dz ) - { - i2 = 0; - i3 = 2; - } - else - { - i2 = 2; - i3 = 0; - } - } - else - { - i1 = 2; - if ( dx > dy ) - { - i2 = 0; - i3 = 1; - } - else - { - i2 = 1; - i3 = 0; - } - } - - uint32_t pcount = (uint32_t)mInputPoints.size(); - const double *points = &mInputPoints[0].x; - for (uint32_t i=0; i 2;) - { - if (0 >= (count--)) - return; - - int32_t u = v; - if (nv <= u) - u = 0; - v = u + 1; - if (nv <= v) - v = 0; - int32_t w = v + 1; - if (nv <= w) - w = 0; - - if (_snip(u, v, w, nv, V)) - { - int32_t a, b, c, s, t; - a = V[u]; - b = V[v]; - c = V[w]; - if ( flipped ) - { - indices.push_back(a); - indices.push_back(b); - indices.push_back(c); - } - else - { - indices.push_back(c); - indices.push_back(b); - indices.push_back(a); - } - m++; - for (s = v, t = v + 1; t < nv; s++, t++) - V[s] = V[t]; - nv--; - count = 2 * nv; - } - } - - free(V); -} - -/// Returns the area of the contour -double CTriangulator::_area() -{ - int32_t n = (uint32_t)mPoints.size(); - double A = 0.0f; - for (int32_t p = n - 1, q = 0; q < n; p = q++) - { - const TVec &pval = mPoints[p]; - const TVec &qval = mPoints[q]; - A += pval.x * qval.y - qval.x * pval.y; - } - A*=0.5f; - return A; -} - -bool CTriangulator::_snip(int32_t u, int32_t v, int32_t w, int32_t n, int32_t *V) -{ - int32_t p; - - const TVec &A = mPoints[ V[u] ]; - const TVec &B = mPoints[ V[v] ]; - const TVec &C = mPoints[ V[w] ]; - - if (mEpsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))) ) - return false; - - for (p = 0; p < n; p++) - { - if ((p == u) || (p == v) || (p == w)) - continue; - const TVec &P = mPoints[ V[p] ]; - if (_insideTriangle(A, B, C, P)) - return false; - } - return true; -} - -/// Tests if a point is inside the given triangle -bool CTriangulator::_insideTriangle(const TVec& A, const TVec& B, const TVec& C,const TVec& P) -{ - double ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; - double cCROSSap, bCROSScp, aCROSSbp; - - ax = C.x - B.x; ay = C.y - B.y; - bx = A.x - C.x; by = A.y - C.y; - cx = B.x - A.x; cy = B.y - A.y; - apx = P.x - A.x; apy = P.y - A.y; - bpx = P.x - B.x; bpy = P.y - B.y; - cpx = P.x - C.x; cpy = P.y - C.y; - - aCROSSbp = ax * bpy - ay * bpx; - cCROSSap = cx * apy - cy * apx; - bCROSScp = bx * cpy - by * cpx; - - return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); -} - -class Triangulate : public fm_Triangulate -{ -public: - Triangulate(void) - { - mPointsFloat = 0; - mPointsDouble = 0; - } - - virtual ~Triangulate(void) - { - reset(); - } - void reset(void) - { - free(mPointsFloat); - free(mPointsDouble); - mPointsFloat = 0; - mPointsDouble = 0; - } - - virtual const double * triangulate3d(uint32_t pcount, - const double *_points, - uint32_t vstride, - uint32_t &tcount, - bool consolidate, - double epsilon) - { - reset(); - - double *points = (double *)malloc(sizeof(double)*pcount*3); - if ( consolidate ) - { - pcount = fm_consolidatePolygon(pcount,_points,vstride,points,1-epsilon); - } - else - { - double *dest = points; - for (uint32_t i=0; i= 3 ) - { - CTriangulator ct; - for (uint32_t i=0; i(t); -} - -void fm_releaseTriangulate(fm_Triangulate *t) -{ - Triangulate *tt = static_cast< Triangulate *>(t); - delete tt; -} - -#endif - -bool validDistance(const REAL *p1,const REAL *p2,REAL epsilon) -{ - bool ret = true; - - REAL dx = p1[0] - p2[0]; - REAL dy = p1[1] - p2[1]; - REAL dz = p1[2] - p2[2]; - REAL dist = dx*dx+dy*dy+dz*dz; - if ( dist < (epsilon*epsilon) ) - { - ret = false; - } - return ret; -} - -bool fm_isValidTriangle(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon) -{ - bool ret = false; - - if ( validDistance(p1,p2,epsilon) && - validDistance(p1,p3,epsilon) && - validDistance(p2,p3,epsilon) ) - { - - REAL area = fm_computeArea(p1,p2,p3); - if ( area > epsilon ) - { - REAL _vertices[3*3],vertices[64*3]; - - _vertices[0] = p1[0]; - _vertices[1] = p1[1]; - _vertices[2] = p1[2]; - - _vertices[3] = p2[0]; - _vertices[4] = p2[1]; - _vertices[5] = p2[2]; - - _vertices[6] = p3[0]; - _vertices[7] = p3[1]; - _vertices[8] = p3[2]; - - uint32_t pcount = fm_consolidatePolygon(3,_vertices,sizeof(REAL)*3,vertices,1-epsilon); - if ( pcount == 3 ) - { - ret = true; - } - } - } - return ret; -} - - -void fm_multiplyQuat(const REAL *left,const REAL *right,REAL *quat) -{ - REAL a,b,c,d; - - a = left[3]*right[3] - left[0]*right[0] - left[1]*right[1] - left[2]*right[2]; - b = left[3]*right[0] + right[3]*left[0] + left[1]*right[2] - right[1]*left[2]; - c = left[3]*right[1] + right[3]*left[1] + left[2]*right[0] - right[2]*left[0]; - d = left[3]*right[2] + right[3]*left[2] + left[0]*right[1] - right[0]*left[1]; - - quat[3] = a; - quat[0] = b; - quat[1] = c; - quat[2] = d; -} - -bool fm_computeCentroid(uint32_t vcount, // number of input data points - const REAL *points, // starting address of points array. - REAL *center) - -{ - bool ret = false; - if ( vcount ) - { - center[0] = 0; - center[1] = 0; - center[2] = 0; - const REAL *p = points; - for (uint32_t i=0; i class Vec3 -{ -public: - Vec3(void) - { - - } - Vec3(Type _x,Type _y,Type _z) - { - x = _x; - y = _y; - z = _z; - } - Type x; - Type y; - Type z; -}; -#endif - -void fm_transformAABB(const REAL bmin[3],const REAL bmax[3],const REAL matrix[16],REAL tbmin[3],REAL tbmax[3]) -{ - Vec3 box[8]; - box[0] = Vec3< REAL >( bmin[0], bmin[1], bmin[2] ); - box[1] = Vec3< REAL >( bmax[0], bmin[1], bmin[2] ); - box[2] = Vec3< REAL >( bmax[0], bmax[1], bmin[2] ); - box[3] = Vec3< REAL >( bmin[0], bmax[1], bmin[2] ); - box[4] = Vec3< REAL >( bmin[0], bmin[1], bmax[2] ); - box[5] = Vec3< REAL >( bmax[0], bmin[1], bmax[2] ); - box[6] = Vec3< REAL >( bmax[0], bmax[1], bmax[2] ); - box[7] = Vec3< REAL >( bmin[0], bmax[1], bmax[2] ); - // transform all 8 corners of the box and then recompute a new AABB - for (unsigned int i=0; i<8; i++) - { - Vec3< REAL > &p = box[i]; - fm_transform(matrix,&p.x,&p.x); - if ( i == 0 ) - { - tbmin[0] = tbmax[0] = p.x; - tbmin[1] = tbmax[1] = p.y; - tbmin[2] = tbmax[2] = p.z; - } - else - { - if ( p.x < tbmin[0] ) tbmin[0] = p.x; - if ( p.y < tbmin[1] ) tbmin[1] = p.y; - if ( p.z < tbmin[2] ) tbmin[2] = p.z; - if ( p.x > tbmax[0] ) tbmax[0] = p.x; - if ( p.y > tbmax[1] ) tbmax[1] = p.y; - if ( p.z > tbmax[2] ) tbmax[2] = p.z; - } - } -} - -REAL fm_normalizeQuat(REAL n[4]) // normalize this quat -{ - REAL dx = n[0]*n[0]; - REAL dy = n[1]*n[1]; - REAL dz = n[2]*n[2]; - REAL dw = n[3]*n[3]; - - REAL dist = dx*dx+dy*dy+dz*dz+dw*dw; - - dist = (REAL)sqrt(dist); - - REAL recip = 1.0f / dist; - - n[0]*=recip; - n[1]*=recip; - n[2]*=recip; - n[3]*=recip; - - return dist; -} - - -}; // end of namespace diff --git a/src/VHACD_Lib/src/VHACD-ASYNC.cpp b/src/VHACD_Lib/src/VHACD-ASYNC.cpp deleted file mode 100644 index baf309d6..00000000 --- a/src/VHACD_Lib/src/VHACD-ASYNC.cpp +++ /dev/null @@ -1,334 +0,0 @@ -#include "../public/VHACD.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define ENABLE_ASYNC 1 - -#define HACD_ALLOC(x) malloc(x) -#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 - { -#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; - }); -#else - 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; -}; - -IVHACD* CreateVHACD_ASYNC(void) -{ - MyHACD_API *m = new MyHACD_API; - return static_cast(m); -} - - -}; // end of VHACD namespace - diff --git a/src/VHACD_Lib/src/VHACD.cpp b/src/VHACD_Lib/src/VHACD.cpp deleted file mode 100644 index 54cc6e37..00000000 --- a/src/VHACD_Lib/src/VHACD.cpp +++ /dev/null @@ -1,1589 +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. - */ - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include -#include -#include -#include -#include -#if _OPENMP -#include -#endif // _OPENMP - -#include "../public/VHACD.h" -#include "btConvexHullComputer.h" -#include "vhacdICHull.h" -#include "vhacdMesh.h" -#include "vhacdSArray.h" -#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)) -#define ABS(a) (((a) < 0) ? -(a) : (a)) -#define ZSGN(a) (((a) < 0) ? -1 : (a) > 0 ? 1 : 0) -#define MAX_DOUBLE (1.79769e+308) - -#ifdef _MSC_VER -#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 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; -} -#endif - -//#define OCL_SOURCE_FROM_FILE -#ifndef OCL_SOURCE_FROM_FILE -const char* oclProgramSource = "\ -__kernel void ComputePartialVolumes(__global short4 * voxels, \ - const int numVoxels, \ - const float4 plane, \ - const float4 minBB, \ - const float4 scale, \ - __local uint4 * localPartialVolumes, \ - __global uint4 * partialVolumes) \ -{ \ - int localId = get_local_id(0); \ - int groupSize = get_local_size(0); \ - int i0 = get_global_id(0) << 2; \ - float4 voxel; \ - uint4 v; \ - voxel = convert_float4(voxels[i0]); \ - v.s0 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 < numVoxels);\ - voxel = convert_float4(voxels[i0 + 1]); \ - v.s1 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 1 < numVoxels);\ - voxel = convert_float4(voxels[i0 + 2]); \ - v.s2 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 2 < numVoxels);\ - voxel = convert_float4(voxels[i0 + 3]); \ - v.s3 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 3 < numVoxels);\ - localPartialVolumes[localId] = v; \ - barrier(CLK_LOCAL_MEM_FENCE); \ - for (int i = groupSize >> 1; i > 0; i >>= 1) \ - { \ - if (localId < i) \ - { \ - localPartialVolumes[localId] += localPartialVolumes[localId + i]; \ - } \ - barrier(CLK_LOCAL_MEM_FENCE); \ - } \ - if (localId == 0) \ - { \ - partialVolumes[get_group_id(0)] = localPartialVolumes[0]; \ - } \ -} \ -__kernel void ComputePartialSums(__global uint4 * data, \ - const int dataSize, \ - __local uint4 * partialSums) \ -{ \ - int globalId = get_global_id(0); \ - int localId = get_local_id(0); \ - int groupSize = get_local_size(0); \ - int i; \ - if (globalId < dataSize) \ - { \ - partialSums[localId] = data[globalId]; \ - } \ - else \ - { \ - partialSums[localId] = (0, 0, 0, 0); \ - } \ - barrier(CLK_LOCAL_MEM_FENCE); \ - for (i = groupSize >> 1; i > 0; i >>= 1) \ - { \ - if (localId < i) \ - { \ - partialSums[localId] += partialSums[localId + i]; \ - } \ - barrier(CLK_LOCAL_MEM_FENCE); \ - } \ - if (localId == 0) \ - { \ - data[get_group_id(0)] = partialSums[0]; \ - } \ -}"; -#endif //OCL_SOURCE_FROM_FILE - -namespace VHACD { -IVHACD* CreateVHACD(void) -{ - return new VHACD(); -} -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; - } - -#ifdef OCL_SOURCE_FROM_FILE - 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; -#else - 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"); - } - 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); - 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 program\n"); - } - return false; - } - 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; -#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); -} -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 += 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); - } -} -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 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); - } - } -} -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(); - } - } - -#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"); - } - 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 - -#ifdef 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; -#if USE_THREAD == 1 && _OPENMP - threadID = omp_get_thread_num(); -#pragma omp flush(cancel) -#endif - if (!cancel) { - //Update progress - if (GetCancel()) { - cancel = true; -#if USE_THREAD == 1 && _OPENMP -#pragma omp flush(cancel) -#endif - } - Plane plane = planes[x]; - - 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 - } - - 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()); -#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) { -#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; - -#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); - } - } - } - } - -#ifdef 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); - } -} -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 << "+ " << 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(); - - if (params.m_logger) { - msg.str(""); - msg << "\t Subdivision level " << sub << 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); - } - } - - 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]; - } - 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 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 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; - 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); - } - } - 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; - } - } - // 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++; - } - } - 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) { - 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; -} - -} // end of VHACD namespace diff --git a/src/VHACD_Lib/src/btAlignedAllocator.cpp b/src/VHACD_Lib/src/btAlignedAllocator.cpp deleted file mode 100644 index 11d594f6..00000000 --- a/src/VHACD_Lib/src/btAlignedAllocator.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#include "btAlignedAllocator.h" - -#ifdef _MSC_VER -#pragma warning(disable:4311 4302) -#endif - -int32_t gNumAlignedAllocs = 0; -int32_t gNumAlignedFree = 0; -int32_t gTotalBytesAlignedAllocs = 0; //detect memory leaks - -static void* btAllocDefault(size_t size) -{ - return malloc(size); -} - -static void btFreeDefault(void* ptr) -{ - free(ptr); -} - -static btAllocFunc* sAllocFunc = btAllocDefault; -static btFreeFunc* sFreeFunc = btFreeDefault; - -#if defined(BT_HAS_ALIGNED_ALLOCATOR) -#include -static void* btAlignedAllocDefault(size_t size, int32_t alignment) -{ - return _aligned_malloc(size, (size_t)alignment); -} - -static void btAlignedFreeDefault(void* ptr) -{ - _aligned_free(ptr); -} -#elif defined(__CELLOS_LV2__) -#include - -static inline void* btAlignedAllocDefault(size_t size, int32_t alignment) -{ - return memalign(alignment, size); -} - -static inline void btAlignedFreeDefault(void* ptr) -{ - free(ptr); -} -#else -static inline void* btAlignedAllocDefault(size_t size, int32_t alignment) -{ - void* ret; - char* real; - unsigned long offset; - - real = (char*)sAllocFunc(size + sizeof(void*) + (alignment - 1)); - if (real) { - offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1); - ret = (void*)((real + sizeof(void*)) + offset); - *((void**)(ret)-1) = (void*)(real); - } - else { - ret = (void*)(real); - } - return (ret); -} - -static inline void btAlignedFreeDefault(void* ptr) -{ - void* real; - - if (ptr) { - real = *((void**)(ptr)-1); - sFreeFunc(real); - } -} -#endif - -static btAlignedAllocFunc* sAlignedAllocFunc = btAlignedAllocDefault; -static btAlignedFreeFunc* sAlignedFreeFunc = btAlignedFreeDefault; - -void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc) -{ - sAlignedAllocFunc = allocFunc ? allocFunc : btAlignedAllocDefault; - sAlignedFreeFunc = freeFunc ? freeFunc : btAlignedFreeDefault; -} - -void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc) -{ - sAllocFunc = allocFunc ? allocFunc : btAllocDefault; - sFreeFunc = freeFunc ? freeFunc : btFreeDefault; -} - -#ifdef BT_DEBUG_MEMORY_ALLOCATIONS -//this generic allocator provides the total allocated number of bytes -#include - -void* btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char* filename) -{ - void* ret; - char* real; - unsigned long offset; - - gTotalBytesAlignedAllocs += size; - gNumAlignedAllocs++; - - real = (char*)sAllocFunc(size + 2 * sizeof(void*) + (alignment - 1)); - if (real) { - offset = (alignment - (unsigned long)(real + 2 * sizeof(void*))) & (alignment - 1); - ret = (void*)((real + 2 * sizeof(void*)) + offset); - *((void**)(ret)-1) = (void*)(real); - *((int32_t*)(ret)-2) = size; - } - else { - ret = (void*)(real); //?? - } - - printf("allocation#%d at address %x, from %s,line %d, size %d\n", gNumAlignedAllocs, real, filename, line, size); - - int32_t* ptr = (int32_t*)ret; - *ptr = 12; - return (ret); -} - -void btAlignedFreeInternal(void* ptr, int32_t line, char* filename) -{ - - void* real; - gNumAlignedFree++; - - if (ptr) { - real = *((void**)(ptr)-1); - int32_t size = *((int32_t*)(ptr)-2); - gTotalBytesAlignedAllocs -= size; - - printf("free #%d at address %x, from %s,line %d, size %d\n", gNumAlignedFree, real, filename, line, size); - - sFreeFunc(real); - } - else { - printf("NULL ptr\n"); - } -} - -#else //BT_DEBUG_MEMORY_ALLOCATIONS - -void* btAlignedAllocInternal(size_t size, int32_t alignment) -{ - gNumAlignedAllocs++; - void* ptr; - ptr = sAlignedAllocFunc(size, alignment); - // printf("btAlignedAllocInternal %d, %x\n",size,ptr); - return ptr; -} - -void btAlignedFreeInternal(void* ptr) -{ - if (!ptr) { - return; - } - - gNumAlignedFree++; - // printf("btAlignedFreeInternal %x\n",ptr); - sAlignedFreeFunc(ptr); -} - -#endif //BT_DEBUG_MEMORY_ALLOCATIONS diff --git a/src/VHACD_Lib/src/btConvexHullComputer.cpp b/src/VHACD_Lib/src/btConvexHullComputer.cpp deleted file mode 100644 index d3d749ad..00000000 --- a/src/VHACD_Lib/src/btConvexHullComputer.cpp +++ /dev/null @@ -1,2479 +0,0 @@ -/* -Copyright (c) 2011 Ole Kniemeyer, MAXON, www.maxon.net - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#include - -#include "btAlignedObjectArray.h" -#include "btConvexHullComputer.h" -#include "btMinMax.h" -#include "btVector3.h" - -#ifdef __GNUC__ -#include -#elif defined(_MSC_VER) -typedef __int32 int32_t; -typedef __int64 int64_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -#else -typedef int32_t int32_t; -typedef long long int32_t int64_t; -typedef uint32_t uint32_t; -typedef unsigned long long int32_t uint64_t; -#endif - -#ifdef _MSC_VER -#pragma warning(disable:4458) -#endif - -//The definition of USE_X86_64_ASM is moved into the build system. You can enable it manually by commenting out the following lines -//#if (defined(__GNUC__) && defined(__x86_64__) && !defined(__ICL)) // || (defined(__ICL) && defined(_M_X64)) bug in Intel compiler, disable inline assembly -// #define USE_X86_64_ASM -//#endif - -//#define DEBUG_CONVEX_HULL -//#define SHOW_ITERATIONS - -#if defined(DEBUG_CONVEX_HULL) || defined(SHOW_ITERATIONS) -#include -#endif - -// Convex hull implementation based on Preparata and Hong -// Ole Kniemeyer, MAXON Computer GmbH -class btConvexHullInternal { -public: - class Point64 { - public: - int64_t x; - int64_t y; - int64_t z; - - Point64(int64_t x, int64_t y, int64_t z) - : x(x) - , y(y) - , z(z) - { - } - - bool isZero() - { - return (x == 0) && (y == 0) && (z == 0); - } - - int64_t dot(const Point64& b) const - { - return x * b.x + y * b.y + z * b.z; - } - }; - - class Point32 { - public: - int32_t x; - int32_t y; - int32_t z; - int32_t index; - - Point32() - { - } - - Point32(int32_t x, int32_t y, int32_t z) - : x(x) - , y(y) - , z(z) - , index(-1) - { - } - - bool operator==(const Point32& b) const - { - return (x == b.x) && (y == b.y) && (z == b.z); - } - - bool operator!=(const Point32& b) const - { - return (x != b.x) || (y != b.y) || (z != b.z); - } - - bool isZero() - { - return (x == 0) && (y == 0) && (z == 0); - } - - Point64 cross(const Point32& b) const - { - return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); - } - - Point64 cross(const Point64& b) const - { - return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); - } - - int64_t dot(const Point32& b) const - { - return x * b.x + y * b.y + z * b.z; - } - - int64_t dot(const Point64& b) const - { - return x * b.x + y * b.y + z * b.z; - } - - Point32 operator+(const Point32& b) const - { - return Point32(x + b.x, y + b.y, z + b.z); - } - - Point32 operator-(const Point32& b) const - { - return Point32(x - b.x, y - b.y, z - b.z); - } - }; - - class Int128 { - public: - uint64_t low; - uint64_t high; - - Int128() - { - } - - Int128(uint64_t low, uint64_t high) - : low(low) - , high(high) - { - } - - Int128(uint64_t low) - : low(low) - , high(0) - { - } - - Int128(int64_t value) - : low(value) - , high((value >= 0) ? 0 : (uint64_t)-1LL) - { - } - - static Int128 mul(int64_t a, int64_t b); - - static Int128 mul(uint64_t a, uint64_t b); - - Int128 operator-() const - { - return Int128((uint64_t) - (int64_t)low, ~high + (low == 0)); - } - - Int128 operator+(const Int128& b) const - { -#ifdef USE_X86_64_ASM - Int128 result; - __asm__("addq %[bl], %[rl]\n\t" - "adcq %[bh], %[rh]\n\t" - : [rl] "=r"(result.low), [rh] "=r"(result.high) - : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) - : "cc"); - return result; -#else - uint64_t lo = low + b.low; - return Int128(lo, high + b.high + (lo < low)); -#endif - } - - Int128 operator-(const Int128& b) const - { -#ifdef USE_X86_64_ASM - Int128 result; - __asm__("subq %[bl], %[rl]\n\t" - "sbbq %[bh], %[rh]\n\t" - : [rl] "=r"(result.low), [rh] "=r"(result.high) - : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) - : "cc"); - return result; -#else - return *this + -b; -#endif - } - - Int128& operator+=(const Int128& b) - { -#ifdef USE_X86_64_ASM - __asm__("addq %[bl], %[rl]\n\t" - "adcq %[bh], %[rh]\n\t" - : [rl] "=r"(low), [rh] "=r"(high) - : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) - : "cc"); -#else - uint64_t lo = low + b.low; - if (lo < low) { - ++high; - } - low = lo; - high += b.high; -#endif - return *this; - } - - Int128& operator++() - { - if (++low == 0) { - ++high; - } - return *this; - } - - Int128 operator*(int64_t b) const; - - btScalar toScalar() const - { - return ((int64_t)high >= 0) ? btScalar(high) * (btScalar(0x100000000LL) * btScalar(0x100000000LL)) + btScalar(low) - : -(-*this).toScalar(); - } - - int32_t getSign() const - { - return ((int64_t)high < 0) ? -1 : (high || low) ? 1 : 0; - } - - bool operator<(const Int128& b) const - { - return (high < b.high) || ((high == b.high) && (low < b.low)); - } - - int32_t ucmp(const Int128& b) const - { - if (high < b.high) { - return -1; - } - if (high > b.high) { - return 1; - } - if (low < b.low) { - return -1; - } - if (low > b.low) { - return 1; - } - return 0; - } - }; - - class Rational64 { - private: - uint64_t m_numerator; - uint64_t m_denominator; - int32_t sign; - - public: - Rational64(int64_t numerator, int64_t denominator) - { - if (numerator > 0) { - sign = 1; - m_numerator = (uint64_t)numerator; - } - else if (numerator < 0) { - sign = -1; - m_numerator = (uint64_t)-numerator; - } - else { - sign = 0; - m_numerator = 0; - } - if (denominator > 0) { - m_denominator = (uint64_t)denominator; - } - else if (denominator < 0) { - sign = -sign; - m_denominator = (uint64_t)-denominator; - } - else { - m_denominator = 0; - } - } - - bool isNegativeInfinity() const - { - return (sign < 0) && (m_denominator == 0); - } - - bool isNaN() const - { - return (sign == 0) && (m_denominator == 0); - } - - int32_t compare(const Rational64& b) const; - - btScalar toScalar() const - { - return sign * ((m_denominator == 0) ? SIMD_INFINITY : (btScalar)m_numerator / m_denominator); - } - }; - - class Rational128 { - private: - Int128 numerator; - Int128 denominator; - int32_t sign; - bool isInt64; - - public: - Rational128(int64_t value) - { - if (value > 0) { - sign = 1; - this->numerator = value; - } - else if (value < 0) { - sign = -1; - this->numerator = -value; - } - else { - sign = 0; - this->numerator = (uint64_t)0; - } - this->denominator = (uint64_t)1; - isInt64 = true; - } - - Rational128(const Int128& numerator, const Int128& denominator) - { - sign = numerator.getSign(); - if (sign >= 0) { - this->numerator = numerator; - } - else { - this->numerator = -numerator; - } - int32_t dsign = denominator.getSign(); - if (dsign >= 0) { - this->denominator = denominator; - } - else { - sign = -sign; - this->denominator = -denominator; - } - isInt64 = false; - } - - int32_t compare(const Rational128& b) const; - - int32_t compare(int64_t b) const; - - btScalar toScalar() const - { - return sign * ((denominator.getSign() == 0) ? SIMD_INFINITY : numerator.toScalar() / denominator.toScalar()); - } - }; - - class PointR128 { - public: - Int128 x; - Int128 y; - Int128 z; - Int128 denominator; - - PointR128() - { - } - - PointR128(Int128 x, Int128 y, Int128 z, Int128 denominator) - : x(x) - , y(y) - , z(z) - , denominator(denominator) - { - } - - btScalar xvalue() const - { - return x.toScalar() / denominator.toScalar(); - } - - btScalar yvalue() const - { - return y.toScalar() / denominator.toScalar(); - } - - btScalar zvalue() const - { - return z.toScalar() / denominator.toScalar(); - } - }; - - class Edge; - class Face; - - class Vertex { - public: - Vertex* next; - Vertex* prev; - Edge* edges; - Face* firstNearbyFace; - Face* lastNearbyFace; - PointR128 point128; - Point32 point; - int32_t copy; - - Vertex() - : next(NULL) - , prev(NULL) - , edges(NULL) - , firstNearbyFace(NULL) - , lastNearbyFace(NULL) - , copy(-1) - { - } - -#ifdef DEBUG_CONVEX_HULL - void print() - { - printf("V%d (%d, %d, %d)", point.index, point.x, point.y, point.z); - } - - void printGraph(); -#endif - - Point32 operator-(const Vertex& b) const - { - return point - b.point; - } - - Rational128 dot(const Point64& b) const - { - return (point.index >= 0) ? Rational128(point.dot(b)) - : Rational128(point128.x * b.x + point128.y * b.y + point128.z * b.z, point128.denominator); - } - - btScalar xvalue() const - { - return (point.index >= 0) ? btScalar(point.x) : point128.xvalue(); - } - - btScalar yvalue() const - { - return (point.index >= 0) ? btScalar(point.y) : point128.yvalue(); - } - - btScalar zvalue() const - { - return (point.index >= 0) ? btScalar(point.z) : point128.zvalue(); - } - - void receiveNearbyFaces(Vertex* src) - { - if (lastNearbyFace) { - lastNearbyFace->nextWithSameNearbyVertex = src->firstNearbyFace; - } - else { - firstNearbyFace = src->firstNearbyFace; - } - if (src->lastNearbyFace) { - lastNearbyFace = src->lastNearbyFace; - } - for (Face* f = src->firstNearbyFace; f; f = f->nextWithSameNearbyVertex) { - btAssert(f->nearbyVertex == src); - f->nearbyVertex = this; - } - src->firstNearbyFace = NULL; - src->lastNearbyFace = NULL; - } - }; - - class Edge { - public: - Edge* next; - Edge* prev; - Edge* reverse; - Vertex* target; - Face* face; - int32_t copy; - - ~Edge() - { - next = NULL; - prev = NULL; - reverse = NULL; - target = NULL; - face = NULL; - } - - void link(Edge* n) - { - btAssert(reverse->target == n->reverse->target); - next = n; - n->prev = this; - } - -#ifdef DEBUG_CONVEX_HULL - void print() - { - printf("E%p : %d -> %d, n=%p p=%p (0 %d\t%d\t%d) -> (%d %d %d)", this, reverse->target->point.index, target->point.index, next, prev, - reverse->target->point.x, reverse->target->point.y, reverse->target->point.z, target->point.x, target->point.y, target->point.z); - } -#endif - }; - - class Face { - public: - Face* next; - Vertex* nearbyVertex; - Face* nextWithSameNearbyVertex; - Point32 origin; - Point32 dir0; - Point32 dir1; - - Face() - : next(NULL) - , nearbyVertex(NULL) - , nextWithSameNearbyVertex(NULL) - { - } - - void init(Vertex* a, Vertex* b, Vertex* c) - { - nearbyVertex = a; - origin = a->point; - dir0 = *b - *a; - dir1 = *c - *a; - if (a->lastNearbyFace) { - a->lastNearbyFace->nextWithSameNearbyVertex = this; - } - else { - a->firstNearbyFace = this; - } - a->lastNearbyFace = this; - } - - Point64 getNormal() - { - return dir0.cross(dir1); - } - }; - - template - class DMul { - private: - static uint32_t high(uint64_t value) - { - return (uint32_t)(value >> 32); - } - - static uint32_t low(uint64_t value) - { - return (uint32_t)value; - } - - static uint64_t mul(uint32_t a, uint32_t b) - { - return (uint64_t)a * (uint64_t)b; - } - - static void shlHalf(uint64_t& value) - { - value <<= 32; - } - - static uint64_t high(Int128 value) - { - return value.high; - } - - static uint64_t low(Int128 value) - { - return value.low; - } - - static Int128 mul(uint64_t a, uint64_t b) - { - return Int128::mul(a, b); - } - - static void shlHalf(Int128& value) - { - value.high = value.low; - value.low = 0; - } - - public: - static void mul(UWord a, UWord b, UWord& resLow, UWord& resHigh) - { - UWord p00 = mul(low(a), low(b)); - UWord p01 = mul(low(a), high(b)); - UWord p10 = mul(high(a), low(b)); - UWord p11 = mul(high(a), high(b)); - UWord p0110 = UWord(low(p01)) + UWord(low(p10)); - p11 += high(p01); - p11 += high(p10); - p11 += high(p0110); - shlHalf(p0110); - p00 += p0110; - if (p00 < p0110) { - ++p11; - } - resLow = p00; - resHigh = p11; - } - }; - -private: - class IntermediateHull { - public: - Vertex* minXy; - Vertex* maxXy; - Vertex* minYx; - Vertex* maxYx; - - IntermediateHull() - : minXy(NULL) - , maxXy(NULL) - , minYx(NULL) - , maxYx(NULL) - { - } - - void print(); - }; - - enum Orientation { NONE, - CLOCKWISE, - COUNTER_CLOCKWISE }; - - template - class PoolArray { - private: - T* array; - int32_t size; - - public: - PoolArray* next; - - PoolArray(int32_t size) - : size(size) - , next(NULL) - { - array = (T*)btAlignedAlloc(sizeof(T) * size, 16); - } - - ~PoolArray() - { - btAlignedFree(array); - } - - T* init() - { - T* o = array; - for (int32_t i = 0; i < size; i++, o++) { - o->next = (i + 1 < size) ? o + 1 : NULL; - } - return array; - } - }; - - template - class Pool { - private: - PoolArray* arrays; - PoolArray* nextArray; - T* freeObjects; - int32_t arraySize; - - public: - Pool() - : arrays(NULL) - , nextArray(NULL) - , freeObjects(NULL) - , arraySize(256) - { - } - - ~Pool() - { - while (arrays) { - PoolArray* p = arrays; - arrays = p->next; - p->~PoolArray(); - btAlignedFree(p); - } - } - - void reset() - { - nextArray = arrays; - freeObjects = NULL; - } - - void setArraySize(int32_t arraySize) - { - this->arraySize = arraySize; - } - - T* newObject() - { - T* o = freeObjects; - if (!o) { - PoolArray* p = nextArray; - if (p) { - nextArray = p->next; - } - else { - p = new (btAlignedAlloc(sizeof(PoolArray), 16)) PoolArray(arraySize); - p->next = arrays; - arrays = p; - } - o = p->init(); - } - freeObjects = o->next; - return new (o) T(); - }; - - void freeObject(T* object) - { - object->~T(); - object->next = freeObjects; - freeObjects = object; - } - }; - - btVector3 scaling; - btVector3 center; - Pool vertexPool; - Pool edgePool; - Pool facePool; - btAlignedObjectArray originalVertices; - int32_t mergeStamp; - int32_t minAxis; - int32_t medAxis; - int32_t maxAxis; - int32_t usedEdgePairs; - int32_t maxUsedEdgePairs; - - static Orientation getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t); - Edge* findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot); - void findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1); - - Edge* newEdgePair(Vertex* from, Vertex* to); - - void removeEdgePair(Edge* edge) - { - Edge* n = edge->next; - Edge* r = edge->reverse; - - btAssert(edge->target && r->target); - - if (n != edge) { - n->prev = edge->prev; - edge->prev->next = n; - r->target->edges = n; - } - else { - r->target->edges = NULL; - } - - n = r->next; - - if (n != r) { - n->prev = r->prev; - r->prev->next = n; - edge->target->edges = n; - } - else { - edge->target->edges = NULL; - } - - edgePool.freeObject(edge); - edgePool.freeObject(r); - usedEdgePairs--; - } - - void computeInternal(int32_t start, int32_t end, IntermediateHull& result); - - bool mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1); - - void merge(IntermediateHull& h0, IntermediateHull& h1); - - btVector3 toBtVector(const Point32& v); - - btVector3 getBtNormal(Face* face); - - bool shiftFace(Face* face, btScalar amount, btAlignedObjectArray stack); - -public: - Vertex* vertexList; - - void compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count); - - btVector3 getCoordinates(const Vertex* v); - - btScalar shrink(btScalar amount, btScalar clampAmount); -}; - -btConvexHullInternal::Int128 btConvexHullInternal::Int128::operator*(int64_t b) const -{ - bool negative = (int64_t)high < 0; - Int128 a = negative ? -*this : *this; - if (b < 0) { - negative = !negative; - b = -b; - } - Int128 result = mul(a.low, (uint64_t)b); - result.high += a.high * (uint64_t)b; - return negative ? -result : result; -} - -btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(int64_t a, int64_t b) -{ - Int128 result; - -#ifdef USE_X86_64_ASM - __asm__("imulq %[b]" - : "=a"(result.low), "=d"(result.high) - : "0"(a), [b] "r"(b) - : "cc"); - return result; - -#else - bool negative = a < 0; - if (negative) { - a = -a; - } - if (b < 0) { - negative = !negative; - b = -b; - } - DMul::mul((uint64_t)a, (uint64_t)b, result.low, result.high); - return negative ? -result : result; -#endif -} - -btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(uint64_t a, uint64_t b) -{ - Int128 result; - -#ifdef USE_X86_64_ASM - __asm__("mulq %[b]" - : "=a"(result.low), "=d"(result.high) - : "0"(a), [b] "r"(b) - : "cc"); - -#else - DMul::mul(a, b, result.low, result.high); -#endif - - return result; -} - -int32_t btConvexHullInternal::Rational64::compare(const Rational64& b) const -{ - if (sign != b.sign) { - return sign - b.sign; - } - else if (sign == 0) { - return 0; - } - -// return (numerator * b.denominator > b.numerator * denominator) ? sign : (numerator * b.denominator < b.numerator * denominator) ? -sign : 0; - -#ifdef USE_X86_64_ASM - - int32_t result; - int64_t tmp; - int64_t dummy; - __asm__("mulq %[bn]\n\t" - "movq %%rax, %[tmp]\n\t" - "movq %%rdx, %%rbx\n\t" - "movq %[tn], %%rax\n\t" - "mulq %[bd]\n\t" - "subq %[tmp], %%rax\n\t" - "sbbq %%rbx, %%rdx\n\t" // rdx:rax contains 128-bit-difference "numerator*b.denominator - b.numerator*denominator" - "setnsb %%bh\n\t" // bh=1 if difference is non-negative, bh=0 otherwise - "orq %%rdx, %%rax\n\t" - "setnzb %%bl\n\t" // bl=1 if difference if non-zero, bl=0 if it is zero - "decb %%bh\n\t" // now bx=0x0000 if difference is zero, 0xff01 if it is negative, 0x0001 if it is positive (i.e., same sign as difference) - "shll $16, %%ebx\n\t" // ebx has same sign as difference - : "=&b"(result), [tmp] "=&r"(tmp), "=a"(dummy) - : "a"(denominator), [bn] "g"(b.numerator), [tn] "g"(numerator), [bd] "g"(b.denominator) - : "%rdx", "cc"); - return result ? result ^ sign // if sign is +1, only bit 0 of result is inverted, which does not change the sign of result (and cannot result in zero) - // if sign is -1, all bits of result are inverted, which changes the sign of result (and again cannot result in zero) - : 0; - -#else - - return sign * Int128::mul(m_numerator, b.m_denominator).ucmp(Int128::mul(m_denominator, b.m_numerator)); - -#endif -} - -int32_t btConvexHullInternal::Rational128::compare(const Rational128& b) const -{ - if (sign != b.sign) { - return sign - b.sign; - } - else if (sign == 0) { - return 0; - } - if (isInt64) { - return -b.compare(sign * (int64_t)numerator.low); - } - - Int128 nbdLow, nbdHigh, dbnLow, dbnHigh; - DMul::mul(numerator, b.denominator, nbdLow, nbdHigh); - DMul::mul(denominator, b.numerator, dbnLow, dbnHigh); - - int32_t cmp = nbdHigh.ucmp(dbnHigh); - if (cmp) { - return cmp * sign; - } - return nbdLow.ucmp(dbnLow) * sign; -} - -int32_t btConvexHullInternal::Rational128::compare(int64_t b) const -{ - if (isInt64) { - int64_t a = sign * (int64_t)numerator.low; - return (a > b) ? 1 : (a < b) ? -1 : 0; - } - if (b > 0) { - if (sign <= 0) { - return -1; - } - } - else if (b < 0) { - if (sign >= 0) { - return 1; - } - b = -b; - } - else { - return sign; - } - - return numerator.ucmp(denominator * b) * sign; -} - -btConvexHullInternal::Edge* btConvexHullInternal::newEdgePair(Vertex* from, Vertex* to) -{ - btAssert(from && to); - Edge* e = edgePool.newObject(); - Edge* r = edgePool.newObject(); - e->reverse = r; - r->reverse = e; - e->copy = mergeStamp; - r->copy = mergeStamp; - e->target = to; - r->target = from; - e->face = NULL; - r->face = NULL; - usedEdgePairs++; - if (usedEdgePairs > maxUsedEdgePairs) { - maxUsedEdgePairs = usedEdgePairs; - } - return e; -} - -bool btConvexHullInternal::mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1) -{ - Vertex* v0 = h0.maxYx; - Vertex* v1 = h1.minYx; - if ((v0->point.x == v1->point.x) && (v0->point.y == v1->point.y)) { - btAssert(v0->point.z < v1->point.z); - Vertex* v1p = v1->prev; - if (v1p == v1) { - c0 = v0; - if (v1->edges) { - btAssert(v1->edges->next == v1->edges); - v1 = v1->edges->target; - btAssert(v1->edges->next == v1->edges); - } - c1 = v1; - return false; - } - Vertex* v1n = v1->next; - v1p->next = v1n; - v1n->prev = v1p; - if (v1 == h1.minXy) { - if ((v1n->point.x < v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y < v1p->point.y))) { - h1.minXy = v1n; - } - else { - h1.minXy = v1p; - } - } - if (v1 == h1.maxXy) { - if ((v1n->point.x > v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y > v1p->point.y))) { - h1.maxXy = v1n; - } - else { - h1.maxXy = v1p; - } - } - } - - v0 = h0.maxXy; - v1 = h1.maxXy; - Vertex* v00 = NULL; - Vertex* v10 = NULL; - int32_t sign = 1; - - for (int32_t side = 0; side <= 1; side++) { - int32_t dx = (v1->point.x - v0->point.x) * sign; - if (dx > 0) { - while (true) { - int32_t dy = v1->point.y - v0->point.y; - - Vertex* w0 = side ? v0->next : v0->prev; - if (w0 != v0) { - int32_t dx0 = (w0->point.x - v0->point.x) * sign; - int32_t dy0 = w0->point.y - v0->point.y; - if ((dy0 <= 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx <= dy * dx0)))) { - v0 = w0; - dx = (v1->point.x - v0->point.x) * sign; - continue; - } - } - - Vertex* w1 = side ? v1->next : v1->prev; - if (w1 != v1) { - int32_t dx1 = (w1->point.x - v1->point.x) * sign; - int32_t dy1 = w1->point.y - v1->point.y; - int32_t dxn = (w1->point.x - v0->point.x) * sign; - if ((dxn > 0) && (dy1 < 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx < dy * dx1)))) { - v1 = w1; - dx = dxn; - continue; - } - } - - break; - } - } - else if (dx < 0) { - while (true) { - int32_t dy = v1->point.y - v0->point.y; - - Vertex* w1 = side ? v1->prev : v1->next; - if (w1 != v1) { - int32_t dx1 = (w1->point.x - v1->point.x) * sign; - int32_t dy1 = w1->point.y - v1->point.y; - if ((dy1 >= 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx <= dy * dx1)))) { - v1 = w1; - dx = (v1->point.x - v0->point.x) * sign; - continue; - } - } - - Vertex* w0 = side ? v0->prev : v0->next; - if (w0 != v0) { - int32_t dx0 = (w0->point.x - v0->point.x) * sign; - int32_t dy0 = w0->point.y - v0->point.y; - int32_t dxn = (v1->point.x - w0->point.x) * sign; - if ((dxn < 0) && (dy0 > 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx < dy * dx0)))) { - v0 = w0; - dx = dxn; - continue; - } - } - - break; - } - } - else { - int32_t x = v0->point.x; - int32_t y0 = v0->point.y; - Vertex* w0 = v0; - Vertex* t; - while (((t = side ? w0->next : w0->prev) != v0) && (t->point.x == x) && (t->point.y <= y0)) { - w0 = t; - y0 = t->point.y; - } - v0 = w0; - - int32_t y1 = v1->point.y; - Vertex* w1 = v1; - while (((t = side ? w1->prev : w1->next) != v1) && (t->point.x == x) && (t->point.y >= y1)) { - w1 = t; - y1 = t->point.y; - } - v1 = w1; - } - - if (side == 0) { - v00 = v0; - v10 = v1; - - v0 = h0.minXy; - v1 = h1.minXy; - sign = -1; - } - } - - v0->prev = v1; - v1->next = v0; - - v00->next = v10; - v10->prev = v00; - - if (h1.minXy->point.x < h0.minXy->point.x) { - h0.minXy = h1.minXy; - } - if (h1.maxXy->point.x >= h0.maxXy->point.x) { - h0.maxXy = h1.maxXy; - } - - h0.maxYx = h1.maxYx; - - c0 = v00; - c1 = v10; - - return true; -} - -void btConvexHullInternal::computeInternal(int32_t start, int32_t end, IntermediateHull& result) -{ - int32_t n = end - start; - switch (n) { - case 0: - result.minXy = NULL; - result.maxXy = NULL; - result.minYx = NULL; - result.maxYx = NULL; - return; - case 2: { - Vertex* v = originalVertices[start]; - Vertex* w = v + 1; - if (v->point != w->point) { - int32_t dx = v->point.x - w->point.x; - int32_t dy = v->point.y - w->point.y; - - if ((dx == 0) && (dy == 0)) { - if (v->point.z > w->point.z) { - Vertex* t = w; - w = v; - v = t; - } - btAssert(v->point.z < w->point.z); - v->next = v; - v->prev = v; - result.minXy = v; - result.maxXy = v; - result.minYx = v; - result.maxYx = v; - } - else { - v->next = w; - v->prev = w; - w->next = v; - w->prev = v; - - if ((dx < 0) || ((dx == 0) && (dy < 0))) { - result.minXy = v; - result.maxXy = w; - } - else { - result.minXy = w; - result.maxXy = v; - } - - if ((dy < 0) || ((dy == 0) && (dx < 0))) { - result.minYx = v; - result.maxYx = w; - } - else { - result.minYx = w; - result.maxYx = v; - } - } - - Edge* e = newEdgePair(v, w); - e->link(e); - v->edges = e; - - e = e->reverse; - e->link(e); - w->edges = e; - - return; - } - } - // lint -fallthrough - case 1: { - Vertex* v = originalVertices[start]; - v->edges = NULL; - v->next = v; - v->prev = v; - - result.minXy = v; - result.maxXy = v; - result.minYx = v; - result.maxYx = v; - - return; - } - } - - int32_t split0 = start + n / 2; - Point32 p = originalVertices[split0 - 1]->point; - int32_t split1 = split0; - while ((split1 < end) && (originalVertices[split1]->point == p)) { - split1++; - } - computeInternal(start, split0, result); - IntermediateHull hull1; - computeInternal(split1, end, hull1); -#ifdef DEBUG_CONVEX_HULL - printf("\n\nMerge\n"); - result.print(); - hull1.print(); -#endif - merge(result, hull1); -#ifdef DEBUG_CONVEX_HULL - printf("\n Result\n"); - result.print(); -#endif -} - -#ifdef DEBUG_CONVEX_HULL -void btConvexHullInternal::IntermediateHull::print() -{ - printf(" Hull\n"); - for (Vertex* v = minXy; v;) { - printf(" "); - v->print(); - if (v == maxXy) { - printf(" maxXy"); - } - if (v == minYx) { - printf(" minYx"); - } - if (v == maxYx) { - printf(" maxYx"); - } - if (v->next->prev != v) { - printf(" Inconsistency"); - } - printf("\n"); - v = v->next; - if (v == minXy) { - break; - } - } - if (minXy) { - minXy->copy = (minXy->copy == -1) ? -2 : -1; - minXy->printGraph(); - } -} - -void btConvexHullInternal::Vertex::printGraph() -{ - print(); - printf("\nEdges\n"); - Edge* e = edges; - if (e) { - do { - e->print(); - printf("\n"); - e = e->next; - } while (e != edges); - do { - Vertex* v = e->target; - if (v->copy != copy) { - v->copy = copy; - v->printGraph(); - } - e = e->next; - } while (e != edges); - } -} -#endif - -btConvexHullInternal::Orientation btConvexHullInternal::getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t) -{ - btAssert(prev->reverse->target == next->reverse->target); - if (prev->next == next) { - if (prev->prev == next) { - Point64 n = t.cross(s); - Point64 m = (*prev->target - *next->reverse->target).cross(*next->target - *next->reverse->target); - btAssert(!m.isZero()); - int64_t dot = n.dot(m); - btAssert(dot != 0); - return (dot > 0) ? COUNTER_CLOCKWISE : CLOCKWISE; - } - return COUNTER_CLOCKWISE; - } - else if (prev->prev == next) { - return CLOCKWISE; - } - else { - return NONE; - } -} - -btConvexHullInternal::Edge* btConvexHullInternal::findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot) -{ - Edge* minEdge = NULL; - -#ifdef DEBUG_CONVEX_HULL - printf("find max edge for %d\n", start->point.index); -#endif - Edge* e = start->edges; - if (e) { - do { - if (e->copy > mergeStamp) { - Point32 t = *e->target - *start; - Rational64 cot(t.dot(sxrxs), t.dot(rxs)); -#ifdef DEBUG_CONVEX_HULL - printf(" Angle is %f (%d) for ", (float)btAtan(cot.toScalar()), (int32_t)cot.isNaN()); - e->print(); -#endif - if (cot.isNaN()) { - btAssert(ccw ? (t.dot(s) < 0) : (t.dot(s) > 0)); - } - else { - int32_t cmp; - if (minEdge == NULL) { - minCot = cot; - minEdge = e; - } - else if ((cmp = cot.compare(minCot)) < 0) { - minCot = cot; - minEdge = e; - } - else if ((cmp == 0) && (ccw == (getOrientation(minEdge, e, s, t) == COUNTER_CLOCKWISE))) { - minEdge = e; - } - } -#ifdef DEBUG_CONVEX_HULL - printf("\n"); -#endif - } - e = e->next; - } while (e != start->edges); - } - return minEdge; -} - -void btConvexHullInternal::findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1) -{ - Edge* start0 = e0; - Edge* start1 = e1; - Point32 et0 = start0 ? start0->target->point : c0->point; - Point32 et1 = start1 ? start1->target->point : c1->point; - Point32 s = c1->point - c0->point; - Point64 normal = ((start0 ? start0 : start1)->target->point - c0->point).cross(s); - int64_t dist = c0->point.dot(normal); - btAssert(!start1 || (start1->target->point.dot(normal) == dist)); - Point64 perp = s.cross(normal); - btAssert(!perp.isZero()); - -#ifdef DEBUG_CONVEX_HULL - printf(" Advancing %d %d (%p %p, %d %d)\n", c0->point.index, c1->point.index, start0, start1, start0 ? start0->target->point.index : -1, start1 ? start1->target->point.index : -1); -#endif - - int64_t maxDot0 = et0.dot(perp); - if (e0) { - while (e0->target != stop0) { - Edge* e = e0->reverse->prev; - if (e->target->point.dot(normal) < dist) { - break; - } - btAssert(e->target->point.dot(normal) == dist); - if (e->copy == mergeStamp) { - break; - } - int64_t dot = e->target->point.dot(perp); - if (dot <= maxDot0) { - break; - } - maxDot0 = dot; - e0 = e; - et0 = e->target->point; - } - } - - int64_t maxDot1 = et1.dot(perp); - if (e1) { - while (e1->target != stop1) { - Edge* e = e1->reverse->next; - if (e->target->point.dot(normal) < dist) { - break; - } - btAssert(e->target->point.dot(normal) == dist); - if (e->copy == mergeStamp) { - break; - } - int64_t dot = e->target->point.dot(perp); - if (dot <= maxDot1) { - break; - } - maxDot1 = dot; - e1 = e; - et1 = e->target->point; - } - } - -#ifdef DEBUG_CONVEX_HULL - printf(" Starting at %d %d\n", et0.index, et1.index); -#endif - - int64_t dx = maxDot1 - maxDot0; - if (dx > 0) { - while (true) { - int64_t dy = (et1 - et0).dot(s); - - if (e0 && (e0->target != stop0)) { - Edge* f0 = e0->next->reverse; - if (f0->copy > mergeStamp) { - int64_t dx0 = (f0->target->point - et0).dot(perp); - int64_t dy0 = (f0->target->point - et0).dot(s); - if ((dx0 == 0) ? (dy0 < 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) >= 0))) { - et0 = f0->target->point; - dx = (et1 - et0).dot(perp); - e0 = (e0 == start0) ? NULL : f0; - continue; - } - } - } - - if (e1 && (e1->target != stop1)) { - Edge* f1 = e1->reverse->next; - if (f1->copy > mergeStamp) { - Point32 d1 = f1->target->point - et1; - if (d1.dot(normal) == 0) { - int64_t dx1 = d1.dot(perp); - int64_t dy1 = d1.dot(s); - int64_t dxn = (f1->target->point - et0).dot(perp); - if ((dxn > 0) && ((dx1 == 0) ? (dy1 < 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) > 0)))) { - e1 = f1; - et1 = e1->target->point; - dx = dxn; - continue; - } - } - else { - btAssert((e1 == start1) && (d1.dot(normal) < 0)); - } - } - } - - break; - } - } - else if (dx < 0) { - while (true) { - int64_t dy = (et1 - et0).dot(s); - - if (e1 && (e1->target != stop1)) { - Edge* f1 = e1->prev->reverse; - if (f1->copy > mergeStamp) { - int64_t dx1 = (f1->target->point - et1).dot(perp); - int64_t dy1 = (f1->target->point - et1).dot(s); - if ((dx1 == 0) ? (dy1 > 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) <= 0))) { - et1 = f1->target->point; - dx = (et1 - et0).dot(perp); - e1 = (e1 == start1) ? NULL : f1; - continue; - } - } - } - - if (e0 && (e0->target != stop0)) { - Edge* f0 = e0->reverse->prev; - if (f0->copy > mergeStamp) { - Point32 d0 = f0->target->point - et0; - if (d0.dot(normal) == 0) { - int64_t dx0 = d0.dot(perp); - int64_t dy0 = d0.dot(s); - int64_t dxn = (et1 - f0->target->point).dot(perp); - if ((dxn < 0) && ((dx0 == 0) ? (dy0 > 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) < 0)))) { - e0 = f0; - et0 = e0->target->point; - dx = dxn; - continue; - } - } - else { - btAssert((e0 == start0) && (d0.dot(normal) < 0)); - } - } - } - - break; - } - } -#ifdef DEBUG_CONVEX_HULL - printf(" Advanced edges to %d %d\n", et0.index, et1.index); -#endif -} - -void btConvexHullInternal::merge(IntermediateHull& h0, IntermediateHull& h1) -{ - if (!h1.maxXy) { - return; - } - if (!h0.maxXy) { - h0 = h1; - return; - } - - mergeStamp--; - - Vertex* c0 = NULL; - Edge* toPrev0 = NULL; - Edge* firstNew0 = NULL; - Edge* pendingHead0 = NULL; - Edge* pendingTail0 = NULL; - Vertex* c1 = NULL; - Edge* toPrev1 = NULL; - Edge* firstNew1 = NULL; - Edge* pendingHead1 = NULL; - Edge* pendingTail1 = NULL; - Point32 prevPoint; - - if (mergeProjection(h0, h1, c0, c1)) { - Point32 s = *c1 - *c0; - Point64 normal = Point32(0, 0, -1).cross(s); - Point64 t = s.cross(normal); - btAssert(!t.isZero()); - - Edge* e = c0->edges; - Edge* start0 = NULL; - if (e) { - do { - int64_t dot = (*e->target - *c0).dot(normal); - btAssert(dot <= 0); - if ((dot == 0) && ((*e->target - *c0).dot(t) > 0)) { - if (!start0 || (getOrientation(start0, e, s, Point32(0, 0, -1)) == CLOCKWISE)) { - start0 = e; - } - } - e = e->next; - } while (e != c0->edges); - } - - e = c1->edges; - Edge* start1 = NULL; - if (e) { - do { - int64_t dot = (*e->target - *c1).dot(normal); - btAssert(dot <= 0); - if ((dot == 0) && ((*e->target - *c1).dot(t) > 0)) { - if (!start1 || (getOrientation(start1, e, s, Point32(0, 0, -1)) == COUNTER_CLOCKWISE)) { - start1 = e; - } - } - e = e->next; - } while (e != c1->edges); - } - - if (start0 || start1) { - findEdgeForCoplanarFaces(c0, c1, start0, start1, NULL, NULL); - if (start0) { - c0 = start0->target; - } - if (start1) { - c1 = start1->target; - } - } - - prevPoint = c1->point; - prevPoint.z++; - } - else { - prevPoint = c1->point; - prevPoint.x++; - } - - Vertex* first0 = c0; - Vertex* first1 = c1; - bool firstRun = true; - - while (true) { - Point32 s = *c1 - *c0; - Point32 r = prevPoint - c0->point; - Point64 rxs = r.cross(s); - Point64 sxrxs = s.cross(rxs); - -#ifdef DEBUG_CONVEX_HULL - printf("\n Checking %d %d\n", c0->point.index, c1->point.index); -#endif - Rational64 minCot0(0, 0); - Edge* min0 = findMaxAngle(false, c0, s, rxs, sxrxs, minCot0); - Rational64 minCot1(0, 0); - Edge* min1 = findMaxAngle(true, c1, s, rxs, sxrxs, minCot1); - if (!min0 && !min1) { - Edge* e = newEdgePair(c0, c1); - e->link(e); - c0->edges = e; - - e = e->reverse; - e->link(e); - c1->edges = e; - return; - } - else { - int32_t cmp = !min0 ? 1 : !min1 ? -1 : minCot0.compare(minCot1); -#ifdef DEBUG_CONVEX_HULL - printf(" -> Result %d\n", cmp); -#endif - if (firstRun || ((cmp >= 0) ? !minCot1.isNegativeInfinity() : !minCot0.isNegativeInfinity())) { - Edge* e = newEdgePair(c0, c1); - if (pendingTail0) { - pendingTail0->prev = e; - } - else { - pendingHead0 = e; - } - e->next = pendingTail0; - pendingTail0 = e; - - e = e->reverse; - if (pendingTail1) { - pendingTail1->next = e; - } - else { - pendingHead1 = e; - } - e->prev = pendingTail1; - pendingTail1 = e; - } - - Edge* e0 = min0; - Edge* e1 = min1; - -#ifdef DEBUG_CONVEX_HULL - printf(" Found min edges to %d %d\n", e0 ? e0->target->point.index : -1, e1 ? e1->target->point.index : -1); -#endif - - if (cmp == 0) { - findEdgeForCoplanarFaces(c0, c1, e0, e1, NULL, NULL); - } - - if ((cmp >= 0) && e1) { - if (toPrev1) { - for (Edge *e = toPrev1->next, *n = NULL; e != min1; e = n) { - n = e->next; - removeEdgePair(e); - } - } - - if (pendingTail1) { - if (toPrev1) { - toPrev1->link(pendingHead1); - } - else { - min1->prev->link(pendingHead1); - firstNew1 = pendingHead1; - } - pendingTail1->link(min1); - pendingHead1 = NULL; - pendingTail1 = NULL; - } - else if (!toPrev1) { - firstNew1 = min1; - } - - prevPoint = c1->point; - c1 = e1->target; - toPrev1 = e1->reverse; - } - - if ((cmp <= 0) && e0) { - if (toPrev0) { - for (Edge *e = toPrev0->prev, *n = NULL; e != min0; e = n) { - n = e->prev; - removeEdgePair(e); - } - } - - if (pendingTail0) { - if (toPrev0) { - pendingHead0->link(toPrev0); - } - else { - pendingHead0->link(min0->next); - firstNew0 = pendingHead0; - } - min0->link(pendingTail0); - pendingHead0 = NULL; - pendingTail0 = NULL; - } - else if (!toPrev0) { - firstNew0 = min0; - } - - prevPoint = c0->point; - c0 = e0->target; - toPrev0 = e0->reverse; - } - } - - if ((c0 == first0) && (c1 == first1)) { - if (toPrev0 == NULL) { - pendingHead0->link(pendingTail0); - c0->edges = pendingTail0; - } - else { - for (Edge *e = toPrev0->prev, *n = NULL; e != firstNew0; e = n) { - n = e->prev; - removeEdgePair(e); - } - if (pendingTail0) { - pendingHead0->link(toPrev0); - firstNew0->link(pendingTail0); - } - } - - if (toPrev1 == NULL) { - pendingTail1->link(pendingHead1); - c1->edges = pendingTail1; - } - else { - for (Edge *e = toPrev1->next, *n = NULL; e != firstNew1; e = n) { - n = e->next; - removeEdgePair(e); - } - if (pendingTail1) { - toPrev1->link(pendingHead1); - pendingTail1->link(firstNew1); - } - } - - return; - } - - firstRun = false; - } -} - -static bool pointCmp(const btConvexHullInternal::Point32& p, const btConvexHullInternal::Point32& q) -{ - return (p.y < q.y) || ((p.y == q.y) && ((p.x < q.x) || ((p.x == q.x) && (p.z < q.z)))); -} - -void btConvexHullInternal::compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count) -{ - btVector3 min(btScalar(1e30), btScalar(1e30), btScalar(1e30)), max(btScalar(-1e30), btScalar(-1e30), btScalar(-1e30)); - const char* ptr = (const char*)coords; - if (doubleCoords) { - for (int32_t i = 0; i < count; i++) { - const double* v = (const double*)ptr; - btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); - ptr += stride; - min.setMin(p); - max.setMax(p); - } - } - else { - for (int32_t i = 0; i < count; i++) { - const float* v = (const float*)ptr; - btVector3 p(v[0], v[1], v[2]); - ptr += stride; - min.setMin(p); - max.setMax(p); - } - } - - btVector3 s = max - min; - maxAxis = s.maxAxis(); - minAxis = s.minAxis(); - if (minAxis == maxAxis) { - minAxis = (maxAxis + 1) % 3; - } - medAxis = 3 - maxAxis - minAxis; - - s /= btScalar(10216); - if (((medAxis + 1) % 3) != maxAxis) { - s *= -1; - } - scaling = s; - - if (s[0] != 0) { - s[0] = btScalar(1) / s[0]; - } - if (s[1] != 0) { - s[1] = btScalar(1) / s[1]; - } - if (s[2] != 0) { - s[2] = btScalar(1) / s[2]; - } - - center = (min + max) * btScalar(0.5); - - btAlignedObjectArray points; - points.resize(count); - ptr = (const char*)coords; - if (doubleCoords) { - for (int32_t i = 0; i < count; i++) { - const double* v = (const double*)ptr; - btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); - ptr += stride; - p = (p - center) * s; - points[i].x = (int32_t)p[medAxis]; - points[i].y = (int32_t)p[maxAxis]; - points[i].z = (int32_t)p[minAxis]; - points[i].index = i; - } - } - else { - for (int32_t i = 0; i < count; i++) { - const float* v = (const float*)ptr; - btVector3 p(v[0], v[1], v[2]); - ptr += stride; - p = (p - center) * s; - points[i].x = (int32_t)p[medAxis]; - points[i].y = (int32_t)p[maxAxis]; - points[i].z = (int32_t)p[minAxis]; - points[i].index = i; - } - } - points.quickSort(pointCmp); - - vertexPool.reset(); - vertexPool.setArraySize(count); - originalVertices.resize(count); - for (int32_t i = 0; i < count; i++) { - Vertex* v = vertexPool.newObject(); - v->edges = NULL; - v->point = points[i]; - v->copy = -1; - originalVertices[i] = v; - } - - points.clear(); - - edgePool.reset(); - edgePool.setArraySize(6 * count); - - usedEdgePairs = 0; - maxUsedEdgePairs = 0; - - mergeStamp = -3; - - IntermediateHull hull; - computeInternal(0, count, hull); - vertexList = hull.minXy; -#ifdef DEBUG_CONVEX_HULL - printf("max. edges %d (3v = %d)", maxUsedEdgePairs, 3 * count); -#endif -} - -btVector3 btConvexHullInternal::toBtVector(const Point32& v) -{ - btVector3 p; - p[medAxis] = btScalar(v.x); - p[maxAxis] = btScalar(v.y); - p[minAxis] = btScalar(v.z); - return p * scaling; -} - -btVector3 btConvexHullInternal::getBtNormal(Face* face) -{ - return toBtVector(face->dir0).cross(toBtVector(face->dir1)).normalized(); -} - -btVector3 btConvexHullInternal::getCoordinates(const Vertex* v) -{ - btVector3 p; - p[medAxis] = v->xvalue(); - p[maxAxis] = v->yvalue(); - p[minAxis] = v->zvalue(); - return p * scaling + center; -} - -btScalar btConvexHullInternal::shrink(btScalar amount, btScalar clampAmount) -{ - if (!vertexList) { - return 0; - } - int32_t stamp = --mergeStamp; - btAlignedObjectArray stack; - vertexList->copy = stamp; - stack.push_back(vertexList); - btAlignedObjectArray faces; - - Point32 ref = vertexList->point; - Int128 hullCenterX(0, 0); - Int128 hullCenterY(0, 0); - Int128 hullCenterZ(0, 0); - Int128 volume(0, 0); - - while (stack.size() > 0) { - Vertex* v = stack[stack.size() - 1]; - stack.pop_back(); - Edge* e = v->edges; - if (e) { - do { - if (e->target->copy != stamp) { - e->target->copy = stamp; - stack.push_back(e->target); - } - if (e->copy != stamp) { - Face* face = facePool.newObject(); - face->init(e->target, e->reverse->prev->target, v); - faces.push_back(face); - Edge* f = e; - - Vertex* a = NULL; - Vertex* b = NULL; - do { - if (a && b) { - int64_t vol = (v->point - ref).dot((a->point - ref).cross(b->point - ref)); - btAssert(vol >= 0); - Point32 c = v->point + a->point + b->point + ref; - hullCenterX += vol * c.x; - hullCenterY += vol * c.y; - hullCenterZ += vol * c.z; - volume += vol; - } - - btAssert(f->copy != stamp); - f->copy = stamp; - f->face = face; - - a = b; - b = f->target; - - f = f->reverse->prev; - } while (f != e); - } - e = e->next; - } while (e != v->edges); - } - } - - if (volume.getSign() <= 0) { - return 0; - } - - btVector3 hullCenter; - hullCenter[medAxis] = hullCenterX.toScalar(); - hullCenter[maxAxis] = hullCenterY.toScalar(); - hullCenter[minAxis] = hullCenterZ.toScalar(); - hullCenter /= 4 * volume.toScalar(); - hullCenter *= scaling; - - int32_t faceCount = faces.size(); - - if (clampAmount > 0) { - btScalar minDist = SIMD_INFINITY; - for (int32_t i = 0; i < faceCount; i++) { - btVector3 normal = getBtNormal(faces[i]); - btScalar dist = normal.dot(toBtVector(faces[i]->origin) - hullCenter); - if (dist < minDist) { - minDist = dist; - } - } - - if (minDist <= 0) { - return 0; - } - - amount = btMin(amount, minDist * clampAmount); - } - - uint32_t seed = 243703; - for (int32_t i = 0; i < faceCount; i++, seed = 1664525 * seed + 1013904223) { - btSwap(faces[i], faces[seed % faceCount]); - } - - for (int32_t i = 0; i < faceCount; i++) { - if (!shiftFace(faces[i], amount, stack)) { - return -amount; - } - } - - return amount; -} - -bool btConvexHullInternal::shiftFace(Face* face, btScalar amount, btAlignedObjectArray stack) -{ - btVector3 origShift = getBtNormal(face) * -amount; - if (scaling[0] != 0) { - origShift[0] /= scaling[0]; - } - if (scaling[1] != 0) { - origShift[1] /= scaling[1]; - } - if (scaling[2] != 0) { - origShift[2] /= scaling[2]; - } - Point32 shift((int32_t)origShift[medAxis], (int32_t)origShift[maxAxis], (int32_t)origShift[minAxis]); - if (shift.isZero()) { - return true; - } - Point64 normal = face->getNormal(); -#ifdef DEBUG_CONVEX_HULL - printf("\nShrinking face (%d %d %d) (%d %d %d) (%d %d %d) by (%d %d %d)\n", - face->origin.x, face->origin.y, face->origin.z, face->dir0.x, face->dir0.y, face->dir0.z, face->dir1.x, face->dir1.y, face->dir1.z, shift.x, shift.y, shift.z); -#endif - int64_t origDot = face->origin.dot(normal); - Point32 shiftedOrigin = face->origin + shift; - int64_t shiftedDot = shiftedOrigin.dot(normal); - btAssert(shiftedDot <= origDot); - if (shiftedDot >= origDot) { - return false; - } - - Edge* intersection = NULL; - - Edge* startEdge = face->nearbyVertex->edges; -#ifdef DEBUG_CONVEX_HULL - printf("Start edge is "); - startEdge->print(); - printf(", normal is (%lld %lld %lld), shifted dot is %lld\n", normal.x, normal.y, normal.z, shiftedDot); -#endif - Rational128 optDot = face->nearbyVertex->dot(normal); - int32_t cmp = optDot.compare(shiftedDot); -#ifdef SHOW_ITERATIONS - int32_t n = 0; -#endif - if (cmp >= 0) { - Edge* e = startEdge; - do { -#ifdef SHOW_ITERATIONS - n++; -#endif - Rational128 dot = e->target->dot(normal); - btAssert(dot.compare(origDot) <= 0); -#ifdef DEBUG_CONVEX_HULL - printf("Moving downwards, edge is "); - e->print(); - printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); -#endif - if (dot.compare(optDot) < 0) { - int32_t c = dot.compare(shiftedDot); - optDot = dot; - e = e->reverse; - startEdge = e; - if (c < 0) { - intersection = e; - break; - } - cmp = c; - } - e = e->prev; - } while (e != startEdge); - - if (!intersection) { - return false; - } - } - else { - Edge* e = startEdge; - do { -#ifdef SHOW_ITERATIONS - n++; -#endif - Rational128 dot = e->target->dot(normal); - btAssert(dot.compare(origDot) <= 0); -#ifdef DEBUG_CONVEX_HULL - printf("Moving upwards, edge is "); - e->print(); - printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); -#endif - if (dot.compare(optDot) > 0) { - cmp = dot.compare(shiftedDot); - if (cmp >= 0) { - intersection = e; - break; - } - optDot = dot; - e = e->reverse; - startEdge = e; - } - e = e->prev; - } while (e != startEdge); - - if (!intersection) { - return true; - } - } - -#ifdef SHOW_ITERATIONS - printf("Needed %d iterations to find initial intersection\n", n); -#endif - - if (cmp == 0) { - Edge* e = intersection->reverse->next; -#ifdef SHOW_ITERATIONS - n = 0; -#endif - while (e->target->dot(normal).compare(shiftedDot) <= 0) { -#ifdef SHOW_ITERATIONS - n++; -#endif - e = e->next; - if (e == intersection->reverse) { - return true; - } -#ifdef DEBUG_CONVEX_HULL - printf("Checking for outwards edge, current edge is "); - e->print(); - printf("\n"); -#endif - } -#ifdef SHOW_ITERATIONS - printf("Needed %d iterations to check for complete containment\n", n); -#endif - } - - Edge* firstIntersection = NULL; - Edge* faceEdge = NULL; - Edge* firstFaceEdge = NULL; - -#ifdef SHOW_ITERATIONS - int32_t m = 0; -#endif - while (true) { -#ifdef SHOW_ITERATIONS - m++; -#endif -#ifdef DEBUG_CONVEX_HULL - printf("Intersecting edge is "); - intersection->print(); - printf("\n"); -#endif - if (cmp == 0) { - Edge* e = intersection->reverse->next; - startEdge = e; -#ifdef SHOW_ITERATIONS - n = 0; -#endif - while (true) { -#ifdef SHOW_ITERATIONS - n++; -#endif - if (e->target->dot(normal).compare(shiftedDot) >= 0) { - break; - } - intersection = e->reverse; - e = e->next; - if (e == startEdge) { - return true; - } - } -#ifdef SHOW_ITERATIONS - printf("Needed %d iterations to advance intersection\n", n); -#endif - } - -#ifdef DEBUG_CONVEX_HULL - printf("Advanced intersecting edge to "); - intersection->print(); - printf(", cmp = %d\n", cmp); -#endif - - if (!firstIntersection) { - firstIntersection = intersection; - } - else if (intersection == firstIntersection) { - break; - } - - int32_t prevCmp = cmp; - Edge* prevIntersection = intersection; - Edge* prevFaceEdge = faceEdge; - - Edge* e = intersection->reverse; -#ifdef SHOW_ITERATIONS - n = 0; -#endif - while (true) { -#ifdef SHOW_ITERATIONS - n++; -#endif - e = e->reverse->prev; - btAssert(e != intersection->reverse); - cmp = e->target->dot(normal).compare(shiftedDot); -#ifdef DEBUG_CONVEX_HULL - printf("Testing edge "); - e->print(); - printf(" -> cmp = %d\n", cmp); -#endif - if (cmp >= 0) { - intersection = e; - break; - } - } -#ifdef SHOW_ITERATIONS - printf("Needed %d iterations to find other intersection of face\n", n); -#endif - - if (cmp > 0) { - Vertex* removed = intersection->target; - e = intersection->reverse; - if (e->prev == e) { - removed->edges = NULL; - } - else { - removed->edges = e->prev; - e->prev->link(e->next); - e->link(e); - } -#ifdef DEBUG_CONVEX_HULL - printf("1: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); -#endif - - Point64 n0 = intersection->face->getNormal(); - Point64 n1 = intersection->reverse->face->getNormal(); - int64_t m00 = face->dir0.dot(n0); - int64_t m01 = face->dir1.dot(n0); - int64_t m10 = face->dir0.dot(n1); - int64_t m11 = face->dir1.dot(n1); - int64_t r0 = (intersection->face->origin - shiftedOrigin).dot(n0); - int64_t r1 = (intersection->reverse->face->origin - shiftedOrigin).dot(n1); - Int128 det = Int128::mul(m00, m11) - Int128::mul(m01, m10); - btAssert(det.getSign() != 0); - Vertex* v = vertexPool.newObject(); - v->point.index = -1; - v->copy = -1; - v->point128 = PointR128(Int128::mul(face->dir0.x * r0, m11) - Int128::mul(face->dir0.x * r1, m01) - + Int128::mul(face->dir1.x * r1, m00) - Int128::mul(face->dir1.x * r0, m10) + det * shiftedOrigin.x, - Int128::mul(face->dir0.y * r0, m11) - Int128::mul(face->dir0.y * r1, m01) - + Int128::mul(face->dir1.y * r1, m00) - Int128::mul(face->dir1.y * r0, m10) + det * shiftedOrigin.y, - Int128::mul(face->dir0.z * r0, m11) - Int128::mul(face->dir0.z * r1, m01) - + Int128::mul(face->dir1.z * r1, m00) - Int128::mul(face->dir1.z * r0, m10) + det * shiftedOrigin.z, - det); - v->point.x = (int32_t)v->point128.xvalue(); - v->point.y = (int32_t)v->point128.yvalue(); - v->point.z = (int32_t)v->point128.zvalue(); - intersection->target = v; - v->edges = e; - - stack.push_back(v); - stack.push_back(removed); - stack.push_back(NULL); - } - - if (cmp || prevCmp || (prevIntersection->reverse->next->target != intersection->target)) { - faceEdge = newEdgePair(prevIntersection->target, intersection->target); - if (prevCmp == 0) { - faceEdge->link(prevIntersection->reverse->next); - } - if ((prevCmp == 0) || prevFaceEdge) { - prevIntersection->reverse->link(faceEdge); - } - if (cmp == 0) { - intersection->reverse->prev->link(faceEdge->reverse); - } - faceEdge->reverse->link(intersection->reverse); - } - else { - faceEdge = prevIntersection->reverse->next; - } - - if (prevFaceEdge) { - if (prevCmp > 0) { - faceEdge->link(prevFaceEdge->reverse); - } - else if (faceEdge != prevFaceEdge->reverse) { - stack.push_back(prevFaceEdge->target); - while (faceEdge->next != prevFaceEdge->reverse) { - Vertex* removed = faceEdge->next->target; - removeEdgePair(faceEdge->next); - stack.push_back(removed); -#ifdef DEBUG_CONVEX_HULL - printf("2: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); -#endif - } - stack.push_back(NULL); - } - } - faceEdge->face = face; - faceEdge->reverse->face = intersection->face; - - if (!firstFaceEdge) { - firstFaceEdge = faceEdge; - } - } -#ifdef SHOW_ITERATIONS - printf("Needed %d iterations to process all intersections\n", m); -#endif - - if (cmp > 0) { - firstFaceEdge->reverse->target = faceEdge->target; - firstIntersection->reverse->link(firstFaceEdge); - firstFaceEdge->link(faceEdge->reverse); - } - else if (firstFaceEdge != faceEdge->reverse) { - stack.push_back(faceEdge->target); - while (firstFaceEdge->next != faceEdge->reverse) { - Vertex* removed = firstFaceEdge->next->target; - removeEdgePair(firstFaceEdge->next); - stack.push_back(removed); -#ifdef DEBUG_CONVEX_HULL - printf("3: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); -#endif - } - stack.push_back(NULL); - } - - btAssert(stack.size() > 0); - vertexList = stack[0]; - -#ifdef DEBUG_CONVEX_HULL - printf("Removing part\n"); -#endif -#ifdef SHOW_ITERATIONS - n = 0; -#endif - int32_t pos = 0; - while (pos < stack.size()) { - int32_t end = stack.size(); - while (pos < end) { - Vertex* kept = stack[pos++]; -#ifdef DEBUG_CONVEX_HULL - kept->print(); -#endif - bool deeper = false; - Vertex* removed; - while ((removed = stack[pos++]) != NULL) { -#ifdef SHOW_ITERATIONS - n++; -#endif - kept->receiveNearbyFaces(removed); - while (removed->edges) { - if (!deeper) { - deeper = true; - stack.push_back(kept); - } - stack.push_back(removed->edges->target); - removeEdgePair(removed->edges); - } - } - if (deeper) { - stack.push_back(NULL); - } - } - } -#ifdef SHOW_ITERATIONS - printf("Needed %d iterations to remove part\n", n); -#endif - - stack.resize(0); - face->origin = shiftedOrigin; - - return true; -} - -static int32_t getVertexCopy(btConvexHullInternal::Vertex* vertex, btAlignedObjectArray& vertices) -{ - int32_t index = vertex->copy; - if (index < 0) { - index = vertices.size(); - vertex->copy = index; - vertices.push_back(vertex); -#ifdef DEBUG_CONVEX_HULL - printf("Vertex %d gets index *%d\n", vertex->point.index, index); -#endif - } - return index; -} - -btScalar btConvexHullComputer::compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) -{ - if (count <= 0) { - vertices.clear(); - edges.clear(); - faces.clear(); - return 0; - } - - btConvexHullInternal hull; - hull.compute(coords, doubleCoords, stride, count); - - btScalar shift = 0; - if ((shrink > 0) && ((shift = hull.shrink(shrink, shrinkClamp)) < 0)) { - vertices.clear(); - edges.clear(); - faces.clear(); - return shift; - } - - vertices.resize(0); - edges.resize(0); - faces.resize(0); - - btAlignedObjectArray oldVertices; - getVertexCopy(hull.vertexList, oldVertices); - int32_t copied = 0; - while (copied < oldVertices.size()) { - btConvexHullInternal::Vertex* v = oldVertices[copied]; - vertices.push_back(hull.getCoordinates(v)); - btConvexHullInternal::Edge* firstEdge = v->edges; - if (firstEdge) { - int32_t firstCopy = -1; - int32_t prevCopy = -1; - btConvexHullInternal::Edge* e = firstEdge; - do { - if (e->copy < 0) { - int32_t s = edges.size(); - edges.push_back(Edge()); - edges.push_back(Edge()); - Edge* c = &edges[s]; - Edge* r = &edges[s + 1]; - e->copy = s; - e->reverse->copy = s + 1; - c->reverse = 1; - r->reverse = -1; - c->targetVertex = getVertexCopy(e->target, oldVertices); - r->targetVertex = copied; -#ifdef DEBUG_CONVEX_HULL - printf(" CREATE: Vertex *%d has edge to *%d\n", copied, c->getTargetVertex()); -#endif - } - if (prevCopy >= 0) { - edges[e->copy].next = prevCopy - e->copy; - } - else { - firstCopy = e->copy; - } - prevCopy = e->copy; - e = e->next; - } while (e != firstEdge); - edges[firstCopy].next = prevCopy - firstCopy; - } - copied++; - } - - for (int32_t i = 0; i < copied; i++) { - btConvexHullInternal::Vertex* v = oldVertices[i]; - btConvexHullInternal::Edge* firstEdge = v->edges; - if (firstEdge) { - btConvexHullInternal::Edge* e = firstEdge; - do { - if (e->copy >= 0) { -#ifdef DEBUG_CONVEX_HULL - printf("Vertex *%d has edge to *%d\n", i, edges[e->copy].getTargetVertex()); -#endif - faces.push_back(e->copy); - btConvexHullInternal::Edge* f = e; - do { -#ifdef DEBUG_CONVEX_HULL - printf(" Face *%d\n", edges[f->copy].getTargetVertex()); -#endif - f->copy = -1; - f = f->reverse->prev; - } while (f != e); - } - e = e->next; - } while (e != firstEdge); - } - } - - return shift; -} diff --git a/src/VHACD_Lib/src/vhacdICHull.cpp b/src/VHACD_Lib/src/vhacdICHull.cpp deleted file mode 100644 index 989587c1..00000000 --- a/src/VHACD_Lib/src/vhacdICHull.cpp +++ /dev/null @@ -1,731 +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. - */ -#include "vhacdICHull.h" -#include - -#ifdef _MSC_VER -#pragma warning(disable:4456 4706) -#endif - - -namespace VHACD { -const double ICHull::sc_eps = 1.0e-15; -const int32_t ICHull::sc_dummyIndex = std::numeric_limits::max(); -ICHull::ICHull() -{ - m_isFlat = false; -} -bool ICHull::AddPoints(const Vec3* points, size_t nPoints) -{ - if (!points) { - return false; - } - CircularListElement* vertex = NULL; - for (size_t i = 0; i < nPoints; i++) { - vertex = m_mesh.AddVertex(); - vertex->GetData().m_pos.X() = points[i].X(); - vertex->GetData().m_pos.Y() = points[i].Y(); - vertex->GetData().m_pos.Z() = points[i].Z(); - vertex->GetData().m_name = static_cast(i); - } - return true; -} -bool ICHull::AddPoint(const Vec3& point, int32_t id) -{ - if (AddPoints(&point, 1)) { - m_mesh.m_vertices.GetData().m_name = id; - return true; - } - return false; -} - -ICHullError ICHull::Process() -{ - uint32_t addedPoints = 0; - if (m_mesh.GetNVertices() < 3) { - return ICHullErrorNotEnoughPoints; - } - if (m_mesh.GetNVertices() == 3) { - m_isFlat = true; - CircularListElement* t1 = m_mesh.AddTriangle(); - CircularListElement* t2 = m_mesh.AddTriangle(); - CircularListElement* v0 = m_mesh.m_vertices.GetHead(); - CircularListElement* v1 = v0->GetNext(); - CircularListElement* v2 = v1->GetNext(); - // Compute the normal to the plane - Vec3 p0 = v0->GetData().m_pos; - Vec3 p1 = v1->GetData().m_pos; - Vec3 p2 = v2->GetData().m_pos; - m_normal = (p1 - p0) ^ (p2 - p0); - m_normal.Normalize(); - t1->GetData().m_vertices[0] = v0; - t1->GetData().m_vertices[1] = v1; - t1->GetData().m_vertices[2] = v2; - t2->GetData().m_vertices[0] = v1; - t2->GetData().m_vertices[1] = v2; - t2->GetData().m_vertices[2] = v2; - return ICHullErrorOK; - } - if (m_isFlat) { - m_mesh.m_edges.Clear(); - m_mesh.m_triangles.Clear(); - m_isFlat = false; - } - if (m_mesh.GetNTriangles() == 0) // we have to create the first polyhedron - { - ICHullError res = DoubleTriangle(); - if (res != ICHullErrorOK) { - return res; - } - else { - addedPoints += 3; - } - } - CircularList& vertices = m_mesh.GetVertices(); - // go to the first added and not processed vertex - while (!(vertices.GetHead()->GetPrev()->GetData().m_tag)) { - vertices.Prev(); - } - while (!vertices.GetData().m_tag) // not processed - { - vertices.GetData().m_tag = true; - if (ProcessPoint()) { - addedPoints++; - CleanUp(addedPoints); - vertices.Next(); - if (!GetMesh().CheckConsistancy()) { - size_t nV = m_mesh.GetNVertices(); - CircularList& vertices = m_mesh.GetVertices(); - for (size_t v = 0; v < nV; ++v) { - if (vertices.GetData().m_name == sc_dummyIndex) { - vertices.Delete(); - break; - } - vertices.Next(); - } - return ICHullErrorInconsistent; - } - } - } - if (m_isFlat) { - SArray*> trianglesToDuplicate; - size_t nT = m_mesh.GetNTriangles(); - for (size_t f = 0; f < nT; f++) { - TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData(); - if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex) { - m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead()); - for (int32_t k = 0; k < 3; k++) { - for (int32_t h = 0; h < 2; h++) { - if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead()) { - currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0; - break; - } - } - } - } - else { - trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead()); - } - m_mesh.m_triangles.Next(); - } - size_t nE = m_mesh.GetNEdges(); - for (size_t e = 0; e < nE; e++) { - TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData(); - if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0) { - m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead()); - } - m_mesh.m_edges.Next(); - } - size_t nV = m_mesh.GetNVertices(); - CircularList& vertices = m_mesh.GetVertices(); - for (size_t v = 0; v < nV; ++v) { - if (vertices.GetData().m_name == sc_dummyIndex) { - vertices.Delete(); - } - else { - vertices.GetData().m_tag = false; - vertices.Next(); - } - } - CleanEdges(); - CleanTriangles(); - CircularListElement* newTriangle; - for (size_t t = 0; t < trianglesToDuplicate.Size(); t++) { - newTriangle = m_mesh.AddTriangle(); - newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1]; - newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0]; - newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2]; - } - } - return ICHullErrorOK; -} -ICHullError ICHull::Process(const uint32_t nPointsCH, - const double minVolume) -{ - uint32_t addedPoints = 0; - if (nPointsCH < 3 || m_mesh.GetNVertices() < 3) { - return ICHullErrorNotEnoughPoints; - } - if (m_mesh.GetNVertices() == 3) { - m_isFlat = true; - CircularListElement* t1 = m_mesh.AddTriangle(); - CircularListElement* t2 = m_mesh.AddTriangle(); - CircularListElement* v0 = m_mesh.m_vertices.GetHead(); - CircularListElement* v1 = v0->GetNext(); - CircularListElement* v2 = v1->GetNext(); - // Compute the normal to the plane - Vec3 p0 = v0->GetData().m_pos; - Vec3 p1 = v1->GetData().m_pos; - Vec3 p2 = v2->GetData().m_pos; - m_normal = (p1 - p0) ^ (p2 - p0); - m_normal.Normalize(); - t1->GetData().m_vertices[0] = v0; - t1->GetData().m_vertices[1] = v1; - t1->GetData().m_vertices[2] = v2; - t2->GetData().m_vertices[0] = v1; - t2->GetData().m_vertices[1] = v0; - t2->GetData().m_vertices[2] = v2; - return ICHullErrorOK; - } - - if (m_isFlat) { - m_mesh.m_triangles.Clear(); - m_mesh.m_edges.Clear(); - m_isFlat = false; - } - - if (m_mesh.GetNTriangles() == 0) // we have to create the first polyhedron - { - ICHullError res = DoubleTriangle(); - if (res != ICHullErrorOK) { - return res; - } - else { - addedPoints += 3; - } - } - CircularList& vertices = m_mesh.GetVertices(); - while (!vertices.GetData().m_tag && addedPoints < nPointsCH) // not processed - { - if (!FindMaxVolumePoint((addedPoints > 4) ? minVolume : 0.0)) { - break; - } - vertices.GetData().m_tag = true; - if (ProcessPoint()) { - addedPoints++; - CleanUp(addedPoints); - if (!GetMesh().CheckConsistancy()) { - size_t nV = m_mesh.GetNVertices(); - CircularList& vertices = m_mesh.GetVertices(); - for (size_t v = 0; v < nV; ++v) { - if (vertices.GetData().m_name == sc_dummyIndex) { - vertices.Delete(); - break; - } - vertices.Next(); - } - return ICHullErrorInconsistent; - } - vertices.Next(); - } - } - // delete remaining points - while (!vertices.GetData().m_tag) { - vertices.Delete(); - } - if (m_isFlat) { - SArray*> trianglesToDuplicate; - size_t nT = m_mesh.GetNTriangles(); - for (size_t f = 0; f < nT; f++) { - TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData(); - if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex) { - m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead()); - for (int32_t k = 0; k < 3; k++) { - for (int32_t h = 0; h < 2; h++) { - if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead()) { - currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0; - break; - } - } - } - } - else { - trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead()); - } - m_mesh.m_triangles.Next(); - } - size_t nE = m_mesh.GetNEdges(); - for (size_t e = 0; e < nE; e++) { - TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData(); - if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0) { - m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead()); - } - m_mesh.m_edges.Next(); - } - size_t nV = m_mesh.GetNVertices(); - CircularList& vertices = m_mesh.GetVertices(); - for (size_t v = 0; v < nV; ++v) { - if (vertices.GetData().m_name == sc_dummyIndex) { - vertices.Delete(); - } - else { - vertices.GetData().m_tag = false; - vertices.Next(); - } - } - CleanEdges(); - CleanTriangles(); - CircularListElement* newTriangle; - for (size_t t = 0; t < trianglesToDuplicate.Size(); t++) { - newTriangle = m_mesh.AddTriangle(); - newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1]; - newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0]; - newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2]; - } - } - return ICHullErrorOK; -} -bool ICHull::FindMaxVolumePoint(const double minVolume) -{ - CircularList& vertices = m_mesh.GetVertices(); - CircularListElement* vMaxVolume = 0; - CircularListElement* vHeadPrev = vertices.GetHead()->GetPrev(); - - double maxVolume = minVolume; - double volume = 0.0; - while (!vertices.GetData().m_tag) // not processed - { - if (ComputePointVolume(volume, false)) { - if (maxVolume < volume) { - maxVolume = volume; - vMaxVolume = vertices.GetHead(); - } - vertices.Next(); - } - } - CircularListElement* vHead = vHeadPrev->GetNext(); - vertices.GetHead() = vHead; - if (!vMaxVolume) { - return false; - } - if (vMaxVolume != vHead) { - Vec3 pos = vHead->GetData().m_pos; - int32_t id = vHead->GetData().m_name; - vHead->GetData().m_pos = vMaxVolume->GetData().m_pos; - vHead->GetData().m_name = vMaxVolume->GetData().m_name; - vMaxVolume->GetData().m_pos = pos; - vHead->GetData().m_name = id; - } - return true; -} -ICHullError ICHull::DoubleTriangle() -{ - // find three non colinear points - m_isFlat = false; - CircularList& vertices = m_mesh.GetVertices(); - CircularListElement* v0 = vertices.GetHead(); - while (Colinear(v0->GetData().m_pos, - v0->GetNext()->GetData().m_pos, - v0->GetNext()->GetNext()->GetData().m_pos)) { - if ((v0 = v0->GetNext()) == vertices.GetHead()) { - return ICHullErrorCoplanarPoints; - } - } - CircularListElement* v1 = v0->GetNext(); - CircularListElement* v2 = v1->GetNext(); - // mark points as processed - v0->GetData().m_tag = v1->GetData().m_tag = v2->GetData().m_tag = true; - - // create two triangles - CircularListElement* f0 = MakeFace(v0, v1, v2, 0); - MakeFace(v2, v1, v0, f0); - - // find a fourth non-coplanar point to form tetrahedron - CircularListElement* v3 = v2->GetNext(); - vertices.GetHead() = v3; - - double vol = ComputeVolume4(v0->GetData().m_pos, v1->GetData().m_pos, v2->GetData().m_pos, v3->GetData().m_pos); - while (fabs(vol) < sc_eps && !v3->GetNext()->GetData().m_tag) { - v3 = v3->GetNext(); - vol = ComputeVolume4(v0->GetData().m_pos, v1->GetData().m_pos, v2->GetData().m_pos, v3->GetData().m_pos); - } - if (fabs(vol) < sc_eps) { - // compute the barycenter - Vec3 bary(0.0, 0.0, 0.0); - CircularListElement* vBary = v0; - do { - bary += vBary->GetData().m_pos; - } while ((vBary = vBary->GetNext()) != v0); - bary /= static_cast(vertices.GetSize()); - - // Compute the normal to the plane - Vec3 p0 = v0->GetData().m_pos; - Vec3 p1 = v1->GetData().m_pos; - Vec3 p2 = v2->GetData().m_pos; - m_normal = (p1 - p0) ^ (p2 - p0); - m_normal.Normalize(); - // add dummy vertex placed at (bary + normal) - vertices.GetHead() = v2; - Vec3 newPt = bary + m_normal; - AddPoint(newPt, sc_dummyIndex); - m_isFlat = true; - return ICHullErrorOK; - } - else if (v3 != vertices.GetHead()) { - TMMVertex temp; - temp.m_name = v3->GetData().m_name; - temp.m_pos = v3->GetData().m_pos; - v3->GetData().m_name = vertices.GetHead()->GetData().m_name; - v3->GetData().m_pos = vertices.GetHead()->GetData().m_pos; - vertices.GetHead()->GetData().m_name = temp.m_name; - vertices.GetHead()->GetData().m_pos = temp.m_pos; - } - return ICHullErrorOK; -} -CircularListElement* ICHull::MakeFace(CircularListElement* v0, - CircularListElement* v1, - CircularListElement* v2, - CircularListElement* fold) -{ - CircularListElement* e0; - CircularListElement* e1; - CircularListElement* e2; - int32_t index = 0; - if (!fold) // if first face to be created - { - e0 = m_mesh.AddEdge(); // create the three edges - e1 = m_mesh.AddEdge(); - e2 = m_mesh.AddEdge(); - } - else // otherwise re-use existing edges (in reverse order) - { - e0 = fold->GetData().m_edges[2]; - e1 = fold->GetData().m_edges[1]; - e2 = fold->GetData().m_edges[0]; - index = 1; - } - e0->GetData().m_vertices[0] = v0; - e0->GetData().m_vertices[1] = v1; - e1->GetData().m_vertices[0] = v1; - e1->GetData().m_vertices[1] = v2; - e2->GetData().m_vertices[0] = v2; - e2->GetData().m_vertices[1] = v0; - // create the new face - CircularListElement* f = m_mesh.AddTriangle(); - f->GetData().m_edges[0] = e0; - f->GetData().m_edges[1] = e1; - f->GetData().m_edges[2] = e2; - f->GetData().m_vertices[0] = v0; - f->GetData().m_vertices[1] = v1; - f->GetData().m_vertices[2] = v2; - // link edges to face f - e0->GetData().m_triangles[index] = e1->GetData().m_triangles[index] = e2->GetData().m_triangles[index] = f; - return f; -} -CircularListElement* ICHull::MakeConeFace(CircularListElement* e, CircularListElement* p) -{ - // create two new edges if they don't already exist - CircularListElement* newEdges[2]; - for (int32_t i = 0; i < 2; ++i) { - if (!(newEdges[i] = e->GetData().m_vertices[i]->GetData().m_duplicate)) { // if the edge doesn't exits add it and mark the vertex as duplicated - newEdges[i] = m_mesh.AddEdge(); - newEdges[i]->GetData().m_vertices[0] = e->GetData().m_vertices[i]; - newEdges[i]->GetData().m_vertices[1] = p; - e->GetData().m_vertices[i]->GetData().m_duplicate = newEdges[i]; - } - } - // make the new face - CircularListElement* newFace = m_mesh.AddTriangle(); - newFace->GetData().m_edges[0] = e; - newFace->GetData().m_edges[1] = newEdges[0]; - newFace->GetData().m_edges[2] = newEdges[1]; - MakeCCW(newFace, e, p); - for (int32_t i = 0; i < 2; ++i) { - for (int32_t j = 0; j < 2; ++j) { - if (!newEdges[i]->GetData().m_triangles[j]) { - newEdges[i]->GetData().m_triangles[j] = newFace; - break; - } - } - } - return newFace; -} -bool ICHull::ComputePointVolume(double& totalVolume, bool markVisibleFaces) -{ - // mark visible faces - CircularListElement* fHead = m_mesh.GetTriangles().GetHead(); - CircularListElement* f = fHead; - CircularList& vertices = m_mesh.GetVertices(); - CircularListElement* vertex0 = vertices.GetHead(); - bool visible = false; - Vec3 pos0 = Vec3(vertex0->GetData().m_pos.X(), - vertex0->GetData().m_pos.Y(), - vertex0->GetData().m_pos.Z()); - double vol = 0.0; - totalVolume = 0.0; - Vec3 ver0, ver1, ver2; - do { - ver0.X() = f->GetData().m_vertices[0]->GetData().m_pos.X(); - ver0.Y() = f->GetData().m_vertices[0]->GetData().m_pos.Y(); - ver0.Z() = f->GetData().m_vertices[0]->GetData().m_pos.Z(); - ver1.X() = f->GetData().m_vertices[1]->GetData().m_pos.X(); - ver1.Y() = f->GetData().m_vertices[1]->GetData().m_pos.Y(); - ver1.Z() = f->GetData().m_vertices[1]->GetData().m_pos.Z(); - ver2.X() = f->GetData().m_vertices[2]->GetData().m_pos.X(); - ver2.Y() = f->GetData().m_vertices[2]->GetData().m_pos.Y(); - ver2.Z() = f->GetData().m_vertices[2]->GetData().m_pos.Z(); - vol = ComputeVolume4(ver0, ver1, ver2, pos0); - if (vol < -sc_eps) { - vol = fabs(vol); - totalVolume += vol; - if (markVisibleFaces) { - f->GetData().m_visible = true; - m_trianglesToDelete.PushBack(f); - } - visible = true; - } - f = f->GetNext(); - } while (f != fHead); - - if (m_trianglesToDelete.Size() == m_mesh.m_triangles.GetSize()) { - for (size_t i = 0; i < m_trianglesToDelete.Size(); i++) { - m_trianglesToDelete[i]->GetData().m_visible = false; - } - visible = false; - } - // if no faces visible from p then p is inside the hull - if (!visible && markVisibleFaces) { - vertices.Delete(); - m_trianglesToDelete.Resize(0); - return false; - } - return true; -} -bool ICHull::ProcessPoint() -{ - double totalVolume = 0.0; - if (!ComputePointVolume(totalVolume, true)) { - return false; - } - // Mark edges in interior of visible region for deletion. - // Create a new face based on each border edge - CircularListElement* v0 = m_mesh.GetVertices().GetHead(); - CircularListElement* eHead = m_mesh.GetEdges().GetHead(); - CircularListElement* e = eHead; - CircularListElement* tmp = 0; - int32_t nvisible = 0; - m_edgesToDelete.Resize(0); - m_edgesToUpdate.Resize(0); - do { - tmp = e->GetNext(); - nvisible = 0; - for (int32_t k = 0; k < 2; k++) { - if (e->GetData().m_triangles[k]->GetData().m_visible) { - nvisible++; - } - } - if (nvisible == 2) { - m_edgesToDelete.PushBack(e); - } - else if (nvisible == 1) { - e->GetData().m_newFace = MakeConeFace(e, v0); - m_edgesToUpdate.PushBack(e); - } - e = tmp; - } while (e != eHead); - return true; -} -bool ICHull::MakeCCW(CircularListElement* f, - CircularListElement* e, - CircularListElement* v) -{ - // the visible face adjacent to e - CircularListElement* fv; - if (e->GetData().m_triangles[0]->GetData().m_visible) { - fv = e->GetData().m_triangles[0]; - } - else { - fv = e->GetData().m_triangles[1]; - } - - // set vertex[0] and vertex[1] to have the same orientation as the corresponding vertices of fv. - int32_t i; // index of e->m_vertices[0] in fv - CircularListElement* v0 = e->GetData().m_vertices[0]; - CircularListElement* v1 = e->GetData().m_vertices[1]; - for (i = 0; fv->GetData().m_vertices[i] != v0; i++) - ; - - if (fv->GetData().m_vertices[(i + 1) % 3] != e->GetData().m_vertices[1]) { - f->GetData().m_vertices[0] = v1; - f->GetData().m_vertices[1] = v0; - } - else { - f->GetData().m_vertices[0] = v0; - f->GetData().m_vertices[1] = v1; - // swap edges - CircularListElement* tmp = f->GetData().m_edges[0]; - f->GetData().m_edges[0] = f->GetData().m_edges[1]; - f->GetData().m_edges[1] = tmp; - } - f->GetData().m_vertices[2] = v; - return true; -} -bool ICHull::CleanUp(uint32_t& addedPoints) -{ - bool r0 = CleanEdges(); - bool r1 = CleanTriangles(); - bool r2 = CleanVertices(addedPoints); - return r0 && r1 && r2; -} -bool ICHull::CleanEdges() -{ - // integrate the new faces into the data structure - CircularListElement* e; - const size_t ne_update = m_edgesToUpdate.Size(); - for (size_t i = 0; i < ne_update; ++i) { - e = m_edgesToUpdate[i]; - if (e->GetData().m_newFace) { - if (e->GetData().m_triangles[0]->GetData().m_visible) { - e->GetData().m_triangles[0] = e->GetData().m_newFace; - } - else { - e->GetData().m_triangles[1] = e->GetData().m_newFace; - } - e->GetData().m_newFace = 0; - } - } - // delete edges maked for deletion - CircularList& edges = m_mesh.GetEdges(); - const size_t ne_delete = m_edgesToDelete.Size(); - for (size_t i = 0; i < ne_delete; ++i) { - edges.Delete(m_edgesToDelete[i]); - } - m_edgesToDelete.Resize(0); - m_edgesToUpdate.Resize(0); - return true; -} -bool ICHull::CleanTriangles() -{ - CircularList& triangles = m_mesh.GetTriangles(); - const size_t nt_delete = m_trianglesToDelete.Size(); - for (size_t i = 0; i < nt_delete; ++i) { - triangles.Delete(m_trianglesToDelete[i]); - } - m_trianglesToDelete.Resize(0); - return true; -} -bool ICHull::CleanVertices(uint32_t& addedPoints) -{ - // mark all vertices incident to some undeleted edge as on the hull - CircularList& edges = m_mesh.GetEdges(); - CircularListElement* e = edges.GetHead(); - size_t nE = edges.GetSize(); - for (size_t i = 0; i < nE; i++) { - e->GetData().m_vertices[0]->GetData().m_onHull = true; - e->GetData().m_vertices[1]->GetData().m_onHull = true; - e = e->GetNext(); - } - // delete all the vertices that have been processed but are not on the hull - CircularList& vertices = m_mesh.GetVertices(); - CircularListElement* vHead = vertices.GetHead(); - CircularListElement* v = vHead; - v = v->GetPrev(); - do { - if (v->GetData().m_tag && !v->GetData().m_onHull) { - CircularListElement* tmp = v->GetPrev(); - vertices.Delete(v); - v = tmp; - addedPoints--; - } - else { - v->GetData().m_duplicate = 0; - v->GetData().m_onHull = false; - v = v->GetPrev(); - } - } while (v->GetData().m_tag && v != vHead); - return true; -} -void ICHull::Clear() -{ - m_mesh.Clear(); - m_edgesToDelete.Resize(0); - m_edgesToUpdate.Resize(0); - m_trianglesToDelete.Resize(0); - m_isFlat = false; -} -const ICHull& ICHull::operator=(ICHull& rhs) -{ - if (&rhs != this) { - m_mesh.Copy(rhs.m_mesh); - m_edgesToDelete = rhs.m_edgesToDelete; - m_edgesToUpdate = rhs.m_edgesToUpdate; - m_trianglesToDelete = rhs.m_trianglesToDelete; - m_isFlat = rhs.m_isFlat; - } - return (*this); -} -bool ICHull::IsInside(const Vec3& pt0, const double eps) -{ - const Vec3 pt(pt0.X(), pt0.Y(), pt0.Z()); - if (m_isFlat) { - size_t nT = m_mesh.m_triangles.GetSize(); - Vec3 ver0, ver1, ver2, a, b, c; - double u, v; - for (size_t t = 0; t < nT; t++) { - ver0.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.X(); - ver0.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Y(); - ver0.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Z(); - ver1.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.X(); - ver1.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Y(); - ver1.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Z(); - ver2.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.X(); - ver2.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Y(); - ver2.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Z(); - a = ver1 - ver0; - b = ver2 - ver0; - c = pt - ver0; - u = c * a; - v = c * b; - if (u >= 0.0 && u <= 1.0 && v >= 0.0 && u + v <= 1.0) { - return true; - } - m_mesh.m_triangles.Next(); - } - return false; - } - else { - size_t nT = m_mesh.m_triangles.GetSize(); - Vec3 ver0, ver1, ver2; - double vol; - for (size_t t = 0; t < nT; t++) { - ver0.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.X(); - ver0.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Y(); - ver0.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[0]->GetData().m_pos.Z(); - ver1.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.X(); - ver1.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Y(); - ver1.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[1]->GetData().m_pos.Z(); - ver2.X() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.X(); - ver2.Y() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Y(); - ver2.Z() = m_mesh.m_triangles.GetHead()->GetData().m_vertices[2]->GetData().m_pos.Z(); - vol = ComputeVolume4(ver0, ver1, ver2, pt); - if (vol < eps) { - return false; - } - m_mesh.m_triangles.Next(); - } - return true; - } -} -} diff --git a/src/VHACD_Lib/src/vhacdManifoldMesh.cpp b/src/VHACD_Lib/src/vhacdManifoldMesh.cpp deleted file mode 100644 index 7aac9c0d..00000000 --- a/src/VHACD_Lib/src/vhacdManifoldMesh.cpp +++ /dev/null @@ -1,202 +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. - */ -#include "vhacdManifoldMesh.h" -namespace VHACD { -TMMVertex::TMMVertex(void) -{ - Initialize(); -} -void TMMVertex::Initialize() -{ - m_name = 0; - m_id = 0; - m_duplicate = 0; - m_onHull = false; - m_tag = false; -} - -TMMVertex::~TMMVertex(void) -{ -} -TMMEdge::TMMEdge(void) -{ - Initialize(); -} -void TMMEdge::Initialize() -{ - m_id = 0; - m_triangles[0] = m_triangles[1] = m_newFace = 0; - m_vertices[0] = m_vertices[1] = 0; -} -TMMEdge::~TMMEdge(void) -{ -} -void TMMTriangle::Initialize() -{ - m_id = 0; - for (int32_t i = 0; i < 3; i++) { - m_edges[i] = 0; - m_vertices[0] = 0; - } - m_visible = false; -} -TMMTriangle::TMMTriangle(void) -{ - Initialize(); -} -TMMTriangle::~TMMTriangle(void) -{ -} -TMMesh::TMMesh() -{ -} -TMMesh::~TMMesh(void) -{ -} -void TMMesh::GetIFS(Vec3* const points, Vec3* const triangles) -{ - size_t nV = m_vertices.GetSize(); - size_t nT = m_triangles.GetSize(); - - for (size_t v = 0; v < nV; v++) { - points[v] = m_vertices.GetData().m_pos; - m_vertices.GetData().m_id = v; - m_vertices.Next(); - } - for (size_t f = 0; f < nT; f++) { - TMMTriangle& currentTriangle = m_triangles.GetData(); - triangles[f].X() = static_cast(currentTriangle.m_vertices[0]->GetData().m_id); - triangles[f].Y() = static_cast(currentTriangle.m_vertices[1]->GetData().m_id); - triangles[f].Z() = static_cast(currentTriangle.m_vertices[2]->GetData().m_id); - m_triangles.Next(); - } -} -void TMMesh::Clear() -{ - m_vertices.Clear(); - m_edges.Clear(); - m_triangles.Clear(); -} -void TMMesh::Copy(TMMesh& mesh) -{ - Clear(); - // updating the id's - size_t nV = mesh.m_vertices.GetSize(); - size_t nE = mesh.m_edges.GetSize(); - size_t nT = mesh.m_triangles.GetSize(); - for (size_t v = 0; v < nV; v++) { - mesh.m_vertices.GetData().m_id = v; - mesh.m_vertices.Next(); - } - for (size_t e = 0; e < nE; e++) { - mesh.m_edges.GetData().m_id = e; - mesh.m_edges.Next(); - } - for (size_t f = 0; f < nT; f++) { - mesh.m_triangles.GetData().m_id = f; - mesh.m_triangles.Next(); - } - // copying data - m_vertices = mesh.m_vertices; - m_edges = mesh.m_edges; - m_triangles = mesh.m_triangles; - - // generate mapping - CircularListElement** vertexMap = new CircularListElement*[nV]; - CircularListElement** edgeMap = new CircularListElement*[nE]; - CircularListElement** triangleMap = new CircularListElement*[nT]; - for (size_t v = 0; v < nV; v++) { - vertexMap[v] = m_vertices.GetHead(); - m_vertices.Next(); - } - for (size_t e = 0; e < nE; e++) { - edgeMap[e] = m_edges.GetHead(); - m_edges.Next(); - } - for (size_t f = 0; f < nT; f++) { - triangleMap[f] = m_triangles.GetHead(); - m_triangles.Next(); - } - - // updating pointers - for (size_t v = 0; v < nV; v++) { - if (vertexMap[v]->GetData().m_duplicate) { - vertexMap[v]->GetData().m_duplicate = edgeMap[vertexMap[v]->GetData().m_duplicate->GetData().m_id]; - } - } - for (size_t e = 0; e < nE; e++) { - if (edgeMap[e]->GetData().m_newFace) { - edgeMap[e]->GetData().m_newFace = triangleMap[edgeMap[e]->GetData().m_newFace->GetData().m_id]; - } - if (nT > 0) { - for (int32_t f = 0; f < 2; f++) { - if (edgeMap[e]->GetData().m_triangles[f]) { - edgeMap[e]->GetData().m_triangles[f] = triangleMap[edgeMap[e]->GetData().m_triangles[f]->GetData().m_id]; - } - } - } - for (int32_t v = 0; v < 2; v++) { - if (edgeMap[e]->GetData().m_vertices[v]) { - edgeMap[e]->GetData().m_vertices[v] = vertexMap[edgeMap[e]->GetData().m_vertices[v]->GetData().m_id]; - } - } - } - for (size_t f = 0; f < nT; f++) { - if (nE > 0) { - for (int32_t e = 0; e < 3; e++) { - if (triangleMap[f]->GetData().m_edges[e]) { - triangleMap[f]->GetData().m_edges[e] = edgeMap[triangleMap[f]->GetData().m_edges[e]->GetData().m_id]; - } - } - } - for (int32_t v = 0; v < 3; v++) { - if (triangleMap[f]->GetData().m_vertices[v]) { - triangleMap[f]->GetData().m_vertices[v] = vertexMap[triangleMap[f]->GetData().m_vertices[v]->GetData().m_id]; - } - } - } - delete[] vertexMap; - delete[] edgeMap; - delete[] triangleMap; -} -bool TMMesh::CheckConsistancy() -{ - size_t nE = m_edges.GetSize(); - size_t nT = m_triangles.GetSize(); - for (size_t e = 0; e < nE; e++) { - for (int32_t f = 0; f < 2; f++) { - if (!m_edges.GetHead()->GetData().m_triangles[f]) { - return false; - } - } - m_edges.Next(); - } - for (size_t f = 0; f < nT; f++) { - for (int32_t e = 0; e < 3; e++) { - int32_t found = 0; - for (int32_t k = 0; k < 2; k++) { - if (m_triangles.GetHead()->GetData().m_edges[e]->GetData().m_triangles[k] == m_triangles.GetHead()) { - found++; - } - } - if (found != 1) { - return false; - } - } - m_triangles.Next(); - } - return true; -} -} \ No newline at end of file diff --git a/src/VHACD_Lib/src/vhacdMesh.cpp b/src/VHACD_Lib/src/vhacdMesh.cpp deleted file mode 100644 index 5d03989f..00000000 --- a/src/VHACD_Lib/src/vhacdMesh.cpp +++ /dev/null @@ -1,376 +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. - */ - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "btConvexHullComputer.h" -#include "vhacdMesh.h" -#include "FloatMath.h" -#include -#include -#include -#include -#include -#include - -namespace VHACD { -Mesh::Mesh() -{ - m_diag = 1.0; -} -Mesh::~Mesh() -{ -} - -Vec3& Mesh::ComputeCenter(void) -{ - const size_t nV = GetNPoints(); - if (nV) - { - double center[3]; - uint32_t pcount = uint32_t(GetNPoints()); - const double *points = GetPoints(); - uint32_t tcount = uint32_t(GetNTriangles()); - const uint32_t *indices = (const uint32_t *)GetTriangles(); - FLOAT_MATH::fm_computeCentroid(pcount, points, tcount, indices, center); - m_center.X() = center[0]; - m_center.Y() = center[1]; - m_center.Z() = center[2]; - m_minBB = GetPoint(0); - m_maxBB = GetPoint(0); - for (size_t v = 1; v < nV; v++) - { - Vec3 p = GetPoint(v); - if (p.X() < m_minBB.X()) - { - m_minBB.X() = p.X(); - } - if (p.Y() < m_minBB.Y()) - { - m_minBB.Y() = p.Y(); - } - if (p.Z() < m_minBB.Z()) - { - m_minBB.Z() = p.Z(); - } - if (p.X() > m_maxBB.X()) - { - m_maxBB.X() = p.X(); - } - if (p.Y() > m_maxBB.Y()) - { - m_maxBB.Y() = p.Y(); - } - if (p.Z() > m_maxBB.Z()) - { - m_maxBB.Z() = p.Z(); - } - } - } - return m_center; -} - -double Mesh::ComputeVolume() const -{ - const size_t nV = GetNPoints(); - const size_t nT = GetNTriangles(); - if (nV == 0 || nT == 0) { - return 0.0; - } - - Vec3 bary(0.0, 0.0, 0.0); - for (size_t v = 0; v < nV; v++) { - bary += GetPoint(v); - } - bary /= static_cast(nV); - - Vec3 ver0, ver1, ver2; - double totalVolume = 0.0; - for (int32_t t = 0; t < int32_t(nT); t++) { - const Vec3& tri = GetTriangle(t); - ver0 = GetPoint(tri[0]); - ver1 = GetPoint(tri[1]); - ver2 = GetPoint(tri[2]); - totalVolume += ComputeVolume4(ver0, ver1, ver2, bary); - } - return totalVolume / 6.0; -} - -void Mesh::ComputeConvexHull(const double* const pts, - const size_t nPts) -{ - ResizePoints(0); - ResizeTriangles(0); - btConvexHullComputer ch; - ch.compute(pts, 3 * sizeof(double), (int32_t)nPts, -1.0, -1.0); - for (int32_t v = 0; v < ch.vertices.size(); v++) { - 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) { - AddTriangle(Vec3(a, b, c)); - edge = edge->getNextEdgeOfFace(); - b = c; - c = edge->getTargetVertex(); - } - } -} -void Mesh::Clip(const Plane& plane, - SArray >& positivePart, - SArray >& negativePart) const -{ - const size_t nV = GetNPoints(); - if (nV == 0) { - return; - } - double d; - for (size_t v = 0; v < nV; v++) { - const Vec3& pt = GetPoint(v); - d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; - if (d > 0.0) { - positivePart.PushBack(pt); - } - else if (d < 0.0) { - negativePart.PushBack(pt); - } - else { - positivePart.PushBack(pt); - negativePart.PushBack(pt); - } - } -} -bool Mesh::IsInside(const Vec3& pt) const -{ - const size_t nV = GetNPoints(); - const size_t nT = GetNTriangles(); - if (nV == 0 || nT == 0) { - return false; - } - Vec3 ver0, ver1, ver2; - double volume; - for (int32_t t = 0; t < int32_t(nT); t++) { - const Vec3& tri = GetTriangle(t); - ver0 = GetPoint(tri[0]); - ver1 = GetPoint(tri[1]); - ver2 = GetPoint(tri[2]); - volume = ComputeVolume4(ver0, ver1, ver2, pt); - if (volume < 0.0) { - return false; - } - } - return true; -} -double Mesh::ComputeDiagBB() -{ - const size_t nPoints = GetNPoints(); - if (nPoints == 0) - return 0.0; - Vec3 minBB = m_points[0]; - Vec3 maxBB = m_points[0]; - double x, y, z; - for (size_t v = 1; v < nPoints; v++) { - x = m_points[v][0]; - y = m_points[v][1]; - z = m_points[v][2]; - if (x < minBB[0]) - minBB[0] = x; - else if (x > maxBB[0]) - maxBB[0] = x; - if (y < minBB[1]) - minBB[1] = y; - else if (y > maxBB[1]) - maxBB[1] = y; - if (z < minBB[2]) - minBB[2] = z; - else if (z > maxBB[2]) - maxBB[2] = z; - } - return (m_diag = (maxBB - minBB).GetNorm()); -} - -#ifdef VHACD_DEBUG_MESH -bool Mesh::SaveVRML2(const std::string& fileName) const -{ - std::ofstream fout(fileName.c_str()); - if (fout.is_open()) { - const Material material; - - if (SaveVRML2(fout, material)) { - fout.close(); - return true; - } - return false; - } - return false; -} -bool Mesh::SaveVRML2(std::ofstream& fout, const Material& material) const -{ - if (fout.is_open()) { - fout.setf(std::ios::fixed, std::ios::floatfield); - fout.setf(std::ios::showpoint); - fout.precision(6); - size_t nV = m_points.Size(); - size_t nT = m_triangles.Size(); - fout << "#VRML V2.0 utf8" << std::endl; - fout << "" << std::endl; - fout << "# Vertices: " << nV << std::endl; - fout << "# Triangles: " << nT << std::endl; - fout << "" << std::endl; - fout << "Group {" << std::endl; - fout << " children [" << std::endl; - fout << " Shape {" << std::endl; - fout << " appearance Appearance {" << std::endl; - fout << " material Material {" << std::endl; - fout << " diffuseColor " << material.m_diffuseColor[0] << " " - << material.m_diffuseColor[1] << " " - << material.m_diffuseColor[2] << std::endl; - fout << " ambientIntensity " << material.m_ambientIntensity << std::endl; - fout << " specularColor " << material.m_specularColor[0] << " " - << material.m_specularColor[1] << " " - << material.m_specularColor[2] << std::endl; - fout << " emissiveColor " << material.m_emissiveColor[0] << " " - << material.m_emissiveColor[1] << " " - << material.m_emissiveColor[2] << std::endl; - fout << " shininess " << material.m_shininess << std::endl; - fout << " transparency " << material.m_transparency << std::endl; - fout << " }" << std::endl; - fout << " }" << std::endl; - fout << " geometry IndexedFaceSet {" << std::endl; - fout << " ccw TRUE" << std::endl; - fout << " solid TRUE" << std::endl; - fout << " convex TRUE" << std::endl; - if (nV > 0) { - fout << " coord DEF co Coordinate {" << std::endl; - fout << " point [" << std::endl; - for (size_t v = 0; v < nV; v++) { - fout << " " << m_points[v][0] << " " - << m_points[v][1] << " " - << m_points[v][2] << "," << std::endl; - } - fout << " ]" << std::endl; - fout << " }" << std::endl; - } - if (nT > 0) { - fout << " coordIndex [ " << std::endl; - for (size_t f = 0; f < nT; f++) { - fout << " " << m_triangles[f][0] << ", " - << m_triangles[f][1] << ", " - << m_triangles[f][2] << ", -1," << std::endl; - } - fout << " ]" << std::endl; - } - fout << " }" << std::endl; - fout << " }" << std::endl; - fout << " ]" << std::endl; - fout << "}" << std::endl; - return true; - } - return false; -} -bool Mesh::SaveOFF(const std::string& fileName) const -{ - std::ofstream fout(fileName.c_str()); - if (fout.is_open()) { - size_t nV = m_points.Size(); - size_t nT = m_triangles.Size(); - fout << "OFF" << std::endl; - fout << nV << " " << nT << " " << 0 << std::endl; - for (size_t v = 0; v < nV; v++) { - fout << m_points[v][0] << " " - << m_points[v][1] << " " - << m_points[v][2] << std::endl; - } - for (size_t f = 0; f < nT; f++) { - fout << "3 " << m_triangles[f][0] << " " - << m_triangles[f][1] << " " - << m_triangles[f][2] << std::endl; - } - fout.close(); - return true; - } - return false; -} - -bool Mesh::LoadOFF(const std::string& fileName, bool invert) -{ - FILE* fid = fopen(fileName.c_str(), "r"); - if (fid) { - const std::string strOFF("OFF"); - char temp[1024]; - fscanf(fid, "%s", temp); - if (std::string(temp) != strOFF) { - fclose(fid); - return false; - } - else { - int32_t nv = 0; - int32_t nf = 0; - int32_t ne = 0; - fscanf(fid, "%i", &nv); - fscanf(fid, "%i", &nf); - fscanf(fid, "%i", &ne); - m_points.Resize(nv); - m_triangles.Resize(nf); - Vec3 coord; - float x, y, z; - for (int32_t p = 0; p < nv; p++) { - fscanf(fid, "%f", &x); - fscanf(fid, "%f", &y); - fscanf(fid, "%f", &z); - m_points[p][0] = x; - m_points[p][1] = y; - m_points[p][2] = z; - } - int32_t i, j, k, s; - for (int32_t t = 0; t < nf; ++t) { - fscanf(fid, "%i", &s); - if (s == 3) { - fscanf(fid, "%i", &i); - fscanf(fid, "%i", &j); - fscanf(fid, "%i", &k); - m_triangles[t][0] = i; - if (invert) { - m_triangles[t][1] = k; - m_triangles[t][2] = j; - } - else { - m_triangles[t][1] = j; - m_triangles[t][2] = k; - } - } - else // Fix me: support only triangular meshes - { - for (int32_t h = 0; h < s; ++h) - fscanf(fid, "%i", &s); - } - } - fclose(fid); - } - } - else { - return false; - } - return true; -} -#endif // VHACD_DEBUG_MESH -} diff --git a/src/VHACD_Lib/src/vhacdRaycastMesh.cpp b/src/VHACD_Lib/src/vhacdRaycastMesh.cpp deleted file mode 100644 index e8b9435d..00000000 --- a/src/VHACD_Lib/src/vhacdRaycastMesh.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "vhacdRaycastMesh.h" -#include -#include - -namespace RAYCAST_MESH -{ - -/* a = b - c */ -#define vector(a,b,c) \ - (a)[0] = (b)[0] - (c)[0]; \ - (a)[1] = (b)[1] - (c)[1]; \ - (a)[2] = (b)[2] - (c)[2]; - -#define innerProduct(v,q) \ - ((v)[0] * (q)[0] + \ - (v)[1] * (q)[1] + \ - (v)[2] * (q)[2]) - -#define crossProduct(a,b,c) \ - (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \ - (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \ - (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1]; - - -static inline bool rayIntersectsTriangle(const double *p,const double *d,const double *v0,const double *v1,const double *v2,double &t) -{ - double e1[3],e2[3],h[3],s[3],q[3]; - double a,f,u,v; - - vector(e1,v1,v0); - vector(e2,v2,v0); - crossProduct(h,d,e2); - a = innerProduct(e1,h); - - if (a > -0.00001 && a < 0.00001) - return(false); - - f = 1/a; - vector(s,p,v0); - u = f * (innerProduct(s,h)); - - if (u < 0.0 || u > 1.0) - return(false); - - crossProduct(q,s,e1); - v = f * innerProduct(d,q); - if (v < 0.0 || u + v > 1.0) - return(false); - // at this stage we can compute t to find out where - // the intersection point is on the line - t = f * innerProduct(e2,q); - if (t > 0) // ray intersection - return(true); - else // this means that there is a line intersection - // but not a ray intersection - return (false); -} - -static double getPointDistance(const double *p1, const double *p2) -{ - double dx = p1[0] - p2[0]; - double dy = p1[1] - p2[1]; - double dz = p1[2] - p2[2]; - return sqrt(dx*dx + dy*dy + dz*dz); -} - -class MyRaycastMesh : public VHACD::RaycastMesh -{ -public: - - template - MyRaycastMesh(uint32_t vcount, - const T *vertices, - uint32_t tcount, - const uint32_t *indices) - { - mVcount = vcount; - mVertices = new double[mVcount * 3]; - for (uint32_t i = 0; i < mVcount; i++) - { - mVertices[i * 3 + 0] = vertices[0]; - mVertices[i * 3 + 1] = vertices[1]; - mVertices[i * 3 + 2] = vertices[2]; - vertices += 3; - } - mTcount = tcount; - mIndices = new uint32_t[mTcount * 3]; - for (uint32_t i = 0; i < mTcount; i++) - { - mIndices[i * 3 + 0] = indices[0]; - mIndices[i * 3 + 1] = indices[1]; - mIndices[i * 3 + 2] = indices[2]; - indices += 3; - } - } - - - ~MyRaycastMesh(void) - { - delete[]mVertices; - delete[]mIndices; - } - - virtual void release(void) - { - delete this; - } - - virtual bool raycast(const double *from, // The starting point of the raycast - const double *to, // The ending point of the raycast - const double *closestToPoint, // The point to match the nearest hit location (can just be the 'from' location of no specific point) - double *hitLocation, // The point where the ray hit nearest to the 'closestToPoint' location - double *hitDistance) final // The distance the ray traveled to the hit location - { - bool ret = false; - - double dir[3]; - - dir[0] = to[0] - from[0]; - dir[1] = to[1] - from[1]; - dir[2] = to[2] - from[2]; - - double distance = sqrt( dir[0]*dir[0] + dir[1]*dir[1]+dir[2]*dir[2] ); - if ( distance < 0.0000000001f ) return false; - double recipDistance = 1.0f / distance; - dir[0]*=recipDistance; - dir[1]*=recipDistance; - dir[2]*=recipDistance; - const uint32_t *indices = mIndices; - const double *vertices = mVertices; - double nearestDistance = distance; - - for (uint32_t tri=0; tri(m); - } - - RaycastMesh * RaycastMesh::createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh - const float *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc. - uint32_t tcount, // The number of triangles in the source triangle mesh - const uint32_t *indices) // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ... - { - MyRaycastMesh *m = new MyRaycastMesh(vcount, vertices, tcount, indices); - return static_cast(m); - } - - -} // end of VHACD namespace \ No newline at end of file diff --git a/src/VHACD_Lib/src/vhacdVolume.cpp b/src/VHACD_Lib/src/vhacdVolume.cpp deleted file mode 100644 index a250e5ac..00000000 --- a/src/VHACD_Lib/src/vhacdVolume.cpp +++ /dev/null @@ -1,1626 +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. - */ - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "btConvexHullComputer.h" -#include "vhacdVolume.h" -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(disable:4458 4100) -#endif - - -namespace VHACD { -/********************************************************/ -/* AABB-triangle overlap test code */ -/* by Tomas Akenine-M�ller */ -/* Function: int32_t triBoxOverlap(float boxcenter[3], */ -/* float boxhalfsize[3],float triverts[3][3]); */ -/* History: */ -/* 2001-03-05: released the code in its first version */ -/* 2001-06-18: changed the order of the tests, faster */ -/* */ -/* Acknowledgement: Many thanks to Pierre Terdiman for */ -/* suggestions and discussions on how to optimize code. */ -/* Thanks to David Hunt for finding a ">="-bug! */ -/********************************************************/ - -#define X 0 -#define Y 1 -#define Z 2 -#define FINDMINMAX(x0, x1, x2, min, max) \ - min = max = x0; \ - if (x1 < min) \ - min = x1; \ - if (x1 > max) \ - max = x1; \ - if (x2 < min) \ - min = x2; \ - if (x2 > max) \ - max = x2; - -#define AXISTEST_X01(a, b, fa, fb) \ - p0 = a * v0[Y] - b * v0[Z]; \ - p2 = a * v2[Y] - b * v2[Z]; \ - if (p0 < p2) { \ - min = p0; \ - max = p2; \ - } \ - else { \ - min = p2; \ - max = p0; \ - } \ - rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ - if (min > rad || max < -rad) \ - return 0; - -#define AXISTEST_X2(a, b, fa, fb) \ - p0 = a * v0[Y] - b * v0[Z]; \ - p1 = a * v1[Y] - b * v1[Z]; \ - if (p0 < p1) { \ - min = p0; \ - max = p1; \ - } \ - else { \ - min = p1; \ - max = p0; \ - } \ - rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ - if (min > rad || max < -rad) \ - return 0; - -#define AXISTEST_Y02(a, b, fa, fb) \ - p0 = -a * v0[X] + b * v0[Z]; \ - p2 = -a * v2[X] + b * v2[Z]; \ - if (p0 < p2) { \ - min = p0; \ - max = p2; \ - } \ - else { \ - min = p2; \ - max = p0; \ - } \ - rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ - if (min > rad || max < -rad) \ - return 0; - -#define AXISTEST_Y1(a, b, fa, fb) \ - p0 = -a * v0[X] + b * v0[Z]; \ - p1 = -a * v1[X] + b * v1[Z]; \ - if (p0 < p1) { \ - min = p0; \ - max = p1; \ - } \ - else { \ - min = p1; \ - max = p0; \ - } \ - rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ - if (min > rad || max < -rad) \ - return 0; - -#define AXISTEST_Z12(a, b, fa, fb) \ - p1 = a * v1[X] - b * v1[Y]; \ - p2 = a * v2[X] - b * v2[Y]; \ - if (p2 < p1) { \ - min = p2; \ - max = p1; \ - } \ - else { \ - min = p1; \ - max = p2; \ - } \ - rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ - if (min > rad || max < -rad) \ - return 0; - -#define AXISTEST_Z0(a, b, fa, fb) \ - p0 = a * v0[X] - b * v0[Y]; \ - p1 = a * v1[X] - b * v1[Y]; \ - if (p0 < p1) { \ - min = p0; \ - max = p1; \ - } \ - else { \ - min = p1; \ - max = p0; \ - } \ - rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ - if (min > rad || max < -rad) \ - return 0; - -int32_t PlaneBoxOverlap(const Vec3& normal, - const Vec3& vert, - const Vec3& maxbox) -{ - int32_t q; - Vec3 vmin, vmax; - double v; - for (q = X; q <= Z; q++) { - v = vert[q]; - if (normal[q] > 0.0) { - vmin[q] = -maxbox[q] - v; - vmax[q] = maxbox[q] - v; - } - else { - vmin[q] = maxbox[q] - v; - vmax[q] = -maxbox[q] - v; - } - } - if (normal * vmin > 0.0) - return 0; - if (normal * vmax >= 0.0) - return 1; - return 0; -} - -int32_t TriBoxOverlap(const Vec3& boxcenter, - const Vec3& boxhalfsize, - const Vec3& triver0, - const Vec3& triver1, - const Vec3& triver2) -{ - /* use separating axis theorem to test overlap between triangle and box */ - /* need to test for overlap in these directions: */ - /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ - /* we do not even need to test these) */ - /* 2) normal of the triangle */ - /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ - /* this gives 3x3=9 more tests */ - - Vec3 v0, v1, v2; - double min, max, p0, p1, p2, rad, fex, fey, fez; // -NJMP- "d" local variable removed - Vec3 normal, e0, e1, e2; - - /* This is the fastest branch on Sun */ - /* move everything so that the boxcenter is in (0,0,0) */ - - v0 = triver0 - boxcenter; - v1 = triver1 - boxcenter; - v2 = triver2 - boxcenter; - - /* compute triangle edges */ - e0 = v1 - v0; /* tri edge 0 */ - e1 = v2 - v1; /* tri edge 1 */ - e2 = v0 - v2; /* tri edge 2 */ - - /* Bullet 3: */ - /* test the 9 tests first (this was faster) */ - fex = fabs(e0[X]); - fey = fabs(e0[Y]); - fez = fabs(e0[Z]); - - AXISTEST_X01(e0[Z], e0[Y], fez, fey); - AXISTEST_Y02(e0[Z], e0[X], fez, fex); - AXISTEST_Z12(e0[Y], e0[X], fey, fex); - - fex = fabs(e1[X]); - fey = fabs(e1[Y]); - fez = fabs(e1[Z]); - - AXISTEST_X01(e1[Z], e1[Y], fez, fey); - AXISTEST_Y02(e1[Z], e1[X], fez, fex); - AXISTEST_Z0(e1[Y], e1[X], fey, fex); - - fex = fabs(e2[X]); - fey = fabs(e2[Y]); - fez = fabs(e2[Z]); - - AXISTEST_X2(e2[Z], e2[Y], fez, fey); - AXISTEST_Y1(e2[Z], e2[X], fez, fex); - AXISTEST_Z12(e2[Y], e2[X], fey, fex); - - /* Bullet 1: */ - /* first test overlap in the {x,y,z}-directions */ - /* find min, max of the triangle each direction, and test for overlap in */ - /* that direction -- this is equivalent to testing a minimal AABB around */ - /* the triangle against the AABB */ - - /* test in X-direction */ - FINDMINMAX(v0[X], v1[X], v2[X], min, max); - if (min > boxhalfsize[X] || max < -boxhalfsize[X]) - return 0; - - /* test in Y-direction */ - FINDMINMAX(v0[Y], v1[Y], v2[Y], min, max); - if (min > boxhalfsize[Y] || max < -boxhalfsize[Y]) - return 0; - - /* test in Z-direction */ - FINDMINMAX(v0[Z], v1[Z], v2[Z], min, max); - if (min > boxhalfsize[Z] || max < -boxhalfsize[Z]) - return 0; - - /* Bullet 2: */ - /* test if the box intersects the plane of the triangle */ - /* compute plane equation of triangle: normal*x+d=0 */ - normal = e0 ^ e1; - - if (!PlaneBoxOverlap(normal, v0, boxhalfsize)) - return 0; - return 1; /* box and triangle overlaps */ -} - -// Slightly modified version of Stan Melax's code for 3x3 matrix diagonalization (Thanks Stan!) -// source: http://www.melax.com/diag.html?attredirects=0 -void Diagonalize(const double (&A)[3][3], double (&Q)[3][3], double (&D)[3][3]) -{ - // A must be a symmetric matrix. - // returns Q and D such that - // Diagonal matrix D = QT * A * Q; and A = Q*D*QT - const int32_t maxsteps = 24; // certainly wont need that many. - int32_t k0, k1, k2; - double o[3], m[3]; - double q[4] = { 0.0, 0.0, 0.0, 1.0 }; - double jr[4]; - double sqw, sqx, sqy, sqz; - double tmp1, tmp2, mq; - double AQ[3][3]; - double thet, sgn, t, c; - for (int32_t i = 0; i < maxsteps; ++i) { - // quat to matrix - sqx = q[0] * q[0]; - sqy = q[1] * q[1]; - sqz = q[2] * q[2]; - sqw = q[3] * q[3]; - Q[0][0] = (sqx - sqy - sqz + sqw); - Q[1][1] = (-sqx + sqy - sqz + sqw); - Q[2][2] = (-sqx - sqy + sqz + sqw); - tmp1 = q[0] * q[1]; - tmp2 = q[2] * q[3]; - Q[1][0] = 2.0 * (tmp1 + tmp2); - Q[0][1] = 2.0 * (tmp1 - tmp2); - tmp1 = q[0] * q[2]; - tmp2 = q[1] * q[3]; - Q[2][0] = 2.0 * (tmp1 - tmp2); - Q[0][2] = 2.0 * (tmp1 + tmp2); - tmp1 = q[1] * q[2]; - tmp2 = q[0] * q[3]; - Q[2][1] = 2.0 * (tmp1 + tmp2); - Q[1][2] = 2.0 * (tmp1 - tmp2); - - // AQ = A * Q - AQ[0][0] = Q[0][0] * A[0][0] + Q[1][0] * A[0][1] + Q[2][0] * A[0][2]; - AQ[0][1] = Q[0][1] * A[0][0] + Q[1][1] * A[0][1] + Q[2][1] * A[0][2]; - AQ[0][2] = Q[0][2] * A[0][0] + Q[1][2] * A[0][1] + Q[2][2] * A[0][2]; - AQ[1][0] = Q[0][0] * A[0][1] + Q[1][0] * A[1][1] + Q[2][0] * A[1][2]; - AQ[1][1] = Q[0][1] * A[0][1] + Q[1][1] * A[1][1] + Q[2][1] * A[1][2]; - AQ[1][2] = Q[0][2] * A[0][1] + Q[1][2] * A[1][1] + Q[2][2] * A[1][2]; - AQ[2][0] = Q[0][0] * A[0][2] + Q[1][0] * A[1][2] + Q[2][0] * A[2][2]; - AQ[2][1] = Q[0][1] * A[0][2] + Q[1][1] * A[1][2] + Q[2][1] * A[2][2]; - AQ[2][2] = Q[0][2] * A[0][2] + Q[1][2] * A[1][2] + Q[2][2] * A[2][2]; - // D = Qt * AQ - D[0][0] = AQ[0][0] * Q[0][0] + AQ[1][0] * Q[1][0] + AQ[2][0] * Q[2][0]; - D[0][1] = AQ[0][0] * Q[0][1] + AQ[1][0] * Q[1][1] + AQ[2][0] * Q[2][1]; - D[0][2] = AQ[0][0] * Q[0][2] + AQ[1][0] * Q[1][2] + AQ[2][0] * Q[2][2]; - D[1][0] = AQ[0][1] * Q[0][0] + AQ[1][1] * Q[1][0] + AQ[2][1] * Q[2][0]; - D[1][1] = AQ[0][1] * Q[0][1] + AQ[1][1] * Q[1][1] + AQ[2][1] * Q[2][1]; - D[1][2] = AQ[0][1] * Q[0][2] + AQ[1][1] * Q[1][2] + AQ[2][1] * Q[2][2]; - D[2][0] = AQ[0][2] * Q[0][0] + AQ[1][2] * Q[1][0] + AQ[2][2] * Q[2][0]; - D[2][1] = AQ[0][2] * Q[0][1] + AQ[1][2] * Q[1][1] + AQ[2][2] * Q[2][1]; - D[2][2] = AQ[0][2] * Q[0][2] + AQ[1][2] * Q[1][2] + AQ[2][2] * Q[2][2]; - o[0] = D[1][2]; - o[1] = D[0][2]; - o[2] = D[0][1]; - m[0] = fabs(o[0]); - m[1] = fabs(o[1]); - m[2] = fabs(o[2]); - - k0 = (m[0] > m[1] && m[0] > m[2]) ? 0 : (m[1] > m[2]) ? 1 : 2; // index of largest element of offdiag - k1 = (k0 + 1) % 3; - k2 = (k0 + 2) % 3; - if (o[k0] == 0.0) { - break; // diagonal already - } - thet = (D[k2][k2] - D[k1][k1]) / (2.0 * o[k0]); - sgn = (thet > 0.0) ? 1.0 : -1.0; - thet *= sgn; // make it positive - t = sgn / (thet + ((thet < 1.E6) ? sqrt(thet * thet + 1.0) : thet)); // sign(T)/(|T|+sqrt(T^2+1)) - c = 1.0 / sqrt(t * t + 1.0); // c= 1/(t^2+1) , t=s/c - if (c == 1.0) { - break; // no room for improvement - reached machine precision. - } - jr[0] = jr[1] = jr[2] = jr[3] = 0.0; - jr[k0] = sgn * sqrt((1.0 - c) / 2.0); // using 1/2 angle identity sin(a/2) = sqrt((1-cos(a))/2) - jr[k0] *= -1.0; // since our quat-to-matrix convention was for v*M instead of M*v - jr[3] = sqrt(1.0 - jr[k0] * jr[k0]); - if (jr[3] == 1.0) { - break; // reached limits of floating point precision - } - q[0] = (q[3] * jr[0] + q[0] * jr[3] + q[1] * jr[2] - q[2] * jr[1]); - q[1] = (q[3] * jr[1] - q[0] * jr[2] + q[1] * jr[3] + q[2] * jr[0]); - q[2] = (q[3] * jr[2] + q[0] * jr[1] - q[1] * jr[0] + q[2] * jr[3]); - q[3] = (q[3] * jr[3] - q[0] * jr[0] - q[1] * jr[1] - q[2] * jr[2]); - mq = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); - q[0] /= mq; - q[1] /= mq; - q[2] /= mq; - q[3] /= mq; - } -} -const double TetrahedronSet::EPS = 0.0000000000001; -VoxelSet::VoxelSet() -{ - m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; - m_minBBVoxels[0] = m_minBBVoxels[1] = m_minBBVoxels[2] = 0; - m_maxBBVoxels[0] = m_maxBBVoxels[1] = m_maxBBVoxels[2] = 1; - m_minBBPts[0] = m_minBBPts[1] = m_minBBPts[2] = 0; - m_maxBBPts[0] = m_maxBBPts[1] = m_maxBBPts[2] = 1; - m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0; - m_barycenterPCA[0] = m_barycenterPCA[1] = m_barycenterPCA[2] = 0.0; - m_scale = 1.0; - m_unitVolume = 1.0; - m_numVoxelsOnSurface = 0; - m_numVoxelsInsideSurface = 0; - memset(m_Q, 0, sizeof(double) * 9); - memset(m_D, 0, sizeof(double) * 9); -} -VoxelSet::~VoxelSet(void) -{ -} -void VoxelSet::ComputeBB() -{ - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - for (int32_t h = 0; h < 3; ++h) { - m_minBBVoxels[h] = m_voxels[0].m_coord[h]; - m_maxBBVoxels[h] = m_voxels[0].m_coord[h]; - } - Vec3 bary(0.0); - for (size_t p = 0; p < nVoxels; ++p) { - for (int32_t h = 0; h < 3; ++h) { - bary[h] += m_voxels[p].m_coord[h]; - if (m_minBBVoxels[h] > m_voxels[p].m_coord[h]) - m_minBBVoxels[h] = m_voxels[p].m_coord[h]; - if (m_maxBBVoxels[h] < m_voxels[p].m_coord[h]) - m_maxBBVoxels[h] = m_voxels[p].m_coord[h]; - } - } - bary /= (double)nVoxels; - for (int32_t h = 0; h < 3; ++h) { - m_minBBPts[h] = m_minBBVoxels[h] * m_scale + m_minBB[h]; - m_maxBBPts[h] = m_maxBBVoxels[h] * m_scale + m_minBB[h]; - m_barycenter[h] = (short)(bary[h] + 0.5); - } -} -void VoxelSet::ComputeConvexHull(Mesh& meshCH, const size_t sampling) const -{ - const size_t CLUSTER_SIZE = 65536; - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - - SArray > cpoints; - - Vec3* points = new Vec3[CLUSTER_SIZE]; - size_t p = 0; - size_t s = 0; - short i, j, k; - while (p < nVoxels) { - size_t q = 0; - while (q < CLUSTER_SIZE && p < nVoxels) { - if (m_voxels[p].m_data == PRIMITIVE_ON_SURFACE) { - ++s; - if (s == sampling) { - s = 0; - i = m_voxels[p].m_coord[0]; - j = m_voxels[p].m_coord[1]; - k = m_voxels[p].m_coord[2]; - Vec3 p0((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); - Vec3 p1((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); - Vec3 p2((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); - Vec3 p3((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); - Vec3 p4((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); - Vec3 p5((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); - Vec3 p6((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); - Vec3 p7((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); - points[q++] = p0 + m_minBB; - points[q++] = p1 + m_minBB; - points[q++] = p2 + m_minBB; - points[q++] = p3 + m_minBB; - points[q++] = p4 + m_minBB; - points[q++] = p5 + m_minBB; - points[q++] = p6 + m_minBB; - points[q++] = p7 + m_minBB; - } - } - ++p; - } - btConvexHullComputer ch; - ch.compute((double*)points, 3 * sizeof(double), (int32_t)q, -1.0, -1.0); - for (int32_t v = 0; v < ch.vertices.size(); v++) { - cpoints.PushBack(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); - } - } - delete[] points; - - points = cpoints.Data(); - btConvexHullComputer ch; - ch.compute((double*)points, 3 * sizeof(double), (int32_t)cpoints.Size(), -1.0, -1.0); - meshCH.ResizePoints(0); - meshCH.ResizeTriangles(0); - for (int32_t v = 0; v < ch.vertices.size(); v++) { - meshCH.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) { - meshCH.AddTriangle(Vec3(a, b, c)); - edge = edge->getNextEdgeOfFace(); - b = c; - c = edge->getTargetVertex(); - } - } -} -void VoxelSet::GetPoints(const Voxel& voxel, - Vec3* const pts) const -{ - short i = voxel.m_coord[0]; - short j = voxel.m_coord[1]; - short k = voxel.m_coord[2]; - pts[0][0] = (i - 0.5) * m_scale + m_minBB[0]; - pts[1][0] = (i + 0.5) * m_scale + m_minBB[0]; - pts[2][0] = (i + 0.5) * m_scale + m_minBB[0]; - pts[3][0] = (i - 0.5) * m_scale + m_minBB[0]; - pts[4][0] = (i - 0.5) * m_scale + m_minBB[0]; - pts[5][0] = (i + 0.5) * m_scale + m_minBB[0]; - pts[6][0] = (i + 0.5) * m_scale + m_minBB[0]; - pts[7][0] = (i - 0.5) * m_scale + m_minBB[0]; - pts[0][1] = (j - 0.5) * m_scale + m_minBB[1]; - pts[1][1] = (j - 0.5) * m_scale + m_minBB[1]; - pts[2][1] = (j + 0.5) * m_scale + m_minBB[1]; - pts[3][1] = (j + 0.5) * m_scale + m_minBB[1]; - pts[4][1] = (j - 0.5) * m_scale + m_minBB[1]; - pts[5][1] = (j - 0.5) * m_scale + m_minBB[1]; - pts[6][1] = (j + 0.5) * m_scale + m_minBB[1]; - pts[7][1] = (j + 0.5) * m_scale + m_minBB[1]; - pts[0][2] = (k - 0.5) * m_scale + m_minBB[2]; - pts[1][2] = (k - 0.5) * m_scale + m_minBB[2]; - pts[2][2] = (k - 0.5) * m_scale + m_minBB[2]; - pts[3][2] = (k - 0.5) * m_scale + m_minBB[2]; - pts[4][2] = (k + 0.5) * m_scale + m_minBB[2]; - pts[5][2] = (k + 0.5) * m_scale + m_minBB[2]; - pts[6][2] = (k + 0.5) * m_scale + m_minBB[2]; - pts[7][2] = (k + 0.5) * m_scale + m_minBB[2]; -} -void VoxelSet::Intersect(const Plane& plane, - SArray >* const positivePts, - SArray >* const negativePts, - const size_t sampling) const -{ - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - const double d0 = m_scale; - double d; - Vec3 pts[8]; - Vec3 pt; - Voxel voxel; - size_t sp = 0; - size_t sn = 0; - for (size_t v = 0; v < nVoxels; ++v) { - voxel = m_voxels[v]; - pt = GetPoint(voxel); - d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; - // if (d >= 0.0 && d <= d0) positivePts->PushBack(pt); - // else if (d < 0.0 && -d <= d0) negativePts->PushBack(pt); - if (d >= 0.0) { - if (d <= d0) { - GetPoints(voxel, pts); - for (int32_t k = 0; k < 8; ++k) { - positivePts->PushBack(pts[k]); - } - } - else { - if (++sp == sampling) { - // positivePts->PushBack(pt); - GetPoints(voxel, pts); - for (int32_t k = 0; k < 8; ++k) { - positivePts->PushBack(pts[k]); - } - sp = 0; - } - } - } - else { - if (-d <= d0) { - GetPoints(voxel, pts); - for (int32_t k = 0; k < 8; ++k) { - negativePts->PushBack(pts[k]); - } - } - else { - if (++sn == sampling) { - // negativePts->PushBack(pt); - GetPoints(voxel, pts); - for (int32_t k = 0; k < 8; ++k) { - negativePts->PushBack(pts[k]); - } - sn = 0; - } - } - } - } -} -void VoxelSet::ComputeExteriorPoints(const Plane& plane, - const Mesh& mesh, - SArray >* const exteriorPts) const -{ - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - double d; - Vec3 pt; - Vec3 pts[8]; - Voxel voxel; - for (size_t v = 0; v < nVoxels; ++v) { - voxel = m_voxels[v]; - pt = GetPoint(voxel); - d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; - if (d >= 0.0) { - if (!mesh.IsInside(pt)) { - GetPoints(voxel, pts); - for (int32_t k = 0; k < 8; ++k) { - exteriorPts->PushBack(pts[k]); - } - } - } - } -} -void VoxelSet::ComputeClippedVolumes(const Plane& plane, - double& positiveVolume, - double& negativeVolume) const -{ - negativeVolume = 0.0; - positiveVolume = 0.0; - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - double d; - Vec3 pt; - size_t nPositiveVoxels = 0; - for (size_t v = 0; v < nVoxels; ++v) { - pt = GetPoint(m_voxels[v]); - d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; - nPositiveVoxels += (d >= 0.0); - } - size_t nNegativeVoxels = nVoxels - nPositiveVoxels; - positiveVolume = m_unitVolume * nPositiveVoxels; - negativeVolume = m_unitVolume * nNegativeVoxels; -} -void VoxelSet::SelectOnSurface(PrimitiveSet* const onSurfP) const -{ - VoxelSet* const onSurf = (VoxelSet*)onSurfP; - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - - for (int32_t h = 0; h < 3; ++h) { - onSurf->m_minBB[h] = m_minBB[h]; - } - onSurf->m_voxels.Resize(0); - onSurf->m_scale = m_scale; - onSurf->m_unitVolume = m_unitVolume; - onSurf->m_numVoxelsOnSurface = 0; - onSurf->m_numVoxelsInsideSurface = 0; - Voxel voxel; - for (size_t v = 0; v < nVoxels; ++v) { - voxel = m_voxels[v]; - if (voxel.m_data == PRIMITIVE_ON_SURFACE) { - onSurf->m_voxels.PushBack(voxel); - ++onSurf->m_numVoxelsOnSurface; - } - } -} -void VoxelSet::Clip(const Plane& plane, - PrimitiveSet* const positivePartP, - PrimitiveSet* const negativePartP) const -{ - VoxelSet* const positivePart = (VoxelSet*)positivePartP; - VoxelSet* const negativePart = (VoxelSet*)negativePartP; - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - - for (int32_t h = 0; h < 3; ++h) { - negativePart->m_minBB[h] = positivePart->m_minBB[h] = m_minBB[h]; - } - positivePart->m_voxels.Resize(0); - negativePart->m_voxels.Resize(0); - positivePart->m_voxels.Allocate(nVoxels); - negativePart->m_voxels.Allocate(nVoxels); - negativePart->m_scale = positivePart->m_scale = m_scale; - negativePart->m_unitVolume = positivePart->m_unitVolume = m_unitVolume; - negativePart->m_numVoxelsOnSurface = positivePart->m_numVoxelsOnSurface = 0; - negativePart->m_numVoxelsInsideSurface = positivePart->m_numVoxelsInsideSurface = 0; - - double d; - Vec3 pt; - Voxel voxel; - const double d0 = m_scale; - for (size_t v = 0; v < nVoxels; ++v) { - voxel = m_voxels[v]; - pt = GetPoint(voxel); - d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d; - if (d >= 0.0) { - if (voxel.m_data == PRIMITIVE_ON_SURFACE || d <= d0) { - voxel.m_data = PRIMITIVE_ON_SURFACE; - positivePart->m_voxels.PushBack(voxel); - ++positivePart->m_numVoxelsOnSurface; - } - else { - positivePart->m_voxels.PushBack(voxel); - ++positivePart->m_numVoxelsInsideSurface; - } - } - else { - if (voxel.m_data == PRIMITIVE_ON_SURFACE || -d <= d0) { - voxel.m_data = PRIMITIVE_ON_SURFACE; - negativePart->m_voxels.PushBack(voxel); - ++negativePart->m_numVoxelsOnSurface; - } - else { - negativePart->m_voxels.PushBack(voxel); - ++negativePart->m_numVoxelsInsideSurface; - } - } - } -} -void VoxelSet::Convert(Mesh& mesh, const VOXEL_VALUE value) const -{ - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - Voxel voxel; - Vec3 pts[8]; - for (size_t v = 0; v < nVoxels; ++v) { - voxel = m_voxels[v]; - if (voxel.m_data == value) { - GetPoints(voxel, pts); - int32_t s = (int32_t)mesh.GetNPoints(); - for (int32_t k = 0; k < 8; ++k) { - mesh.AddPoint(pts[k]); - } - mesh.AddTriangle(Vec3(s + 0, s + 2, s + 1)); - mesh.AddTriangle(Vec3(s + 0, s + 3, s + 2)); - mesh.AddTriangle(Vec3(s + 4, s + 5, s + 6)); - mesh.AddTriangle(Vec3(s + 4, s + 6, s + 7)); - mesh.AddTriangle(Vec3(s + 7, s + 6, s + 2)); - mesh.AddTriangle(Vec3(s + 7, s + 2, s + 3)); - mesh.AddTriangle(Vec3(s + 4, s + 1, s + 5)); - mesh.AddTriangle(Vec3(s + 4, s + 0, s + 1)); - mesh.AddTriangle(Vec3(s + 6, s + 5, s + 1)); - mesh.AddTriangle(Vec3(s + 6, s + 1, s + 2)); - mesh.AddTriangle(Vec3(s + 7, s + 0, s + 4)); - mesh.AddTriangle(Vec3(s + 7, s + 3, s + 0)); - } - } -} -void VoxelSet::ComputePrincipalAxes() -{ - const size_t nVoxels = m_voxels.Size(); - if (nVoxels == 0) - return; - m_barycenterPCA[0] = m_barycenterPCA[1] = m_barycenterPCA[2] = 0.0; - for (size_t v = 0; v < nVoxels; ++v) { - Voxel& voxel = m_voxels[v]; - m_barycenterPCA[0] += voxel.m_coord[0]; - m_barycenterPCA[1] += voxel.m_coord[1]; - m_barycenterPCA[2] += voxel.m_coord[2]; - } - m_barycenterPCA /= (double)nVoxels; - - double covMat[3][3] = { { 0.0, 0.0, 0.0 }, - { 0.0, 0.0, 0.0 }, - { 0.0, 0.0, 0.0 } }; - double x, y, z; - for (size_t v = 0; v < nVoxels; ++v) { - Voxel& voxel = m_voxels[v]; - x = voxel.m_coord[0] - m_barycenter[0]; - y = voxel.m_coord[1] - m_barycenter[1]; - z = voxel.m_coord[2] - m_barycenter[2]; - covMat[0][0] += x * x; - covMat[1][1] += y * y; - covMat[2][2] += z * z; - covMat[0][1] += x * y; - covMat[0][2] += x * z; - covMat[1][2] += y * z; - } - covMat[0][0] /= nVoxels; - covMat[1][1] /= nVoxels; - covMat[2][2] /= nVoxels; - covMat[0][1] /= nVoxels; - covMat[0][2] /= nVoxels; - covMat[1][2] /= nVoxels; - covMat[1][0] = covMat[0][1]; - covMat[2][0] = covMat[0][2]; - covMat[2][1] = covMat[1][2]; - Diagonalize(covMat, m_Q, m_D); -} -Volume::Volume() -{ - m_dim[0] = m_dim[1] = m_dim[2] = 0; - m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; - m_maxBB[0] = m_maxBB[1] = m_maxBB[2] = 1.0; - m_numVoxelsOnSurface = 0; - m_numVoxelsInsideSurface = 0; - m_numVoxelsOutsideSurface = 0; - m_scale = 1.0; - m_data = 0; -} -Volume::~Volume(void) -{ - delete[] m_data; -} -void Volume::Allocate() -{ - delete[] m_data; - size_t size = m_dim[0] * m_dim[1] * m_dim[2]; - m_data = new unsigned char[size]; - memset(m_data, PRIMITIVE_UNDEFINED, sizeof(unsigned char) * size); -} -void Volume::Free() -{ - delete[] m_data; - m_data = 0; -} -void Volume::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) -{ - const short neighbours[6][3] = { { 1, 0, 0 }, - { 0, 1, 0 }, - { 0, 0, 1 }, - { -1, 0, 0 }, - { 0, -1, 0 }, - { 0, 0, -1 } }; - std::queue > fifo; - Vec3 current; - short a, b, c; - for (size_t i = i0; i < i1; ++i) { - for (size_t j = j0; j < j1; ++j) { - for (size_t k = k0; k < k1; ++k) { - - if (GetVoxel(i, j, k) == PRIMITIVE_UNDEFINED) { - current[0] = (short)i; - current[1] = (short)j; - current[2] = (short)k; - fifo.push(current); - GetVoxel(current[0], current[1], current[2]) = PRIMITIVE_OUTSIDE_SURFACE; - ++m_numVoxelsOutsideSurface; - while (fifo.size() > 0) { - current = fifo.front(); - fifo.pop(); - for (int32_t h = 0; h < 6; ++h) { - a = current[0] + neighbours[h][0]; - b = current[1] + neighbours[h][1]; - c = current[2] + neighbours[h][2]; - if (a < 0 || a >= (int32_t)m_dim[0] || b < 0 || b >= (int32_t)m_dim[1] || c < 0 || c >= (int32_t)m_dim[2]) { - continue; - } - unsigned char& v = GetVoxel(a, b, c); - if (v == PRIMITIVE_UNDEFINED) { - v = PRIMITIVE_OUTSIDE_SURFACE; - ++m_numVoxelsOutsideSurface; - fifo.push(Vec3(a, b, c)); - } - } - } - } - } - } - } -} -void Volume::FillInsideSurface() -{ - const size_t i0 = m_dim[0]; - const size_t j0 = m_dim[1]; - const size_t k0 = m_dim[2]; - for (size_t i = 0; i < i0; ++i) { - for (size_t j = 0; j < j0; ++j) { - for (size_t k = 0; k < k0; ++k) { - unsigned char& v = GetVoxel(i, j, k); - if (v == PRIMITIVE_UNDEFINED) { - v = PRIMITIVE_INSIDE_SURFACE; - ++m_numVoxelsInsideSurface; - } - } - } - } -} -void Volume::Convert(Mesh& mesh, const VOXEL_VALUE value) const -{ - const size_t i0 = m_dim[0]; - const size_t j0 = m_dim[1]; - const size_t k0 = m_dim[2]; - for (size_t i = 0; i < i0; ++i) { - for (size_t j = 0; j < j0; ++j) { - for (size_t k = 0; k < k0; ++k) { - const unsigned char& voxel = GetVoxel(i, j, k); - if (voxel == value) { - Vec3 p0((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); - Vec3 p1((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale); - Vec3 p2((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); - Vec3 p3((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale); - Vec3 p4((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); - Vec3 p5((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale); - Vec3 p6((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); - Vec3 p7((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale); - int32_t s = (int32_t)mesh.GetNPoints(); - mesh.AddPoint(p0 + m_minBB); - mesh.AddPoint(p1 + m_minBB); - mesh.AddPoint(p2 + m_minBB); - mesh.AddPoint(p3 + m_minBB); - mesh.AddPoint(p4 + m_minBB); - mesh.AddPoint(p5 + m_minBB); - mesh.AddPoint(p6 + m_minBB); - mesh.AddPoint(p7 + m_minBB); - mesh.AddTriangle(Vec3(s + 0, s + 2, s + 1)); - mesh.AddTriangle(Vec3(s + 0, s + 3, s + 2)); - mesh.AddTriangle(Vec3(s + 4, s + 5, s + 6)); - mesh.AddTriangle(Vec3(s + 4, s + 6, s + 7)); - mesh.AddTriangle(Vec3(s + 7, s + 6, s + 2)); - mesh.AddTriangle(Vec3(s + 7, s + 2, s + 3)); - mesh.AddTriangle(Vec3(s + 4, s + 1, s + 5)); - mesh.AddTriangle(Vec3(s + 4, s + 0, s + 1)); - mesh.AddTriangle(Vec3(s + 6, s + 5, s + 1)); - mesh.AddTriangle(Vec3(s + 6, s + 1, s + 2)); - mesh.AddTriangle(Vec3(s + 7, s + 0, s + 4)); - mesh.AddTriangle(Vec3(s + 7, s + 3, s + 0)); - } - } - } - } -} -void Volume::Convert(VoxelSet& vset) const -{ - for (int32_t h = 0; h < 3; ++h) { - vset.m_minBB[h] = m_minBB[h]; - } - vset.m_voxels.Allocate(m_numVoxelsInsideSurface + m_numVoxelsOnSurface); - vset.m_scale = m_scale; - vset.m_unitVolume = m_scale * m_scale * m_scale; - const short i0 = (short)m_dim[0]; - const short j0 = (short)m_dim[1]; - const short k0 = (short)m_dim[2]; - Voxel voxel; - vset.m_numVoxelsOnSurface = 0; - vset.m_numVoxelsInsideSurface = 0; - for (short i = 0; i < i0; ++i) { - for (short j = 0; j < j0; ++j) { - for (short k = 0; k < k0; ++k) { - const unsigned char& value = GetVoxel(i, j, k); - if (value == PRIMITIVE_INSIDE_SURFACE) { - voxel.m_coord[0] = i; - voxel.m_coord[1] = j; - voxel.m_coord[2] = k; - voxel.m_data = PRIMITIVE_INSIDE_SURFACE; - vset.m_voxels.PushBack(voxel); - ++vset.m_numVoxelsInsideSurface; - } - else if (value == PRIMITIVE_ON_SURFACE) { - voxel.m_coord[0] = i; - voxel.m_coord[1] = j; - voxel.m_coord[2] = k; - voxel.m_data = PRIMITIVE_ON_SURFACE; - vset.m_voxels.PushBack(voxel); - ++vset.m_numVoxelsOnSurface; - } - } - } - } -} - -void Volume::Convert(TetrahedronSet& tset) const -{ - tset.m_tetrahedra.Allocate(5 * (m_numVoxelsInsideSurface + m_numVoxelsOnSurface)); - tset.m_scale = m_scale; - const short i0 = (short)m_dim[0]; - const short j0 = (short)m_dim[1]; - const short k0 = (short)m_dim[2]; - tset.m_numTetrahedraOnSurface = 0; - tset.m_numTetrahedraInsideSurface = 0; - Tetrahedron tetrahedron; - for (short i = 0; i < i0; ++i) { - for (short j = 0; j < j0; ++j) { - for (short k = 0; k < k0; ++k) { - const unsigned char& value = GetVoxel(i, j, k); - if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { - tetrahedron.m_data = value; - Vec3 p1((i - 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); - Vec3 p2((i + 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); - Vec3 p3((i + 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); - Vec3 p4((i - 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k - 0.5) * m_scale + m_minBB[2]); - Vec3 p5((i - 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); - Vec3 p6((i + 0.5) * m_scale + m_minBB[0], (j - 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); - Vec3 p7((i + 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); - Vec3 p8((i - 0.5) * m_scale + m_minBB[0], (j + 0.5) * m_scale + m_minBB[1], (k + 0.5) * m_scale + m_minBB[2]); - - tetrahedron.m_pts[0] = p2; - tetrahedron.m_pts[1] = p4; - tetrahedron.m_pts[2] = p7; - tetrahedron.m_pts[3] = p5; - tset.m_tetrahedra.PushBack(tetrahedron); - - tetrahedron.m_pts[0] = p6; - tetrahedron.m_pts[1] = p2; - tetrahedron.m_pts[2] = p7; - tetrahedron.m_pts[3] = p5; - tset.m_tetrahedra.PushBack(tetrahedron); - - tetrahedron.m_pts[0] = p3; - tetrahedron.m_pts[1] = p4; - tetrahedron.m_pts[2] = p7; - tetrahedron.m_pts[3] = p2; - tset.m_tetrahedra.PushBack(tetrahedron); - - tetrahedron.m_pts[0] = p1; - tetrahedron.m_pts[1] = p4; - tetrahedron.m_pts[2] = p2; - tetrahedron.m_pts[3] = p5; - tset.m_tetrahedra.PushBack(tetrahedron); - - tetrahedron.m_pts[0] = p8; - tetrahedron.m_pts[1] = p5; - tetrahedron.m_pts[2] = p7; - tetrahedron.m_pts[3] = p4; - tset.m_tetrahedra.PushBack(tetrahedron); - if (value == PRIMITIVE_INSIDE_SURFACE) { - tset.m_numTetrahedraInsideSurface += 5; - } - else { - tset.m_numTetrahedraOnSurface += 5; - } - } - } - } - } -} - -void Volume::AlignToPrincipalAxes(double (&rot)[3][3]) const -{ - const short i0 = (short)m_dim[0]; - const short j0 = (short)m_dim[1]; - const short k0 = (short)m_dim[2]; - Vec3 barycenter(0.0); - size_t nVoxels = 0; - for (short i = 0; i < i0; ++i) { - for (short j = 0; j < j0; ++j) { - for (short k = 0; k < k0; ++k) { - const unsigned char& value = GetVoxel(i, j, k); - if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { - barycenter[0] += i; - barycenter[1] += j; - barycenter[2] += k; - ++nVoxels; - } - } - } - } - barycenter /= (double)nVoxels; - - double covMat[3][3] = { { 0.0, 0.0, 0.0 }, - { 0.0, 0.0, 0.0 }, - { 0.0, 0.0, 0.0 } }; - double x, y, z; - for (short i = 0; i < i0; ++i) { - for (short j = 0; j < j0; ++j) { - for (short k = 0; k < k0; ++k) { - const unsigned char& value = GetVoxel(i, j, k); - if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) { - x = i - barycenter[0]; - y = j - barycenter[1]; - z = k - barycenter[2]; - covMat[0][0] += x * x; - covMat[1][1] += y * y; - covMat[2][2] += z * z; - covMat[0][1] += x * y; - covMat[0][2] += x * z; - covMat[1][2] += y * z; - } - } - } - } - covMat[1][0] = covMat[0][1]; - covMat[2][0] = covMat[0][2]; - covMat[2][1] = covMat[1][2]; - double D[3][3]; - Diagonalize(covMat, rot, D); -} -TetrahedronSet::TetrahedronSet() -{ - m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0; - m_maxBB[0] = m_maxBB[1] = m_maxBB[2] = 1.0; - m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0; - m_scale = 1.0; - m_numTetrahedraOnSurface = 0; - m_numTetrahedraInsideSurface = 0; - memset(m_Q, 0, sizeof(double) * 9); - memset(m_D, 0, sizeof(double) * 9); -} -TetrahedronSet::~TetrahedronSet(void) -{ -} -void TetrahedronSet::ComputeBB() -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; - - for (int32_t h = 0; h < 3; ++h) { - m_minBB[h] = m_maxBB[h] = m_tetrahedra[0].m_pts[0][h]; - m_barycenter[h] = 0.0; - } - for (size_t p = 0; p < nTetrahedra; ++p) { - for (int32_t i = 0; i < 4; ++i) { - for (int32_t h = 0; h < 3; ++h) { - if (m_minBB[h] > m_tetrahedra[p].m_pts[i][h]) - m_minBB[h] = m_tetrahedra[p].m_pts[i][h]; - if (m_maxBB[h] < m_tetrahedra[p].m_pts[i][h]) - m_maxBB[h] = m_tetrahedra[p].m_pts[i][h]; - m_barycenter[h] += m_tetrahedra[p].m_pts[i][h]; - } - } - } - m_barycenter /= (double)(4 * nTetrahedra); -} -void TetrahedronSet::ComputeConvexHull(Mesh& meshCH, const size_t sampling) const -{ - const size_t CLUSTER_SIZE = 65536; - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; - - SArray > cpoints; - - Vec3* points = new Vec3[CLUSTER_SIZE]; - size_t p = 0; - while (p < nTetrahedra) { - size_t q = 0; - size_t s = 0; - while (q < CLUSTER_SIZE && p < nTetrahedra) { - if (m_tetrahedra[p].m_data == PRIMITIVE_ON_SURFACE) { - ++s; - if (s == sampling) { - s = 0; - for (int32_t a = 0; a < 4; ++a) { - points[q++] = m_tetrahedra[p].m_pts[a]; - for (int32_t xx = 0; xx < 3; ++xx) { - assert(m_tetrahedra[p].m_pts[a][xx] + EPS >= m_minBB[xx]); - assert(m_tetrahedra[p].m_pts[a][xx] <= m_maxBB[xx] + EPS); - } - } - } - } - ++p; - } - btConvexHullComputer ch; - ch.compute((double*)points, 3 * sizeof(double), (int32_t)q, -1.0, -1.0); - for (int32_t v = 0; v < ch.vertices.size(); v++) { - cpoints.PushBack(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); - } - } - delete[] points; - - points = cpoints.Data(); - btConvexHullComputer ch; - ch.compute((double*)points, 3 * sizeof(double), (int32_t)cpoints.Size(), -1.0, -1.0); - meshCH.ResizePoints(0); - meshCH.ResizeTriangles(0); - for (int32_t v = 0; v < ch.vertices.size(); v++) { - meshCH.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) { - meshCH.AddTriangle(Vec3(a, b, c)); - edge = edge->getNextEdgeOfFace(); - b = c; - c = edge->getTargetVertex(); - } - } -} -inline bool TetrahedronSet::Add(Tetrahedron& tetrahedron) -{ - double v = ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3]); - - const double EPS = 0.0000000001; - if (fabs(v) < EPS) { - return false; - } - else if (v < 0.0) { - Vec3 tmp = tetrahedron.m_pts[0]; - tetrahedron.m_pts[0] = tetrahedron.m_pts[1]; - tetrahedron.m_pts[1] = tmp; - } - - for (int32_t a = 0; a < 4; ++a) { - for (int32_t xx = 0; xx < 3; ++xx) { - assert(tetrahedron.m_pts[a][xx] + EPS >= m_minBB[xx]); - assert(tetrahedron.m_pts[a][xx] <= m_maxBB[xx] + EPS); - } - } - m_tetrahedra.PushBack(tetrahedron); - return true; -} - -void TetrahedronSet::AddClippedTetrahedra(const Vec3 (&pts)[10], const int32_t nPts) -{ - const int32_t tetF[4][3] = { { 0, 1, 2 }, { 2, 1, 3 }, { 3, 1, 0 }, { 3, 0, 2 } }; - if (nPts < 4) { - return; - } - else if (nPts == 4) { - Tetrahedron tetrahedron; - tetrahedron.m_data = PRIMITIVE_ON_SURFACE; - tetrahedron.m_pts[0] = pts[0]; - tetrahedron.m_pts[1] = pts[1]; - tetrahedron.m_pts[2] = pts[2]; - tetrahedron.m_pts[3] = pts[3]; - if (Add(tetrahedron)) { - ++m_numTetrahedraOnSurface; - } - } - else if (nPts == 5) { - const int32_t tet[15][4] = { - { 0, 1, 2, 3 }, { 1, 2, 3, 4 }, { 0, 2, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 2, 4 }, - }; - const int32_t rem[5] = { 4, 0, 1, 2, 3 }; - double maxVol = 0.0; - int32_t h0 = -1; - Tetrahedron tetrahedron0; - tetrahedron0.m_data = PRIMITIVE_ON_SURFACE; - for (int32_t h = 0; h < 5; ++h) { - double v = ComputeVolume4(pts[tet[h][0]], pts[tet[h][1]], pts[tet[h][2]], pts[tet[h][3]]); - if (v > maxVol) { - h0 = h; - tetrahedron0.m_pts[0] = pts[tet[h][0]]; - tetrahedron0.m_pts[1] = pts[tet[h][1]]; - tetrahedron0.m_pts[2] = pts[tet[h][2]]; - tetrahedron0.m_pts[3] = pts[tet[h][3]]; - maxVol = v; - } - else if (-v > maxVol) { - h0 = h; - tetrahedron0.m_pts[0] = pts[tet[h][1]]; - tetrahedron0.m_pts[1] = pts[tet[h][0]]; - tetrahedron0.m_pts[2] = pts[tet[h][2]]; - tetrahedron0.m_pts[3] = pts[tet[h][3]]; - maxVol = -v; - } - } - if (h0 == -1) - return; - if (Add(tetrahedron0)) { - ++m_numTetrahedraOnSurface; - } - else { - return; - } - int32_t a = rem[h0]; - maxVol = 0.0; - int32_t h1 = -1; - Tetrahedron tetrahedron1; - tetrahedron1.m_data = PRIMITIVE_ON_SURFACE; - for (int32_t h = 0; h < 4; ++h) { - double v = ComputeVolume4(pts[a], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); - if (v > maxVol) { - h1 = h; - tetrahedron1.m_pts[0] = pts[a]; - tetrahedron1.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; - tetrahedron1.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; - tetrahedron1.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; - maxVol = v; - } - } - if (h1 == -1 && Add(tetrahedron1)) { - ++m_numTetrahedraOnSurface; - } - } - else if (nPts == 6) { - - const int32_t tet[15][4] = { { 2, 3, 4, 5 }, { 1, 3, 4, 5 }, { 1, 2, 4, 5 }, { 1, 2, 3, 5 }, { 1, 2, 3, 4 }, - { 0, 3, 4, 5 }, { 0, 2, 4, 5 }, { 0, 2, 3, 5 }, { 0, 2, 3, 4 }, { 0, 1, 4, 5 }, - { 0, 1, 3, 5 }, { 0, 1, 3, 4 }, { 0, 1, 2, 5 }, { 0, 1, 2, 4 }, { 0, 1, 2, 3 } }; - const int32_t rem[15][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 0, 5 }, - { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 }, { 2, 3 }, - { 2, 4 }, { 2, 5 }, { 3, 4 }, { 3, 5 }, { 4, 5 } }; - double maxVol = 0.0; - int32_t h0 = -1; - Tetrahedron tetrahedron0; - tetrahedron0.m_data = PRIMITIVE_ON_SURFACE; - for (int32_t h = 0; h < 15; ++h) { - double v = ComputeVolume4(pts[tet[h][0]], pts[tet[h][1]], pts[tet[h][2]], pts[tet[h][3]]); - if (v > maxVol) { - h0 = h; - tetrahedron0.m_pts[0] = pts[tet[h][0]]; - tetrahedron0.m_pts[1] = pts[tet[h][1]]; - tetrahedron0.m_pts[2] = pts[tet[h][2]]; - tetrahedron0.m_pts[3] = pts[tet[h][3]]; - maxVol = v; - } - else if (-v > maxVol) { - h0 = h; - tetrahedron0.m_pts[0] = pts[tet[h][1]]; - tetrahedron0.m_pts[1] = pts[tet[h][0]]; - tetrahedron0.m_pts[2] = pts[tet[h][2]]; - tetrahedron0.m_pts[3] = pts[tet[h][3]]; - maxVol = -v; - } - } - if (h0 == -1) - return; - if (Add(tetrahedron0)) { - ++m_numTetrahedraOnSurface; - } - else { - return; - } - - int32_t a0 = rem[h0][0]; - int32_t a1 = rem[h0][1]; - int32_t h1 = -1; - Tetrahedron tetrahedron1; - tetrahedron1.m_data = PRIMITIVE_ON_SURFACE; - maxVol = 0.0; - for (int32_t h = 0; h < 4; ++h) { - double v = ComputeVolume4(pts[a0], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); - if (v > maxVol) { - h1 = h; - tetrahedron1.m_pts[0] = pts[a0]; - tetrahedron1.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; - tetrahedron1.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; - tetrahedron1.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; - maxVol = v; - } - } - if (h1 != -1 && Add(tetrahedron1)) { - ++m_numTetrahedraOnSurface; - } - else { - h1 = -1; - } - maxVol = 0.0; - int32_t h2 = -1; - Tetrahedron tetrahedron2; - tetrahedron2.m_data = PRIMITIVE_ON_SURFACE; - for (int32_t h = 0; h < 4; ++h) { - double v = ComputeVolume4(pts[a0], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]); - if (h == h1) - continue; - if (v > maxVol) { - h2 = h; - tetrahedron2.m_pts[0] = pts[a1]; - tetrahedron2.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]]; - tetrahedron2.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]]; - tetrahedron2.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]]; - maxVol = v; - } - } - if (h1 != -1) { - for (int32_t h = 0; h < 4; ++h) { - double v = ComputeVolume4(pts[a1], tetrahedron1.m_pts[tetF[h][0]], tetrahedron1.m_pts[tetF[h][1]], tetrahedron1.m_pts[tetF[h][2]]); - if (h == 1) - continue; - if (v > maxVol) { - h2 = h; - tetrahedron2.m_pts[0] = pts[a1]; - tetrahedron2.m_pts[1] = tetrahedron1.m_pts[tetF[h][0]]; - tetrahedron2.m_pts[2] = tetrahedron1.m_pts[tetF[h][1]]; - tetrahedron2.m_pts[3] = tetrahedron1.m_pts[tetF[h][2]]; - maxVol = v; - } - } - } - if (h2 != -1 && Add(tetrahedron2)) { - ++m_numTetrahedraOnSurface; - } - } - else { - assert(0); - } -} - -void TetrahedronSet::Intersect(const Plane& plane, - SArray >* const positivePts, - SArray >* const negativePts, - const size_t sampling) const -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; -} -void TetrahedronSet::ComputeExteriorPoints(const Plane& plane, - const Mesh& mesh, - SArray >* const exteriorPts) const -{ -} -void TetrahedronSet::ComputeClippedVolumes(const Plane& plane, - double& positiveVolume, - double& negativeVolume) const -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; -} - -void TetrahedronSet::SelectOnSurface(PrimitiveSet* const onSurfP) const -{ - TetrahedronSet* const onSurf = (TetrahedronSet*)onSurfP; - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; - onSurf->m_tetrahedra.Resize(0); - onSurf->m_scale = m_scale; - onSurf->m_numTetrahedraOnSurface = 0; - onSurf->m_numTetrahedraInsideSurface = 0; - onSurf->m_barycenter = m_barycenter; - onSurf->m_minBB = m_minBB; - onSurf->m_maxBB = m_maxBB; - for (int32_t i = 0; i < 3; ++i) { - for (int32_t j = 0; j < 3; ++j) { - onSurf->m_Q[i][j] = m_Q[i][j]; - onSurf->m_D[i][j] = m_D[i][j]; - } - } - Tetrahedron tetrahedron; - for (size_t v = 0; v < nTetrahedra; ++v) { - tetrahedron = m_tetrahedra[v]; - if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { - onSurf->m_tetrahedra.PushBack(tetrahedron); - ++onSurf->m_numTetrahedraOnSurface; - } - } -} -void TetrahedronSet::Clip(const Plane& plane, - PrimitiveSet* const positivePartP, - PrimitiveSet* const negativePartP) const -{ - TetrahedronSet* const positivePart = (TetrahedronSet*)positivePartP; - TetrahedronSet* const negativePart = (TetrahedronSet*)negativePartP; - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; - positivePart->m_tetrahedra.Resize(0); - negativePart->m_tetrahedra.Resize(0); - positivePart->m_tetrahedra.Allocate(nTetrahedra); - negativePart->m_tetrahedra.Allocate(nTetrahedra); - negativePart->m_scale = positivePart->m_scale = m_scale; - negativePart->m_numTetrahedraOnSurface = positivePart->m_numTetrahedraOnSurface = 0; - negativePart->m_numTetrahedraInsideSurface = positivePart->m_numTetrahedraInsideSurface = 0; - negativePart->m_barycenter = m_barycenter; - positivePart->m_barycenter = m_barycenter; - negativePart->m_minBB = m_minBB; - positivePart->m_minBB = m_minBB; - negativePart->m_maxBB = m_maxBB; - positivePart->m_maxBB = m_maxBB; - for (int32_t i = 0; i < 3; ++i) { - for (int32_t j = 0; j < 3; ++j) { - negativePart->m_Q[i][j] = positivePart->m_Q[i][j] = m_Q[i][j]; - negativePart->m_D[i][j] = positivePart->m_D[i][j] = m_D[i][j]; - } - } - - Tetrahedron tetrahedron; - double delta, alpha; - int32_t sign[4]; - int32_t npos, nneg; - Vec3 posPts[10]; - Vec3 negPts[10]; - Vec3 P0, P1, M; - const Vec3 n(plane.m_a, plane.m_b, plane.m_c); - const int32_t edges[6][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 } }; - double dist; - for (size_t v = 0; v < nTetrahedra; ++v) { - tetrahedron = m_tetrahedra[v]; - npos = nneg = 0; - for (int32_t i = 0; i < 4; ++i) { - dist = plane.m_a * tetrahedron.m_pts[i][0] + plane.m_b * tetrahedron.m_pts[i][1] + plane.m_c * tetrahedron.m_pts[i][2] + plane.m_d; - if (dist > 0.0) { - sign[i] = 1; - posPts[npos] = tetrahedron.m_pts[i]; - ++npos; - } - else { - sign[i] = -1; - negPts[nneg] = tetrahedron.m_pts[i]; - ++nneg; - } - } - - if (npos == 4) { - positivePart->Add(tetrahedron); - if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { - ++positivePart->m_numTetrahedraOnSurface; - } - else { - ++positivePart->m_numTetrahedraInsideSurface; - } - } - else if (nneg == 4) { - negativePart->Add(tetrahedron); - if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { - ++negativePart->m_numTetrahedraOnSurface; - } - else { - ++negativePart->m_numTetrahedraInsideSurface; - } - } - else { - int32_t nnew = 0; - for (int32_t j = 0; j < 6; ++j) { - if (sign[edges[j][0]] * sign[edges[j][1]] == -1) { - P0 = tetrahedron.m_pts[edges[j][0]]; - P1 = tetrahedron.m_pts[edges[j][1]]; - delta = (P0 - P1) * n; - alpha = -(plane.m_d + (n * P1)) / delta; - assert(alpha >= 0.0 && alpha <= 1.0); - M = alpha * P0 + (1 - alpha) * P1; - for (int32_t xx = 0; xx < 3; ++xx) { - assert(M[xx] + EPS >= m_minBB[xx]); - assert(M[xx] <= m_maxBB[xx] + EPS); - } - posPts[npos++] = M; - negPts[nneg++] = M; - ++nnew; - } - } - negativePart->AddClippedTetrahedra(negPts, nneg); - positivePart->AddClippedTetrahedra(posPts, npos); - } - } -} -void TetrahedronSet::Convert(Mesh& mesh, const VOXEL_VALUE value) const -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; - for (size_t v = 0; v < nTetrahedra; ++v) { - const Tetrahedron& tetrahedron = m_tetrahedra[v]; - if (tetrahedron.m_data == value) { - int32_t s = (int32_t)mesh.GetNPoints(); - mesh.AddPoint(tetrahedron.m_pts[0]); - mesh.AddPoint(tetrahedron.m_pts[1]); - mesh.AddPoint(tetrahedron.m_pts[2]); - mesh.AddPoint(tetrahedron.m_pts[3]); - mesh.AddTriangle(Vec3(s + 0, s + 1, s + 2)); - mesh.AddTriangle(Vec3(s + 2, s + 1, s + 3)); - mesh.AddTriangle(Vec3(s + 3, s + 1, s + 0)); - mesh.AddTriangle(Vec3(s + 3, s + 0, s + 2)); - } - } -} -const double TetrahedronSet::ComputeVolume() const -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return 0.0; - double volume = 0.0; - for (size_t v = 0; v < nTetrahedra; ++v) { - const Tetrahedron& tetrahedron = m_tetrahedra[v]; - volume += fabs(ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3])); - } - return volume / 6.0; -} -const double TetrahedronSet::ComputeMaxVolumeError() const -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return 0.0; - double volume = 0.0; - for (size_t v = 0; v < nTetrahedra; ++v) { - const Tetrahedron& tetrahedron = m_tetrahedra[v]; - if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) { - volume += fabs(ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3])); - } - } - return volume / 6.0; -} -void TetrahedronSet::RevertAlignToPrincipalAxes() -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; - double x, y, z; - for (size_t v = 0; v < nTetrahedra; ++v) { - Tetrahedron& tetrahedron = m_tetrahedra[v]; - for (int32_t i = 0; i < 4; ++i) { - x = tetrahedron.m_pts[i][0] - m_barycenter[0]; - y = tetrahedron.m_pts[i][1] - m_barycenter[1]; - z = tetrahedron.m_pts[i][2] - m_barycenter[2]; - tetrahedron.m_pts[i][0] = m_Q[0][0] * x + m_Q[0][1] * y + m_Q[0][2] * z + m_barycenter[0]; - tetrahedron.m_pts[i][1] = m_Q[1][0] * x + m_Q[1][1] * y + m_Q[1][2] * z + m_barycenter[1]; - tetrahedron.m_pts[i][2] = m_Q[2][0] * x + m_Q[2][1] * y + m_Q[2][2] * z + m_barycenter[2]; - } - } - ComputeBB(); -} -void TetrahedronSet::ComputePrincipalAxes() -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; - double covMat[3][3] = { { 0.0, 0.0, 0.0 }, - { 0.0, 0.0, 0.0 }, - { 0.0, 0.0, 0.0 } }; - double x, y, z; - for (size_t v = 0; v < nTetrahedra; ++v) { - Tetrahedron& tetrahedron = m_tetrahedra[v]; - for (int32_t i = 0; i < 4; ++i) { - x = tetrahedron.m_pts[i][0] - m_barycenter[0]; - y = tetrahedron.m_pts[i][1] - m_barycenter[1]; - z = tetrahedron.m_pts[i][2] - m_barycenter[2]; - covMat[0][0] += x * x; - covMat[1][1] += y * y; - covMat[2][2] += z * z; - covMat[0][1] += x * y; - covMat[0][2] += x * z; - covMat[1][2] += y * z; - } - } - double n = nTetrahedra * 4.0; - covMat[0][0] /= n; - covMat[1][1] /= n; - covMat[2][2] /= n; - covMat[0][1] /= n; - covMat[0][2] /= n; - covMat[1][2] /= n; - covMat[1][0] = covMat[0][1]; - covMat[2][0] = covMat[0][2]; - covMat[2][1] = covMat[1][2]; - Diagonalize(covMat, m_Q, m_D); -} -void TetrahedronSet::AlignToPrincipalAxes() -{ - const size_t nTetrahedra = m_tetrahedra.Size(); - if (nTetrahedra == 0) - return; - double x, y, z; - for (size_t v = 0; v < nTetrahedra; ++v) { - Tetrahedron& tetrahedron = m_tetrahedra[v]; - for (int32_t i = 0; i < 4; ++i) { - x = tetrahedron.m_pts[i][0] - m_barycenter[0]; - y = tetrahedron.m_pts[i][1] - m_barycenter[1]; - z = tetrahedron.m_pts[i][2] - m_barycenter[2]; - tetrahedron.m_pts[i][0] = m_Q[0][0] * x + m_Q[1][0] * y + m_Q[2][0] * z + m_barycenter[0]; - tetrahedron.m_pts[i][1] = m_Q[0][1] * x + m_Q[1][1] * y + m_Q[2][1] * z + m_barycenter[1]; - tetrahedron.m_pts[i][2] = m_Q[0][2] * x + m_Q[1][2] * y + m_Q[2][2] * z + m_barycenter[2]; - } - } - ComputeBB(); -} -} diff --git a/src/dll/CMakeLists.txt b/src/dll/CMakeLists.txt deleted file mode 100644 index 6d954559..00000000 --- a/src/dll/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -project(VHACD_DLL) -include(${CMAKE_COMMON_INC}) - -if (NOT NO_OPENCL) - find_package(OpenCL QUIET) -endif() -if (NOT NO_OPENMP) - find_package(OpenMP QUIET) -endif() -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") -endif() - -add_library(libvhacd SHARED ${PROJECT_CPP_FILES} ${PROJECT_C_FILES} ${PROJECT_INC_FILES} ${PROJECT_INL_FILES}) - -target_include_directories(libvhacd PRIVATE ${CMAKE_SOURCE_DIR}/VHACD_Lib/public) -target_link_libraries(libvhacd vhacd) - -if (OpenCL_FOUND) - include_directories("${OpenCL_INCLUDE_DIRS}") - add_definitions( -DOPENCL_FOUND=1 ) - target_link_libraries(libvhacd ${OpenCL_LIBRARIES}) -endif() - -if (NOT WIN32 AND NOT APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall") - target_link_libraries(libvhacd rt) -endif() - diff --git a/src/dll/dll.cpp b/src/dll/dll.cpp new file mode 100644 index 00000000..2a751165 --- /dev/null +++ b/src/dll/dll.cpp @@ -0,0 +1,93 @@ +#include +#include "VHACD.h" + +#ifdef WIN32 +#define EXTERN extern "C" __declspec(dllexport) +#else +#define EXTERN extern "C" +#endif + +struct ConvexHullCSharp { + double* m_points; + uint32_t* m_triangles; + uint32_t m_nPoints; + uint32_t m_nTriangles; +}; + +EXTERN void* CreateVHACD() +{ + return VHACD::CreateVHACD(); +} + +EXTERN void DestroyVHACD(void* pVHACD) +{ + auto vhacd = (VHACD::IVHACD*)pVHACD; + vhacd->Clean(); + vhacd->Release(); +} + +EXTERN bool ComputeFloat( + void* pVHACD, + const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const void* params) +{ + auto vhacd = (VHACD::IVHACD*)pVHACD; + return vhacd->Compute(points, countPoints, triangles, countTriangles, *(VHACD::IVHACD::Parameters const *)params); +} + +EXTERN bool ComputeDouble( + void* pVHACD, + const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const void* params) +{ + auto vhacd = (VHACD::IVHACD*)pVHACD; + return vhacd->Compute(points, countPoints, triangles, countTriangles, *(VHACD::IVHACD::Parameters const *)params); +} + +EXTERN uint32_t GetNConvexHulls( + void* pVHACD + ) +{ + auto vhacd = (VHACD::IVHACD*)pVHACD; + return vhacd->GetNConvexHulls(); +} + +EXTERN void GetConvexHull( + void* pVHACD, + const uint32_t index, + void* convexHull) +{ + auto vhacd = (VHACD::IVHACD*)pVHACD; + VHACD::IVHACD::ConvexHull ch; + vhacd->GetConvexHull(index, ch); + ConvexHullCSharp* convexHullCSharp = (ConvexHullCSharp*) convexHull; + + convexHullCSharp->m_nPoints = ch.m_points.size(); + convexHullCSharp->m_points = new double[convexHullCSharp->m_nPoints * 3]; + for(uint32_t i = 0; i < ch.m_points.size(); i++) { + convexHullCSharp->m_points[i * 3 + 0] = ch.m_points[i].mX; + convexHullCSharp->m_points[i * 3 + 1] = ch.m_points[i].mY; + convexHullCSharp->m_points[i * 3 + 2] = ch.m_points[i].mZ; + } + + convexHullCSharp->m_nTriangles = ch.m_triangles.size(); + convexHullCSharp->m_triangles = new uint32_t[convexHullCSharp->m_nTriangles * 3]; + for(uint32_t i = 0; i < ch.m_triangles.size(); i++) { + convexHullCSharp->m_triangles[i * 3 + 0] = ch.m_triangles[i].mI0; + convexHullCSharp->m_triangles[i * 3 + 1] = ch.m_triangles[i].mI1; + convexHullCSharp->m_triangles[i * 3 + 2] = ch.m_triangles[i].mI2; + } +} + +EXTERN void FreeConvexHull( + void* convexHull) +{ + delete [] ((ConvexHullCSharp*) convexHull)->m_points; + delete [] ((ConvexHullCSharp*) convexHull)->m_triangles; +} \ No newline at end of file diff --git a/src/dll/src/dll.cpp b/src/dll/src/dll.cpp deleted file mode 100644 index fa99cade..00000000 --- a/src/dll/src/dll.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "VHACD.h" - -#ifdef WIN32 -#define EXTERN extern "C" __declspec(dllexport) -#else -#define EXTERN extern "C" -#endif - -EXTERN void* CreateVHACD() -{ - return VHACD::CreateVHACD(); -} - -EXTERN void DestroyVHACD(void* pVHACD) -{ - auto vhacd = (VHACD::IVHACD*)pVHACD; - vhacd->Clean(); - vhacd->Release(); -} - -EXTERN bool ComputeFloat( - void* pVHACD, - const float* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const void* params) -{ - auto vhacd = (VHACD::IVHACD*)pVHACD; - return vhacd->Compute(points, countPoints, triangles, countTriangles, *(VHACD::IVHACD::Parameters const *)params); -} - -EXTERN bool ComputeDouble( - void* pVHACD, - const double* const points, - const uint32_t countPoints, - const uint32_t* const triangles, - const uint32_t countTriangles, - const void* params) -{ - auto vhacd = (VHACD::IVHACD*)pVHACD; - return vhacd->Compute(points, countPoints, triangles, countTriangles, *(VHACD::IVHACD::Parameters const *)params); -} - -EXTERN uint32_t GetNConvexHulls( - void* pVHACD - ) -{ - auto vhacd = (VHACD::IVHACD*)pVHACD; - return vhacd->GetNConvexHulls(); -} - -EXTERN void GetConvexHull( - void* pVHACD, - const uint32_t index, - void* ch) -{ - auto vhacd = (VHACD::IVHACD*)pVHACD; - return vhacd->GetConvexHull(index, *(VHACD::IVHACD::ConvexHull *)ch); -} diff --git a/src/inc/VHACD.h b/src/inc/VHACD.h new file mode 100644 index 00000000..8a9bc116 --- /dev/null +++ b/src/inc/VHACD.h @@ -0,0 +1,8367 @@ +/* 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_H +# define VHACD_H + +// Please view this slide deck which describes usage and how the algorithm works. +// https://docs.google.com/presentation/d/1OZ4mtZYrGEC8qffqb8F7Le2xzufiqvaPpRbLHKKgTIM/edit?usp=sharing + +// VHACD is now a header only library. +// In just *one* of your CPP files *before* you include 'VHACD.h' you must declare +// #define ENABLE_VHACD_IMPLEMENTATION 1 +// This will compile the implementation code into your project. If you don't +// have this define, you will get link errors since the implementation code will +// not be present. If you define it more than once in your code base, you will get +// link errors due to a duplicate implementation. This is the same pattern used by +// ImGui and StbLib and other popular open source libraries. + +# define VHACD_VERSION_MAJOR 4 +# define VHACD_VERSION_MINOR 1 + +// Changes for version 4.1 +// +// Various minor tweaks mostly to the test application and some default values. + +// Changes for version 4.0 +// +// * The code has been significantly refactored to be cleaner and easier to maintain +// * All OpenCL related code removed +// * All Bullet code removed +// * All SIMD code removed +// * Old plane splitting code removed +// +// * The code is now delivered as a single header file 'VHACD.h' which has both the API +// * declaration as well as the implementation. Simply add '#define ENABLE_VHACD_IMPLEMENTATION 1' +// * to any CPP in your application prior to including 'VHACD.h'. Only do this in one CPP though. +// * If you do not have this define once, you will get link errors since the implementation code +// * will not be compiled in. If you have this define more than once, you are likely to get +// * duplicate symbol link errors. +// +// * Since the library is now delivered as a single header file, we do not provide binaries +// * or build scripts as these are not needed. +// +// * The old DebugView and test code has all been removed and replaced with a much smaller and +// * simpler test console application with some test meshes to work with. +// +// * The convex hull generation code has changed. The previous version came from Bullet. +// * However, the new version is courtesy of Julio Jerez, the author of the Newton +// * physics engine. His new version is faster and more numerically stable. +// +// * The code can now detect if the input mesh is, itself, already a convex object and +// * can early out. +// +// * Significant performance improvements have been made to the code and it is now much +// * faster, stable, and is easier to tune than previous versions. +// +// * A bug was fixed with the shrink wrapping code (project hull vertices) that could +// * sometime produce artifacts in the results. The new version uses a 'closest point' +// * algorithm that is more reliable. +// +// * You can now select which 'fill mode' to use. For perfectly closed meshes, the default +// * behavior using a flood fill generally works fine. However, some meshes have small +// * holes in them and therefore the flood fill will fail, treating the mesh as being +// * hollow. In these cases, you can use the 'raycast' fill option to determine which +// * parts of the voxelized mesh are 'inside' versus being 'outside'. Finally, there +// * are some rare instances where a user might actually want the mesh to be treated as +// * hollow, in which case you can pass in 'surface' only. +// * +// * A new optional virtual interface called 'IUserProfiler' was provided. +// * This allows the user to provide an optional profiling callback interface to assist in +// * diagnosing performance issues. This change was made by Danny Couture at Epic for the UE4 integration. +// * Some profiling macros were also declared in support of this feature. +// * +// * Another new optional virtual interface called 'IUserTaskRunner' was provided. +// * This interface is used to run logical 'tasks' in a background thread. If none is provided +// * then a default implementation using std::thread will be executed. +// * This change was made by Danny Couture at Epic to speed up the voxelization step. +// * + + + +// The history of V-HACD: +// +// The initial version was written by John W. Ratcliff and was called 'ACD' +// This version did not perform CSG operations on the source mesh, so if you +// recursed too deeply it would produce hollow results. +// +// The next version was written by Khaled Mamou and was called 'HACD' +// In this version Khaled tried to perform a CSG operation on the source +// mesh to produce more robust results. However, Khaled learned that the +// CSG library he was using had licensing issues so he started work on the +// next version. +// +// The next version was called 'V-HACD' because Khaled made the observation +// that plane splitting would be far easier to implement working in voxel space. +// +// V-HACD has been integrated into UE4, Blender, and a number of other projects. +// This new release, version4, is a significant refactor of the code to fix +// some bugs, improve performance, and to make the codebase easier to maintain +// going forward. + +#include +#include + +#include +#include +#include +#include + +namespace VHACD { + +struct Vertex +{ + double mX; + double mY; + double mZ; + + Vertex() = default; + Vertex(double x, double y, double z) : mX(x), mY(y), mZ(z) {} + + const double& operator[](size_t idx) const + { + switch(idx) + { + case 0: return mX; + case 1: return mY; + case 2: return mZ; + }; + return mX; + } +}; + +struct Triangle +{ + uint32_t mI0; + uint32_t mI1; + uint32_t mI2; + + Triangle() = default; + Triangle(uint32_t i0, uint32_t i1, uint32_t i2) : mI0(i0), mI1(i1), mI2(i2) {} +}; + +template +class Vector3 +{ +public: + /* + * Getters + */ + T& operator[](size_t i); + const T& operator[](size_t i) const; + T& GetX(); + T& GetY(); + T& GetZ(); + const T& GetX() const; + const T& GetY() const; + const T& GetZ() const; + + /* + * Normalize and norming + */ + T Normalize(); + Vector3 Normalized(); + T GetNorm() const; + T GetNormSquared() const; + int LongestAxis() const; + + /* + * Vector-vector operations + */ + Vector3& operator=(const Vector3& rhs); + Vector3& operator+=(const Vector3& rhs); + Vector3& operator-=(const Vector3& rhs); + + Vector3 CWiseMul(const Vector3& rhs) const; + Vector3 Cross(const Vector3& rhs) const; + T Dot(const Vector3& rhs) const; + Vector3 operator+(const Vector3& rhs) const; + Vector3 operator-(const Vector3& rhs) const; + + /* + * Vector-scalar operations + */ + Vector3& operator-=(T a); + Vector3& operator+=(T a); + Vector3& operator/=(T a); + Vector3& operator*=(T a); + + Vector3 operator*(T rhs) const; + Vector3 operator/(T rhs) const; + + /* + * Unary operations + */ + Vector3 operator-() const; + + /* + * Comparison operators + */ + bool operator<(const Vector3& rhs) const; + bool operator>(const Vector3& rhs) const; + + /* + * Returns true if all elements of *this are greater than or equal to all elements of rhs, coefficient wise + * LE is less than or equal + */ + bool CWiseAllGE(const Vector3& rhs) const; + bool CWiseAllLE(const Vector3& rhs) const; + + Vector3 CWiseMin(const Vector3& rhs) const; + Vector3 CWiseMax(const Vector3& rhs) const; + T MinCoeff() const; + T MaxCoeff() const; + + T MinCoeff(uint32_t& idx) const; + T MaxCoeff(uint32_t& idx) const; + + /* + * Constructors + */ + Vector3() = default; + Vector3(T a); + Vector3(T x, T y, T z); + Vector3(const Vector3& rhs); + ~Vector3() = default; + + template + Vector3(const Vector3& rhs); + + Vector3(const VHACD::Vertex&); + Vector3(const VHACD::Triangle&); + + operator VHACD::Vertex() const; + +private: + std::array m_data{ T(0.0) }; +}; + +typedef VHACD::Vector3 Vect3; + +struct BoundsAABB +{ + BoundsAABB() = default; + BoundsAABB(const std::vector& points); + BoundsAABB(const Vect3& min, + const Vect3& max); + + BoundsAABB Union(const BoundsAABB& b); + + bool Intersects(const BoundsAABB& b) const; + + double SurfaceArea() const; + double Volume() const; + + BoundsAABB Inflate(double ratio) const; + + VHACD::Vect3 ClosestPoint(const VHACD::Vect3& p) const; + + VHACD::Vect3& GetMin(); + VHACD::Vect3& GetMax(); + const VHACD::Vect3& GetMin() const; + const VHACD::Vect3& GetMax() const; + + VHACD::Vect3 GetSize() const; + VHACD::Vect3 GetCenter() const; + + VHACD::Vect3 m_min{ double(0.0) }; + VHACD::Vect3 m_max{ double(0.0) }; +}; + +/** +* This enumeration determines how the voxels as filled to create a solid +* object. The default should be 'FLOOD_FILL' which generally works fine +* for closed meshes. However, if the mesh is not watertight, then using +* RAYCAST_FILL may be preferable as it will determine if a voxel is part +* of the interior of the source mesh by raycasting around it. +* +* Finally, there are some cases where you might actually want a convex +* decomposition to treat the source mesh as being hollow. If that is the +* case you can pass in 'SURFACE_ONLY' and then the convex decomposition +* will converge only onto the 'skin' of the surface mesh. +*/ +enum class FillMode +{ + FLOOD_FILL, // This is the default behavior, after the voxelization step it uses a flood fill to determine 'inside' + // from 'outside'. However, meshes with holes can fail and create hollow results. + SURFACE_ONLY, // Only consider the 'surface', will create 'skins' with hollow centers. + RAYCAST_FILL, // Uses raycasting to determine inside from outside. +}; + +class IVHACD +{ +public: + /** + * This optional pure virtual interface is used to notify the caller of the progress + * of convex decomposition as well as a signal when it is complete when running in + * a background thread + */ + class IUserCallback + { + public: + virtual ~IUserCallback(){}; + + /** + * Notifies the application of the current state of the convex decomposition operation + * + * @param overallProgress : Total progress from 0-100% + * @param stageProgress : Progress of the current stage 0-100% + * @param stage : A text description of the current stage we are in + * @param operation : A text description of what operation is currently being performed. + */ + virtual void Update(const double overallProgress, + const double stageProgress, + const char* const stage, + const char* operation) = 0; + + // This is an optional user callback which is only called when running V-HACD asynchronously. + // This is a callback performed to notify the user that the + // convex decomposition background process is completed. This call back will occur from + // a different thread so the user should take that into account. + virtual void NotifyVHACDComplete() + { + } + }; + + /** + * Optional user provided pure virtual interface to be notified of warning or informational messages + */ + class IUserLogger + { + public: + virtual ~IUserLogger(){}; + virtual void Log(const char* const msg) = 0; + }; + + /** + * An optional user provided pure virtual interface to perform a background task. + * This was added by Danny Couture at Epic as they wanted to use their own + * threading system instead of the standard library version which is the default. + */ + class IUserTaskRunner + { + public: + virtual ~IUserTaskRunner(){}; + virtual void* StartTask(std::function func) = 0; + virtual void JoinTask(void* Task) = 0; + }; + + /** + * A simple class that represents a convex hull as a triangle mesh with + * double precision vertices. Polygons are not currently provided. + */ + class ConvexHull + { + public: + std::vector m_points; + std::vector m_triangles; + + double m_volume{ 0 }; // The volume of the convex hull + VHACD::Vect3 m_center{ 0, 0, 0 }; // The centroid of the convex hull + uint32_t m_meshId{ 0 }; // A unique id for this convex hull + VHACD::Vect3 mBmin; // Bounding box minimum of the AABB + VHACD::Vect3 mBmax; // Bounding box maximum of the AABB + }; + + /** + * This class provides the parameters controlling the convex decomposition operation + */ + class Parameters + { + public: + IUserCallback* m_callback{nullptr}; // Optional user provided callback interface for progress + IUserLogger* m_logger{nullptr}; // Optional user provided callback interface for log messages + IUserTaskRunner* m_taskRunner{nullptr}; // Optional user provided interface for creating tasks + uint32_t m_maxConvexHulls{ 64 }; // The maximum number of convex hulls to produce + uint32_t m_resolution{ 400000 }; // The voxel resolution to use + double m_minimumVolumePercentErrorAllowed{ 1 }; // if the voxels are within 1% of the volume of the hull, we consider this a close enough approximation + uint32_t m_maxRecursionDepth{ 10 }; // The maximum recursion depth + bool m_shrinkWrap{true}; // Whether or not to shrinkwrap the voxel positions to the source mesh on output + FillMode m_fillMode{ FillMode::FLOOD_FILL }; // How to fill the interior of the voxelized mesh + uint32_t m_maxNumVerticesPerCH{ 64 }; // The maximum number of vertices allowed in any output convex hull + bool m_asyncACD{ true }; // Whether or not to run asynchronously, taking advantage of additional cores + uint32_t m_minEdgeLength{ 2 }; // Once a voxel patch has an edge length of less than 4 on all 3 sides, we don't keep recursing + bool m_findBestPlane{ false }; // Whether or not to attempt to split planes along the best location. Experimental feature. False by default. + }; + + /** + * Will cause the convex decomposition operation to be canceled early. No results will be produced but the background operation will end as soon as it can. + */ + virtual void Cancel() = 0; + + /** + * Compute a convex decomposition of a triangle mesh using float vertices and the provided user parameters. + * + * @param points : The vertices of the source mesh as floats in the form of X1,Y1,Z1, X2,Y2,Z2,.. etc. + * @param countPoints : The number of vertices in the source mesh. + * @param triangles : The indices of triangles in the source mesh in the form of I1,I2,I3, .... + * @param countTriangles : The number of triangles in the source mesh + * @param params : The convex decomposition parameters to apply + * @return : Returns true if the convex decomposition operation can be started + */ + virtual bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) = 0; + + /** + * Compute a convex decomposition of a triangle mesh using double vertices and the provided user parameters. + * + * @param points : The vertices of the source mesh as floats in the form of X1,Y1,Z1, X2,Y2,Z2,.. etc. + * @param countPoints : The number of vertices in the source mesh. + * @param triangles : The indices of triangles in the source mesh in the form of I1,I2,I3, .... + * @param countTriangles : The number of triangles in the source mesh + * @param params : The convex decomposition parameters to apply + * @return : Returns true if the convex decomposition operation can be started + */ + virtual bool Compute(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) = 0; + + /** + * Returns the number of convex hulls that were produced. + * + * @return : Returns the number of convex hulls produced, or zero if it failed or was canceled + */ + virtual uint32_t GetNConvexHulls() const = 0; + + /** + * Retrieves one of the convex hulls in the solution set + * + * @param index : Which convex hull to retrieve + * @param ch : The convex hull descriptor to return + * @return : Returns true if the convex hull exists and could be retrieved + */ + virtual bool GetConvexHull(const uint32_t index, + ConvexHull& ch) const = 0; + + /** + * Releases any memory allocated by the V-HACD class + */ + virtual void Clean() = 0; // release internally allocated memory + + /** + * Releases this instance of the V-HACD class + */ + virtual void Release() = 0; // release IVHACD + + // 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() const + { + return true; + } + + /** + * At the request of LegionFu : out_look@foxmail.com + * This method will return which convex hull is closest to the source position. + * You can use this method to figure out, for example, which vertices in the original + * source mesh are best associated with which convex hull. + * + * @param pos : The input 3d position to test against + * + * @return : Returns which convex hull this position is closest to. + */ + virtual uint32_t findNearestConvexHull(const double pos[3], + double& distanceToHull) = 0; + +protected: + virtual ~IVHACD() + { + } +}; +/* + * Out of line definitions + */ + + template + T clamp(const T& v, const T& lo, const T& hi) + { + if (v < lo) + { + return lo; + } + if (v > hi) + { + return hi; + } + return v ; + } + +/* + * Getters + */ + template + inline T& Vector3::operator[](size_t i) + { + return m_data[i]; + } + + template + inline const T& Vector3::operator[](size_t i) const + { + return m_data[i]; + } + + template + inline T& Vector3::GetX() + { + return m_data[0]; + } + + template + inline T& Vector3::GetY() + { + return m_data[1]; + } + + template + inline T& Vector3::GetZ() + { + return m_data[2]; + } + + template + inline const T& Vector3::GetX() const + { + return m_data[0]; + } + + template + inline const T& Vector3::GetY() const + { + return m_data[1]; + } + + template + inline const T& Vector3::GetZ() const + { + return m_data[2]; + } + +/* + * Normalize and norming + */ + template + inline T Vector3::Normalize() + { + T n = GetNorm(); + if (n != T(0.0)) (*this) /= n; + return n; + } + + template + inline Vector3 Vector3::Normalized() + { + Vector3 ret = *this; + T n = GetNorm(); + if (n != T(0.0)) ret /= n; + return ret; + } + + template + inline T Vector3::GetNorm() const + { + return std::sqrt(GetNormSquared()); + } + + template + inline T Vector3::GetNormSquared() const + { + return this->Dot(*this); + } + + template + inline int Vector3::LongestAxis() const + { + auto it = std::max_element(m_data.begin(), m_data.end()); + return int(std::distance(m_data.begin(), it)); + } + +/* + * Vector-vector operations + */ + template + inline Vector3& Vector3::operator=(const Vector3& rhs) + { + GetX() = rhs.GetX(); + GetY() = rhs.GetY(); + GetZ() = rhs.GetZ(); + return *this; + } + + template + inline Vector3& Vector3::operator+=(const Vector3& rhs) + { + GetX() += rhs.GetX(); + GetY() += rhs.GetY(); + GetZ() += rhs.GetZ(); + return *this; + } + + template + inline Vector3& Vector3::operator-=(const Vector3& rhs) + { + GetX() -= rhs.GetX(); + GetY() -= rhs.GetY(); + GetZ() -= rhs.GetZ(); + return *this; + } + + template + inline Vector3 Vector3::CWiseMul(const Vector3& rhs) const + { + return Vector3(GetX() * rhs.GetX(), + GetY() * rhs.GetY(), + GetZ() * rhs.GetZ()); + } + + template + inline Vector3 Vector3::Cross(const Vector3& rhs) const + { + return Vector3(GetY() * rhs.GetZ() - GetZ() * rhs.GetY(), + GetZ() * rhs.GetX() - GetX() * rhs.GetZ(), + GetX() * rhs.GetY() - GetY() * rhs.GetX()); + } + + template + inline T Vector3::Dot(const Vector3& rhs) const + { + return GetX() * rhs.GetX() + + GetY() * rhs.GetY() + + GetZ() * rhs.GetZ(); + } + + template + inline Vector3 Vector3::operator+(const Vector3& rhs) const + { + return Vector3(GetX() + rhs.GetX(), + GetY() + rhs.GetY(), + GetZ() + rhs.GetZ()); + } + + template + inline Vector3 Vector3::operator-(const Vector3& rhs) const + { + return Vector3(GetX() - rhs.GetX(), + GetY() - rhs.GetY(), + GetZ() - rhs.GetZ()); + } + + template + inline Vector3 operator*(T lhs, const Vector3& rhs) + { + return Vector3(lhs * rhs.GetX(), + lhs * rhs.GetY(), + lhs * rhs.GetZ()); + } + +/* + * Vector-scalar operations + */ + template + inline Vector3& Vector3::operator-=(T a) + { + GetX() -= a; + GetY() -= a; + GetZ() -= a; + return *this; + } + + template + inline Vector3& Vector3::operator+=(T a) + { + GetX() += a; + GetY() += a; + GetZ() += a; + return *this; + } + + template + inline Vector3& Vector3::operator/=(T a) + { + GetX() /= a; + GetY() /= a; + GetZ() /= a; + return *this; + } + + template + inline Vector3& Vector3::operator*=(T a) + { + GetX() *= a; + GetY() *= a; + GetZ() *= a; + return *this; + } + + template + inline Vector3 Vector3::operator*(T rhs) const + { + return Vector3(GetX() * rhs, + GetY() * rhs, + GetZ() * rhs); + } + + template + inline Vector3 Vector3::operator/(T rhs) const + { + return Vector3(GetX() / rhs, + GetY() / rhs, + GetZ() / rhs); + } + +/* + * Unary operations + */ + template + inline Vector3 Vector3::operator-() const + { + return Vector3(-GetX(), + -GetY(), + -GetZ()); + } + +/* + * Comparison operators + */ + template + inline bool Vector3::operator<(const Vector3& rhs) const + { + if (GetX() == rhs.GetX()) + { + if (GetY() == rhs.GetY()) + { + return (GetZ() < rhs.GetZ()); + } + return (GetY() < rhs.GetY()); + } + return (GetX() < rhs.GetX()); + } + + template + inline bool Vector3::operator>(const Vector3& rhs) const + { + if (GetX() == rhs.GetX()) + { + if (GetY() == rhs.GetY()) + { + return (GetZ() > rhs.GetZ()); + } + return (GetY() > rhs.GetY()); + } + return (GetX() > rhs.GetZ()); + } + + template + inline bool Vector3::CWiseAllGE(const Vector3& rhs) const + { + return GetX() >= rhs.GetX() + && GetY() >= rhs.GetY() + && GetZ() >= rhs.GetZ(); + } + + template + inline bool Vector3::CWiseAllLE(const Vector3& rhs) const + { + return GetX() <= rhs.GetX() + && GetY() <= rhs.GetY() + && GetZ() <= rhs.GetZ(); + } + + template + inline Vector3 Vector3::CWiseMin(const Vector3& rhs) const + { + return Vector3(std::min(GetX(), rhs.GetX()), + std::min(GetY(), rhs.GetY()), + std::min(GetZ(), rhs.GetZ())); + } + + template + inline Vector3 Vector3::CWiseMax(const Vector3& rhs) const + { + return Vector3(std::max(GetX(), rhs.GetX()), + std::max(GetY(), rhs.GetY()), + std::max(GetZ(), rhs.GetZ())); + } + + template + inline T Vector3::MinCoeff() const + { + return *std::min_element(m_data.begin(), m_data.end()); + } + + template + inline T Vector3::MaxCoeff() const + { + return *std::max_element(m_data.begin(), m_data.end()); + } + + template + inline T Vector3::MinCoeff(uint32_t& idx) const + { + auto it = std::min_element(m_data.begin(), m_data.end()); + idx = uint32_t(std::distance(m_data.begin(), it)); + return *it; + } + + template + inline T Vector3::MaxCoeff(uint32_t& idx) const + { + auto it = std::max_element(m_data.begin(), m_data.end()); + idx = uint32_t(std::distance(m_data.begin(), it)); + return *it; + } + +/* + * Constructors + */ + template + inline Vector3::Vector3(T a) + : m_data{a, a, a} + { + } + + template + inline Vector3::Vector3(T x, T y, T z) + : m_data{x, y, z} + { + } + + template + inline Vector3::Vector3(const Vector3& rhs) + : m_data{rhs.m_data} + { + } + + template + template + inline Vector3::Vector3(const Vector3& rhs) + : m_data{T(rhs.GetX()), T(rhs.GetY()), T(rhs.GetZ())} + { + } + + template + inline Vector3::Vector3(const VHACD::Vertex& rhs) + : Vector3(rhs.mX, rhs.mY, rhs.mZ) + { + static_assert(std::is_same::value, "Vertex to Vector3 constructor only enabled for double"); + } + + template + inline Vector3::Vector3(const VHACD::Triangle& rhs) + : Vector3(rhs.mI0, rhs.mI1, rhs.mI2) + { + static_assert(std::is_same::value, "Triangle to Vector3 constructor only enabled for uint32_t"); + } + + template + inline Vector3::operator VHACD::Vertex() const + { + static_assert(std::is_same::value, "Vector3 to Vertex conversion only enable for double"); + return ::VHACD::Vertex( GetX(), GetY(), GetZ()); + } + +IVHACD* CreateVHACD(); // Create a synchronous (blocking) implementation of V-HACD +IVHACD* CreateVHACD_ASYNC(); // Create an asynchronous (non-blocking) implementation of V-HACD + +} // namespace VHACD + +#if ENABLE_VHACD_IMPLEMENTATION +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4100 4127 4189 4244 4456 4701 4702 4996) +#endif // _MSC_VER + +#ifdef __GNUC__ +#pragma GCC diagnostic push +// Minimum set of warnings used for cleanup +// #pragma GCC diagnostic warning "-Wall" +// #pragma GCC diagnostic warning "-Wextra" +// #pragma GCC diagnostic warning "-Wpedantic" +// #pragma GCC diagnostic warning "-Wold-style-cast" +// #pragma GCC diagnostic warning "-Wnon-virtual-dtor" +// #pragma GCC diagnostic warning "-Wshadow" +#endif // __GNUC__ + +// Scoped Timer +namespace VHACD { + +class Timer +{ +public: + Timer() + : m_startTime(std::chrono::high_resolution_clock::now()) + { + } + + void Reset() + { + m_startTime = std::chrono::high_resolution_clock::now(); + } + + double GetElapsedSeconds() + { + auto s = PeekElapsedSeconds(); + Reset(); + return s; + } + + double PeekElapsedSeconds() + { + auto now = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff = now - m_startTime; + return diff.count(); + } + +private: + std::chrono::time_point m_startTime; +}; + +class ScopedTime +{ +public: + ScopedTime(const char* action, + VHACD::IVHACD::IUserLogger* logger) + : m_action(action) + , m_logger(logger) + { + m_timer.Reset(); + } + + ~ScopedTime() + { + double dtime = m_timer.GetElapsedSeconds(); + if( m_logger ) + { + char scratch[512]; + snprintf(scratch, + sizeof(scratch),"%s took %0.5f seconds", + m_action, + dtime); + m_logger->Log(scratch); + } + } + + const char* m_action{ nullptr }; + Timer m_timer; + VHACD::IVHACD::IUserLogger* m_logger{ nullptr }; +}; +BoundsAABB::BoundsAABB(const std::vector& points) + : m_min(points[0]) + , m_max(points[0]) +{ + for (uint32_t i = 1; i < points.size(); ++i) + { + const VHACD::Vertex& p = points[i]; + m_min = m_min.CWiseMin(p); + m_max = m_max.CWiseMax(p); + } +} + +BoundsAABB::BoundsAABB(const VHACD::Vect3& min, + const VHACD::Vect3& max) + : m_min(min) + , m_max(max) +{ +} + +BoundsAABB BoundsAABB::Union(const BoundsAABB& b) +{ + return BoundsAABB(GetMin().CWiseMin(b.GetMin()), + GetMax().CWiseMax(b.GetMax())); +} + +bool VHACD::BoundsAABB::Intersects(const VHACD::BoundsAABB& b) const +{ + if ( ( GetMin().GetX() > b.GetMax().GetX()) + || (b.GetMin().GetX() > GetMax().GetX())) + return false; + if ( ( GetMin().GetY() > b.GetMax().GetY()) + || (b.GetMin().GetY() > GetMax().GetY())) + return false; + if ( ( GetMin().GetZ() > b.GetMax().GetZ()) + || (b.GetMin().GetZ() > GetMax().GetZ())) + return false; + return true; +} + +double BoundsAABB::SurfaceArea() const +{ + VHACD::Vect3 d = GetMax() - GetMin(); + return double(2.0) * (d.GetX() * d.GetY() + d.GetX() * d.GetZ() + d.GetY() * d.GetZ()); +} + +double VHACD::BoundsAABB::Volume() const +{ + VHACD::Vect3 d = GetMax() - GetMin(); + return d.GetX() * d.GetY() * d.GetZ(); +} + +BoundsAABB VHACD::BoundsAABB::Inflate(double ratio) const +{ + double inflate = (GetMin() - GetMax()).GetNorm() * double(0.5) * ratio; + return BoundsAABB(GetMin() - inflate, + GetMax() + inflate); +} + +VHACD::Vect3 VHACD::BoundsAABB::ClosestPoint(const VHACD::Vect3& p) const +{ + return p.CWiseMax(GetMin()).CWiseMin(GetMax()); +} + +VHACD::Vect3& VHACD::BoundsAABB::GetMin() +{ + return m_min; +} + +VHACD::Vect3& VHACD::BoundsAABB::GetMax() +{ + return m_max; +} + +inline const VHACD::Vect3& VHACD::BoundsAABB::GetMin() const +{ + return m_min; +} + +const VHACD::Vect3& VHACD::BoundsAABB::GetMax() const +{ + return m_max; +} + +VHACD::Vect3 VHACD::BoundsAABB::GetSize() const +{ + return GetMax() - GetMin(); +} + +VHACD::Vect3 VHACD::BoundsAABB::GetCenter() const +{ + return (GetMin() + GetMax()) * double(0.5); +} + +/* + * Relies on three way comparison, which std::sort doesn't use + */ +template +void Sort(T* const array, int elements) +{ + const int batchSize = 8; + int stack[1024][2]; + + stack[0][0] = 0; + stack[0][1] = elements - 1; + int stackIndex = 1; + const dCompareKey comparator; + while (stackIndex) + { + stackIndex--; + int lo = stack[stackIndex][0]; + int hi = stack[stackIndex][1]; + if ((hi - lo) > batchSize) + { + int mid = (lo + hi) >> 1; + if (comparator.Compare(array[lo], array[mid]) > 0) + { + std::swap(array[lo], + array[mid]); + } + if (comparator.Compare(array[mid], array[hi]) > 0) + { + std::swap(array[mid], + array[hi]); + } + if (comparator.Compare(array[lo], array[mid]) > 0) + { + std::swap(array[lo], + array[mid]); + } + int i = lo + 1; + int j = hi - 1; + const T pivot(array[mid]); + do + { + while (comparator.Compare(array[i], pivot) < 0) + { + i++; + } + while (comparator.Compare(array[j], pivot) > 0) + { + j--; + } + + if (i <= j) + { + std::swap(array[i], + array[j]); + i++; + j--; + } + } while (i <= j); + + if (i < hi) + { + stack[stackIndex][0] = i; + stack[stackIndex][1] = hi; + stackIndex++; + } + if (lo < j) + { + stack[stackIndex][0] = lo; + stack[stackIndex][1] = j; + stackIndex++; + } + assert(stackIndex < int(sizeof(stack) / (2 * sizeof(stack[0][0])))); + } + } + + int stride = batchSize + 1; + if (elements < stride) + { + stride = elements; + } + for (int i = 1; i < stride; ++i) + { + if (comparator.Compare(array[0], array[i]) > 0) + { + std::swap(array[0], + array[i]); + } + } + + for (int i = 1; i < elements; ++i) + { + int j = i; + const T tmp(array[i]); + for (; comparator.Compare(array[j - 1], tmp) > 0; --j) + { + assert(j > 0); + array[j] = array[j - 1]; + } + array[j] = tmp; + } +} + +/* +Maintaining comment due to attribution +Purpose: + +TRIANGLE_AREA_3D computes the area of a triangle in 3D. + +Modified: + +22 April 1999 + +Author: + +John Burkardt + +Parameters: + +Input, double X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, the (getX,getY,getZ) +coordinates of the corners of the triangle. + +Output, double TRIANGLE_AREA_3D, the area of the triangle. +*/ +double ComputeArea(const VHACD::Vect3& p1, + const VHACD::Vect3& p2, + const VHACD::Vect3& p3) +{ + /* + Find the projection of (P3-P1) onto (P2-P1). + */ + double base = (p2 - p1).GetNorm(); + /* + The height of the triangle is the length of (P3-P1) after its + projection onto (P2-P1) has been subtracted. + */ + double height; + if (base == double(0.0)) + { + height = double(0.0); + } + else + { + double dot = (p3 - p1).Dot(p2 - p1); + double alpha = dot / (base * base); + + VHACD::Vect3 a = p3 - p1 - alpha * (p2 - p1); + height = a.GetNorm(); + } + + return double(0.5) * base * height; +} + +bool ComputeCentroid(const std::vector& points, + const std::vector& indices, + VHACD::Vect3& center) + +{ + bool ret = false; + if (points.size()) + { + center = VHACD::Vect3(0); + + VHACD::Vect3 numerator(0); + double denominator = 0; + + for (uint32_t i = 0; i < indices.size(); i++) + { + uint32_t i1 = indices[i].mI0; + uint32_t i2 = indices[i].mI1; + uint32_t i3 = indices[i].mI2; + + const VHACD::Vect3& p1 = points[i1]; + const VHACD::Vect3& p2 = points[i2]; + const VHACD::Vect3& p3 = points[i3]; + + // Compute the average of the sum of the three positions + VHACD::Vect3 sum = (p1 + p2 + p3) / 3; + + // Compute the area of this triangle + double area = ComputeArea(p1, + p2, + p3); + + numerator += (sum * area); + + denominator += area; + } + double recip = 1 / denominator; + center = numerator * recip; + ret = true; + } + return ret; +} + +double Determinant3x3(const std::array& matrix, + double& error) +{ + double det = double(0.0); + error = double(0.0); + + double a01xa12 = matrix[0].GetY() * matrix[1].GetZ(); + double a02xa11 = matrix[0].GetZ() * matrix[1].GetY(); + error += (std::abs(a01xa12) + std::abs(a02xa11)) * std::abs(matrix[2].GetX()); + det += (a01xa12 - a02xa11) * matrix[2].GetX(); + + double a00xa12 = matrix[0].GetX() * matrix[1].GetZ(); + double a02xa10 = matrix[0].GetZ() * matrix[1].GetX(); + error += (std::abs(a00xa12) + std::abs(a02xa10)) * std::abs(matrix[2].GetY()); + det -= (a00xa12 - a02xa10) * matrix[2].GetY(); + + double a00xa11 = matrix[0].GetX() * matrix[1].GetY(); + double a01xa10 = matrix[0].GetY() * matrix[1].GetX(); + error += (std::abs(a00xa11) + std::abs(a01xa10)) * std::abs(matrix[2].GetZ()); + det += (a00xa11 - a01xa10) * matrix[2].GetZ(); + + return det; +} + +double ComputeMeshVolume(const std::vector& vertices, + const std::vector& indices) +{ + double volume = 0; + for (uint32_t i = 0; i < indices.size(); i++) + { + const std::array m = { + vertices[indices[i].mI0], + vertices[indices[i].mI1], + vertices[indices[i].mI2] + }; + double placeholder; + volume += Determinant3x3(m, + placeholder); + } + + volume *= (double(1.0) / double(6.0)); + if (volume < 0) + volume *= -1; + return volume; +} + +/* + * To minimize memory allocations while maintaining pointer stability. + * Used in KdTreeNode and ConvexHull, as both use tree data structures that rely on pointer stability + * Neither rely on random access or iteration + * They just dump elements into a memory pool, then refer to pointers to the elements + * All elements are default constructed in NodeStorage's m_nodes array + */ +template +class NodeBundle +{ + struct NodeStorage { + bool IsFull() const; + + T& GetNextNode(); + + std::size_t m_index; + std::array m_nodes; + }; + + std::list m_list; + typename std::list::iterator m_head{ m_list.end() }; + +public: + T& GetNextNode(); + + T& GetFirstNode(); + + void Clear(); +}; + +template +bool NodeBundle::NodeStorage::IsFull() const +{ + return m_index == MaxBundleSize; +} + +template +T& NodeBundle::NodeStorage::GetNextNode() +{ + assert(m_index < MaxBundleSize); + T& ret = m_nodes[m_index]; + m_index++; + return ret; +} + +template +T& NodeBundle::GetNextNode() +{ + /* + * || short circuits, so doesn't dereference if m_bundle == m_bundleHead.end() + */ + if ( m_head == m_list.end() + || m_head->IsFull()) + { + m_head = m_list.emplace(m_list.end()); + } + + return m_head->GetNextNode(); +} + +template +T& NodeBundle::GetFirstNode() +{ + assert(m_head != m_list.end()); + return m_list.front().m_nodes[0]; +} + +template +void NodeBundle::Clear() +{ + m_list.clear(); +} + +/* + * Returns index of highest set bit in x + */ +inline int dExp2(int x) +{ + int exp; + for (exp = -1; x; x >>= 1) + { + exp++; + } + return exp; +} + +/* + * Reverses the order of the bits in v and returns the result + * Does not put fill any of the bits higher than the highest bit in v + * Only used to calculate index of ndNormalMap::m_normal when tessellating a triangle + */ +inline int dBitReversal(int v, + int base) +{ + int x = 0; + int power = dExp2(base) - 1; + do + { + x += (v & 1) << power; + v >>= 1; + power--; + } while (v); + return x; +} + +class Googol +{ + #define VHACD_GOOGOL_SIZE 4 +public: + Googol() = default; + Googol(double value); + + operator double() const; + Googol operator+(const Googol &A) const; + Googol operator-(const Googol &A) const; + Googol operator*(const Googol &A) const; + Googol operator/ (const Googol &A) const; + + Googol& operator+= (const Googol &A); + Googol& operator-= (const Googol &A); + + bool operator>(const Googol &A) const; + bool operator>=(const Googol &A) const; + bool operator<(const Googol &A) const; + bool operator<=(const Googol &A) const; + bool operator==(const Googol &A) const; + bool operator!=(const Googol &A) const; + + Googol Abs() const; + Googol Floor() const; + Googol InvSqrt() const; + Googol Sqrt() const; + + void ToString(char* const string) const; + +private: + void NegateMantissa(std::array& mantissa) const; + void CopySignedMantissa(std::array& mantissa) const; + int NormalizeMantissa(std::array& mantissa) const; + void ShiftRightMantissa(std::array& mantissa, + int bits) const; + uint64_t CheckCarrier(uint64_t a, uint64_t b) const; + + int LeadingZeros(uint64_t a) const; + void ExtendedMultiply(uint64_t a, + uint64_t b, + uint64_t& high, + uint64_t& low) const; + void ScaleMantissa(uint64_t* out, + uint64_t scale) const; + + int m_sign{ 0 }; + int m_exponent{ 0 }; + std::array m_mantissa{ 0 }; + +public: + static Googol m_zero; + static Googol m_one; + static Googol m_two; + static Googol m_three; + static Googol m_half; +}; + +Googol Googol::m_zero(double(0.0)); +Googol Googol::m_one(double(1.0)); +Googol Googol::m_two(double(2.0)); +Googol Googol::m_three(double(3.0)); +Googol Googol::m_half(double(0.5)); + +Googol::Googol(double value) +{ + int exp; + double mantissa = fabs(frexp(value, &exp)); + + m_exponent = exp; + m_sign = (value >= 0) ? 0 : 1; + + m_mantissa[0] = uint64_t(double(uint64_t(1) << 62) * mantissa); +} + +Googol::operator double() const +{ + double mantissa = (double(1.0) / double(uint64_t(1) << 62)) * double(m_mantissa[0]); + mantissa = ldexp(mantissa, m_exponent) * (m_sign ? double(-1.0) : double(1.0)); + return mantissa; +} + +Googol Googol::operator+(const Googol &A) const +{ + Googol tmp; + if (m_mantissa[0] && A.m_mantissa[0]) + { + std::array mantissa0; + std::array mantissa1; + std::array mantissa; + + CopySignedMantissa(mantissa0); + A.CopySignedMantissa(mantissa1); + + int exponentDiff = m_exponent - A.m_exponent; + int exponent = m_exponent; + if (exponentDiff > 0) + { + ShiftRightMantissa(mantissa1, + exponentDiff); + } + else if (exponentDiff < 0) + { + exponent = A.m_exponent; + ShiftRightMantissa(mantissa0, + -exponentDiff); + } + + uint64_t carrier = 0; + for (int i = VHACD_GOOGOL_SIZE - 1; i >= 0; i--) + { + uint64_t m0 = mantissa0[i]; + uint64_t m1 = mantissa1[i]; + mantissa[i] = m0 + m1 + carrier; + carrier = CheckCarrier(m0, m1) | CheckCarrier(m0 + m1, carrier); + } + + int sign = 0; + if (int64_t(mantissa[0]) < 0) + { + sign = 1; + NegateMantissa(mantissa); + } + + int bits = NormalizeMantissa(mantissa); + if (bits <= (-64 * VHACD_GOOGOL_SIZE)) + { + tmp.m_sign = 0; + tmp.m_exponent = 0; + } + else + { + tmp.m_sign = sign; + tmp.m_exponent = int(exponent + bits); + } + + tmp.m_mantissa = mantissa; + } + else if (A.m_mantissa[0]) + { + tmp = A; + } + else + { + tmp = *this; + } + + return tmp; +} + +Googol Googol::operator-(const Googol &A) const +{ + Googol tmp(A); + tmp.m_sign = !tmp.m_sign; + return *this + tmp; +} + +Googol Googol::operator*(const Googol &A) const +{ + if (m_mantissa[0] && A.m_mantissa[0]) + { + std::array mantissaAcc{ 0 }; + for (int i = VHACD_GOOGOL_SIZE - 1; i >= 0; i--) + { + uint64_t a = m_mantissa[i]; + if (a) + { + uint64_t mantissaScale[2 * VHACD_GOOGOL_SIZE] = { 0 }; + A.ScaleMantissa(&mantissaScale[i], a); + + uint64_t carrier = 0; + for (int j = 0; j < 2 * VHACD_GOOGOL_SIZE; j++) + { + const int k = 2 * VHACD_GOOGOL_SIZE - 1 - j; + uint64_t m0 = mantissaAcc[k]; + uint64_t m1 = mantissaScale[k]; + mantissaAcc[k] = m0 + m1 + carrier; + carrier = CheckCarrier(m0, m1) | CheckCarrier(m0 + m1, carrier); + } + } + } + + uint64_t carrier = 0; + int bits = LeadingZeros(mantissaAcc[0]) - 2; + for (int i = 0; i < 2 * VHACD_GOOGOL_SIZE; i++) + { + const int k = 2 * VHACD_GOOGOL_SIZE - 1 - i; + uint64_t a = mantissaAcc[k]; + mantissaAcc[k] = (a << uint64_t(bits)) | carrier; + carrier = a >> uint64_t(64 - bits); + } + + int exp = m_exponent + A.m_exponent - (bits - 2); + + Googol tmp; + tmp.m_sign = m_sign ^ A.m_sign; + tmp.m_exponent = exp; + for (std::size_t i = 0; i < tmp.m_mantissa.size(); ++i) + { + tmp.m_mantissa[i] = mantissaAcc[i]; + } + + return tmp; + } + return Googol(double(0.0)); +} + +Googol Googol::operator/(const Googol &A) const +{ + Googol tmp(double(1.0) / A); + tmp = tmp * (m_two - A * tmp); + tmp = tmp * (m_two - A * tmp); + bool test = false; + int passes = 0; + do + { + passes++; + Googol tmp0(tmp); + tmp = tmp * (m_two - A * tmp); + test = tmp0 == tmp; + } while (test && (passes < (2 * VHACD_GOOGOL_SIZE))); + return (*this) * tmp; +} + +Googol& Googol::operator+=(const Googol &A) +{ + *this = *this + A; + return *this; +} + +Googol& Googol::operator-=(const Googol &A) +{ + *this = *this - A; + return *this; +} + +bool Googol::operator>(const Googol &A) const +{ + Googol tmp(*this - A); + return double(tmp) > double(0.0); +} + +bool Googol::operator>=(const Googol &A) const +{ + Googol tmp(*this - A); + return double(tmp) >= double(0.0); +} + +bool Googol::operator<(const Googol &A) const +{ + Googol tmp(*this - A); + return double(tmp) < double(0.0); +} + +bool Googol::operator<=(const Googol &A) const +{ + Googol tmp(*this - A); + return double(tmp) <= double(0.0); +} + +bool Googol::operator==(const Googol &A) const +{ + return m_sign == A.m_sign + && m_exponent == A.m_exponent + && m_mantissa == A.m_mantissa; +} + +bool Googol::operator!=(const Googol &A) const +{ + return !(*this == A); +} + +Googol Googol::Abs() const +{ + Googol tmp(*this); + tmp.m_sign = 0; + return tmp; +} + +Googol Googol::Floor() const +{ + if (m_exponent < 1) + { + return Googol(double(0.0)); + } + int bits = m_exponent + 2; + int start = 0; + while (bits >= 64) + { + bits -= 64; + start++; + } + + Googol tmp(*this); + for (int i = VHACD_GOOGOL_SIZE - 1; i > start; i--) + { + tmp.m_mantissa[i] = 0; + } + // some compilers do no like this and I do not know why is that + //uint64_t mask = (-1LL) << (64 - bits); + uint64_t mask(~0ULL); + mask <<= (64 - bits); + tmp.m_mantissa[start] &= mask; + return tmp; +} + +Googol Googol::InvSqrt() const +{ + const Googol& me = *this; + Googol x(double(1.0) / sqrt(me)); + + int test = 0; + int passes = 0; + do + { + passes++; + Googol tmp(x); + x = m_half * x * (m_three - me * x * x); + test = (x != tmp); + } while (test && (passes < (2 * VHACD_GOOGOL_SIZE))); + return x; +} + +Googol Googol::Sqrt() const +{ + return *this * InvSqrt(); +} + +void Googol::ToString(char* const string) const +{ + Googol tmp(*this); + Googol base(double(10.0)); + while (double(tmp) > double(1.0)) + { + tmp = tmp / base; + } + + int index = 0; + while (tmp.m_mantissa[0]) + { + tmp = tmp * base; + Googol digit(tmp.Floor()); + tmp -= digit; + double val = digit; + string[index] = char(val) + '0'; + index++; + } + string[index] = 0; +} + +void Googol::NegateMantissa(std::array& mantissa) const +{ + uint64_t carrier = 1; + for (size_t i = mantissa.size() - 1; i < mantissa.size(); i--) + { + uint64_t a = ~mantissa[i] + carrier; + if (a) + { + carrier = 0; + } + mantissa[i] = a; + } +} + +void Googol::CopySignedMantissa(std::array& mantissa) const +{ + mantissa = m_mantissa; + if (m_sign) + { + NegateMantissa(mantissa); + } +} + +int Googol::NormalizeMantissa(std::array& mantissa) const +{ + int bits = 0; + if (int64_t(mantissa[0] * 2) < 0) + { + bits = 1; + ShiftRightMantissa(mantissa, 1); + } + else + { + while (!mantissa[0] && bits > (-64 * VHACD_GOOGOL_SIZE)) + { + bits -= 64; + for (int i = 1; i < VHACD_GOOGOL_SIZE; i++) { + mantissa[i - 1] = mantissa[i]; + } + mantissa[VHACD_GOOGOL_SIZE - 1] = 0; + } + + if (bits > (-64 * VHACD_GOOGOL_SIZE)) + { + int n = LeadingZeros(mantissa[0]) - 2; + if (n > 0) + { + uint64_t carrier = 0; + for (int i = VHACD_GOOGOL_SIZE - 1; i >= 0; i--) + { + uint64_t a = mantissa[i]; + mantissa[i] = (a << n) | carrier; + carrier = a >> (64 - n); + } + bits -= n; + } + else if (n < 0) + { + // this is very rare but it does happens, whee the leading zeros of the mantissa is an exact multiple of 64 + uint64_t carrier = 0; + int shift = -n; + for (int i = 0; i < VHACD_GOOGOL_SIZE; i++) + { + uint64_t a = mantissa[i]; + mantissa[i] = (a >> shift) | carrier; + carrier = a << (64 - shift); + } + bits -= n; + } + } + } + return bits; +} + +void Googol::ShiftRightMantissa(std::array& mantissa, + int bits) const +{ + uint64_t carrier = 0; + if (int64_t(mantissa[0]) < int64_t(0)) + { + carrier = uint64_t(-1); + } + + while (bits >= 64) + { + for (int i = VHACD_GOOGOL_SIZE - 2; i >= 0; i--) + { + mantissa[i + 1] = mantissa[i]; + } + mantissa[0] = carrier; + bits -= 64; + } + + if (bits > 0) + { + carrier <<= (64 - bits); + for (int i = 0; i < VHACD_GOOGOL_SIZE; i++) + { + uint64_t a = mantissa[i]; + mantissa[i] = (a >> bits) | carrier; + carrier = a << (64 - bits); + } + } +} + +uint64_t Googol::CheckCarrier(uint64_t a, uint64_t b) const +{ + return ((uint64_t(-1) - b) < a) ? uint64_t(1) : 0; +} + +int Googol::LeadingZeros(uint64_t a) const +{ + #define VHACD_COUNTBIT(mask, add) \ + do { \ + uint64_t test = a & mask; \ + n += test ? 0 : add; \ + a = test ? test : (a & ~mask); \ + } while (false) + + int n = 0; + VHACD_COUNTBIT(0xffffffff00000000LL, 32); + VHACD_COUNTBIT(0xffff0000ffff0000LL, 16); + VHACD_COUNTBIT(0xff00ff00ff00ff00LL, 8); + VHACD_COUNTBIT(0xf0f0f0f0f0f0f0f0LL, 4); + VHACD_COUNTBIT(0xccccccccccccccccLL, 2); + VHACD_COUNTBIT(0xaaaaaaaaaaaaaaaaLL, 1); + + return n; +} + +void Googol::ExtendedMultiply(uint64_t a, + uint64_t b, + uint64_t& high, + uint64_t& low) const +{ + uint64_t bLow = b & 0xffffffff; + uint64_t bHigh = b >> 32; + uint64_t aLow = a & 0xffffffff; + uint64_t aHigh = a >> 32; + + uint64_t l = bLow * aLow; + + uint64_t c1 = bHigh * aLow; + uint64_t c2 = bLow * aHigh; + uint64_t m = c1 + c2; + uint64_t carrier = CheckCarrier(c1, c2) << 32; + + uint64_t h = bHigh * aHigh + carrier; + + uint64_t ml = m << 32; + uint64_t ll = l + ml; + uint64_t mh = (m >> 32) + CheckCarrier(l, ml); + uint64_t hh = h + mh; + + low = ll; + high = hh; +} + +void Googol::ScaleMantissa(uint64_t* dst, + uint64_t scale) const +{ + uint64_t carrier = 0; + for (int i = VHACD_GOOGOL_SIZE - 1; i >= 0; i--) + { + if (m_mantissa[i]) + { + uint64_t low; + uint64_t high; + ExtendedMultiply(scale, + m_mantissa[i], + high, + low); + uint64_t acc = low + carrier; + carrier = CheckCarrier(low, + carrier); + carrier += high; + dst[i + 1] = acc; + } + else + { + dst[i + 1] = carrier; + carrier = 0; + } + + } + dst[0] = carrier; +} + +Googol Determinant3x3(const std::array, 3>& matrix) +{ + Googol det = double(0.0); + + Googol a01xa12 = matrix[0].GetY() * matrix[1].GetZ(); + Googol a02xa11 = matrix[0].GetZ() * matrix[1].GetY(); + det += (a01xa12 - a02xa11) * matrix[2].GetX(); + + Googol a00xa12 = matrix[0].GetX() * matrix[1].GetZ(); + Googol a02xa10 = matrix[0].GetZ() * matrix[1].GetX(); + det -= (a00xa12 - a02xa10) * matrix[2].GetY(); + + Googol a00xa11 = matrix[0].GetX() * matrix[1].GetY(); + Googol a01xa10 = matrix[0].GetY() * matrix[1].GetX(); + det += (a00xa11 - a01xa10) * matrix[2].GetZ(); + return det; +} + +class HullPlane : public VHACD::Vect3 +{ +public: + HullPlane(const HullPlane&) = default; + HullPlane(double x, + double y, + double z, + double w); + + HullPlane(const VHACD::Vect3& p, + double w); + + HullPlane(const VHACD::Vect3& p0, + const VHACD::Vect3& p1, + const VHACD::Vect3& p2); + + HullPlane Scale(double s) const; + + HullPlane& operator=(const HullPlane& rhs); + + double Evalue(const VHACD::Vect3 &point) const; + + double& GetW(); + const double& GetW() const; + +private: + double m_w; +}; + +HullPlane::HullPlane(double x, + double y, + double z, + double w) + : VHACD::Vect3(x, y, z) + , m_w(w) +{ +} + +HullPlane::HullPlane(const VHACD::Vect3& p, + double w) + : VHACD::Vect3(p) + , m_w(w) +{ +} + +HullPlane::HullPlane(const VHACD::Vect3& p0, + const VHACD::Vect3& p1, + const VHACD::Vect3& p2) + : VHACD::Vect3((p1 - p0).Cross(p2 - p0)) + , m_w(-Dot(p0)) +{ +} + +HullPlane HullPlane::Scale(double s) const +{ + return HullPlane(*this * s, + m_w * s); +} + +HullPlane& HullPlane::operator=(const HullPlane& rhs) +{ + GetX() = rhs.GetX(); + GetY() = rhs.GetY(); + GetZ() = rhs.GetZ(); + m_w = rhs.m_w; + return *this; +} + +double HullPlane::Evalue(const VHACD::Vect3& point) const +{ + return Dot(point) + m_w; +} + +double& HullPlane::GetW() +{ + return m_w; +} + +const double& HullPlane::GetW() const +{ + return m_w; +} + +class ConvexHullFace +{ +public: + ConvexHullFace() = default; + double Evalue(const std::vector& pointArray, + const VHACD::Vect3& point) const; + HullPlane GetPlaneEquation(const std::vector& pointArray, + bool& isValid) const; + + std::array m_index; +private: + int m_mark{ 0 }; + std::array::iterator, 3> m_twin; + + friend class ConvexHull; +}; + +double ConvexHullFace::Evalue(const std::vector& pointArray, + const VHACD::Vect3& point) const +{ + const VHACD::Vect3& p0 = pointArray[m_index[0]]; + const VHACD::Vect3& p1 = pointArray[m_index[1]]; + const VHACD::Vect3& p2 = pointArray[m_index[2]]; + + std::array matrix = { p2 - p0, p1 - p0, point - p0 }; + double error; + double det = Determinant3x3(matrix, + error); + + // the code use double, however the threshold for accuracy test is the machine precision of a float. + // by changing this to a smaller number, the code should run faster since many small test will be considered valid + // the precision must be a power of two no smaller than the machine precision of a double, (1<<48) + // float64(1<<30) can be a good value + + // double precision = double (1.0f) / double (1<<30); + double precision = double(1.0) / double(1 << 24); + double errbound = error * precision; + if (fabs(det) > errbound) + { + return det; + } + + const VHACD::Vector3 p0g = pointArray[m_index[0]]; + const VHACD::Vector3 p1g = pointArray[m_index[1]]; + const VHACD::Vector3 p2g = pointArray[m_index[2]]; + const VHACD::Vector3 pointg = point; + std::array, 3> exactMatrix = { p2g - p0g, p1g - p0g, pointg - p0g }; + return Determinant3x3(exactMatrix); +} + +HullPlane ConvexHullFace::GetPlaneEquation(const std::vector& pointArray, + bool& isvalid) const +{ + const VHACD::Vect3& p0 = pointArray[m_index[0]]; + const VHACD::Vect3& p1 = pointArray[m_index[1]]; + const VHACD::Vect3& p2 = pointArray[m_index[2]]; + HullPlane plane(p0, p1, p2); + + isvalid = false; + double mag2 = plane.Dot(plane); + if (mag2 > double(1.0e-16)) + { + isvalid = true; + plane = plane.Scale(double(1.0) / sqrt(mag2)); + } + return plane; +} + +class ConvexHullVertex : public VHACD::Vect3 +{ +public: + ConvexHullVertex() = default; + ConvexHullVertex(const ConvexHullVertex&) = default; + ConvexHullVertex& operator=(const ConvexHullVertex& rhs) = default; + using VHACD::Vect3::operator=; + + int m_mark{ 0 }; +}; + + +class ConvexHullAABBTreeNode +{ + #define VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE 8 +public: + ConvexHullAABBTreeNode() = default; + ConvexHullAABBTreeNode(ConvexHullAABBTreeNode* parent); + + VHACD::Vect3 m_box[2]; + ConvexHullAABBTreeNode* m_left{ nullptr }; + ConvexHullAABBTreeNode* m_right{ nullptr }; + ConvexHullAABBTreeNode* m_parent{ nullptr }; + + size_t m_count; + std::array m_indices; +}; + +ConvexHullAABBTreeNode::ConvexHullAABBTreeNode(ConvexHullAABBTreeNode* parent) + : m_parent(parent) +{ +} + +class ConvexHull +{ + class ndNormalMap; + +public: + ConvexHull(const ConvexHull& source); + ConvexHull(const std::vector<::VHACD::Vertex>& vertexCloud, + double distTol, + int maxVertexCount = 0x7fffffff); + ~ConvexHull() = default; + + const std::vector& GetVertexPool() const; + + const std::list& GetList() const { return m_list; } + +private: + void BuildHull(const std::vector<::VHACD::Vertex>& vertexCloud, + double distTol, + int maxVertexCount); + + void GetUniquePoints(std::vector& points); + int InitVertexArray(std::vector& points, + NodeBundle& memoryPool); + + ConvexHullAABBTreeNode* BuildTreeNew(std::vector& points, + std::vector& memoryPool) const; + ConvexHullAABBTreeNode* BuildTreeOld(std::vector& points, + NodeBundle& memoryPool); + ConvexHullAABBTreeNode* BuildTreeRecurse(ConvexHullAABBTreeNode* const parent, + ConvexHullVertex* const points, + int count, + int baseIndex, + NodeBundle& memoryPool) const; + + std::list::iterator AddFace(int i0, + int i1, + int i2); + + void CalculateConvexHull3D(ConvexHullAABBTreeNode* vertexTree, + std::vector& points, + int count, + double distTol, + int maxVertexCount); + + int SupportVertex(ConvexHullAABBTreeNode** const tree, + const std::vector& points, + const VHACD::Vect3& dir, + const bool removeEntry = true) const; + double TetrahedrumVolume(const VHACD::Vect3& p0, + const VHACD::Vect3& p1, + const VHACD::Vect3& p2, + const VHACD::Vect3& p3) const; + + std::list m_list; + VHACD::Vect3 m_aabbP0{ 0 }; + VHACD::Vect3 m_aabbP1{ 0 }; + double m_diag{ 0.0 }; + std::vector m_points; +}; + +class ConvexHull::ndNormalMap +{ +public: + ndNormalMap(); + + static const ndNormalMap& GetNormalMap(); + + void TessellateTriangle(int level, + const VHACD::Vect3& p0, + const VHACD::Vect3& p1, + const VHACD::Vect3& p2, + int& count); + + std::array m_normal; + int m_count{ 128 }; +}; + +const ConvexHull::ndNormalMap& ConvexHull::ndNormalMap::GetNormalMap() +{ + static ndNormalMap normalMap; + return normalMap; +} + +void ConvexHull::ndNormalMap::TessellateTriangle(int level, + const VHACD::Vect3& p0, + const VHACD::Vect3& p1, + const VHACD::Vect3& p2, + int& count) +{ + if (level) + { + assert(fabs(p0.Dot(p0) - double(1.0)) < double(1.0e-4)); + assert(fabs(p1.Dot(p1) - double(1.0)) < double(1.0e-4)); + assert(fabs(p2.Dot(p2) - double(1.0)) < double(1.0e-4)); + VHACD::Vect3 p01(p0 + p1); + VHACD::Vect3 p12(p1 + p2); + VHACD::Vect3 p20(p2 + p0); + + p01 = p01 * (double(1.0) / p01.GetNorm()); + p12 = p12 * (double(1.0) / p12.GetNorm()); + p20 = p20 * (double(1.0) / p20.GetNorm()); + + assert(fabs(p01.GetNormSquared() - double(1.0)) < double(1.0e-4)); + assert(fabs(p12.GetNormSquared() - double(1.0)) < double(1.0e-4)); + assert(fabs(p20.GetNormSquared() - double(1.0)) < double(1.0e-4)); + + TessellateTriangle(level - 1, p0, p01, p20, count); + TessellateTriangle(level - 1, p1, p12, p01, count); + TessellateTriangle(level - 1, p2, p20, p12, count); + TessellateTriangle(level - 1, p01, p12, p20, count); + } + else + { + /* + * This is just m_normal[index] = n.Normalized(), but due to tiny floating point errors, causes + * different outputs, so I'm leaving it + */ + HullPlane n(p0, p1, p2); + n = n.Scale(double(1.0) / n.GetNorm()); + n.GetW() = double(0.0); + int index = dBitReversal(count, + int(m_normal.size())); + m_normal[index] = n; + count++; + assert(count <= int(m_normal.size())); + } +} + +ConvexHull::ndNormalMap::ndNormalMap() +{ + VHACD::Vect3 p0(double( 1.0), double( 0.0), double( 0.0)); + VHACD::Vect3 p1(double(-1.0), double( 0.0), double( 0.0)); + VHACD::Vect3 p2(double( 0.0), double( 1.0), double( 0.0)); + VHACD::Vect3 p3(double( 0.0), double(-1.0), double( 0.0)); + VHACD::Vect3 p4(double( 0.0), double( 0.0), double( 1.0)); + VHACD::Vect3 p5(double( 0.0), double( 0.0), double(-1.0)); + + int count = 0; + int subdivisions = 2; + TessellateTriangle(subdivisions, p4, p0, p2, count); + TessellateTriangle(subdivisions, p0, p5, p2, count); + TessellateTriangle(subdivisions, p5, p1, p2, count); + TessellateTriangle(subdivisions, p1, p4, p2, count); + TessellateTriangle(subdivisions, p0, p4, p3, count); + TessellateTriangle(subdivisions, p5, p0, p3, count); + TessellateTriangle(subdivisions, p1, p5, p3, count); + TessellateTriangle(subdivisions, p4, p1, p3, count); +} + +ConvexHull::ConvexHull(const std::vector<::VHACD::Vertex>& vertexCloud, + double distTol, + int maxVertexCount) +{ + if (vertexCloud.size() >= 4) + { + BuildHull(vertexCloud, + distTol, + maxVertexCount); + } +} + +const std::vector& ConvexHull::GetVertexPool() const +{ + return m_points; +} + +void ConvexHull::BuildHull(const std::vector<::VHACD::Vertex>& vertexCloud, + double distTol, + int maxVertexCount) +{ + size_t treeCount = vertexCloud.size() / (VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE >> 1); + treeCount = std::max(treeCount, size_t(4)) * 2; + + std::vector points(vertexCloud.size()); + /* + * treePool provides a memory pool for the AABB tree + * Each node is either a leaf or non-leaf node + * Non-leaf nodes have up to 8 vertices + * Vertices are specified by the m_indices array and are accessed via the points array + * + * Later on in ConvexHull::SupportVertex, the tree is used directly + * It differentiates between ConvexHullAABBTreeNode and ConvexHull3DPointCluster by whether the m_left and m_right + * pointers are null or not + * + * Pointers have to be stable + */ + NodeBundle treePool; + for (size_t i = 0; i < vertexCloud.size(); ++i) + { + points[i] = VHACD::Vect3(vertexCloud[i]); + } + int count = InitVertexArray(points, + treePool); + + if (m_points.size() >= 4) + { + CalculateConvexHull3D(&treePool.GetFirstNode(), + points, + count, + distTol, + maxVertexCount); + } +} + +void ConvexHull::GetUniquePoints(std::vector& points) +{ + class CompareVertex + { + public: + int Compare(const ConvexHullVertex& elementA, const ConvexHullVertex& elementB) const + { + for (int i = 0; i < 3; i++) + { + if (elementA[i] < elementB[i]) + { + return -1; + } + else if (elementA[i] > elementB[i]) + { + return 1; + } + } + return 0; + } + }; + + int count = int(points.size()); + Sort(points.data(), + count); + + int indexCount = 0; + CompareVertex compareVertex; + for (int i = 1; i < count; ++i) + { + for (; i < count; ++i) + { + if (compareVertex.Compare(points[indexCount], points[i])) + { + indexCount++; + points[indexCount] = points[i]; + break; + } + } + } + points.resize(indexCount + 1); +} + +ConvexHullAABBTreeNode* ConvexHull::BuildTreeRecurse(ConvexHullAABBTreeNode* const parent, + ConvexHullVertex* const points, + int count, + int baseIndex, + NodeBundle& memoryPool) const +{ + ConvexHullAABBTreeNode* tree = nullptr; + + assert(count); + VHACD::Vect3 minP( double(1.0e15)); + VHACD::Vect3 maxP(-double(1.0e15)); + if (count <= VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE) + { + ConvexHullAABBTreeNode& clump = memoryPool.GetNextNode(); + + clump.m_count = count; + for (int i = 0; i < count; ++i) + { + clump.m_indices[i] = i + baseIndex; + + const VHACD::Vect3& p = points[i]; + minP = minP.CWiseMin(p); + maxP = maxP.CWiseMax(p); + } + + clump.m_left = nullptr; + clump.m_right = nullptr; + tree = &clump; + } + else + { + VHACD::Vect3 median(0); + VHACD::Vect3 varian(0); + for (int i = 0; i < count; ++i) + { + const VHACD::Vect3& p = points[i]; + minP = minP.CWiseMin(p); + maxP = maxP.CWiseMax(p); + median += p; + varian += p.CWiseMul(p); + } + + varian = varian * double(count) - median.CWiseMul(median); + int index = 0; + double maxVarian = double(-1.0e10); + for (int i = 0; i < 3; ++i) + { + if (varian[i] > maxVarian) + { + index = i; + maxVarian = varian[i]; + } + } + VHACD::Vect3 center(median * (double(1.0) / double(count))); + + double test = center[index]; + + int i0 = 0; + int i1 = count - 1; + do + { + for (; i0 <= i1; i0++) + { + double val = points[i0][index]; + if (val > test) + { + break; + } + } + + for (; i1 >= i0; i1--) + { + double val = points[i1][index]; + if (val < test) + { + break; + } + } + + if (i0 < i1) + { + std::swap(points[i0], + points[i1]); + i0++; + i1--; + } + } while (i0 <= i1); + + if (i0 == 0) + { + i0 = count / 2; + } + if (i0 >= (count - 1)) + { + i0 = count / 2; + } + + tree = &memoryPool.GetNextNode(); + + assert(i0); + assert(count - i0); + + tree->m_left = BuildTreeRecurse(tree, + points, + i0, + baseIndex, + memoryPool); + tree->m_right = BuildTreeRecurse(tree, + &points[i0], + count - i0, + i0 + baseIndex, + memoryPool); + } + + assert(tree); + tree->m_parent = parent; + /* + * WARNING: Changing the compiler conversion of 1.0e-3f changes the results of the convex decomposition + * Inflate the tree's bounding box slightly + */ + tree->m_box[0] = minP - VHACD::Vect3(double(1.0e-3f)); + tree->m_box[1] = maxP + VHACD::Vect3(double(1.0e-3f)); + return tree; +} + +ConvexHullAABBTreeNode* ConvexHull::BuildTreeOld(std::vector& points, + NodeBundle& memoryPool) +{ + GetUniquePoints(points); + int count = int(points.size()); + if (count < 4) + { + return nullptr; + } + return BuildTreeRecurse(nullptr, + points.data(), + count, + 0, + memoryPool); +} + +ConvexHullAABBTreeNode* ConvexHull::BuildTreeNew(std::vector& points, + std::vector& memoryPool) const +{ + class dCluster + { + public: + VHACD::Vect3 m_sum{ double(0.0) }; + VHACD::Vect3 m_sum2{ double(0.0) }; + int m_start{ 0 }; + int m_count{ 0 }; + }; + + dCluster firstCluster; + firstCluster.m_count = int(points.size()); + + for (int i = 0; i < firstCluster.m_count; ++i) + { + const VHACD::Vect3& p = points[i]; + firstCluster.m_sum += p; + firstCluster.m_sum2 += p.CWiseMul(p); + } + + int baseCount = 0; + const int clusterSize = 16; + + if (firstCluster.m_count > clusterSize) + { + dCluster spliteStack[128]; + spliteStack[0] = firstCluster; + size_t stack = 1; + + while (stack) + { + stack--; + dCluster cluster (spliteStack[stack]); + + const VHACD::Vect3 origin(cluster.m_sum * (double(1.0) / cluster.m_count)); + const VHACD::Vect3 variance2(cluster.m_sum2 * (double(1.0) / cluster.m_count) - origin.CWiseMul(origin)); + double maxVariance2 = variance2.MaxCoeff(); + + if ( (cluster.m_count <= clusterSize) + || (stack > (sizeof(spliteStack) / sizeof(spliteStack[0]) - 4)) + || (maxVariance2 < 1.e-4f)) + { + // no sure if this is beneficial, + // the array is so small that seem too much overhead + //int maxIndex = 0; + //double min_x = 1.0e20f; + //for (int i = 0; i < cluster.m_count; ++i) + //{ + // if (points[cluster.m_start + i].getX() < min_x) + // { + // maxIndex = i; + // min_x = points[cluster.m_start + i].getX(); + // } + //} + //Swap(points[cluster.m_start], points[cluster.m_start + maxIndex]); + // + //for (int i = 2; i < cluster.m_count; ++i) + //{ + // int j = i; + // ConvexHullVertex tmp(points[cluster.m_start + i]); + // for (; points[cluster.m_start + j - 1].getX() > tmp.getX(); --j) + // { + // assert(j > 0); + // points[cluster.m_start + j] = points[cluster.m_start + j - 1]; + // } + // points[cluster.m_start + j] = tmp; + //} + + int count = cluster.m_count; + for (int i = cluster.m_count - 1; i > 0; --i) + { + for (int j = i - 1; j >= 0; --j) + { + VHACD::Vect3 error(points[cluster.m_start + j] - points[cluster.m_start + i]); + double mag2 = error.Dot(error); + if (mag2 < double(1.0e-6)) + { + points[cluster.m_start + j] = points[cluster.m_start + i]; + count--; + break; + } + } + } + + assert(baseCount <= cluster.m_start); + for (int i = 0; i < count; ++i) + { + points[baseCount] = points[cluster.m_start + i]; + baseCount++; + } + } + else + { + const int firstSortAxis = variance2.LongestAxis(); + double axisVal = origin[firstSortAxis]; + + int i0 = 0; + int i1 = cluster.m_count - 1; + + const int start = cluster.m_start; + while (i0 < i1) + { + while ( (points[start + i0][firstSortAxis] <= axisVal) + && (i0 < i1)) + { + ++i0; + }; + + while ( (points[start + i1][firstSortAxis] > axisVal) + && (i0 < i1)) + { + --i1; + } + + assert(i0 <= i1); + if (i0 < i1) + { + std::swap(points[start + i0], + points[start + i1]); + ++i0; + --i1; + } + } + + while ( (points[start + i0][firstSortAxis] <= axisVal) + && (i0 < cluster.m_count)) + { + ++i0; + }; + + #ifdef _DEBUG + for (int i = 0; i < i0; ++i) + { + assert(points[start + i][firstSortAxis] <= axisVal); + } + + for (int i = i0; i < cluster.m_count; ++i) + { + assert(points[start + i][firstSortAxis] > axisVal); + } + #endif + + VHACD::Vect3 xc(0); + VHACD::Vect3 x2c(0); + for (int i = 0; i < i0; ++i) + { + const VHACD::Vect3& x = points[start + i]; + xc += x; + x2c += x.CWiseMul(x); + } + + dCluster cluster_i1(cluster); + cluster_i1.m_start = start + i0; + cluster_i1.m_count = cluster.m_count - i0; + cluster_i1.m_sum -= xc; + cluster_i1.m_sum2 -= x2c; + spliteStack[stack] = cluster_i1; + assert(cluster_i1.m_count > 0); + stack++; + + dCluster cluster_i0(cluster); + cluster_i0.m_start = start; + cluster_i0.m_count = i0; + cluster_i0.m_sum = xc; + cluster_i0.m_sum2 = x2c; + assert(cluster_i0.m_count > 0); + spliteStack[stack] = cluster_i0; + stack++; + } + } + } + + points.resize(baseCount); + if (baseCount < 4) + { + return nullptr; + } + + VHACD::Vect3 sum(0); + VHACD::Vect3 sum2(0); + VHACD::Vect3 minP(double( 1.0e15)); + VHACD::Vect3 maxP(double(-1.0e15)); + class dTreeBox + { + public: + VHACD::Vect3 m_min; + VHACD::Vect3 m_max; + VHACD::Vect3 m_sum; + VHACD::Vect3 m_sum2; + ConvexHullAABBTreeNode* m_parent; + ConvexHullAABBTreeNode** m_child; + int m_start; + int m_count; + }; + + for (int i = 0; i < baseCount; ++i) + { + const VHACD::Vect3& p = points[i]; + sum += p; + sum2 += p.CWiseMul(p); + minP = minP.CWiseMin(p); + maxP = maxP.CWiseMax(p); + } + + dTreeBox treeBoxStack[128]; + treeBoxStack[0].m_start = 0; + treeBoxStack[0].m_count = baseCount; + treeBoxStack[0].m_sum = sum; + treeBoxStack[0].m_sum2 = sum2; + treeBoxStack[0].m_min = minP; + treeBoxStack[0].m_max = maxP; + treeBoxStack[0].m_child = nullptr; + treeBoxStack[0].m_parent = nullptr; + + int stack = 1; + ConvexHullAABBTreeNode* root = nullptr; + while (stack) + { + stack--; + dTreeBox box(treeBoxStack[stack]); + if (box.m_count <= VHACD_CONVEXHULL_3D_VERTEX_CLUSTER_SIZE) + { + assert(memoryPool.size() != memoryPool.capacity() + && "memoryPool is going to be reallocated, pointers will be invalid"); + memoryPool.emplace_back(); + ConvexHullAABBTreeNode& clump = memoryPool.back(); + + clump.m_count = box.m_count; + for (int i = 0; i < box.m_count; ++i) + { + clump.m_indices[i] = i + box.m_start; + } + clump.m_box[0] = box.m_min; + clump.m_box[1] = box.m_max; + + if (box.m_child) + { + *box.m_child = &clump; + } + + if (!root) + { + root = &clump; + } + } + else + { + const VHACD::Vect3 origin(box.m_sum * (double(1.0) / box.m_count)); + const VHACD::Vect3 variance2(box.m_sum2 * (double(1.0) / box.m_count) - origin.CWiseMul(origin)); + + int firstSortAxis = 0; + if ((variance2.GetY() >= variance2.GetX()) && (variance2.GetY() >= variance2.GetZ())) + { + firstSortAxis = 1; + } + else if ((variance2.GetZ() >= variance2.GetX()) && (variance2.GetZ() >= variance2.GetY())) + { + firstSortAxis = 2; + } + double axisVal = origin[firstSortAxis]; + + int i0 = 0; + int i1 = box.m_count - 1; + + const int start = box.m_start; + while (i0 < i1) + { + while ((points[start + i0][firstSortAxis] <= axisVal) && (i0 < i1)) + { + ++i0; + }; + + while ((points[start + i1][firstSortAxis] > axisVal) && (i0 < i1)) + { + --i1; + } + + assert(i0 <= i1); + if (i0 < i1) + { + std::swap(points[start + i0], + points[start + i1]); + ++i0; + --i1; + } + } + + while ((points[start + i0][firstSortAxis] <= axisVal) && (i0 < box.m_count)) + { + ++i0; + }; + + #ifdef _DEBUG + for (int i = 0; i < i0; ++i) + { + assert(points[start + i][firstSortAxis] <= axisVal); + } + + for (int i = i0; i < box.m_count; ++i) + { + assert(points[start + i][firstSortAxis] > axisVal); + } + #endif + + assert(memoryPool.size() != memoryPool.capacity() + && "memoryPool is going to be reallocated, pointers will be invalid"); + memoryPool.emplace_back(); + ConvexHullAABBTreeNode& node = memoryPool.back(); + + node.m_box[0] = box.m_min; + node.m_box[1] = box.m_max; + if (box.m_child) + { + *box.m_child = &node; + } + + if (!root) + { + root = &node; + } + + { + VHACD::Vect3 xc(0); + VHACD::Vect3 x2c(0); + VHACD::Vect3 p0(double( 1.0e15)); + VHACD::Vect3 p1(double(-1.0e15)); + for (int i = i0; i < box.m_count; ++i) + { + const VHACD::Vect3& p = points[start + i]; + xc += p; + x2c += p.CWiseMul(p); + p0 = p0.CWiseMin(p); + p1 = p1.CWiseMax(p); + } + + dTreeBox cluster_i1(box); + cluster_i1.m_start = start + i0; + cluster_i1.m_count = box.m_count - i0; + cluster_i1.m_sum = xc; + cluster_i1.m_sum2 = x2c; + cluster_i1.m_min = p0; + cluster_i1.m_max = p1; + cluster_i1.m_parent = &node; + cluster_i1.m_child = &node.m_right; + treeBoxStack[stack] = cluster_i1; + assert(cluster_i1.m_count > 0); + stack++; + } + + { + VHACD::Vect3 xc(0); + VHACD::Vect3 x2c(0); + VHACD::Vect3 p0(double( 1.0e15)); + VHACD::Vect3 p1(double(-1.0e15)); + for (int i = 0; i < i0; ++i) + { + const VHACD::Vect3& p = points[start + i]; + xc += p; + x2c += p.CWiseMul(p); + p0 = p0.CWiseMin(p); + p1 = p1.CWiseMax(p); + } + + dTreeBox cluster_i0(box); + cluster_i0.m_start = start; + cluster_i0.m_count = i0; + cluster_i0.m_min = p0; + cluster_i0.m_max = p1; + cluster_i0.m_sum = xc; + cluster_i0.m_sum2 = x2c; + cluster_i0.m_parent = &node; + cluster_i0.m_child = &node.m_left; + assert(cluster_i0.m_count > 0); + treeBoxStack[stack] = cluster_i0; + stack++; + } + } + } + + return root; +} + +int ConvexHull::SupportVertex(ConvexHullAABBTreeNode** const treePointer, + const std::vector& points, + const VHACD::Vect3& dirPlane, + const bool removeEntry) const +{ +#define VHACD_STACK_DEPTH_3D 64 + double aabbProjection[VHACD_STACK_DEPTH_3D]; + ConvexHullAABBTreeNode* stackPool[VHACD_STACK_DEPTH_3D]; + + VHACD::Vect3 dir(dirPlane); + + int index = -1; + int stack = 1; + stackPool[0] = *treePointer; + aabbProjection[0] = double(1.0e20); + double maxProj = double(-1.0e20); + int ix = (dir[0] > double(0.0)) ? 1 : 0; + int iy = (dir[1] > double(0.0)) ? 1 : 0; + int iz = (dir[2] > double(0.0)) ? 1 : 0; + while (stack) + { + stack--; + double boxSupportValue = aabbProjection[stack]; + if (boxSupportValue > maxProj) + { + ConvexHullAABBTreeNode* me = stackPool[stack]; + + /* + * If the node is not a leaf node... + */ + if (me->m_left && me->m_right) + { + const VHACD::Vect3 leftSupportPoint(me->m_left->m_box[ix].GetX(), + me->m_left->m_box[iy].GetY(), + me->m_left->m_box[iz].GetZ()); + double leftSupportDist = leftSupportPoint.Dot(dir); + + const VHACD::Vect3 rightSupportPoint(me->m_right->m_box[ix].GetX(), + me->m_right->m_box[iy].GetY(), + me->m_right->m_box[iz].GetZ()); + double rightSupportDist = rightSupportPoint.Dot(dir); + + /* + * ...push the shorter side first + * So we can explore the tree in the larger side first + */ + if (rightSupportDist >= leftSupportDist) + { + aabbProjection[stack] = leftSupportDist; + stackPool[stack] = me->m_left; + stack++; + assert(stack < VHACD_STACK_DEPTH_3D); + aabbProjection[stack] = rightSupportDist; + stackPool[stack] = me->m_right; + stack++; + assert(stack < VHACD_STACK_DEPTH_3D); + } + else + { + aabbProjection[stack] = rightSupportDist; + stackPool[stack] = me->m_right; + stack++; + assert(stack < VHACD_STACK_DEPTH_3D); + aabbProjection[stack] = leftSupportDist; + stackPool[stack] = me->m_left; + stack++; + assert(stack < VHACD_STACK_DEPTH_3D); + } + } + /* + * If it is a node... + */ + else + { + ConvexHullAABBTreeNode* cluster = me; + for (size_t i = 0; i < cluster->m_count; ++i) + { + const ConvexHullVertex& p = points[cluster->m_indices[i]]; + assert(p.GetX() >= cluster->m_box[0].GetX()); + assert(p.GetX() <= cluster->m_box[1].GetX()); + assert(p.GetY() >= cluster->m_box[0].GetY()); + assert(p.GetY() <= cluster->m_box[1].GetY()); + assert(p.GetZ() >= cluster->m_box[0].GetZ()); + assert(p.GetZ() <= cluster->m_box[1].GetZ()); + if (!p.m_mark) + { + //assert(p.m_w == double(0.0f)); + double dist = p.Dot(dir); + if (dist > maxProj) + { + maxProj = dist; + index = cluster->m_indices[i]; + } + } + else if (removeEntry) + { + cluster->m_indices[i] = cluster->m_indices[cluster->m_count - 1]; + cluster->m_count = cluster->m_count - 1; + i--; + } + } + + if (cluster->m_count == 0) + { + ConvexHullAABBTreeNode* const parent = cluster->m_parent; + if (parent) + { + ConvexHullAABBTreeNode* const sibling = (parent->m_left != cluster) ? parent->m_left : parent->m_right; + assert(sibling != cluster); + ConvexHullAABBTreeNode* const grandParent = parent->m_parent; + if (grandParent) + { + sibling->m_parent = grandParent; + if (grandParent->m_right == parent) + { + grandParent->m_right = sibling; + } + else + { + grandParent->m_left = sibling; + } + } + else + { + sibling->m_parent = nullptr; + *treePointer = sibling; + } + } + } + } + } + } + + assert(index != -1); + return index; +} + +double ConvexHull::TetrahedrumVolume(const VHACD::Vect3& p0, + const VHACD::Vect3& p1, + const VHACD::Vect3& p2, + const VHACD::Vect3& p3) const +{ + const VHACD::Vect3 p1p0(p1 - p0); + const VHACD::Vect3 p2p0(p2 - p0); + const VHACD::Vect3 p3p0(p3 - p0); + return p3p0.Dot(p1p0.Cross(p2p0)); +} + +int ConvexHull::InitVertexArray(std::vector& points, + NodeBundle& memoryPool) +// std::vector& memoryPool) +{ +#if 1 + ConvexHullAABBTreeNode* tree = BuildTreeOld(points, + memoryPool); +#else + ConvexHullAABBTreeNode* tree = BuildTreeNew(points, (char**)&memoryPool, maxMemSize); +#endif + int count = int(points.size()); + if (count < 4) + { + m_points.resize(0); + return 0; + } + + m_points.resize(count); + m_aabbP0 = tree->m_box[0]; + m_aabbP1 = tree->m_box[1]; + + VHACD::Vect3 boxSize(tree->m_box[1] - tree->m_box[0]); + m_diag = boxSize.GetNorm(); + const ndNormalMap& normalMap = ndNormalMap::GetNormalMap(); + + int index0 = SupportVertex(&tree, + points, + normalMap.m_normal[0]); + m_points[0] = points[index0]; + points[index0].m_mark = 1; + + bool validTetrahedrum = false; + VHACD::Vect3 e1(double(0.0)); + for (int i = 1; i < normalMap.m_count; ++i) + { + int index = SupportVertex(&tree, + points, + normalMap.m_normal[i]); + assert(index >= 0); + + e1 = points[index] - m_points[0]; + double error2 = e1.GetNormSquared(); + if (error2 > (double(1.0e-4) * m_diag * m_diag)) + { + m_points[1] = points[index]; + points[index].m_mark = 1; + validTetrahedrum = true; + break; + } + } + if (!validTetrahedrum) + { + m_points.resize(0); + assert(0); + return count; + } + + validTetrahedrum = false; + VHACD::Vect3 e2(double(0.0)); + VHACD::Vect3 normal(double(0.0)); + for (int i = 2; i < normalMap.m_count; ++i) + { + int index = SupportVertex(&tree, + points, + normalMap.m_normal[i]); + assert(index >= 0); + e2 = points[index] - m_points[0]; + normal = e1.Cross(e2); + double error2 = normal.GetNorm(); + if (error2 > (double(1.0e-4) * m_diag * m_diag)) + { + m_points[2] = points[index]; + points[index].m_mark = 1; + validTetrahedrum = true; + break; + } + } + + if (!validTetrahedrum) + { + m_points.resize(0); + assert(0); + return count; + } + + // find the largest possible tetrahedron + validTetrahedrum = false; + VHACD::Vect3 e3(double(0.0)); + + index0 = SupportVertex(&tree, + points, + normal); + e3 = points[index0] - m_points[0]; + double err2 = normal.Dot(e3); + if (fabs(err2) > (double(1.0e-6) * m_diag * m_diag)) + { + // we found a valid tetrahedral, about and start build the hull by adding the rest of the points + m_points[3] = points[index0]; + points[index0].m_mark = 1; + validTetrahedrum = true; + } + if (!validTetrahedrum) + { + VHACD::Vect3 n(-normal); + int index = SupportVertex(&tree, + points, + n); + e3 = points[index] - m_points[0]; + double error2 = normal.Dot(e3); + if (fabs(error2) > (double(1.0e-6) * m_diag * m_diag)) + { + // we found a valid tetrahedral, about and start build the hull by adding the rest of the points + m_points[3] = points[index]; + points[index].m_mark = 1; + validTetrahedrum = true; + } + } + if (!validTetrahedrum) + { + for (int i = 3; i < normalMap.m_count; ++i) + { + int index = SupportVertex(&tree, + points, + normalMap.m_normal[i]); + assert(index >= 0); + + //make sure the volume of the fist tetrahedral is no negative + e3 = points[index] - m_points[0]; + double error2 = normal.Dot(e3); + if (fabs(error2) > (double(1.0e-6) * m_diag * m_diag)) + { + // we found a valid tetrahedral, about and start build the hull by adding the rest of the points + m_points[3] = points[index]; + points[index].m_mark = 1; + validTetrahedrum = true; + break; + } + } + } + if (!validTetrahedrum) + { + // the points do not form a convex hull + m_points.resize(0); + return count; + } + + m_points.resize(4); + double volume = TetrahedrumVolume(m_points[0], + m_points[1], + m_points[2], + m_points[3]); + if (volume > double(0.0)) + { + std::swap(m_points[2], + m_points[3]); + } + assert(TetrahedrumVolume(m_points[0], m_points[1], m_points[2], m_points[3]) < double(0.0)); + return count; +} + +std::list::iterator ConvexHull::AddFace(int i0, + int i1, + int i2) +{ + ConvexHullFace face; + face.m_index[0] = i0; + face.m_index[1] = i1; + face.m_index[2] = i2; + + std::list::iterator node = m_list.emplace(m_list.end(), face); + return node; +} + +void ConvexHull::CalculateConvexHull3D(ConvexHullAABBTreeNode* vertexTree, + std::vector& points, + int count, + double distTol, + int maxVertexCount) +{ + distTol = fabs(distTol) * m_diag; + std::list::iterator f0Node = AddFace(0, 1, 2); + std::list::iterator f1Node = AddFace(0, 2, 3); + std::list::iterator f2Node = AddFace(2, 1, 3); + std::list::iterator f3Node = AddFace(1, 0, 3); + + ConvexHullFace& f0 = *f0Node; + ConvexHullFace& f1 = *f1Node; + ConvexHullFace& f2 = *f2Node; + ConvexHullFace& f3 = *f3Node; + + f0.m_twin[0] = f3Node; + f0.m_twin[1] = f2Node; + f0.m_twin[2] = f1Node; + + f1.m_twin[0] = f0Node; + f1.m_twin[1] = f2Node; + f1.m_twin[2] = f3Node; + + f2.m_twin[0] = f0Node; + f2.m_twin[1] = f3Node; + f2.m_twin[2] = f1Node; + + f3.m_twin[0] = f0Node; + f3.m_twin[1] = f1Node; + f3.m_twin[2] = f2Node; + + std::list::iterator> boundaryFaces; + boundaryFaces.push_back(f0Node); + boundaryFaces.push_back(f1Node); + boundaryFaces.push_back(f2Node); + boundaryFaces.push_back(f3Node); + + m_points.resize(count); + + count -= 4; + maxVertexCount -= 4; + int currentIndex = 4; + + /* + * Some are iterators into boundaryFaces, others into m_list + */ + std::vector::iterator> stack; + std::vector::iterator> coneList; + std::vector::iterator> deleteList; + + stack.reserve(1024 + count); + coneList.reserve(1024 + count); + deleteList.reserve(1024 + count); + + while (boundaryFaces.size() && count && (maxVertexCount > 0)) + { + // my definition of the optimal convex hull of a given vertex count, + // is the convex hull formed by a subset of the input vertex that minimizes the volume difference + // between the perfect hull formed from all input vertex and the hull of the sub set of vertex. + // When using a priority heap this algorithms will generate the an optimal of a fix vertex count. + // Since all Newton's tools do not have a limit on the point count of a convex hull, I can use either a stack or a queue. + // a stack maximize construction speed, a Queue tend to maximize the volume of the generated Hull approaching a perfect Hull. + // For now we use a queue. + // For general hulls it does not make a difference if we use a stack, queue, or a priority heap. + // perfect optimal hull only apply for when build hull of a limited vertex count. + // + // Also when building Hulls of a limited vertex count, this function runs in constant time. + // yes that is correct, it does not makes a difference if you build a N point hull from 100 vertex + // or from 100000 vertex input array. + + // using a queue (some what slower by better hull when reduced vertex count is desired) + bool isvalid; + std::list::iterator faceNode = boundaryFaces.back(); + ConvexHullFace& face = *faceNode; + HullPlane planeEquation(face.GetPlaneEquation(m_points, isvalid)); + + int index = 0; + double dist = 0; + VHACD::Vect3 p; + if (isvalid) + { + index = SupportVertex(&vertexTree, + points, + planeEquation); + p = points[index]; + dist = planeEquation.Evalue(p); + } + + if ( isvalid + && (dist >= distTol) + && (face.Evalue(m_points, p) < double(0.0))) + { + stack.push_back(faceNode); + + deleteList.clear(); + while (stack.size()) + { + std::list::iterator node1 = stack.back(); + ConvexHullFace& face1 = *node1; + + stack.pop_back(); + + if (!face1.m_mark && (face1.Evalue(m_points, p) < double(0.0))) + { + #ifdef _DEBUG + for (const auto node : deleteList) + { + assert(node != node1); + } + #endif + + deleteList.push_back(node1); + face1.m_mark = 1; + for (std::list::iterator& twinNode : face1.m_twin) + { + ConvexHullFace& twinFace = *twinNode; + if (!twinFace.m_mark) + { + stack.push_back(twinNode); + } + } + } + } + + m_points[currentIndex] = points[index]; + points[index].m_mark = 1; + + coneList.clear(); + for (std::list::iterator node1 : deleteList) + { + ConvexHullFace& face1 = *node1; + assert(face1.m_mark == 1); + for (std::size_t j0 = 0; j0 < face1.m_twin.size(); ++j0) + { + std::list::iterator twinNode = face1.m_twin[j0]; + ConvexHullFace& twinFace = *twinNode; + if (!twinFace.m_mark) + { + std::size_t j1 = (j0 == 2) ? 0 : j0 + 1; + std::list::iterator newNode = AddFace(currentIndex, + face1.m_index[j0], + face1.m_index[j1]); + boundaryFaces.push_front(newNode); + ConvexHullFace& newFace = *newNode; + + newFace.m_twin[1] = twinNode; + for (std::size_t k = 0; k < twinFace.m_twin.size(); ++k) + { + if (twinFace.m_twin[k] == node1) + { + twinFace.m_twin[k] = newNode; + } + } + coneList.push_back(newNode); + } + } + } + + for (std::size_t i = 0; i < coneList.size() - 1; ++i) + { + std::list::iterator nodeA = coneList[i]; + ConvexHullFace& faceA = *nodeA; + assert(faceA.m_mark == 0); + for (std::size_t j = i + 1; j < coneList.size(); j++) + { + std::list::iterator nodeB = coneList[j]; + ConvexHullFace& faceB = *nodeB; + assert(faceB.m_mark == 0); + if (faceA.m_index[2] == faceB.m_index[1]) + { + faceA.m_twin[2] = nodeB; + faceB.m_twin[0] = nodeA; + break; + } + } + + for (std::size_t j = i + 1; j < coneList.size(); j++) + { + std::list::iterator nodeB = coneList[j]; + ConvexHullFace& faceB = *nodeB; + assert(faceB.m_mark == 0); + if (faceA.m_index[1] == faceB.m_index[2]) + { + faceA.m_twin[0] = nodeB; + faceB.m_twin[2] = nodeA; + break; + } + } + } + + for (std::list::iterator node : deleteList) + { + auto it = std::find(boundaryFaces.begin(), + boundaryFaces.end(), + node); + if (it != boundaryFaces.end()) + { + boundaryFaces.erase(it); + } + m_list.erase(node); + } + + maxVertexCount--; + currentIndex++; + count--; + } + else + { + auto it = std::find(boundaryFaces.begin(), + boundaryFaces.end(), + faceNode); + if (it != boundaryFaces.end()) + { + boundaryFaces.erase(it); + } + } + } + m_points.resize(currentIndex); +} + +//*********************************************************************************************** +// End of ConvexHull generation code by Julio Jerez +//*********************************************************************************************** + +class KdTreeNode; + +enum Axes +{ + X_AXIS = 0, + Y_AXIS = 1, + Z_AXIS = 2 +}; + +class KdTreeFindNode +{ +public: + KdTreeFindNode() = default; + + KdTreeNode* m_node{ nullptr }; + double m_distance{ 0.0 }; +}; + +class KdTree +{ +public: + KdTree() = default; + + const VHACD::Vertex& GetPosition(uint32_t index) const; + + uint32_t Search(const VHACD::Vect3& pos, + double radius, + uint32_t maxObjects, + KdTreeFindNode* found) const; + + uint32_t Add(const VHACD::Vertex& v); + + KdTreeNode& GetNewNode(uint32_t index); + + uint32_t GetNearest(const VHACD::Vect3& pos, + double radius, + bool& _found) const; // returns the nearest possible neighbor's index. + + const std::vector& GetVertices() const; + std::vector&& TakeVertices(); + + uint32_t GetVCount() const; + +private: + KdTreeNode* m_root{ nullptr }; + NodeBundle m_bundle; + + std::vector m_vertices; +}; + +class KdTreeNode +{ +public: + KdTreeNode() = default; + KdTreeNode(uint32_t index); + + void Add(KdTreeNode& node, + Axes dim, + const KdTree& iface); + + uint32_t GetIndex() const; + + void Search(Axes axis, + const VHACD::Vect3& pos, + double radius, + uint32_t& count, + uint32_t maxObjects, + KdTreeFindNode* found, + const KdTree& iface); + +private: + uint32_t m_index = 0; + KdTreeNode* m_left = nullptr; + KdTreeNode* m_right = nullptr; +}; + +const VHACD::Vertex& KdTree::GetPosition(uint32_t index) const +{ + assert(index < m_vertices.size()); + return m_vertices[index]; +} + +uint32_t KdTree::Search(const VHACD::Vect3& pos, + double radius, + uint32_t maxObjects, + KdTreeFindNode* found) const +{ + if (!m_root) + return 0; + uint32_t count = 0; + m_root->Search(X_AXIS, pos, radius, count, maxObjects, found, *this); + return count; +} + +uint32_t KdTree::Add(const VHACD::Vertex& v) +{ + uint32_t ret = uint32_t(m_vertices.size()); + m_vertices.emplace_back(v); + KdTreeNode& node = GetNewNode(ret); + if (m_root) + { + m_root->Add(node, + X_AXIS, + *this); + } + else + { + m_root = &node; + } + return ret; +} + +KdTreeNode& KdTree::GetNewNode(uint32_t index) +{ + KdTreeNode& node = m_bundle.GetNextNode(); + node = KdTreeNode(index); + return node; +} + +uint32_t KdTree::GetNearest(const VHACD::Vect3& pos, + double radius, + bool& _found) const // returns the nearest possible neighbor's index. +{ + uint32_t ret = 0; + + _found = false; + KdTreeFindNode found; + uint32_t count = Search(pos, radius, 1, &found); + if (count) + { + KdTreeNode* node = found.m_node; + ret = node->GetIndex(); + _found = true; + } + return ret; +} + +const std::vector& KdTree::GetVertices() const +{ + return m_vertices; +} + +std::vector&& KdTree::TakeVertices() +{ + return std::move(m_vertices); +} + +uint32_t KdTree::GetVCount() const +{ + return uint32_t(m_vertices.size()); +} + +KdTreeNode::KdTreeNode(uint32_t index) + : m_index(index) +{ +} + +void KdTreeNode::Add(KdTreeNode& node, + Axes dim, + const KdTree& tree) +{ + Axes axis = X_AXIS; + uint32_t idx = 0; + switch (dim) + { + case X_AXIS: + idx = 0; + axis = Y_AXIS; + break; + case Y_AXIS: + idx = 1; + axis = Z_AXIS; + break; + case Z_AXIS: + idx = 2; + axis = X_AXIS; + break; + } + + const VHACD::Vertex& nodePosition = tree.GetPosition(node.m_index); + const VHACD::Vertex& position = tree.GetPosition(m_index); + if (nodePosition[idx] <= position[idx]) + { + if (m_left) + m_left->Add(node, axis, tree); + else + m_left = &node; + } + else + { + if (m_right) + m_right->Add(node, axis, tree); + else + m_right = &node; + } +} + +uint32_t KdTreeNode::GetIndex() const +{ + return m_index; +} + +void KdTreeNode::Search(Axes axis, + const VHACD::Vect3& pos, + double radius, + uint32_t& count, + uint32_t maxObjects, + KdTreeFindNode* found, + const KdTree& iface) +{ + const VHACD::Vect3 position = iface.GetPosition(m_index); + + const VHACD::Vect3 d = pos - position; + + KdTreeNode* search1 = 0; + KdTreeNode* search2 = 0; + + uint32_t idx = 0; + switch (axis) + { + case X_AXIS: + idx = 0; + axis = Y_AXIS; + break; + case Y_AXIS: + idx = 1; + axis = Z_AXIS; + break; + case Z_AXIS: + idx = 2; + axis = X_AXIS; + break; + } + + if (d[idx] <= 0) // JWR if we are to the left + { + search1 = m_left; // JWR then search to the left + if (-d[idx] < radius) // JWR if distance to the right is less than our search radius, continue on the right + // as well. + search2 = m_right; + } + else + { + search1 = m_right; // JWR ok, we go down the left tree + if (d[idx] < radius) // JWR if the distance from the right is less than our search radius + search2 = m_left; + } + + double r2 = radius * radius; + double m = d.GetNormSquared(); + + if (m < r2) + { + switch (count) + { + case 0: + { + found[count].m_node = this; + found[count].m_distance = m; + break; + } + case 1: + { + if (m < found[0].m_distance) + { + if (maxObjects == 1) + { + found[0].m_node = this; + found[0].m_distance = m; + } + else + { + found[1] = found[0]; + found[0].m_node = this; + found[0].m_distance = m; + } + } + else if (maxObjects > 1) + { + found[1].m_node = this; + found[1].m_distance = m; + } + break; + } + default: + { + bool inserted = false; + + for (uint32_t i = 0; i < count; i++) + { + if (m < found[i].m_distance) // if this one is closer than a pre-existing one... + { + // insertion sort... + uint32_t scan = count; + if (scan >= maxObjects) + scan = maxObjects - 1; + for (uint32_t j = scan; j > i; j--) + { + found[j] = found[j - 1]; + } + found[i].m_node = this; + found[i].m_distance = m; + inserted = true; + break; + } + } + + if (!inserted && count < maxObjects) + { + found[count].m_node = this; + found[count].m_distance = m; + } + } + break; + } + + count++; + + if (count > maxObjects) + { + count = maxObjects; + } + } + + + if (search1) + search1->Search(axis, pos, radius, count, maxObjects, found, iface); + + if (search2) + search2->Search(axis, pos, radius, count, maxObjects, found, iface); +} + +class VertexIndex +{ +public: + VertexIndex(double granularity, + bool snapToGrid); + + VHACD::Vect3 SnapToGrid(VHACD::Vect3 p); + + uint32_t GetIndex(VHACD::Vect3 p, + bool& newPos); + + const std::vector& GetVertices() const; + + std::vector&& TakeVertices(); + + uint32_t GetVCount() const; + + bool SaveAsObj(const char* fname, + uint32_t tcount, + uint32_t* indices) + { + bool ret = false; + + FILE* fph = fopen(fname, "wb"); + if (fph) + { + ret = true; + + const std::vector& v = GetVertices(); + for (uint32_t i = 0; i < v.size(); ++i) + { + fprintf(fph, "v %0.9f %0.9f %0.9f\r\n", + v[i].mX, + v[i].mY, + v[i].mZ); + } + + for (uint32_t i = 0; i < tcount; i++) + { + uint32_t i1 = *indices++; + uint32_t i2 = *indices++; + uint32_t i3 = *indices++; + fprintf(fph, "f %d %d %d\r\n", + i1 + 1, + i2 + 1, + i3 + 1); + } + fclose(fph); + } + + return ret; + } + +private: + bool m_snapToGrid : 1; + double m_granularity; + KdTree m_KdTree; +}; + +VertexIndex::VertexIndex(double granularity, + bool snapToGrid) + : m_snapToGrid(snapToGrid) + , m_granularity(granularity) +{ +} + +VHACD::Vect3 VertexIndex::SnapToGrid(VHACD::Vect3 p) +{ + for (int i = 0; i < 3; ++i) + { + double m = fmod(p[i], m_granularity); + p[i] -= m; + } + return p; +} + +uint32_t VertexIndex::GetIndex(VHACD::Vect3 p, + bool& newPos) +{ + uint32_t ret; + + newPos = false; + + if (m_snapToGrid) + { + p = SnapToGrid(p); + } + + bool found; + ret = m_KdTree.GetNearest(p, m_granularity, found); + if (!found) + { + newPos = true; + ret = m_KdTree.Add(VHACD::Vertex(p.GetX(), p.GetY(), p.GetZ())); + } + + return ret; +} + +const std::vector& VertexIndex::GetVertices() const +{ + return m_KdTree.GetVertices(); +} + +std::vector&& VertexIndex::TakeVertices() +{ + return std::move(m_KdTree.TakeVertices()); +} + +uint32_t VertexIndex::GetVCount() const +{ + return m_KdTree.GetVCount(); +} + +/* + * A wrapper class for 3 10 bit integers packed into a 32 bit integer + * Layout is [PAD][X][Y][Z] + * Pad is bits 31-30, X is 29-20, Y is 19-10, and Z is 9-0 + */ +class Voxel +{ + /* + * Specify all of them for consistency + */ + static constexpr int VoxelBitsZStart = 0; + static constexpr int VoxelBitsYStart = 10; + static constexpr int VoxelBitsXStart = 20; + static constexpr int VoxelBitMask = 0x03FF; // bits 0 through 9 inclusive +public: + Voxel() = default; + + Voxel(uint32_t index); + + Voxel(uint32_t x, + uint32_t y, + uint32_t z); + + bool operator==(const Voxel &v) const; + + VHACD::Vector3 GetVoxel() const; + + uint32_t GetX() const; + uint32_t GetY() const; + uint32_t GetZ() const; + + uint32_t GetVoxelAddress() const; + +private: + uint32_t m_voxel{ 0 }; +}; + +Voxel::Voxel(uint32_t index) + : m_voxel(index) +{ +} + +Voxel::Voxel(uint32_t x, + uint32_t y, + uint32_t z) + : m_voxel((x << VoxelBitsXStart) | (y << VoxelBitsYStart) | (z << VoxelBitsZStart)) +{ + assert(x < 1024 && "Voxel constructed with X outside of range"); + assert(y < 1024 && "Voxel constructed with Y outside of range"); + assert(z < 1024 && "Voxel constructed with Z outside of range"); +} + +bool Voxel::operator==(const Voxel& v) const +{ + return m_voxel == v.m_voxel; +} + +VHACD::Vector3 Voxel::GetVoxel() const +{ + return VHACD::Vector3(GetX(), GetY(), GetZ()); +} + +uint32_t Voxel::GetX() const +{ + return (m_voxel >> VoxelBitsXStart) & VoxelBitMask; +} + +uint32_t Voxel::GetY() const +{ + return (m_voxel >> VoxelBitsYStart) & VoxelBitMask; +} + +uint32_t Voxel::GetZ() const +{ + return (m_voxel >> VoxelBitsZStart) & VoxelBitMask; +} + +uint32_t Voxel::GetVoxelAddress() const +{ + return m_voxel; +} + +struct SimpleMesh +{ + std::vector m_vertices; + std::vector m_indices; +}; + +/*======================== 0-tests ========================*/ +inline bool IntersectRayAABB(const VHACD::Vect3& start, + const VHACD::Vect3& dir, + const VHACD::BoundsAABB& bounds, + double& t) +{ + //! calculate candidate plane on each axis + bool inside = true; + VHACD::Vect3 ta(double(-1.0)); + + //! use unrolled loops + for (uint32_t i = 0; i < 3; ++i) + { + if (start[i] < bounds.GetMin()[i]) + { + if (dir[i] != double(0.0)) + ta[i] = (bounds.GetMin()[i] - start[i]) / dir[i]; + inside = false; + } + else if (start[i] > bounds.GetMax()[i]) + { + if (dir[i] != double(0.0)) + ta[i] = (bounds.GetMax()[i] - start[i]) / dir[i]; + inside = false; + } + } + + //! if point inside all planes + if (inside) + { + t = double(0.0); + return true; + } + + //! we now have t values for each of possible intersection planes + //! find the maximum to get the intersection point + uint32_t taxis; + double tmax = ta.MaxCoeff(taxis); + + if (tmax < double(0.0)) + return false; + + //! check that the intersection point lies on the plane we picked + //! we don't test the axis of closest intersection for precision reasons + + //! no eps for now + double eps = double(0.0); + + VHACD::Vect3 hit = start + dir * tmax; + + if (( hit.GetX() < bounds.GetMin().GetX() - eps + || hit.GetX() > bounds.GetMax().GetX() + eps) + && taxis != 0) + return false; + if (( hit.GetY() < bounds.GetMin().GetY() - eps + || hit.GetY() > bounds.GetMax().GetY() + eps) + && taxis != 1) + return false; + if (( hit.GetZ() < bounds.GetMin().GetZ() - eps + || hit.GetZ() > bounds.GetMax().GetZ() + eps) + && taxis != 2) + return false; + + //! output results + t = tmax; + + return true; +} + +// Moller and Trumbore's method +inline bool IntersectRayTriTwoSided(const VHACD::Vect3& p, + const VHACD::Vect3& dir, + const VHACD::Vect3& a, + const VHACD::Vect3& b, + const VHACD::Vect3& c, + double& t, + double& u, + double& v, + double& w, + double& sign, + VHACD::Vect3* normal) +{ + VHACD::Vect3 ab = b - a; + VHACD::Vect3 ac = c - a; + VHACD::Vect3 n = ab.Cross(ac); + + double d = -dir.Dot(n); + double ood = double(1.0) / d; // No need to check for division by zero here as infinity arithmetic will save us... + VHACD::Vect3 ap = p - a; + + t = ap.Dot(n) * ood; + if (t < double(0.0)) + { + return false; + } + + VHACD::Vect3 e = -dir.Cross(ap); + v = ac.Dot(e) * ood; + if (v < double(0.0) || v > double(1.0)) // ...here... + { + return false; + } + w = -ab.Dot(e) * ood; + if (w < double(0.0) || v + w > double(1.0)) // ...and here + { + return false; + } + + u = double(1.0) - v - w; + if (normal) + { + *normal = n; + } + + sign = d; + + return true; +} + +// RTCD 5.1.5, page 142 +inline VHACD::Vect3 ClosestPointOnTriangle(const VHACD::Vect3& a, + const VHACD::Vect3& b, + const VHACD::Vect3& c, + const VHACD::Vect3& p, + double& v, + double& w) +{ + VHACD::Vect3 ab = b - a; + VHACD::Vect3 ac = c - a; + VHACD::Vect3 ap = p - a; + + double d1 = ab.Dot(ap); + double d2 = ac.Dot(ap); + if ( d1 <= double(0.0) + && d2 <= double(0.0)) + { + v = double(0.0); + w = double(0.0); + return a; + } + + VHACD::Vect3 bp = p - b; + double d3 = ab.Dot(bp); + double d4 = ac.Dot(bp); + if ( d3 >= double(0.0) + && d4 <= d3) + { + v = double(1.0); + w = double(0.0); + return b; + } + + double vc = d1 * d4 - d3 * d2; + if ( vc <= double(0.0) + && d1 >= double(0.0) + && d3 <= double(0.0)) + { + v = d1 / (d1 - d3); + w = double(0.0); + return a + v * ab; + } + + VHACD::Vect3 cp = p - c; + double d5 = ab.Dot(cp); + double d6 = ac.Dot(cp); + if (d6 >= double(0.0) && d5 <= d6) + { + v = double(0.0); + w = double(1.0); + return c; + } + + double vb = d5 * d2 - d1 * d6; + if ( vb <= double(0.0) + && d2 >= double(0.0) + && d6 <= double(0.0)) + { + v = double(0.0); + w = d2 / (d2 - d6); + return a + w * ac; + } + + double va = d3 * d6 - d5 * d4; + if ( va <= double(0.0) + && (d4 - d3) >= double(0.0) + && (d5 - d6) >= double(0.0)) + { + w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + v = double(1.0) - w; + return b + w * (c - b); + } + + double denom = double(1.0) / (va + vb + vc); + v = vb * denom; + w = vc * denom; + return a + ab * v + ac * w; +} + +class AABBTree +{ +public: + AABBTree() = default; + AABBTree(AABBTree&&) = default; + AABBTree& operator=(AABBTree&&) = default; + + AABBTree(const std::vector& vertices, + const std::vector& indices); + + bool TraceRay(const VHACD::Vect3& start, + const VHACD::Vect3& to, + double& outT, + double& faceSign, + VHACD::Vect3& hitLocation) const; + + bool TraceRay(const VHACD::Vect3& start, + const VHACD::Vect3& dir, + uint32_t& insideCount, + uint32_t& outsideCount) const; + + bool TraceRay(const VHACD::Vect3& start, + const VHACD::Vect3& dir, + double& outT, + double& u, + double& v, + double& w, + double& faceSign, + uint32_t& faceIndex) const; + + VHACD::Vect3 GetCenter() const; + VHACD::Vect3 GetMinExtents() const; + VHACD::Vect3 GetMaxExtents() const; + + bool GetClosestPointWithinDistance(const VHACD::Vect3& point, + double maxDistance, + VHACD::Vect3& closestPoint) const; + +private: + struct Node + { + union + { + uint32_t m_children; + uint32_t m_numFaces{ 0 }; + }; + + uint32_t* m_faces{ nullptr }; + VHACD::BoundsAABB m_extents; + }; + + struct FaceSorter + { + FaceSorter(const std::vector& positions, + const std::vector& indices, + uint32_t axis); + + bool operator()(uint32_t lhs, uint32_t rhs) const; + + double GetCentroid(uint32_t face) const; + + const std::vector& m_vertices; + const std::vector& m_indices; + uint32_t m_axis; + }; + + // partition the objects and return the number of objects in the lower partition + uint32_t PartitionMedian(Node& n, + uint32_t* faces, + uint32_t numFaces); + uint32_t PartitionSAH(Node& n, + uint32_t* faces, + uint32_t numFaces); + + void Build(); + + void BuildRecursive(uint32_t nodeIndex, + uint32_t* faces, + uint32_t numFaces); + + void TraceRecursive(uint32_t nodeIndex, + const VHACD::Vect3& start, + const VHACD::Vect3& dir, + double& outT, + double& u, + double& v, + double& w, + double& faceSign, + uint32_t& faceIndex) const; + + + bool GetClosestPointWithinDistance(const VHACD::Vect3& point, + const double maxDis, + double& dis, + double& v, + double& w, + uint32_t& faceIndex, + VHACD::Vect3& closest) const; + + void GetClosestPointWithinDistanceSqRecursive(uint32_t nodeIndex, + const VHACD::Vect3& point, + double& outDisSq, + double& outV, + double& outW, + uint32_t& outFaceIndex, + VHACD::Vect3& closest) const; + + VHACD::BoundsAABB CalculateFaceBounds(uint32_t* faces, + uint32_t numFaces); + + // track the next free node + uint32_t m_freeNode; + + const std::vector* m_vertices{ nullptr }; + const std::vector* m_indices{ nullptr }; + + std::vector m_faces; + std::vector m_nodes; + std::vector m_faceBounds; + + // stats + uint32_t m_treeDepth{ 0 }; + uint32_t m_innerNodes{ 0 }; + uint32_t m_leafNodes{ 0 }; + + uint32_t s_depth{ 0 }; +}; + +AABBTree::FaceSorter::FaceSorter(const std::vector& positions, + const std::vector& indices, + uint32_t axis) + : m_vertices(positions) + , m_indices(indices) + , m_axis(axis) +{ +} + +inline bool AABBTree::FaceSorter::operator()(uint32_t lhs, + uint32_t rhs) const +{ + double a = GetCentroid(lhs); + double b = GetCentroid(rhs); + + if (a == b) + { + return lhs < rhs; + } + else + { + return a < b; + } +} + +inline double AABBTree::FaceSorter::GetCentroid(uint32_t face) const +{ + const VHACD::Vect3& a = m_vertices[m_indices[face].mI0]; + const VHACD::Vect3& b = m_vertices[m_indices[face].mI1]; + const VHACD::Vect3& c = m_vertices[m_indices[face].mI2]; + + return (a[m_axis] + b[m_axis] + c[m_axis]) / double(3.0); +} + +AABBTree::AABBTree(const std::vector& vertices, + const std::vector& indices) + : m_vertices(&vertices) + , m_indices(&indices) +{ + Build(); +} + +bool AABBTree::TraceRay(const VHACD::Vect3& start, + const VHACD::Vect3& to, + double& outT, + double& faceSign, + VHACD::Vect3& hitLocation) const +{ + VHACD::Vect3 dir = to - start; + double distance = dir.Normalize(); + double u, v, w; + uint32_t faceIndex; + bool hit = TraceRay(start, + dir, + outT, + u, + v, + w, + faceSign, + faceIndex); + if (hit) + { + hitLocation = start + dir * outT; + } + + if (hit && outT > distance) + { + hit = false; + } + return hit; +} + +bool AABBTree::TraceRay(const VHACD::Vect3& start, + const VHACD::Vect3& dir, + uint32_t& insideCount, + uint32_t& outsideCount) const +{ + double outT, u, v, w, faceSign; + uint32_t faceIndex; + bool hit = TraceRay(start, + dir, + outT, + u, + v, + w, + faceSign, + faceIndex); + if (hit) + { + if (faceSign >= 0) + { + insideCount++; + } + else + { + outsideCount++; + } + } + return hit; +} + +bool AABBTree::TraceRay(const VHACD::Vect3& start, + const VHACD::Vect3& dir, + double& outT, + double& u, + double& v, + double& w, + double& faceSign, + uint32_t& faceIndex) const +{ + outT = FLT_MAX; + TraceRecursive(0, + start, + dir, + outT, + u, + v, + w, + faceSign, + faceIndex); + return (outT != FLT_MAX); +} + +VHACD::Vect3 AABBTree::GetCenter() const +{ + return m_nodes[0].m_extents.GetCenter(); +} + +VHACD::Vect3 AABBTree::GetMinExtents() const +{ + return m_nodes[0].m_extents.GetMin(); +} + +VHACD::Vect3 AABBTree::GetMaxExtents() const +{ + return m_nodes[0].m_extents.GetMax(); +} + +bool AABBTree::GetClosestPointWithinDistance(const VHACD::Vect3& point, + double maxDistance, + VHACD::Vect3& closestPoint) const +{ + double dis, v, w; + uint32_t faceIndex; + bool hit = GetClosestPointWithinDistance(point, + maxDistance, + dis, + v, + w, + faceIndex, + closestPoint); + return hit; +} + +// partition faces around the median face +uint32_t AABBTree::PartitionMedian(Node& n, + uint32_t* faces, + uint32_t numFaces) +{ + FaceSorter predicate(*m_vertices, + *m_indices, + n.m_extents.GetSize().LongestAxis()); + std::nth_element(faces, + faces + numFaces / 2, + faces + numFaces, + predicate); + + return numFaces / 2; +} + +// partition faces based on the surface area heuristic +uint32_t AABBTree::PartitionSAH(Node&, + uint32_t* faces, + uint32_t numFaces) +{ + uint32_t bestAxis = 0; + uint32_t bestIndex = 0; + double bestCost = FLT_MAX; + + for (uint32_t a = 0; a < 3; ++a) + { + // sort faces by centroids + FaceSorter predicate(*m_vertices, + *m_indices, + a); + std::sort(faces, + faces + numFaces, + predicate); + + // two passes over data to calculate upper and lower bounds + std::vector cumulativeLower(numFaces); + std::vector cumulativeUpper(numFaces); + + VHACD::BoundsAABB lower; + VHACD::BoundsAABB upper; + + for (uint32_t i = 0; i < numFaces; ++i) + { + lower.Union(m_faceBounds[faces[i]]); + upper.Union(m_faceBounds[faces[numFaces - i - 1]]); + + cumulativeLower[i] = lower.SurfaceArea(); + cumulativeUpper[numFaces - i - 1] = upper.SurfaceArea(); + } + + double invTotalSA = double(1.0) / cumulativeUpper[0]; + + // test all split positions + for (uint32_t i = 0; i < numFaces - 1; ++i) + { + double pBelow = cumulativeLower[i] * invTotalSA; + double pAbove = cumulativeUpper[i] * invTotalSA; + + double cost = double(0.125) + (pBelow * i + pAbove * (numFaces - i)); + if (cost <= bestCost) + { + bestCost = cost; + bestIndex = i; + bestAxis = a; + } + } + } + + // re-sort by best axis + FaceSorter predicate(*m_vertices, + *m_indices, + bestAxis); + std::sort(faces, + faces + numFaces, + predicate); + + return bestIndex + 1; +} + +void AABBTree::Build() +{ + const uint32_t numFaces = uint32_t(m_indices->size()); + + // build initial list of faces + m_faces.reserve(numFaces); + + // calculate bounds of each face and store + m_faceBounds.reserve(numFaces); + + std::vector stack; + for (uint32_t i = 0; i < numFaces; ++i) + { + VHACD::BoundsAABB top = CalculateFaceBounds(&i, + 1); + + m_faces.push_back(i); + m_faceBounds.push_back(top); + } + + m_nodes.reserve(uint32_t(numFaces * double(1.5))); + + // allocate space for all the nodes + m_freeNode = 1; + + // start building + BuildRecursive(0, + m_faces.data(), + numFaces); + + assert(s_depth == 0); +} + +void AABBTree::BuildRecursive(uint32_t nodeIndex, + uint32_t* faces, + uint32_t numFaces) +{ + const uint32_t kMaxFacesPerLeaf = 6; + + // if we've run out of nodes allocate some more + if (nodeIndex >= m_nodes.size()) + { + uint32_t s = std::max(uint32_t(double(1.5) * m_nodes.size()), 512U); + m_nodes.resize(s); + } + + // a reference to the current node, need to be careful here as this reference may become invalid if array is resized + Node& n = m_nodes[nodeIndex]; + + // track max tree depth + ++s_depth; + m_treeDepth = std::max(m_treeDepth, s_depth); + + n.m_extents = CalculateFaceBounds(faces, + numFaces); + + // calculate bounds of faces and add node + if (numFaces <= kMaxFacesPerLeaf) + { + n.m_faces = faces; + n.m_numFaces = numFaces; + + ++m_leafNodes; + } + else + { + ++m_innerNodes; + + // face counts for each branch + const uint32_t leftCount = PartitionMedian(n, faces, numFaces); + // const uint32_t leftCount = PartitionSAH(n, faces, numFaces); + const uint32_t rightCount = numFaces - leftCount; + + // alloc 2 nodes + m_nodes[nodeIndex].m_children = m_freeNode; + + // allocate two nodes + m_freeNode += 2; + + // split faces in half and build each side recursively + BuildRecursive(m_nodes[nodeIndex].m_children + 0, faces, leftCount); + BuildRecursive(m_nodes[nodeIndex].m_children + 1, faces + leftCount, rightCount); + } + + --s_depth; +} + +void AABBTree::TraceRecursive(uint32_t nodeIndex, + const VHACD::Vect3& start, + const VHACD::Vect3& dir, + double& outT, + double& outU, + double& outV, + double& outW, + double& faceSign, + uint32_t& faceIndex) const +{ + const Node& node = m_nodes[nodeIndex]; + + if (node.m_faces == NULL) + { + // find closest node + const Node& leftChild = m_nodes[node.m_children + 0]; + const Node& rightChild = m_nodes[node.m_children + 1]; + + double dist[2] = { FLT_MAX, FLT_MAX }; + + IntersectRayAABB(start, + dir, + leftChild.m_extents, + dist[0]); + IntersectRayAABB(start, + dir, + rightChild.m_extents, + dist[1]); + + uint32_t closest = 0; + uint32_t furthest = 1; + + if (dist[1] < dist[0]) + { + closest = 1; + furthest = 0; + } + + if (dist[closest] < outT) + { + TraceRecursive(node.m_children + closest, + start, + dir, + outT, + outU, + outV, + outW, + faceSign, + faceIndex); + } + + if (dist[furthest] < outT) + { + TraceRecursive(node.m_children + furthest, + start, + dir, + outT, + outU, + outV, + outW, + faceSign, + faceIndex); + } + } + else + { + double t, u, v, w, s; + + for (uint32_t i = 0; i < node.m_numFaces; ++i) + { + uint32_t indexStart = node.m_faces[i]; + + const VHACD::Vect3& a = (*m_vertices)[(*m_indices)[indexStart].mI0]; + const VHACD::Vect3& b = (*m_vertices)[(*m_indices)[indexStart].mI1]; + const VHACD::Vect3& c = (*m_vertices)[(*m_indices)[indexStart].mI2]; + if (IntersectRayTriTwoSided(start, dir, a, b, c, t, u, v, w, s, NULL)) + { + if (t < outT) + { + outT = t; + outU = u; + outV = v; + outW = w; + faceSign = s; + faceIndex = node.m_faces[i]; + } + } + } + } +} + +bool AABBTree::GetClosestPointWithinDistance(const VHACD::Vect3& point, + const double maxDis, + double& dis, + double& v, + double& w, + uint32_t& faceIndex, + VHACD::Vect3& closest) const +{ + dis = maxDis; + faceIndex = uint32_t(~0); + double disSq = dis * dis; + + GetClosestPointWithinDistanceSqRecursive(0, + point, + disSq, + v, + w, + faceIndex, + closest); + dis = sqrt(disSq); + + return (faceIndex < (~(static_cast(0)))); +} + +void AABBTree::GetClosestPointWithinDistanceSqRecursive(uint32_t nodeIndex, + const VHACD::Vect3& point, + double& outDisSq, + double& outV, + double& outW, + uint32_t& outFaceIndex, + VHACD::Vect3& closestPoint) const +{ + const Node& node = m_nodes[nodeIndex]; + + if (node.m_faces == nullptr) + { + // find closest node + const Node& leftChild = m_nodes[node.m_children + 0]; + const Node& rightChild = m_nodes[node.m_children + 1]; + + // double dist[2] = { FLT_MAX, FLT_MAX }; + VHACD::Vect3 lp = leftChild.m_extents.ClosestPoint(point); + VHACD::Vect3 rp = rightChild.m_extents.ClosestPoint(point); + + + uint32_t closest = 0; + uint32_t furthest = 1; + double dcSq = (point - lp).GetNormSquared(); + double dfSq = (point - rp).GetNormSquared(); + if (dfSq < dcSq) + { + closest = 1; + furthest = 0; + std::swap(dfSq, dcSq); + } + + if (dcSq < outDisSq) + { + GetClosestPointWithinDistanceSqRecursive(node.m_children + closest, + point, + outDisSq, + outV, + outW, + outFaceIndex, + closestPoint); + } + + if (dfSq < outDisSq) + { + GetClosestPointWithinDistanceSqRecursive(node.m_children + furthest, + point, + outDisSq, + outV, + outW, + outFaceIndex, + closestPoint); + } + } + else + { + + double v, w; + for (uint32_t i = 0; i < node.m_numFaces; ++i) + { + uint32_t indexStart = node.m_faces[i]; + + const VHACD::Vect3& a = (*m_vertices)[(*m_indices)[indexStart].mI0]; + const VHACD::Vect3& b = (*m_vertices)[(*m_indices)[indexStart].mI1]; + const VHACD::Vect3& c = (*m_vertices)[(*m_indices)[indexStart].mI2]; + + VHACD::Vect3 cp = ClosestPointOnTriangle(a, b, c, point, v, w); + double disSq = (cp - point).GetNormSquared(); + + if (disSq < outDisSq) + { + closestPoint = cp; + outDisSq = disSq; + outV = v; + outW = w; + outFaceIndex = node.m_faces[i]; + } + } + } +} + +VHACD::BoundsAABB AABBTree::CalculateFaceBounds(uint32_t* faces, + uint32_t numFaces) +{ + VHACD::Vect3 minExtents( FLT_MAX); + VHACD::Vect3 maxExtents(-FLT_MAX); + + // calculate face bounds + for (uint32_t i = 0; i < numFaces; ++i) + { + VHACD::Vect3 a = (*m_vertices)[(*m_indices)[faces[i]].mI0]; + VHACD::Vect3 b = (*m_vertices)[(*m_indices)[faces[i]].mI1]; + VHACD::Vect3 c = (*m_vertices)[(*m_indices)[faces[i]].mI2]; + + minExtents = a.CWiseMin(minExtents); + maxExtents = a.CWiseMax(maxExtents); + + minExtents = b.CWiseMin(minExtents); + maxExtents = b.CWiseMax(maxExtents); + + minExtents = c.CWiseMin(minExtents); + maxExtents = c.CWiseMax(maxExtents); + } + + return VHACD::BoundsAABB(minExtents, + maxExtents); +} + +enum class VoxelValue : uint8_t +{ + PRIMITIVE_UNDEFINED = 0, + PRIMITIVE_OUTSIDE_SURFACE_TOWALK = 1, + PRIMITIVE_OUTSIDE_SURFACE = 2, + PRIMITIVE_INSIDE_SURFACE = 3, + PRIMITIVE_ON_SURFACE = 4 +}; + +class Volume +{ +public: + void Voxelize(const std::vector& points, + const std::vector& triangles, + const size_t dim, + FillMode fillMode, + const AABBTree& aabbTree); + + void RaycastFill(const AABBTree& aabbTree); + + void SetVoxel(const size_t i, + const size_t j, + const size_t k, + VoxelValue value); + + VoxelValue& GetVoxel(const size_t i, + const size_t j, + const size_t k); + + const VoxelValue& GetVoxel(const size_t i, + const size_t j, + const size_t k) const; + + const std::vector& GetSurfaceVoxels() const; + const std::vector& GetInteriorVoxels() const; + + double GetScale() const; + const VHACD::BoundsAABB& GetBounds() const; + const VHACD::Vector3& GetDimensions() const; + + VHACD::BoundsAABB m_bounds; + double m_scale{ 1.0 }; + VHACD::Vector3 m_dim{ 0 }; + size_t m_numVoxelsOnSurface{ 0 }; + size_t m_numVoxelsInsideSurface{ 0 }; + size_t m_numVoxelsOutsideSurface{ 0 }; + std::vector m_data; +private: + + void MarkOutsideSurface(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 FillOutsideSurface(); + + void FillInsideSurface(); + + std::vector m_surfaceVoxels; + std::vector m_interiorVoxels; +}; + +bool PlaneBoxOverlap(const VHACD::Vect3& normal, + const VHACD::Vect3& vert, + const VHACD::Vect3& maxbox) +{ + int32_t q; + VHACD::Vect3 vmin; + VHACD::Vect3 vmax; + double v; + for (q = 0; q < 3; q++) + { + v = vert[q]; + if (normal[q] > double(0.0)) + { + vmin[q] = -maxbox[q] - v; + vmax[q] = maxbox[q] - v; + } + else + { + vmin[q] = maxbox[q] - v; + vmax[q] = -maxbox[q] - v; + } + } + if (normal.Dot(vmin) > double(0.0)) + return false; + if (normal.Dot(vmax) >= double(0.0)) + return true; + return false; +} + +bool AxisTest(double a, double b, double fa, double fb, + double v0, double v1, double v2, double v3, + double boxHalfSize1, double boxHalfSize2) +{ + double p0 = a * v0 + b * v1; + double p1 = a * v2 + b * v3; + + double min = std::min(p0, p1); + double max = std::max(p0, p1); + + double rad = fa * boxHalfSize1 + fb * boxHalfSize2; + if (min > rad || max < -rad) + { + return false; + } + + return true; +} + +bool TriBoxOverlap(const VHACD::Vect3& boxCenter, + const VHACD::Vect3& boxHalfSize, + const VHACD::Vect3& triVer0, + const VHACD::Vect3& triVer1, + const VHACD::Vect3& triVer2) +{ + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-direction) */ + /* this gives 3x3=9 more tests */ + + VHACD::Vect3 v0 = triVer0 - boxCenter; + VHACD::Vect3 v1 = triVer1 - boxCenter; + VHACD::Vect3 v2 = triVer2 - boxCenter; + VHACD::Vect3 e0 = v1 - v0; + VHACD::Vect3 e1 = v2 - v1; + VHACD::Vect3 e2 = v0 - v2; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + double fex = fabs(e0[0]); + double fey = fabs(e0[1]); + double fez = fabs(e0[2]); + + /* + * These should use Get*() instead of subscript for consistency, but the function calls are long enough already + */ + if (!AxisTest( e0[2], -e0[1], fez, fey, v0[1], v0[2], v2[1], v2[2], boxHalfSize[1], boxHalfSize[2])) return 0; // X01 + if (!AxisTest(-e0[2], e0[0], fez, fex, v0[0], v0[2], v2[0], v2[2], boxHalfSize[0], boxHalfSize[2])) return 0; // Y02 + if (!AxisTest( e0[1], -e0[0], fey, fex, v1[0], v1[1], v2[0], v2[1], boxHalfSize[0], boxHalfSize[1])) return 0; // Z12 + + fex = fabs(e1[0]); + fey = fabs(e1[1]); + fez = fabs(e1[2]); + + if (!AxisTest( e1[2], -e1[1], fez, fey, v0[1], v0[2], v2[1], v2[2], boxHalfSize[1], boxHalfSize[2])) return 0; // X01 + if (!AxisTest(-e1[2], e1[0], fez, fex, v0[0], v0[2], v2[0], v2[2], boxHalfSize[0], boxHalfSize[2])) return 0; // Y02 + if (!AxisTest( e1[1], -e1[0], fey, fex, v0[0], v0[1], v1[0], v1[1], boxHalfSize[0], boxHalfSize[2])) return 0; // Z0 + + fex = fabs(e2[0]); + fey = fabs(e2[1]); + fez = fabs(e2[2]); + + if (!AxisTest( e2[2], -e2[1], fez, fey, v0[1], v0[2], v1[1], v1[2], boxHalfSize[1], boxHalfSize[2])) return 0; // X2 + if (!AxisTest(-e2[2], e2[0], fez, fex, v0[0], v0[2], v1[0], v1[2], boxHalfSize[0], boxHalfSize[2])) return 0; // Y1 + if (!AxisTest( e2[1], -e2[0], fey, fex, v1[0], v1[1], v2[0], v2[1], boxHalfSize[0], boxHalfSize[1])) return 0; // Z12 + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in 0-direction */ + double min = std::min({v0.GetX(), v1.GetX(), v2.GetX()}); + double max = std::max({v0.GetX(), v1.GetX(), v2.GetX()}); + if (min > boxHalfSize[0] || max < -boxHalfSize[0]) + return false; + + /* test in 1-direction */ + min = std::min({v0.GetY(), v1.GetY(), v2.GetY()}); + max = std::max({v0.GetY(), v1.GetY(), v2.GetY()}); + if (min > boxHalfSize[1] || max < -boxHalfSize[1]) + return false; + + /* test in getZ-direction */ + min = std::min({v0.GetZ(), v1.GetZ(), v2.GetZ()}); + max = std::max({v0.GetZ(), v1.GetZ(), v2.GetZ()}); + if (min > boxHalfSize[2] || max < -boxHalfSize[2]) + return false; + + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + VHACD::Vect3 normal = e0.Cross(e1); + + if (!PlaneBoxOverlap(normal, v0, boxHalfSize)) + return false; + return true; /* box and triangle overlaps */ +} + +void Volume::Voxelize(const std::vector& points, + const std::vector& indices, + const size_t dimensions, + FillMode fillMode, + const AABBTree& aabbTree) +{ + double a = std::pow(dimensions, 0.33); + size_t dim = a * double(1.5); + dim = std::max(dim, size_t(32)); + + if (points.size() == 0) + { + return; + } + + m_bounds = BoundsAABB(points); + + VHACD::Vect3 d = m_bounds.GetSize(); + double r; + // Equal comparison is important here to avoid taking the last branch when d[0] == d[1] with d[2] being the smallest + // dimension. That would lead to dimensions in i and j to be a lot bigger than expected and make the amount of + // voxels in the volume totally unmanageable. + if (d[0] >= d[1] && d[0] >= d[2]) + { + r = d[0]; + m_dim[0] = uint32_t(dim); + m_dim[1] = uint32_t(2 + static_cast(dim * d[1] / d[0])); + m_dim[2] = uint32_t(2 + static_cast(dim * d[2] / d[0])); + } + else if (d[1] >= d[0] && d[1] >= d[2]) + { + r = d[1]; + m_dim[1] = uint32_t(dim); + m_dim[0] = uint32_t(2 + static_cast(dim * d[0] / d[1])); + m_dim[2] = uint32_t(2 + static_cast(dim * d[2] / d[1])); + } + else + { + r = d[2]; + m_dim[2] = uint32_t(dim); + m_dim[0] = uint32_t(2 + static_cast(dim * d[0] / d[2])); + m_dim[1] = uint32_t(2 + static_cast(dim * d[1] / d[2])); + } + + m_scale = r / (dim - 1); + double invScale = (dim - 1) / r; + + m_data = std::vector(m_dim[0] * m_dim[1] * m_dim[2], + VoxelValue::PRIMITIVE_UNDEFINED); + m_numVoxelsOnSurface = 0; + m_numVoxelsInsideSurface = 0; + m_numVoxelsOutsideSurface = 0; + + VHACD::Vect3 p[3]; + VHACD::Vect3 boxcenter; + VHACD::Vect3 pt; + const VHACD::Vect3 boxhalfsize(double(0.5)); + for (size_t t = 0; t < indices.size(); ++t) + { + size_t i0, j0, k0; + size_t i1, j1, k1; + VHACD::Vector3 tri = indices[t]; + for (int32_t c = 0; c < 3; ++c) + { + pt = points[tri[c]]; + + p[c] = (pt - m_bounds.GetMin()) * invScale; + + size_t i = static_cast(p[c][0] + double(0.5)); + size_t j = static_cast(p[c][1] + double(0.5)); + size_t k = static_cast(p[c][2] + double(0.5)); + + assert(i < m_dim[0] && j < m_dim[1] && k < m_dim[2]); + + if (c == 0) + { + i0 = i1 = i; + j0 = j1 = j; + k0 = k1 = k; + } + else + { + i0 = std::min(i0, i); + j0 = std::min(j0, j); + k0 = std::min(k0, k); + + i1 = std::max(i1, i); + j1 = std::max(j1, j); + k1 = std::max(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_id = i0; i_id < i1; ++i_id) + { + boxcenter[0] = uint32_t(i_id); + for (size_t j_id = j0; j_id < j1; ++j_id) + { + boxcenter[1] = uint32_t(j_id); + for (size_t k_id = k0; k_id < k1; ++k_id) + { + boxcenter[2] = uint32_t(k_id); + bool res = TriBoxOverlap(boxcenter, + boxhalfsize, + p[0], + p[1], + p[2]); + VoxelValue& value = GetVoxel(i_id, + j_id, + k_id); + if ( res + && value == VoxelValue::PRIMITIVE_UNDEFINED) + { + value = VoxelValue::PRIMITIVE_ON_SURFACE; + ++m_numVoxelsOnSurface; + m_surfaceVoxels.emplace_back(uint32_t(i_id), + uint32_t(j_id), + uint32_t(k_id)); + } + } + } + } + } + + if (fillMode == FillMode::SURFACE_ONLY) + { + const size_t i0_local = m_dim[0]; + const size_t j0_local = m_dim[1]; + const size_t k0_local = m_dim[2]; + for (size_t i_id = 0; i_id < i0_local; ++i_id) + { + for (size_t j_id = 0; j_id < j0_local; ++j_id) + { + for (size_t k_id = 0; k_id < k0_local; ++k_id) + { + const VoxelValue& voxel = GetVoxel(i_id, + j_id, + k_id); + if (voxel != VoxelValue::PRIMITIVE_ON_SURFACE) + { + SetVoxel(i_id, + j_id, + k_id, + VoxelValue::PRIMITIVE_OUTSIDE_SURFACE); + } + } + } + } + } + else if (fillMode == FillMode::FLOOD_FILL) + { + /* + * Marking the outside edges of the voxel cube to be outside surfaces to walk + */ + MarkOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1); + MarkOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]); + MarkOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]); + MarkOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]); + MarkOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]); + MarkOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]); + FillOutsideSurface(); + FillInsideSurface(); + } + else if (fillMode == FillMode::RAYCAST_FILL) + { + RaycastFill(aabbTree); + } +} + +void Volume::RaycastFill(const AABBTree& aabbTree) +{ + const uint32_t i0 = m_dim[0]; + const uint32_t j0 = m_dim[1]; + const uint32_t k0 = m_dim[2]; + + size_t maxSize = i0 * j0 * k0; + + std::vector temp; + temp.reserve(maxSize); + uint32_t count{ 0 }; + m_numVoxelsInsideSurface = 0; + for (uint32_t i = 0; i < i0; ++i) + { + for (uint32_t j = 0; j < j0; ++j) + { + for (uint32_t k = 0; k < k0; ++k) + { + VoxelValue& voxel = GetVoxel(i, j, k); + if (voxel != VoxelValue::PRIMITIVE_ON_SURFACE) + { + VHACD::Vect3 start = VHACD::Vect3(i, j, k) * m_scale + m_bounds.GetMin(); + + uint32_t insideCount = 0; + uint32_t outsideCount = 0; + + VHACD::Vect3 directions[6] = { + VHACD::Vect3( 1, 0, 0), + VHACD::Vect3(-1, 0, 0), // this was 1, 0, 0 in the original code, but looks wrong + VHACD::Vect3( 0, 1, 0), + VHACD::Vect3( 0, -1, 0), + VHACD::Vect3( 0, 0, 1), + VHACD::Vect3( 0, 0, -1) + }; + + for (uint32_t r = 0; r < 6; r++) + { + aabbTree.TraceRay(start, + directions[r], + insideCount, + outsideCount); + // Early out if we hit the outside of the mesh + if (outsideCount) + { + break; + } + // Early out if we accumulated 3 inside hits + if (insideCount >= 3) + { + break; + } + } + + if (outsideCount == 0 && insideCount >= 3) + { + voxel = VoxelValue::PRIMITIVE_INSIDE_SURFACE; + temp.emplace_back(i, j, k); + count++; + m_numVoxelsInsideSurface++; + } + else + { + voxel = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE; + } + } + } + } + } + + if (count) + { + m_interiorVoxels = std::move(temp); + } +} + +void Volume::SetVoxel(const size_t i, + const size_t j, + const size_t k, + VoxelValue value) +{ + assert(i < m_dim[0]); + assert(j < m_dim[1]); + assert(k < m_dim[2]); + + m_data[k + j * m_dim[2] + i * m_dim[1] * m_dim[2]] = value; +} + +VoxelValue& Volume::GetVoxel(const size_t i, + const size_t j, + const size_t k) +{ + assert(i < m_dim[0]); + assert(j < m_dim[1]); + assert(k < m_dim[2]); + return m_data[k + j * m_dim[2] + i * m_dim[1] * m_dim[2]]; +} + +const VoxelValue& Volume::GetVoxel(const size_t i, + const size_t j, + const size_t k) const +{ + assert(i < m_dim[0]); + assert(j < m_dim[1]); + assert(k < m_dim[2]); + return m_data[k + j * m_dim[2] + i * m_dim[1] * m_dim[2]]; +} + +const std::vector& Volume::GetSurfaceVoxels() const +{ + return m_surfaceVoxels; +} + +const std::vector& Volume::GetInteriorVoxels() const +{ + return m_interiorVoxels; +} + +double Volume::GetScale() const +{ + return m_scale; +} + +const VHACD::BoundsAABB& Volume::GetBounds() const +{ + return m_bounds; +} + +const VHACD::Vector3& Volume::GetDimensions() const +{ + return m_dim; +} + +void Volume::MarkOutsideSurface(const size_t i0, + const size_t j0, + const size_t k0, + const size_t i1, + const size_t j1, + const size_t k1) +{ + for (size_t i = i0; i < i1; ++i) + { + for (size_t j = j0; j < j1; ++j) + { + for (size_t k = k0; k < k1; ++k) + { + VoxelValue& v = GetVoxel(i, j, k); + if (v == VoxelValue::PRIMITIVE_UNDEFINED) + { + v = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE_TOWALK; + } + } + } + } +} + +inline void WalkForward(int64_t start, + int64_t end, + VoxelValue* ptr, + int64_t stride, + int64_t maxDistance) +{ + for (int64_t i = start, count = 0; + count < maxDistance && i < end && *ptr == VoxelValue::PRIMITIVE_UNDEFINED; + ++i, ptr += stride, ++count) + { + *ptr = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE_TOWALK; + } +} + +inline void WalkBackward(int64_t start, + int64_t end, + VoxelValue* ptr, + int64_t stride, + int64_t maxDistance) +{ + for (int64_t i = start, count = 0; + count < maxDistance && i >= end && *ptr == VoxelValue::PRIMITIVE_UNDEFINED; + --i, ptr -= stride, ++count) + { + *ptr = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE_TOWALK; + } +} + +void Volume::FillOutsideSurface() +{ + size_t voxelsWalked = 0; + const int64_t i0 = m_dim[0]; + const int64_t j0 = m_dim[1]; + const int64_t k0 = m_dim[2]; + + // Avoid striding too far in each direction to stay in L1 cache as much as possible. + // The cache size required for the walk is roughly (4 * walkDistance * 64) since + // the k direction doesn't count as it's walking byte per byte directly in a cache lines. + // ~16k is required for a walk distance of 64 in each directions. + const size_t walkDistance = 64; + + // using the stride directly instead of calling GetVoxel for each iterations saves + // a lot of multiplications and pipeline stalls due to data dependencies on imul. + const size_t istride = &GetVoxel(1, 0, 0) - &GetVoxel(0, 0, 0); + const size_t jstride = &GetVoxel(0, 1, 0) - &GetVoxel(0, 0, 0); + const size_t kstride = &GetVoxel(0, 0, 1) - &GetVoxel(0, 0, 0); + + // It might seem counter intuitive to go over the whole voxel range multiple times + // but since we do the run in memory order, it leaves us with far fewer cache misses + // than a BFS algorithm and it has the additional benefit of not requiring us to + // store and manipulate a fifo for recursion that might become huge when the number + // of voxels is large. + // This will outperform the BFS algorithm by several orders of magnitude in practice. + do + { + voxelsWalked = 0; + for (int64_t i = 0; i < i0; ++i) + { + for (int64_t j = 0; j < j0; ++j) + { + for (int64_t k = 0; k < k0; ++k) + { + VoxelValue& voxel = GetVoxel(i, j, k); + if (voxel == VoxelValue::PRIMITIVE_OUTSIDE_SURFACE_TOWALK) + { + voxelsWalked++; + voxel = VoxelValue::PRIMITIVE_OUTSIDE_SURFACE; + + // walk in each direction to mark other voxel that should be walked. + // this will generate a 3d pattern that will help the overall + // algorithm converge faster while remaining cache friendly. + WalkForward(k + 1, k0, &voxel + kstride, kstride, walkDistance); + WalkBackward(k - 1, 0, &voxel - kstride, kstride, walkDistance); + + WalkForward(j + 1, j0, &voxel + jstride, jstride, walkDistance); + WalkBackward(j - 1, 0, &voxel - jstride, jstride, walkDistance); + + WalkForward(i + 1, i0, &voxel + istride, istride, walkDistance); + WalkBackward(i - 1, 0, &voxel - istride, istride, walkDistance); + } + } + } + } + + m_numVoxelsOutsideSurface += voxelsWalked; + } while (voxelsWalked != 0); +} + +void Volume::FillInsideSurface() +{ + const uint32_t i0 = uint32_t(m_dim[0]); + const uint32_t j0 = uint32_t(m_dim[1]); + const uint32_t k0 = uint32_t(m_dim[2]); + + size_t maxSize = i0 * j0 * k0; + + std::vector temp; + temp.reserve(maxSize); + uint32_t count{ 0 }; + + for (uint32_t i = 0; i < i0; ++i) + { + for (uint32_t j = 0; j < j0; ++j) + { + for (uint32_t k = 0; k < k0; ++k) + { + VoxelValue& v = GetVoxel(i, j, k); + if (v == VoxelValue::PRIMITIVE_UNDEFINED) + { + v = VoxelValue::PRIMITIVE_INSIDE_SURFACE; + temp.emplace_back(i, j, k); + count++; + ++m_numVoxelsInsideSurface; + } + } + } + } + + if ( count ) + { + m_interiorVoxels = std::move(temp); + } +} + +//****************************************************************************************** +// ShrinkWrap helper class +//****************************************************************************************** +// This is a code snippet which 'shrinkwraps' a convex hull +// to a source mesh. +// +// It is a somewhat complicated algorithm. It works as follows: +// +// * Step #1 : Compute the mean unit normal vector for each vertex in the convex hull +// * Step #2 : For each vertex in the conex hull we project is slightly outwards along the mean normal vector +// * Step #3 : We then raycast from this slightly extruded point back into the opposite direction of the mean normal vector +// resulting in a raycast from slightly beyond the vertex in the hull into the source mesh we are trying +// to 'shrink wrap' against +// * Step #4 : If the raycast fails we leave the original vertex alone +// * Step #5 : If the raycast hits a backface we leave the original vertex alone +// * Step #6 : If the raycast hits too far away (no more than a certain threshold distance) we live it alone +// * Step #7 : If the point we hit on the source mesh is not still within the convex hull, we reject it. +// * Step #8 : If all of the previous conditions are met, then we take the raycast hit location as the 'new position' +// * Step #9 : Once all points have been projected, if possible, we need to recompute the convex hull again based on these shrinkwrapped points +// * Step #10 : In theory that should work.. let's see... + +//*********************************************************************************************** +// QuickHull implementation +//*********************************************************************************************** + +////////////////////////////////////////////////////////////////////////// +// Quickhull base class holding the hull during construction +////////////////////////////////////////////////////////////////////////// +class QuickHull +{ +public: + uint32_t ComputeConvexHull(const std::vector& vertices, + uint32_t maxHullVertices); + + const std::vector& GetVertices() const; + const std::vector& GetIndices() const; + +private: + std::vector m_vertices; + std::vector m_indices; +}; + +uint32_t QuickHull::ComputeConvexHull(const std::vector& vertices, + uint32_t maxHullVertices) +{ + m_indices.clear(); + + VHACD::ConvexHull ch(vertices, + double(0.0001), + maxHullVertices); + + auto& vlist = ch.GetVertexPool(); + if ( !vlist.empty() ) + { + size_t vcount = vlist.size(); + m_vertices.resize(vcount); + std::copy(vlist.begin(), + vlist.end(), + m_vertices.begin()); + } + + for (std::list::const_iterator node = ch.GetList().begin(); node != ch.GetList().end(); ++node) + { + const VHACD::ConvexHullFace& face = *node; + m_indices.emplace_back(face.m_index[0], + face.m_index[1], + face.m_index[2]); + } + + return uint32_t(m_indices.size()); +} + +const std::vector& QuickHull::GetVertices() const +{ + return m_vertices; +} + +const std::vector& QuickHull::GetIndices() const +{ + return m_indices; +} + +//****************************************************************************************** +// Implementation of the ShrinkWrap function +//****************************************************************************************** + +void ShrinkWrap(SimpleMesh& sourceConvexHull, + const AABBTree& aabbTree, + uint32_t maxHullVertexCount, + double distanceThreshold, + bool doShrinkWrap) +{ + std::vector verts; // New verts for the new convex hull + verts.reserve(sourceConvexHull.m_vertices.size()); + // Examine each vertex and see if it is within the voxel distance. + // If it is, then replace the point with the shrinkwrapped / projected point + for (uint32_t j = 0; j < sourceConvexHull.m_vertices.size(); j++) + { + VHACD::Vertex& p = sourceConvexHull.m_vertices[j]; + if (doShrinkWrap) + { + VHACD::Vect3 closest; + if (aabbTree.GetClosestPointWithinDistance(p, distanceThreshold, closest)) + { + p = closest; + } + } + verts.emplace_back(p); + } + // Final step is to recompute the convex hull + VHACD::QuickHull qh; + uint32_t tcount = qh.ComputeConvexHull(verts, + maxHullVertexCount); + if (tcount) + { + sourceConvexHull.m_vertices = qh.GetVertices(); + sourceConvexHull.m_indices = qh.GetIndices(); + } +} + +//******************************************************************************************************************** + +#if !VHACD_DISABLE_THREADING + +//******************************************************************************************************************** +// Definition of the ThreadPool +//******************************************************************************************************************** + +class ThreadPool { + public: + ThreadPool(); + ThreadPool(int worker); + ~ThreadPool(); + template + auto enqueue(F&& f, Args&& ... args) +#ifndef __cpp_lib_is_invocable + -> std::future< typename std::result_of< F( Args... ) >::type>; +#else + -> std::future< typename std::invoke_result_t>; +#endif + private: + std::vector workers; + std::deque> tasks; + std::mutex task_mutex; + std::condition_variable cv; + bool closed; + int count; +}; + +ThreadPool::ThreadPool() + : ThreadPool(1) +{ +} + +ThreadPool::ThreadPool(int worker) + : closed(false) + , count(0) +{ + workers.reserve(worker); + for(int i=0; i lock(this->task_mutex); + while(true) + { + while (this->tasks.empty()) + { + if (this->closed) + { + return; + } + this->cv.wait(lock); + } + auto task = this->tasks.front(); + this->tasks.pop_front(); + lock.unlock(); + task(); + lock.lock(); + } + } + ); + } +} + +template +auto ThreadPool::enqueue(F&& f, Args&& ... args) +#ifndef __cpp_lib_is_invocable + -> std::future< typename std::result_of< F( Args... ) >::type> +#else + -> std::future< typename std::invoke_result_t> +#endif +{ + +#ifndef __cpp_lib_is_invocable + using return_type = typename std::result_of< F( Args... ) >::type; +#else + using return_type = typename std::invoke_result_t< F, Args... >; +#endif + auto task = std::make_shared > ( + std::bind(std::forward(f), std::forward(args)...) + ); + auto result = task->get_future(); + + { + std::unique_lock lock(task_mutex); + if (!closed) + { + tasks.emplace_back([task] + { + (*task)(); + }); + cv.notify_one(); + } + } + + return result; +} + +ThreadPool::~ThreadPool() { + { + std::unique_lock lock(task_mutex); + closed = true; + } + cv.notify_all(); + for (auto && worker : workers) + { + worker.join(); + } +} +#endif + +enum class Stages +{ + COMPUTE_BOUNDS_OF_INPUT_MESH, + REINDEXING_INPUT_MESH, + CREATE_RAYCAST_MESH, + VOXELIZING_INPUT_MESH, + BUILD_INITIAL_CONVEX_HULL, + PERFORMING_DECOMPOSITION, + INITIALIZING_CONVEX_HULLS_FOR_MERGING, + COMPUTING_COST_MATRIX, + MERGING_CONVEX_HULLS, + FINALIZING_RESULTS, + NUM_STAGES +}; + +class VHACDCallbacks +{ +public: + virtual void ProgressUpdate(Stages stage, + double stageProgress, + const char *operation) = 0; + virtual bool IsCanceled() const = 0; + + virtual ~VHACDCallbacks() = default; +}; + +enum class SplitAxis +{ + X_AXIS_NEGATIVE, + X_AXIS_POSITIVE, + Y_AXIS_NEGATIVE, + Y_AXIS_POSITIVE, + Z_AXIS_NEGATIVE, + Z_AXIS_POSITIVE, +}; + +// This class represents a collection of voxels, the convex hull +// which surrounds them, and a triangle mesh representation of those voxels +class VoxelHull +{ +public: + + // This method constructs a new VoxelHull based on a plane split of the parent + // convex hull + VoxelHull(const VoxelHull& parent, + SplitAxis axis, + uint32_t splitLoc); + + // Here we construct the initial convex hull around the + // entire voxel set + VoxelHull(Volume& voxels, + const IVHACD::Parameters ¶ms, + VHACDCallbacks *callbacks); + + ~VoxelHull() = default; + + // Helper method to refresh the min/max voxel bounding region + void MinMaxVoxelRegion(const Voxel &v); + + void BuildRaycastMesh(); + + // We now compute the convex hull relative to a triangle mesh generated + // from the voxels + void ComputeConvexHull(); + + // Returns true if this convex hull should be considered done + bool IsComplete(); + + + // Convert a voxel position into it's correct double precision location + VHACD::Vect3 GetPoint(const int32_t x, + const int32_t y, + const int32_t z, + const double scale, + const VHACD::Vect3& bmin) const; + + // Sees if we have already got an index for this voxel position. + // If the voxel position has already been indexed, we just return + // that index value. + // If not, then we convert it into the floating point position and + // add it to the index map + uint32_t GetVertexIndex(const VHACD::Vector3& p); + + // This method will convert the voxels into an actual indexed triangle mesh of boxes + // This serves two purposes. + // The primary purpose is so that when we compute a convex hull it considered all of the points + // for each voxel, not just the center point. If you don't do this, then the hulls don't fit the + // mesh accurately enough. + // The second reason we convert it into a triangle mesh is so that we can do raycasting against it + // to search for the best splitting plane fairly quickly. That algorithm will be discussed in the + // method which computes the best splitting plane. + void BuildVoxelMesh(); + + // Convert a single voxel position into an actual 3d box mesh comprised + // of 12 triangles + void AddVoxelBox(const Voxel &v); + + // Add the triangle represented by these 3 indices into the 'box' set of vertices + // to the output mesh + void AddTri(const std::array, 8>& box, + uint32_t i1, + uint32_t i2, + uint32_t i3); + + // Here we convert from voxel space to a 3d position, index it, and add + // the triangle positions and indices for the output mesh + void AddTriangle(const VHACD::Vector3& p1, + const VHACD::Vector3& p2, + const VHACD::Vector3& p3); + + // When computing the split plane, we start by simply + // taking the midpoint of the longest side. However, + // we can also search the surface and look for the greatest + // spot of concavity and use that as the split location. + // This will make the convex decomposition more efficient + // as it will tend to cut across the greatest point of + // concavity on the surface. + SplitAxis ComputeSplitPlane(uint32_t& location); + + VHACD::Vect3 GetPosition(const VHACD::Vector3& ip) const; + + double Raycast(const VHACD::Vector3& p1, + const VHACD::Vector3& p2) const; + + bool FindConcavity(uint32_t idx, + uint32_t& splitLoc); + + // Finding the greatest area of concavity.. + bool FindConcavityX(uint32_t& splitLoc); + + // Finding the greatest area of concavity.. + bool FindConcavityY(uint32_t& splitLoc); + + // Finding the greatest area of concavity.. + bool FindConcavityZ(uint32_t& splitLoc); + + // This operation is performed in a background thread. + // It splits the voxels by a plane + void PerformPlaneSplit(); + + // Used only for debugging. Saves the voxelized mesh to disk + // Optionally saves the original source mesh as well for comparison + void SaveVoxelMesh(const SimpleMesh& inputMesh, + bool saveVoxelMesh, + bool saveSourceMesh); + + void SaveOBJ(const char* fname, + const VoxelHull* h); + + void SaveOBJ(const char* fname); + +private: + void WriteOBJ(FILE* fph, + const std::vector& vertices, + const std::vector& indices, + uint32_t baseIndex); +public: + + SplitAxis m_axis{ SplitAxis::X_AXIS_NEGATIVE }; + Volume* m_voxels{ nullptr }; // The voxelized data set + double m_voxelScale{ 0 }; // Size of a single voxel + double m_voxelScaleHalf{ 0 }; // 1/2 of the size of a single voxel + VHACD::BoundsAABB m_voxelBounds; + VHACD::Vect3 m_voxelAdjust; // Minimum coordinates of the voxel space, with adjustment + uint32_t m_depth{ 0 }; // How deep in the recursion of the binary tree this hull is + uint32_t m_index{ 0 }; // Each convex hull is given a unique id to distinguish it from the others + double m_volumeError{ 0 }; // The percentage error from the convex hull volume vs. the voxel volume + double m_voxelVolume{ 0 }; // The volume of the voxels + double m_hullVolume{ 0 }; // The volume of the enclosing convex hull + + std::unique_ptr m_convexHull{ nullptr }; // The convex hull which encloses this set of voxels. + std::vector m_surfaceVoxels; // The voxels which are on the surface of the source mesh. + std::vector m_newSurfaceVoxels; // Voxels which are on the surface as a result of a plane split + std::vector m_interiorVoxels; // Voxels which are part of the interior of the hull + + std::unique_ptr m_hullA{ nullptr }; // hull resulting from one side of the plane split + std::unique_ptr m_hullB{ nullptr }; // hull resulting from the other side of the plane split + + // Defines the coordinates this convex hull comprises within the voxel volume + // of the entire source + VHACD::Vector3 m_1{ 0 }; + VHACD::Vector3 m_2{ 0 }; + AABBTree m_AABBTree; + std::unordered_map m_voxelIndexMap; // Maps from a voxel coordinate space into a vertex index space + std::vector m_vertices; + std::vector m_indices; + static uint32_t m_voxelHullCount; + IVHACD::Parameters m_params; + VHACDCallbacks* m_callbacks{ nullptr }; +}; + +uint32_t VoxelHull::m_voxelHullCount = 0; + +VoxelHull::VoxelHull(const VoxelHull& parent, + SplitAxis axis, + uint32_t splitLoc) + : m_axis(axis) + , m_voxels(parent.m_voxels) + , m_voxelScale(m_voxels->GetScale()) + , m_voxelScaleHalf(m_voxelScale * double(0.5)) + , m_voxelBounds(m_voxels->GetBounds()) + , m_voxelAdjust(m_voxelBounds.GetMin() - m_voxelScaleHalf) + , m_depth(parent.m_depth + 1) + , m_index(++m_voxelHullCount) + , m_1(parent.m_1) + , m_2(parent.m_2) + , m_params(parent.m_params) +{ + // Default copy the voxel region from the parent, but values will + // be adjusted next based on the split axis and location + switch ( m_axis ) + { + case SplitAxis::X_AXIS_NEGATIVE: + m_2.GetX() = splitLoc; + break; + case SplitAxis::X_AXIS_POSITIVE: + m_1.GetX() = splitLoc + 1; + break; + case SplitAxis::Y_AXIS_NEGATIVE: + m_2.GetY() = splitLoc; + break; + case SplitAxis::Y_AXIS_POSITIVE: + m_1.GetY() = splitLoc + 1; + break; + case SplitAxis::Z_AXIS_NEGATIVE: + m_2.GetZ() = splitLoc; + break; + case SplitAxis::Z_AXIS_POSITIVE: + m_1.GetZ() = splitLoc + 1; + break; + } + + // First, we copy all of the interior voxels from our parent + // which intersect our region + for (auto& i : parent.m_interiorVoxels) + { + VHACD::Vector3 v = i.GetVoxel(); + if (v.CWiseAllGE(m_1) && v.CWiseAllLE(m_2)) + { + bool newSurface = false; + switch ( m_axis ) + { + case SplitAxis::X_AXIS_NEGATIVE: + if ( v.GetX() == splitLoc ) + { + newSurface = true; + } + break; + case SplitAxis::X_AXIS_POSITIVE: + if ( v.GetX() == m_1.GetX() ) + { + newSurface = true; + } + break; + case SplitAxis::Y_AXIS_NEGATIVE: + if ( v.GetY() == splitLoc ) + { + newSurface = true; + } + break; + case SplitAxis::Y_AXIS_POSITIVE: + if ( v.GetY() == m_1.GetY() ) + { + newSurface = true; + } + break; + case SplitAxis::Z_AXIS_NEGATIVE: + if ( v.GetZ() == splitLoc ) + { + newSurface = true; + } + break; + case SplitAxis::Z_AXIS_POSITIVE: + if ( v.GetZ() == m_1.GetZ() ) + { + newSurface = true; + } + break; + } + // If his interior voxels lie directly on the split plane then + // these become new surface voxels for our patch + if ( newSurface ) + { + m_newSurfaceVoxels.push_back(i); + } + else + { + m_interiorVoxels.push_back(i); + } + } + } + // Next we copy all of the surface voxels which intersect our region + for (auto& i : parent.m_surfaceVoxels) + { + VHACD::Vector3 v = i.GetVoxel(); + if (v.CWiseAllGE(m_1) && v.CWiseAllLE(m_2)) + { + m_surfaceVoxels.push_back(i); + } + } + // Our parent's new surface voxels become our new surface voxels so long as they intersect our region + for (auto& i : parent.m_newSurfaceVoxels) + { + VHACD::Vector3 v = i.GetVoxel(); + if (v.CWiseAllGE(m_1) && v.CWiseAllLE(m_2)) + { + m_newSurfaceVoxels.push_back(i); + } + } + + // Recompute the min-max bounding box which would be different after the split occurs + m_1 = VHACD::Vector3(0x7FFFFFFF); + m_2 = VHACD::Vector3(0); + for (auto& i : m_surfaceVoxels) + { + MinMaxVoxelRegion(i); + } + for (auto& i : m_newSurfaceVoxels) + { + MinMaxVoxelRegion(i); + } + for (auto& i : m_interiorVoxels) + { + MinMaxVoxelRegion(i); + } + + BuildVoxelMesh(); + BuildRaycastMesh(); // build a raycast mesh of the voxel mesh + ComputeConvexHull(); +} + +VoxelHull::VoxelHull(Volume& voxels, + const IVHACD::Parameters& params, + VHACDCallbacks* callbacks) + : m_voxels(&voxels) + , m_voxelScale(m_voxels->GetScale()) + , m_voxelScaleHalf(m_voxelScale * double(0.5)) + , m_voxelBounds(m_voxels->GetBounds()) + , m_voxelAdjust(m_voxelBounds.GetMin() - m_voxelScaleHalf) + , m_index(++m_voxelHullCount) + // Here we get a copy of all voxels which lie on the surface mesh + , m_surfaceVoxels(m_voxels->GetSurfaceVoxels()) + // Now we get a copy of all voxels which are considered part of the 'inVoxelizeterior' of the source mesh + , m_interiorVoxels(m_voxels->GetInteriorVoxels()) + , m_2(m_voxels->GetDimensions() - 1) + , m_params(params) + , m_callbacks(callbacks) +{ + BuildVoxelMesh(); + BuildRaycastMesh(); // build a raycast mesh of the voxel mesh + ComputeConvexHull(); +} + +void VoxelHull::MinMaxVoxelRegion(const Voxel& v) +{ + VHACD::Vector3 x = v.GetVoxel(); + m_1 = m_1.CWiseMin(x); + m_2 = m_2.CWiseMax(x); +} + +void VoxelHull::BuildRaycastMesh() +{ + // Create a raycast mesh representation of the voxelized surface mesh + if ( !m_indices.empty() ) + { + m_AABBTree = AABBTree(m_vertices, + m_indices); + } +} + +void VoxelHull::ComputeConvexHull() +{ + if ( !m_vertices.empty() ) + { + // we compute the convex hull as follows... + VHACD::QuickHull qh; + uint32_t tcount = qh.ComputeConvexHull(m_vertices, + uint32_t(m_vertices.size())); + if ( tcount ) + { + m_convexHull = std::unique_ptr(new IVHACD::ConvexHull); + + m_convexHull->m_points = qh.GetVertices(); + m_convexHull->m_triangles = qh.GetIndices(); + + VHACD::ComputeCentroid(m_convexHull->m_points, + m_convexHull->m_triangles, + m_convexHull->m_center); + m_convexHull->m_volume = VHACD::ComputeMeshVolume(m_convexHull->m_points, + m_convexHull->m_triangles); + } + } + if ( m_convexHull ) + { + m_hullVolume = m_convexHull->m_volume; + } + // This is the volume of a single voxel + double singleVoxelVolume = m_voxelScale * m_voxelScale * m_voxelScale; + size_t voxelCount = m_interiorVoxels.size() + m_newSurfaceVoxels.size() + m_surfaceVoxels.size(); + m_voxelVolume = singleVoxelVolume * double(voxelCount); + double diff = fabs(m_hullVolume - m_voxelVolume); + m_volumeError = (diff * 100) / m_voxelVolume; +} + +bool VoxelHull::IsComplete() +{ + bool ret = false; + if ( m_convexHull == nullptr ) + { + ret = true; + } + else if ( m_volumeError < m_params.m_minimumVolumePercentErrorAllowed ) + { + ret = true; + } + else if ( m_depth > m_params.m_maxRecursionDepth ) + { + ret = true; + } + else + { + // We compute the voxel width on all 3 axes and see if they are below the min threshold size + VHACD::Vector3 d = m_2 - m_1; + if ( d.GetX() <= m_params.m_minEdgeLength && + d.GetY() <= m_params.m_minEdgeLength && + d.GetZ() <= m_params.m_minEdgeLength ) + { + ret = true; + } + } + return ret; +} + +VHACD::Vect3 VoxelHull::GetPoint(const int32_t x, + const int32_t y, + const int32_t z, + const double scale, + const VHACD::Vect3& bmin) const +{ + return VHACD::Vect3(x * scale + bmin.GetX(), + y * scale + bmin.GetY(), + z * scale + bmin.GetZ()); +} + +uint32_t VoxelHull::GetVertexIndex(const VHACD::Vector3& p) +{ + uint32_t ret = 0; + uint32_t address = (p.GetX() << 20) | (p.GetY() << 10) | p.GetZ(); + auto found = m_voxelIndexMap.find(address); + if ( found != m_voxelIndexMap.end() ) + { + ret = found->second; + } + else + { + VHACD::Vect3 vertex = GetPoint(p.GetX(), + p.GetY(), + p.GetZ(), + m_voxelScale, + m_voxelAdjust); + ret = uint32_t(m_voxelIndexMap.size()); + m_voxelIndexMap[address] = ret; + m_vertices.emplace_back(vertex); + } + return ret; +} + +void VoxelHull::BuildVoxelMesh() +{ + // When we build the triangle mesh we do *not* need the interior voxels, only the ones + // which lie upon the logical surface of the mesh. + // Each time we perform a plane split, voxels which are along the splitting plane become + // 'new surface voxels'. + + for (auto& i : m_surfaceVoxels) + { + AddVoxelBox(i); + } + for (auto& i : m_newSurfaceVoxels) + { + AddVoxelBox(i); + } +} + +void VoxelHull::AddVoxelBox(const Voxel &v) +{ + // The voxel position of the upper left corner of the box + VHACD::Vector3 bmin(v.GetX(), + v.GetY(), + v.GetZ()); + // The voxel position of the lower right corner of the box + VHACD::Vector3 bmax(bmin.GetX() + 1, + bmin.GetY() + 1, + bmin.GetZ() + 1); + + // Build the set of 8 voxel positions representing + // the coordinates of the box + std::array, 8> box{{ + { bmin.GetX(), bmin.GetY(), bmin.GetZ() }, + { bmax.GetX(), bmin.GetY(), bmin.GetZ() }, + { bmax.GetX(), bmax.GetY(), bmin.GetZ() }, + { bmin.GetX(), bmax.GetY(), bmin.GetZ() }, + { bmin.GetX(), bmin.GetY(), bmax.GetZ() }, + { bmax.GetX(), bmin.GetY(), bmax.GetZ() }, + { bmax.GetX(), bmax.GetY(), bmax.GetZ() }, + { bmin.GetX(), bmax.GetY(), bmax.GetZ() } + }}; + + // Now add the 12 triangles comprising the 3d box + AddTri(box, 2, 1, 0); + AddTri(box, 3, 2, 0); + + AddTri(box, 7, 2, 3); + AddTri(box, 7, 6, 2); + + AddTri(box, 5, 1, 2); + AddTri(box, 5, 2, 6); + + AddTri(box, 5, 4, 1); + AddTri(box, 4, 0, 1); + + AddTri(box, 4, 6, 7); + AddTri(box, 4, 5, 6); + + AddTri(box, 4, 7, 0); + AddTri(box, 7, 3, 0); +} + +void VoxelHull::AddTri(const std::array, 8>& box, + uint32_t i1, + uint32_t i2, + uint32_t i3) +{ + AddTriangle(box[i1], box[i2], box[i3]); +} + +void VoxelHull::AddTriangle(const VHACD::Vector3& p1, + const VHACD::Vector3& p2, + const VHACD::Vector3& p3) +{ + uint32_t i1 = GetVertexIndex(p1); + uint32_t i2 = GetVertexIndex(p2); + uint32_t i3 = GetVertexIndex(p3); + + m_indices.emplace_back(i1, i2, i3); +} + +SplitAxis VoxelHull::ComputeSplitPlane(uint32_t& location) +{ + SplitAxis ret = SplitAxis::X_AXIS_NEGATIVE; + + VHACD::Vector3 d = m_2 - m_1; + + if ( d.GetX() >= d.GetY() && d.GetX() >= d.GetZ() ) + { + ret = SplitAxis::X_AXIS_NEGATIVE; + location = (m_2.GetX() + 1 + m_1.GetX()) / 2; + uint32_t edgeLoc; + if ( m_params.m_findBestPlane && FindConcavityX(edgeLoc) ) + { + location = edgeLoc; + } + } + else if ( d.GetY() >= d.GetX() && d.GetY() >= d.GetZ() ) + { + ret = SplitAxis::Y_AXIS_NEGATIVE; + location = (m_2.GetY() + 1 + m_1.GetY()) / 2; + uint32_t edgeLoc; + if ( m_params.m_findBestPlane && FindConcavityY(edgeLoc) ) + { + location = edgeLoc; + } + } + else + { + ret = SplitAxis::Z_AXIS_NEGATIVE; + location = (m_2.GetZ() + 1 + m_1.GetZ()) / 2; + uint32_t edgeLoc; + if ( m_params.m_findBestPlane && FindConcavityZ(edgeLoc) ) + { + location = edgeLoc; + } + } + + return ret; +} + +VHACD::Vect3 VoxelHull::GetPosition(const VHACD::Vector3& ip) const +{ + return GetPoint(ip.GetX(), + ip.GetY(), + ip.GetZ(), + m_voxelScale, + m_voxelAdjust); +} + +double VoxelHull::Raycast(const VHACD::Vector3& p1, + const VHACD::Vector3& p2) const +{ + double ret; + VHACD::Vect3 from = GetPosition(p1); + VHACD::Vect3 to = GetPosition(p2); + + double outT; + double faceSign; + VHACD::Vect3 hitLocation; + if (m_AABBTree.TraceRay(from, to, outT, faceSign, hitLocation)) + { + ret = (from - hitLocation).GetNorm(); + } + else + { + ret = 0; // if it doesn't hit anything, just assign it to zero. + } + + return ret; +} + +bool VoxelHull::FindConcavity(uint32_t idx, + uint32_t& splitLoc) +{ + bool ret = false; + + int32_t d = (m_2[idx] - m_1[idx]) + 1; // The length of the getX axis in voxel space + + uint32_t idx1; + uint32_t idx2; + uint32_t idx3; + switch (idx) + { + case 0: // X + idx1 = 0; + idx2 = 1; + idx3 = 2; + break; + case 1: // Y + idx1 = 1; + idx2 = 0; + idx3 = 2; + break; + case 2: + idx1 = 2; + idx2 = 1; + idx3 = 0; + break; + default: + /* + * To silence uninitialized variable warnings + */ + idx1 = 0; + idx2 = 0; + idx3 = 0; + assert(0 && "findConcavity::idx must be 0, 1, or 2"); + break; + } + + // We will compute the edge error on the XY plane and the XZ plane + // searching for the greatest location of concavity + std::vector edgeError1 = std::vector(d); + std::vector edgeError2 = std::vector(d); + + // Counter of number of voxel samples on the XY plane we have accumulated + uint32_t index1 = 0; + + // Compute Edge Error on the XY plane + for (uint32_t i0 = m_1[idx1]; i0 <= m_2[idx1]; i0++) + { + double errorTotal = 0; + // We now perform a raycast from the sides inward on the XY plane to + // determine the total error (distance of the surface from the sides) + // along this getX position. + for (uint32_t i1 = m_1[idx2]; i1 <= m_2[idx2]; i1++) + { + VHACD::Vector3 p1; + VHACD::Vector3 p2; + switch (idx) + { + case 0: + { + p1 = VHACD::Vector3(i0, i1, m_1.GetZ() - 2); + p2 = VHACD::Vector3(i0, i1, m_2.GetZ() + 2); + break; + } + case 1: + { + p1 = VHACD::Vector3(i1, i0, m_1.GetZ() - 2); + p2 = VHACD::Vector3(i1, i0, m_2.GetZ() + 2); + break; + } + case 2: + { + p1 = VHACD::Vector3(m_1.GetX() - 2, i1, i0); + p2 = VHACD::Vector3(m_2.GetX() + 2, i1, i0); + break; + } + } + + double e1 = Raycast(p1, p2); + double e2 = Raycast(p2, p1); + + errorTotal = errorTotal + e1 + e2; + } + // The total amount of edge error along this voxel location + edgeError1[index1] = errorTotal; + index1++; + } + + // Compute edge error along the XZ plane + uint32_t index2 = 0; + + for (uint32_t i0 = m_1[idx1]; i0 <= m_2[idx1]; i0++) + { + double errorTotal = 0; + + for (uint32_t i1 = m_1[idx3]; i1 <= m_2[idx3]; i1++) + { + VHACD::Vector3 p1; + VHACD::Vector3 p2; + switch (idx) + { + case 0: + { + p1 = VHACD::Vector3(i0, m_1.GetY() - 2, i1); + p2 = VHACD::Vector3(i0, m_2.GetY() + 2, i1); + break; + } + case 1: + { + p1 = VHACD::Vector3(m_1.GetX() - 2, i0, i1); + p2 = VHACD::Vector3(m_2.GetX() + 2, i0, i1); + break; + } + case 2: + { + p1 = VHACD::Vector3(i1, m_1.GetY() - 2, i0); + p2 = VHACD::Vector3(i1, m_2.GetY() + 2, i0); + break; + } + } + + double e1 = Raycast(p1, p2); // raycast from one side to the interior + double e2 = Raycast(p2, p1); // raycast from the other side to the interior + + errorTotal = errorTotal + e1 + e2; + } + edgeError2[index2] = errorTotal; + index2++; + } + + + // we now compute the first derivative to find the greatest spot of concavity on the XY plane + double maxDiff = 0; + uint32_t maxC = 0; + for (uint32_t x = 1; x < index1; x++) + { + if ( edgeError1[x] > 0 && edgeError1[x - 1] > 0 ) + { + double diff = abs(edgeError1[x] - edgeError1[x - 1]); + if ( diff > maxDiff ) + { + maxDiff = diff; + maxC = x-1; + } + } + } + + // Now see if there is a greater concavity on the XZ plane + for (uint32_t x = 1; x < index2; x++) + { + if ( edgeError2[x] > 0 && edgeError2[x - 1] > 0 ) + { + double diff = abs(edgeError2[x] - edgeError2[x - 1]); + if ( diff > maxDiff ) + { + maxDiff = diff; + maxC = x - 1; + } + } + } + + splitLoc = maxC + m_1[idx1]; + + // we do not allow an edge split if it is too close to the ends + if ( splitLoc > (m_1[idx1] + 4) + && splitLoc < (m_2[idx1] - 4) ) + { + ret = true; + } + + return ret; +} + +// Finding the greatest area of concavity.. +bool VoxelHull::FindConcavityX(uint32_t& splitLoc) +{ + return FindConcavity(0, splitLoc); +} + +// Finding the greatest area of concavity.. +bool VoxelHull::FindConcavityY(uint32_t& splitLoc) +{ + return FindConcavity(1, splitLoc); +} + +// Finding the greatest area of concavity.. +bool VoxelHull::FindConcavityZ(uint32_t &splitLoc) +{ + return FindConcavity(2, splitLoc); +} + +void VoxelHull::PerformPlaneSplit() +{ + if ( IsComplete() ) + { + } + else + { + uint32_t splitLoc; + SplitAxis axis = ComputeSplitPlane(splitLoc); + switch ( axis ) + { + case SplitAxis::X_AXIS_NEGATIVE: + case SplitAxis::X_AXIS_POSITIVE: + // Split on the getX axis at this split location + m_hullA = std::unique_ptr(new VoxelHull(*this, SplitAxis::X_AXIS_NEGATIVE, splitLoc)); + m_hullB = std::unique_ptr(new VoxelHull(*this, SplitAxis::X_AXIS_POSITIVE, splitLoc)); + break; + case SplitAxis::Y_AXIS_NEGATIVE: + case SplitAxis::Y_AXIS_POSITIVE: + // Split on the 1 axis at this split location + m_hullA = std::unique_ptr(new VoxelHull(*this, SplitAxis::Y_AXIS_NEGATIVE, splitLoc)); + m_hullB = std::unique_ptr(new VoxelHull(*this, SplitAxis::Y_AXIS_POSITIVE, splitLoc)); + break; + case SplitAxis::Z_AXIS_NEGATIVE: + case SplitAxis::Z_AXIS_POSITIVE: + // Split on the getZ axis at this split location + m_hullA = std::unique_ptr(new VoxelHull(*this, SplitAxis::Z_AXIS_NEGATIVE, splitLoc)); + m_hullB = std::unique_ptr(new VoxelHull(*this, SplitAxis::Z_AXIS_POSITIVE, splitLoc)); + break; + } + } +} + +void VoxelHull::SaveVoxelMesh(const SimpleMesh &inputMesh, + bool saveVoxelMesh, + bool saveSourceMesh) +{ + char scratch[512]; + snprintf(scratch, + sizeof(scratch), + "voxel-mesh-%03d.obj", + m_index); + FILE *fph = fopen(scratch, + "wb"); + if ( fph ) + { + uint32_t baseIndex = 1; + if ( saveVoxelMesh ) + { + WriteOBJ(fph, + m_vertices, + m_indices, + baseIndex); + baseIndex += uint32_t(m_vertices.size()); + } + if ( saveSourceMesh ) + { + WriteOBJ(fph, + inputMesh.m_vertices, + inputMesh.m_indices, + baseIndex); + } + fclose(fph); + } +} + +void VoxelHull::SaveOBJ(const char* fname, + const VoxelHull* h) +{ + FILE *fph = fopen(fname,"wb"); + if ( fph ) + { + uint32_t baseIndex = 1; + WriteOBJ(fph, + m_vertices, + m_indices, + baseIndex); + + baseIndex += uint32_t(m_vertices.size()); + + WriteOBJ(fph, + h->m_vertices, + h->m_indices, + baseIndex); + fclose(fph); + } +} + +void VoxelHull::SaveOBJ(const char *fname) +{ + FILE *fph = fopen(fname, "wb"); + if ( fph ) + { + printf("Saving '%s' with %d vertices and %d triangles\n", + fname, + uint32_t(m_vertices.size()), + uint32_t(m_indices.size())); + WriteOBJ(fph, + m_vertices, + m_indices, + 1); + fclose(fph); + } +} + +void VoxelHull::WriteOBJ(FILE* fph, + const std::vector& vertices, + const std::vector& indices, + uint32_t baseIndex) +{ + if (!fph) + { + return; + } + + for (size_t i = 0; i < vertices.size(); ++i) + { + const VHACD::Vertex& v = vertices[i]; + fprintf(fph, "v %0.9f %0.9f %0.9f\n", + v.mX, + v.mY, + v.mZ); + } + + for (size_t i = 0; i < indices.size(); ++i) + { + const VHACD::Triangle& t = indices[i]; + fprintf(fph, "f %d %d %d\n", + t.mI0 + baseIndex, + t.mI1 + baseIndex, + t.mI2 + baseIndex); + } +} + +class VHACDImpl; + +// This class represents a single task to compute the volume error +// of two convex hulls combined +class CostTask +{ +public: + VHACDImpl* m_this{ nullptr }; + IVHACD::ConvexHull* m_hullA{ nullptr }; + IVHACD::ConvexHull* m_hullB{ nullptr }; + double m_concavity{ 0 }; // concavity of the two combined + std::future m_future; +}; + +class HullPair +{ +public: + HullPair() = default; + HullPair(uint32_t hullA, + uint32_t hullB, + double concavity); + + bool operator<(const HullPair &h) const; + + uint32_t m_hullA{ 0 }; + uint32_t m_hullB{ 0 }; + double m_concavity{ 0 }; +}; + +HullPair::HullPair(uint32_t hullA, + uint32_t hullB, + double concavity) + : m_hullA(hullA) + , m_hullB(hullB) + , m_concavity(concavity) +{ +} + +bool HullPair::operator<(const HullPair &h) const +{ + return m_concavity > h.m_concavity ? true : false; +} + +// void jobCallback(void* userPtr); + +class VHACDImpl : public IVHACD, public VHACDCallbacks +{ + // Don't consider more than 100,000 convex hulls. + static constexpr uint32_t MaxConvexHullFragments{ 100000 }; +public: + VHACDImpl() = default; + + /* + * Overrides VHACD::IVHACD + */ + ~VHACDImpl() override + { + Clean(); + } + + void Cancel() override final; + + bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) override final; + + bool Compute(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) override final; + + uint32_t GetNConvexHulls() const override final; + + bool GetConvexHull(const uint32_t index, + ConvexHull& ch) const override final; + + void Clean() override final; // release internally allocated memory + + void Release() override final; + + // 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. + bool ComputeCenterOfMass(double centerOfMass[3]) const override final; + + // 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. + bool IsReady(void) const override final; + + /** + * At the request of LegionFu : out_look@foxmail.com + * This method will return which convex hull is closest to the source position. + * You can use this method to figure out, for example, which vertices in the original + * source mesh are best associated with which convex hull. + * + * @param pos : The input 3d position to test against + * + * @return : Returns which convex hull this position is closest to. + */ + uint32_t findNearestConvexHull(const double pos[3], + double& distanceToHull) override final; + +// private: + bool Compute(const std::vector& points, + const std::vector& triangles, + const Parameters& params); + + // Take the source position, normalize it, and then convert it into an index position + uint32_t GetIndex(VHACD::VertexIndex& vi, + const VHACD::Vertex& p); + + // This copies the input mesh while scaling the input positions + // to fit into a normalized unit cube. It also re-indexes all of the + // vertex positions in case they weren't clean coming in. + void CopyInputMesh(const std::vector& points, + const std::vector& triangles); + + void ScaleOutputConvexHull(ConvexHull &ch); + + void AddCostToPriorityQueue(CostTask& task); + + void ReleaseConvexHull(ConvexHull* ch); + + void PerformConvexDecomposition(); + + double ComputeConvexHullVolume(const ConvexHull& sm); + + double ComputeVolume4(const VHACD::Vect3& a, + const VHACD::Vect3& b, + const VHACD::Vect3& c, + const VHACD::Vect3& d); + + double ComputeConcavity(double volumeSeparate, + double volumeCombined, + double volumeMesh); + + // See if we can compute the cost without having to actually merge convex hulls. + // If the axis aligned bounding boxes (slightly inflated) of the two convex hulls + // do not intersect, then we don't need to actually compute the merged convex hull + // volume. + bool DoFastCost(CostTask& mt); + + void PerformMergeCostTask(CostTask& mt); + + ConvexHull* ComputeReducedConvexHull(const ConvexHull& ch, + uint32_t maxVerts, + bool projectHullVertices); + + // Take the points in convex hull A and the points in convex hull B and generate + // a new convex hull on the combined set of points. + // Once completed, we create a SimpleMesh instance to hold the triangle mesh + // and we compute an inflated AABB for it. + ConvexHull* ComputeCombinedConvexHull(const ConvexHull& sm1, + const ConvexHull& sm2); + + + ConvexHull* GetHull(uint32_t index); + + bool RemoveHull(uint32_t index); + + ConvexHull* CopyConvexHull(const ConvexHull& source); + + const char* GetStageName(Stages stage) const; + + /* + * Overrides VHACD::VHACDCallbacks + */ + void ProgressUpdate(Stages stage, + double stageProgress, + const char* operation) override final; + + bool IsCanceled() const override final; + + std::atomic m_canceled{ false }; + Parameters m_params; // Convex decomposition parameters + + std::vector m_convexHulls; // Finalized convex hulls + std::vector> m_voxelHulls; // completed voxel hulls + std::vector> m_pendingHulls; + + std::vector> m_trees; + VHACD::AABBTree m_AABBTree; + VHACD::Volume m_voxelize; + VHACD::Vect3 m_center; + double m_scale{ double(1.0) }; + double m_recipScale{ double(1.0) }; + SimpleMesh m_inputMesh; // re-indexed and normalized input mesh + std::vector m_vertices; + std::vector m_indices; + + double m_overallHullVolume{ double(0.0) }; + double m_voxelScale{ double(0.0) }; + double m_voxelHalfScale{ double(0.0) }; + VHACD::Vect3 m_voxelBmin; + VHACD::Vect3 m_voxelBmax; + uint32_t m_meshId{ 0 }; + std::priority_queue m_hullPairQueue; +#if !VHACD_DISABLE_THREADING + std::unique_ptr m_threadPool{ nullptr }; +#endif + std::unordered_map m_hulls; + + double m_overallProgress{ double(0.0) }; + double m_stageProgress{ double(0.0) }; + double m_operationProgress{ double(0.0) }; +}; + +void VHACDImpl::Cancel() +{ + m_canceled = true; +} + +bool VHACDImpl::Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) +{ + std::vector v; + v.reserve(countPoints); + for (uint32_t i = 0; i < countPoints; ++i) + { + v.emplace_back(points[i * 3 + 0], + points[i * 3 + 1], + points[i * 3 + 2]); + } + + std::vector t; + t.reserve(countTriangles); + for (uint32_t i = 0; i < countTriangles; ++i) + { + t.emplace_back(triangles[i * 3 + 0], + triangles[i * 3 + 1], + triangles[i * 3 + 2]); + } + + return Compute(v, t, params); +} + +bool VHACDImpl::Compute(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) +{ + std::vector v; + v.reserve(countPoints); + for (uint32_t i = 0; i < countPoints; ++i) + { + v.emplace_back(points[i * 3 + 0], + points[i * 3 + 1], + points[i * 3 + 2]); + } + + std::vector t; + t.reserve(countTriangles); + for (uint32_t i = 0; i < countTriangles; ++i) + { + t.emplace_back(triangles[i * 3 + 0], + triangles[i * 3 + 1], + triangles[i * 3 + 2]); + } + + return Compute(v, t, params); +} + +uint32_t VHACDImpl::GetNConvexHulls() const +{ + return uint32_t(m_convexHulls.size()); +} + +bool VHACDImpl::GetConvexHull(const uint32_t index, + ConvexHull& ch) const +{ + bool ret = false; + + if ( index < uint32_t(m_convexHulls.size() )) + { + ch = *m_convexHulls[index]; + ret = true; + } + + return ret; +} + +void VHACDImpl::Clean() +{ +#if !VHACD_DISABLE_THREADING + m_threadPool = nullptr; +#endif + + m_trees.clear(); + + for (auto& ch : m_convexHulls) + { + ReleaseConvexHull(ch); + } + m_convexHulls.clear(); + + for (auto& ch : m_hulls) + { + ReleaseConvexHull(ch.second); + } + m_hulls.clear(); + + m_voxelHulls.clear(); + + m_pendingHulls.clear(); + + m_vertices.clear(); + m_indices.clear(); +} + +void VHACDImpl::Release() +{ + delete this; +} + +bool VHACDImpl::ComputeCenterOfMass(double centerOfMass[3]) const +{ + bool ret = false; + + return ret; +} + +bool VHACDImpl::IsReady() const +{ + return true; +} + +uint32_t VHACDImpl::findNearestConvexHull(const double pos[3], + double& distanceToHull) +{ + uint32_t ret = 0; // The default return code is zero + + uint32_t hullCount = GetNConvexHulls(); + distanceToHull = 0; + // First, make sure that we have valid and completed results + if ( hullCount ) + { + // See if we already have AABB trees created for each convex hull + if ( m_trees.empty() ) + { + // For each convex hull, we generate an AABB tree for fast closest point queries + for (uint32_t i = 0; i < hullCount; i++) + { + VHACD::IVHACD::ConvexHull ch; + GetConvexHull(i,ch); + // Pass the triangle mesh to create an AABB tree instance based on it. + m_trees.emplace_back(new AABBTree(ch.m_points, + ch.m_triangles)); + } + } + // We now compute the closest point to each convex hull and save the nearest one + double closest = 1e99; + for (uint32_t i = 0; i < hullCount; i++) + { + std::unique_ptr& t = m_trees[i]; + if ( t ) + { + VHACD::Vect3 closestPoint; + VHACD::Vect3 position(pos[0], + pos[1], + pos[2]); + if ( t->GetClosestPointWithinDistance(position, 1e99, closestPoint)) + { + VHACD::Vect3 d = position - closestPoint; + double distanceSquared = d.GetNormSquared(); + if ( distanceSquared < closest ) + { + closest = distanceSquared; + ret = i; + } + } + } + } + distanceToHull = sqrt(closest); // compute the distance to the nearest convex hull + } + + return ret; +} + +bool VHACDImpl::Compute(const std::vector& points, + const std::vector& triangles, + const Parameters& params) +{ + bool ret = false; + + m_params = params; + m_canceled = false; + + Clean(); // release any previous results +#if !VHACD_DISABLE_THREADING + if ( m_params.m_asyncACD ) + { + m_threadPool = std::unique_ptr(new ThreadPool(8)); + } +#endif + CopyInputMesh(points, + triangles); + if ( !m_canceled ) + { + // We now recursively perform convex decomposition until complete + PerformConvexDecomposition(); + } + + if ( m_canceled ) + { + Clean(); + ret = false; + if ( m_params.m_logger ) + { + m_params.m_logger->Log("VHACD operation canceled before it was complete."); + } + } + else + { + ret = true; + } +#if !VHACD_DISABLE_THREADING + m_threadPool = nullptr; +#endif + return ret; +} + +uint32_t VHACDImpl::GetIndex(VHACD::VertexIndex& vi, + const VHACD::Vertex& p) +{ + VHACD::Vect3 pos = (VHACD::Vect3(p) - m_center) * m_recipScale; + bool newPos; + uint32_t ret = vi.GetIndex(pos, + newPos); + return ret; +} + +void VHACDImpl::CopyInputMesh(const std::vector& points, + const std::vector& triangles) +{ + m_vertices.clear(); + m_indices.clear(); + m_indices.reserve(triangles.size()); + + // First we must find the bounding box of this input vertices and normalize them into a unit-cube + VHACD::Vect3 bmin( FLT_MAX); + VHACD::Vect3 bmax(-FLT_MAX); + ProgressUpdate(Stages::COMPUTE_BOUNDS_OF_INPUT_MESH, + 0, + "ComputingBounds"); + for (uint32_t i = 0; i < points.size(); i++) + { + const VHACD::Vertex& p = points[i]; + + bmin = bmin.CWiseMin(p); + bmax = bmax.CWiseMax(p); + } + ProgressUpdate(Stages::COMPUTE_BOUNDS_OF_INPUT_MESH, + 100, + "ComputingBounds"); + + m_center = (bmax + bmin) * double(0.5); + + VHACD::Vect3 scale = bmax - bmin; + m_scale = scale.MaxCoeff(); + + m_recipScale = m_scale > double(0.0) ? double(1.0) / m_scale : double(0.0); + + { + VHACD::VertexIndex vi = VHACD::VertexIndex(double(0.001), false); + + uint32_t dcount = 0; + + for (uint32_t i = 0; i < triangles.size() && !m_canceled; ++i) + { + const VHACD::Triangle& t = triangles[i]; + const VHACD::Vertex& p1 = points[t.mI0]; + const VHACD::Vertex& p2 = points[t.mI1]; + const VHACD::Vertex& p3 = points[t.mI2]; + + uint32_t i1 = GetIndex(vi, p1); + uint32_t i2 = GetIndex(vi, p2); + uint32_t i3 = GetIndex(vi, p3); + + if ( i1 == i2 || i1 == i3 || i2 == i3 ) + { + dcount++; + } + else + { + m_indices.emplace_back(i1, i2, i3); + } + } + + if ( dcount ) + { + if ( m_params.m_logger ) + { + char scratch[512]; + snprintf(scratch, + sizeof(scratch), + "Skipped %d degenerate triangles", dcount); + m_params.m_logger->Log(scratch); + } + } + + m_vertices = vi.TakeVertices(); + } + + // Create the raycast mesh + if ( !m_canceled ) + { + ProgressUpdate(Stages::CREATE_RAYCAST_MESH, + 0, + "Building RaycastMesh"); + m_AABBTree = VHACD::AABBTree(m_vertices, + m_indices); + ProgressUpdate(Stages::CREATE_RAYCAST_MESH, + 100, + "RaycastMesh completed"); + } + if ( !m_canceled ) + { + ProgressUpdate(Stages::VOXELIZING_INPUT_MESH, + 0, + "Voxelizing Input Mesh"); + m_voxelize = VHACD::Volume(); + m_voxelize.Voxelize(m_vertices, + m_indices, + m_params.m_resolution, + m_params.m_fillMode, + m_AABBTree); + m_voxelScale = m_voxelize.GetScale(); + m_voxelHalfScale = m_voxelScale * double(0.5); + m_voxelBmin = m_voxelize.GetBounds().GetMin(); + m_voxelBmax = m_voxelize.GetBounds().GetMax(); + ProgressUpdate(Stages::VOXELIZING_INPUT_MESH, + 100, + "Voxelization complete"); + } + + m_inputMesh.m_vertices = m_vertices; + m_inputMesh.m_indices = m_indices; + if ( !m_canceled ) + { + ProgressUpdate(Stages::BUILD_INITIAL_CONVEX_HULL, + 0, + "Build initial ConvexHull"); + std::unique_ptr vh = std::unique_ptr(new VoxelHull(m_voxelize, + m_params, + this)); + if ( vh->m_convexHull ) + { + m_overallHullVolume = vh->m_convexHull->m_volume; + } + m_pendingHulls.push_back(std::move(vh)); + ProgressUpdate(Stages::BUILD_INITIAL_CONVEX_HULL, + 100, + "Initial ConvexHull complete"); + } +} + +void VHACDImpl::ScaleOutputConvexHull(ConvexHull& ch) +{ + for (uint32_t i = 0; i < ch.m_points.size(); i++) + { + VHACD::Vect3 p = ch.m_points[i]; + p = (p * m_scale) + m_center; + ch.m_points[i] = p; + } + ch.m_volume = ComputeConvexHullVolume(ch); // get the combined volume + VHACD::BoundsAABB b(ch.m_points); + ch.mBmin = b.GetMin(); + ch.mBmax = b.GetMax(); + ComputeCentroid(ch.m_points, + ch.m_triangles, + ch.m_center); +} + +void VHACDImpl::AddCostToPriorityQueue(CostTask& task) +{ + HullPair hp(task.m_hullA->m_meshId, + task.m_hullB->m_meshId, + task.m_concavity); + m_hullPairQueue.push(hp); +} + +void VHACDImpl::ReleaseConvexHull(ConvexHull* ch) +{ + if ( ch ) + { + delete ch; + } +} + +void jobCallback(std::unique_ptr& userPtr) +{ + userPtr->PerformPlaneSplit(); +} + +void computeMergeCostTask(CostTask& ptr) +{ + ptr.m_this->PerformMergeCostTask(ptr); +} + +void VHACDImpl::PerformConvexDecomposition() +{ + { + ScopedTime st("Convex Decomposition", + m_params.m_logger); + double maxHulls = pow(2, m_params.m_maxRecursionDepth); + // We recursively split convex hulls until we can + // no longer recurse further. + Timer t; + + while ( !m_pendingHulls.empty() && !m_canceled ) + { + size_t count = m_pendingHulls.size() + m_voxelHulls.size(); + double e = t.PeekElapsedSeconds(); + if ( e >= double(0.1) ) + { + t.Reset(); + double stageProgress = (double(count) * double(100.0)) / maxHulls; + ProgressUpdate(Stages::PERFORMING_DECOMPOSITION, + stageProgress, + "Performing recursive decomposition of convex hulls"); + } + // First we make a copy of the hulls we are processing + std::vector> oldList = std::move(m_pendingHulls); + // For each hull we want to split, we either + // immediately perform the plane split or we post it as + // a job to be performed in a background thread + std::vector> futures(oldList.size()); + uint32_t futureCount = 0; + for (auto& i : oldList) + { + if ( i->IsComplete() || count > MaxConvexHullFragments ) + { + } + else + { +#if !VHACD_DISABLE_THREADING + if ( m_threadPool ) + { + futures[futureCount] = m_threadPool->enqueue([&i] + { + jobCallback(i); + }); + futureCount++; + } + else +#endif + { + i->PerformPlaneSplit(); + } + } + } + // Wait for any outstanding jobs to complete in the background threads + if ( futureCount ) + { + for (uint32_t i = 0; i < futureCount; i++) + { + futures[i].get(); + } + } + // Now, we rebuild the pending convex hulls list by + // adding the two children to the output list if + // we need to recurse them further + for (auto& vh : oldList) + { + if ( vh->IsComplete() || count > MaxConvexHullFragments ) + { + if ( vh->m_convexHull ) + { + m_voxelHulls.push_back(std::move(vh)); + } + } + else + { + if ( vh->m_hullA ) + { + m_pendingHulls.push_back(std::move(vh->m_hullA)); + } + if ( vh->m_hullB ) + { + m_pendingHulls.push_back(std::move(vh->m_hullB)); + } + } + } + } + } + + if ( !m_canceled ) + { + // Give each convex hull a unique guid + m_meshId = 0; + m_hulls.clear(); + + // Build the convex hull id map + std::vector hulls; + + ProgressUpdate(Stages::INITIALIZING_CONVEX_HULLS_FOR_MERGING, + 0, + "Initializing ConvexHulls"); + for (auto& vh : m_voxelHulls) + { + if ( m_canceled ) + { + break; + } + ConvexHull* ch = CopyConvexHull(*vh->m_convexHull); + m_meshId++; + ch->m_meshId = m_meshId; + m_hulls[m_meshId] = ch; + // Compute the volume of the convex hull + ch->m_volume = ComputeConvexHullVolume(*ch); + // Compute the AABB of the convex hull + VHACD::BoundsAABB b = VHACD::BoundsAABB(ch->m_points).Inflate(double(0.1)); + ch->mBmin = b.GetMin(); + ch->mBmax = b.GetMax(); + + ComputeCentroid(ch->m_points, + ch->m_triangles, + ch->m_center); + + hulls.push_back(ch); + } + ProgressUpdate(Stages::INITIALIZING_CONVEX_HULLS_FOR_MERGING, + 100, + "ConvexHull initialization complete"); + + m_voxelHulls.clear(); + + // here we merge convex hulls as needed until the match the + // desired maximum hull count. + size_t hullCount = hulls.size(); + + if ( hullCount > m_params.m_maxConvexHulls && !m_canceled) + { + size_t costMatrixSize = ((hullCount * hullCount) - hullCount) >> 1; + std::vector tasks; + tasks.reserve(costMatrixSize); + + ScopedTime st("Computing the Cost Matrix", + m_params.m_logger); + // First thing we need to do is compute the cost matrix + // This is computed as the volume error of any two convex hulls + // combined + ProgressUpdate(Stages::COMPUTING_COST_MATRIX, + 0, + "Computing Hull Merge Cost Matrix"); + for (size_t i = 1; i < hullCount && !m_canceled; i++) + { + ConvexHull* chA = hulls[i]; + + for (size_t j = 0; j < i && !m_canceled; j++) + { + ConvexHull* chB = hulls[j]; + + CostTask t; + t.m_hullA = chA; + t.m_hullB = chB; + t.m_this = this; + + if ( DoFastCost(t) ) + { + } + else + { + tasks.push_back(std::move(t)); + CostTask* task = &tasks.back(); +#if !VHACD_DISABLE_THREADING + if ( m_threadPool ) + { + task->m_future = m_threadPool->enqueue([task] + { + computeMergeCostTask(*task); + }); + } +#endif + } + } + } + + if ( !m_canceled ) + { +#if !VHACD_DISABLE_THREADING + if ( m_threadPool ) + { + for (CostTask& task : tasks) + { + task.m_future.get(); + } + + for (CostTask& task : tasks) + { + AddCostToPriorityQueue(task); + } + } + else +#endif + { + for (CostTask& task : tasks) + { + PerformMergeCostTask(task); + AddCostToPriorityQueue(task); + } + } + ProgressUpdate(Stages::COMPUTING_COST_MATRIX, + 100, + "Finished cost matrix"); + } + + if ( !m_canceled ) + { + ScopedTime stMerging("Merging Convex Hulls", + m_params.m_logger); + Timer t; + // Now that we know the cost to merge each hull, we can begin merging them. + bool cancel = false; + + uint32_t maxMergeCount = uint32_t(m_hulls.size()) - m_params.m_maxConvexHulls; + uint32_t startCount = uint32_t(m_hulls.size()); + + while ( !cancel + && m_hulls.size() > m_params.m_maxConvexHulls + && !m_hullPairQueue.empty() + && !m_canceled) + { + double e = t.PeekElapsedSeconds(); + if ( e >= double(0.1) ) + { + t.Reset(); + uint32_t hullsProcessed = startCount - uint32_t(m_hulls.size() ); + double stageProgress = double(hullsProcessed * 100) / double(maxMergeCount); + ProgressUpdate(Stages::MERGING_CONVEX_HULLS, + stageProgress, + "Merging Convex Hulls"); + } + + HullPair hp = m_hullPairQueue.top(); + m_hullPairQueue.pop(); + + // It is entirely possible that the hull pair queue can + // have references to convex hulls that are no longer valid + // because they were previously merged. So we check for this + // and if either hull referenced in this pair no longer + // exists, then we skip it. + + // Look up this pair of hulls by ID + ConvexHull* ch1 = GetHull(hp.m_hullA); + ConvexHull* ch2 = GetHull(hp.m_hullB); + + // If both hulls are still valid, then we merge them, delete the old + // two hulls and recompute the cost matrix for the new combined hull + // we have created + if ( ch1 && ch2 ) + { + // This is the convex hull which results from combining the + // vertices in the two source hulls + ConvexHull* combinedHull = ComputeCombinedConvexHull(*ch1, + *ch2); + // The two old convex hulls are going to get removed + RemoveHull(hp.m_hullA); + RemoveHull(hp.m_hullB); + + m_meshId++; + combinedHull->m_meshId = m_meshId; + tasks.clear(); + tasks.reserve(m_hulls.size()); + + // Compute the cost between this new merged hull + // and all existing convex hulls and then + // add that to the priority queue + for (auto& i : m_hulls) + { + if ( m_canceled ) + { + break; + } + ConvexHull* secondHull = i.second; + CostTask ct; + ct.m_hullA = combinedHull; + ct.m_hullB = secondHull; + ct.m_this = this; + if ( DoFastCost(ct) ) + { + } + else + { + tasks.push_back(std::move(ct)); + } + } + m_hulls[combinedHull->m_meshId] = combinedHull; + // See how many merge cost tasks were posted + // If there are 8 or more and we are running asynchronously, then do them that way. +#if !VHACD_DISABLE_THREADING + if ( m_threadPool && tasks.size() >= 2) + { + for (CostTask& task : tasks) + { + task.m_future = m_threadPool->enqueue([&task] + { + computeMergeCostTask(task); + }); + } + + for (CostTask& task : tasks) + { + task.m_future.get(); + } + } + else +#endif + { + for (CostTask& task : tasks) + { + PerformMergeCostTask(task); + } + } + + for (CostTask& task : tasks) + { + AddCostToPriorityQueue(task); + } + } + } + // Ok...once we are done, we copy the results! + m_meshId -= 0; + ProgressUpdate(Stages::FINALIZING_RESULTS, + 0, + "Finalizing results"); + for (auto& i : m_hulls) + { + if ( m_canceled ) + { + break; + } + ConvexHull* ch = i.second; + // We now must reduce the convex hull + if ( ch->m_points.size() > m_params.m_maxNumVerticesPerCH || m_params.m_shrinkWrap) + { + ConvexHull* reduce = ComputeReducedConvexHull(*ch, + m_params.m_maxNumVerticesPerCH, + m_params.m_shrinkWrap); + ReleaseConvexHull(ch); + ch = reduce; + } + ScaleOutputConvexHull(*ch); + ch->m_meshId = m_meshId; + m_meshId++; + m_convexHulls.push_back(ch); + } + m_hulls.clear(); // since the hulls were moved into the output list, we don't need to delete them from this container + ProgressUpdate(Stages::FINALIZING_RESULTS, + 100, + "Finalized results complete"); + } + } + else + { + ProgressUpdate(Stages::FINALIZING_RESULTS, + 0, + "Finalizing results"); + m_meshId = 0; + for (auto& ch : hulls) + { + // We now must reduce the convex hull + if ( ch->m_points.size() > m_params.m_maxNumVerticesPerCH || m_params.m_shrinkWrap ) + { + ConvexHull* reduce = ComputeReducedConvexHull(*ch, + m_params.m_maxNumVerticesPerCH, + m_params.m_shrinkWrap); + ReleaseConvexHull(ch); + ch = reduce; + } + ScaleOutputConvexHull(*ch); + ch->m_meshId = m_meshId; + m_meshId++; + m_convexHulls.push_back(ch); + } + m_hulls.clear(); + ProgressUpdate(Stages::FINALIZING_RESULTS, + 100, + "Finalized results"); + } + } +} + +double VHACDImpl::ComputeConvexHullVolume(const ConvexHull& sm) +{ + double totalVolume = 0; + VHACD::Vect3 bary(0, 0, 0); + for (uint32_t i = 0; i < sm.m_points.size(); i++) + { + VHACD::Vect3 p(sm.m_points[i]); + bary += p; + } + bary /= double(sm.m_points.size()); + + for (uint32_t i = 0; i < sm.m_triangles.size(); i++) + { + uint32_t i1 = sm.m_triangles[i].mI0; + uint32_t i2 = sm.m_triangles[i].mI1; + uint32_t i3 = sm.m_triangles[i].mI2; + + VHACD::Vect3 ver0(sm.m_points[i1]); + VHACD::Vect3 ver1(sm.m_points[i2]); + VHACD::Vect3 ver2(sm.m_points[i3]); + + totalVolume += ComputeVolume4(ver0, + ver1, + ver2, + bary); + + } + totalVolume = totalVolume / double(6.0); + return totalVolume; +} + +double VHACDImpl::ComputeVolume4(const VHACD::Vect3& a, + const VHACD::Vect3& b, + const VHACD::Vect3& c, + const VHACD::Vect3& d) +{ + VHACD::Vect3 ad = a - d; + VHACD::Vect3 bd = b - d; + VHACD::Vect3 cd = c - d; + VHACD::Vect3 bcd = bd.Cross(cd); + double dot = ad.Dot(bcd); + return dot; +} + +double VHACDImpl::ComputeConcavity(double volumeSeparate, + double volumeCombined, + double volumeMesh) +{ + return fabs(volumeSeparate - volumeCombined) / volumeMesh; +} + +bool VHACDImpl::DoFastCost(CostTask& mt) +{ + bool ret = false; + + ConvexHull* ch1 = mt.m_hullA; + ConvexHull* ch2 = mt.m_hullB; + + VHACD::BoundsAABB ch1b(ch1->mBmin, + ch1->mBmax); + VHACD::BoundsAABB ch2b(ch2->mBmin, + ch2->mBmax); + if (!ch1b.Intersects(ch2b)) + { + VHACD::BoundsAABB b = ch1b.Union(ch2b); + + double combinedVolume = b.Volume(); + double concavity = ComputeConcavity(ch1->m_volume + ch2->m_volume, + combinedVolume, + m_overallHullVolume); + HullPair hp(ch1->m_meshId, + ch2->m_meshId, + concavity); + m_hullPairQueue.push(hp); + ret = true; + } + return ret; +} + +void VHACDImpl::PerformMergeCostTask(CostTask& mt) +{ + ConvexHull* ch1 = mt.m_hullA; + ConvexHull* ch2 = mt.m_hullB; + + double volume1 = ch1->m_volume; + double volume2 = ch2->m_volume; + + ConvexHull* combined = ComputeCombinedConvexHull(*ch1, + *ch2); // Build the combined convex hull + double combinedVolume = ComputeConvexHullVolume(*combined); // get the combined volume + mt.m_concavity = ComputeConcavity(volume1 + volume2, + combinedVolume, + m_overallHullVolume); + ReleaseConvexHull(combined); +} + +IVHACD::ConvexHull* VHACDImpl::ComputeReducedConvexHull(const ConvexHull& ch, + uint32_t maxVerts, + bool projectHullVertices) +{ + SimpleMesh sourceConvexHull; + + sourceConvexHull.m_vertices = ch.m_points; + sourceConvexHull.m_indices = ch.m_triangles; + + ShrinkWrap(sourceConvexHull, + m_AABBTree, + maxVerts, + m_voxelScale * 4, + projectHullVertices); + + ConvexHull *ret = new ConvexHull; + + ret->m_points = sourceConvexHull.m_vertices; + ret->m_triangles = sourceConvexHull.m_indices; + + VHACD::BoundsAABB b = VHACD::BoundsAABB(ret->m_points).Inflate(double(0.1)); + ret->mBmin = b.GetMin(); + ret->mBmax = b.GetMax(); + ComputeCentroid(ret->m_points, + ret->m_triangles, + ret->m_center); + + ret->m_volume = ComputeConvexHullVolume(*ret); + + // Return the convex hull + return ret; +} + +IVHACD::ConvexHull* VHACDImpl::ComputeCombinedConvexHull(const ConvexHull& sm1, + const ConvexHull& sm2) +{ + uint32_t vcount = uint32_t(sm1.m_points.size() + sm2.m_points.size()); // Total vertices from both hulls + std::vector vertices(vcount); + auto it = std::copy(sm1.m_points.begin(), + sm1.m_points.end(), + vertices.begin()); + std::copy(sm2.m_points.begin(), + sm2.m_points.end(), + it); + + VHACD::QuickHull qh; + qh.ComputeConvexHull(vertices, + vcount); + + ConvexHull* ret = new ConvexHull; + ret->m_points = qh.GetVertices(); + ret->m_triangles = qh.GetIndices(); + + ret->m_volume = ComputeConvexHullVolume(*ret); + + VHACD::BoundsAABB b = VHACD::BoundsAABB(qh.GetVertices()).Inflate(double(0.1)); + ret->mBmin = b.GetMin(); + ret->mBmax = b.GetMax(); + ComputeCentroid(ret->m_points, + ret->m_triangles, + ret->m_center); + + // Return the convex hull + return ret; +} + +IVHACD::ConvexHull* VHACDImpl::GetHull(uint32_t index) +{ + ConvexHull* ret = nullptr; + + auto found = m_hulls.find(index); + if ( found != m_hulls.end() ) + { + ret = found->second; + } + + return ret; +} + +bool VHACDImpl::RemoveHull(uint32_t index) +{ + bool ret = false; + auto found = m_hulls.find(index); + if ( found != m_hulls.end() ) + { + ret = true; + ReleaseConvexHull(found->second); + m_hulls.erase(found); + } + return ret; +} + +IVHACD::ConvexHull* VHACDImpl::CopyConvexHull(const ConvexHull& source) +{ + ConvexHull *ch = new ConvexHull; + *ch = source; + + return ch; +} + +const char* VHACDImpl::GetStageName(Stages stage) const +{ + const char *ret = "unknown"; + switch ( stage ) + { + case Stages::COMPUTE_BOUNDS_OF_INPUT_MESH: + ret = "COMPUTE_BOUNDS_OF_INPUT_MESH"; + break; + case Stages::REINDEXING_INPUT_MESH: + ret = "REINDEXING_INPUT_MESH"; + break; + case Stages::CREATE_RAYCAST_MESH: + ret = "CREATE_RAYCAST_MESH"; + break; + case Stages::VOXELIZING_INPUT_MESH: + ret = "VOXELIZING_INPUT_MESH"; + break; + case Stages::BUILD_INITIAL_CONVEX_HULL: + ret = "BUILD_INITIAL_CONVEX_HULL"; + break; + case Stages::PERFORMING_DECOMPOSITION: + ret = "PERFORMING_DECOMPOSITION"; + break; + case Stages::INITIALIZING_CONVEX_HULLS_FOR_MERGING: + ret = "INITIALIZING_CONVEX_HULLS_FOR_MERGING"; + break; + case Stages::COMPUTING_COST_MATRIX: + ret = "COMPUTING_COST_MATRIX"; + break; + case Stages::MERGING_CONVEX_HULLS: + ret = "MERGING_CONVEX_HULLS"; + break; + case Stages::FINALIZING_RESULTS: + ret = "FINALIZING_RESULTS"; + break; + case Stages::NUM_STAGES: + // Should be unreachable, here to silence enumeration value not handled in switch warnings + // GCC/Clang's -Wswitch + break; + } + return ret; +} + +void VHACDImpl::ProgressUpdate(Stages stage, + double stageProgress, + const char* operation) +{ + if ( m_params.m_callback ) + { + double overallProgress = (double(stage) * 100) / double(Stages::NUM_STAGES); + const char *s = GetStageName(stage); + m_params.m_callback->Update(overallProgress, + stageProgress, + s, + operation); + } +} + +bool VHACDImpl::IsCanceled() const +{ + return m_canceled; +} + +IVHACD* CreateVHACD(void) +{ + VHACDImpl *ret = new VHACDImpl; + return static_cast< IVHACD *>(ret); +} + +IVHACD* CreateVHACD(void); + +#if !VHACD_DISABLE_THREADING + +class LogMessage +{ +public: + double m_overallProgress{ double(-1.0) }; + double m_stageProgress{ double(-1.0) }; + std::string m_stage; + std::string m_operation; +}; + +class VHACDAsyncImpl : public VHACD::IVHACD, + public VHACD::IVHACD::IUserCallback, + VHACD::IVHACD::IUserLogger, + VHACD::IVHACD::IUserTaskRunner +{ +public: + VHACDAsyncImpl() = default; + + ~VHACDAsyncImpl() override; + + void Cancel() override final; + + bool Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) override final; + + bool Compute(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) override final; + + bool GetConvexHull(const uint32_t index, + VHACD::IVHACD::ConvexHull& ch) const override final; + + uint32_t GetNConvexHulls() const override final; + + void Clean() override final; // release internally allocated memory + + void Release() override final; // release IVHACD + + // 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. + bool ComputeCenterOfMass(double centerOfMass[3]) const override; + + bool IsReady() const override final; + + /** + * At the request of LegionFu : out_look@foxmail.com + * This method will return which convex hull is closest to the source position. + * You can use this method to figure out, for example, which vertices in the original + * source mesh are best associated with which convex hull. + * + * @param pos : The input 3d position to test against + * + * @return : Returns which convex hull this position is closest to. + */ + uint32_t findNearestConvexHull(const double pos[3], + double& distanceToHull) override final; + + void Update(const double overallProgress, + const double stageProgress, + const char* const stage, + const char *operation) override final; + + void Log(const char* const msg) override final; + + void* StartTask(std::function func) override; + + void JoinTask(void* Task) override; + + bool Compute(const Parameters params); + + bool ComputeNow(const std::vector& points, + const std::vector& triangles, + const Parameters& _desc); + + // 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() const; + +private: + VHACD::VHACDImpl m_VHACD; + std::vector m_vertices; + std::vector m_indices; + VHACD::IVHACD::IUserCallback* m_callback{ nullptr }; + VHACD::IVHACD::IUserLogger* m_logger{ nullptr }; + VHACD::IVHACD::IUserTaskRunner* m_taskRunner{ nullptr }; + void* m_task{ nullptr }; + std::atomic m_running{ false }; + std::atomic m_cancel{ 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 m_messageMutex; + mutable std::vector m_messages; + mutable std::atomic m_haveMessages{ false }; +}; + +VHACDAsyncImpl::~VHACDAsyncImpl() +{ + Cancel(); +} + +void VHACDAsyncImpl::Cancel() +{ + m_cancel = true; + m_VHACD.Cancel(); + + if (m_task) + { + m_taskRunner->JoinTask(m_task); // Wait for the thread to fully exit before we delete the instance + m_task = nullptr; + } + m_cancel = false; // clear the cancel semaphore +} + +bool VHACDAsyncImpl::Compute(const float* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) +{ + m_vertices.reserve(countPoints); + for (uint32_t i = 0; i < countPoints; ++i) + { + m_vertices.emplace_back(points[i * 3 + 0], + points[i * 3 + 1], + points[i * 3 + 2]); + } + + m_indices.reserve(countTriangles); + for (uint32_t i = 0; i < countTriangles; ++i) + { + m_indices.emplace_back(triangles[i * 3 + 0], + triangles[i * 3 + 1], + triangles[i * 3 + 2]); + } + + return Compute(params); +} + +bool VHACDAsyncImpl::Compute(const double* const points, + const uint32_t countPoints, + const uint32_t* const triangles, + const uint32_t countTriangles, + const Parameters& params) +{ + // We need to copy the input vertices and triangles into our own buffers so we can operate + // on them safely from the background thread. + // Can't be local variables due to being asynchronous + m_vertices.reserve(countPoints); + for (uint32_t i = 0; i < countPoints; ++i) + { + m_vertices.emplace_back(points[i * 3 + 0], + points[i * 3 + 1], + points[i * 3 + 2]); + } + + m_indices.reserve(countTriangles); + for (uint32_t i = 0; i < countTriangles; ++i) + { + m_indices.emplace_back(triangles[i * 3 + 0], + triangles[i * 3 + 1], + triangles[i * 3 + 2]); + } + + return Compute(params); +} + +bool VHACDAsyncImpl::GetConvexHull(const uint32_t index, + VHACD::IVHACD::ConvexHull& ch) const +{ + return m_VHACD.GetConvexHull(index, + ch); +} + +uint32_t VHACDAsyncImpl::GetNConvexHulls() const +{ + ProcessPendingMessages(); + return m_VHACD.GetNConvexHulls(); +} + +void VHACDAsyncImpl::Clean() +{ + Cancel(); + m_VHACD.Clean(); +} + +void VHACDAsyncImpl::Release() +{ + delete this; +} + +bool VHACDAsyncImpl::ComputeCenterOfMass(double centerOfMass[3]) const +{ + bool ret = false; + + centerOfMass[0] = 0; + centerOfMass[1] = 0; + centerOfMass[2] = 0; + + if (IsReady()) + { + ret = m_VHACD.ComputeCenterOfMass(centerOfMass); + } + return ret; +} + +bool VHACDAsyncImpl::IsReady() const +{ + ProcessPendingMessages(); + return !m_running; +} + +uint32_t VHACDAsyncImpl::findNearestConvexHull(const double pos[3], + double& distanceToHull) +{ + uint32_t ret = 0; // The default return code is zero + + distanceToHull = 0; + // First, make sure that we have valid and completed results + if (IsReady() ) + { + ret = m_VHACD.findNearestConvexHull(pos,distanceToHull); + } + + return ret; +} + +void VHACDAsyncImpl::Update(const double overallProgress, + const double stageProgress, + const char* const stage, + const char* operation) +{ + m_messageMutex.lock(); + LogMessage m; + m.m_operation = std::string(operation); + m.m_overallProgress = overallProgress; + m.m_stageProgress = stageProgress; + m.m_stage = std::string(stage); + m_messages.push_back(m); + m_haveMessages = true; + m_messageMutex.unlock(); +} + +void VHACDAsyncImpl::Log(const char* const msg) +{ + m_messageMutex.lock(); + LogMessage m; + m.m_operation = std::string(msg); + m_haveMessages = true; + m_messages.push_back(m); + m_messageMutex.unlock(); +} + +void* VHACDAsyncImpl::StartTask(std::function func) +{ + return new std::thread(func); +} + +void VHACDAsyncImpl::JoinTask(void* Task) +{ + std::thread* t = static_cast(Task); + t->join(); + delete t; +} + +bool VHACDAsyncImpl::Compute(Parameters params) +{ + Cancel(); // if we previously had a solution running; cancel it. + + m_taskRunner = params.m_taskRunner ? params.m_taskRunner : this; + params.m_taskRunner = m_taskRunner; + + m_running = true; + m_task = m_taskRunner->StartTask([this, params]() { + ComputeNow(m_vertices, + m_indices, + params); + // If we have a user provided callback and the user did *not* call 'cancel' we notify him that the + // task is completed. However..if the user selected 'cancel' we do not send a completed notification event. + if (params.m_callback && !m_cancel) + { + params.m_callback->NotifyVHACDComplete(); + } + m_running = false; + }); + return true; +} + +bool VHACDAsyncImpl::ComputeNow(const std::vector& points, + const std::vector& triangles, + const Parameters& _desc) +{ + uint32_t ret = 0; + + Parameters desc; + m_callback = _desc.m_callback; + m_logger = _desc.m_logger; + + 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 not task runner provided, then use the default one + if (desc.m_taskRunner == nullptr) + { + desc.m_taskRunner = this; + } + + bool ok = m_VHACD.Compute(points, + triangles, + desc); + if (ok) + { + ret = m_VHACD.GetNConvexHulls(); + } + + return ret ? true : false; +} + +void VHACDAsyncImpl::ProcessPendingMessages() const +{ + if (m_cancel) + { + return; + } + if ( m_haveMessages ) + { + m_messageMutex.lock(); + for (auto& i : m_messages) + { + if ( i.m_overallProgress == -1 ) + { + if ( m_logger ) + { + m_logger->Log(i.m_operation.c_str()); + } + } + else if ( m_callback ) + { + m_callback->Update(i.m_overallProgress, + i.m_stageProgress, + i.m_stage.c_str(), + i.m_operation.c_str()); + } + } + m_messages.clear(); + m_haveMessages = false; + m_messageMutex.unlock(); + } +} + +IVHACD* CreateVHACD_ASYNC() +{ + VHACDAsyncImpl* m = new VHACDAsyncImpl; + return static_cast(m); +} +#endif + +} // namespace VHACD + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif // __GNUC__ + +#endif // ENABLE_VHACD_IMPLEMENTATION + +#endif // VHACD_H diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt deleted file mode 100644 index dc3c9a19..00000000 --- a/src/test/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -project(VHACD_TEST) -include(${CMAKE_COMMON_INC}) - -if (NOT NO_OPENCL) - find_package(OpenCL QUIET) -endif() -if (NOT NO_OPENMP) - find_package(OpenMP QUIET) -endif() -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") -endif() - - -add_executable(testVHACD ${PROJECT_CPP_FILES} ${PROJECT_C_FILES} ${PROJECT_INC_FILES} ${PROJECT_INL_FILES}) - -target_include_directories(testVHACD PRIVATE ${CMAKE_SOURCE_DIR}/VHACD_Lib/public ${CMAKE_CURRENT_SOURCE_DIR}/inc) -target_link_libraries(testVHACD vhacd) - -if (OpenCL_FOUND) - include_directories("${OpenCL_INCLUDE_DIRS}") - add_definitions( -DOPENCL_FOUND=1 ) - target_link_libraries(testVHACD ${OpenCL_LIBRARIES}) -endif() - -if (NOT WIN32 AND NOT APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall") - target_link_libraries(testVHACD rt) -endif() - diff --git a/src/test/inc/oclHelper.h b/src/test/inc/oclHelper.h deleted file mode 100644 index ff63c300..00000000 --- a/src/test/inc/oclHelper.h +++ /dev/null @@ -1,50 +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. - */ -#ifdef OPENCL_FOUND - -#pragma once -#ifndef OCL_HELPER_H -#define OCL_HELPER_H - -#include -#include - -#ifdef __MACH__ -#include -#else -#include -#endif - -class OCLHelper { -public: - OCLHelper(void){}; - ~OCLHelper(void){}; - bool InitPlatform(const unsigned int platformIndex = 0); - bool InitDevice(const unsigned int deviceIndex); - bool GetPlatformsInfo(std::vector& info, const std::string& indentation); - bool GetDevicesInfo(std::vector& info, const std::string& indentation); - cl_platform_id* GetPlatform() { return &m_platform; } - const cl_platform_id* GetPlatform() const { return &m_platform; } - cl_device_id* GetDevice() { return &m_device; } - const cl_device_id* GetDevice() const { return &m_device; } -private: - cl_platform_id m_platform; - cl_device_id m_device; - cl_int m_lastError; -}; - -#endif // OCL_HELPER_H - -#endif //OPENCL_FOUND diff --git a/src/test/src/main.cpp b/src/test/src/main.cpp deleted file mode 100644 index 8e740c8a..00000000 --- a/src/test/src/main.cpp +++ /dev/null @@ -1,729 +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. - */ - -#define _CRT_SECURE_NO_WARNINGS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#define _CRTDBG_MAP_ALLOC - -#ifdef _CRTDBG_MAP_ALLOC -#include -#include -#endif // _CRTDBG_MAP_ALLOC - -#include "VHACD.h" -#include "oclHelper.h" - -using namespace VHACD; -using namespace std; - -class MyCallback : public IVHACD::IUserCallback { -public: - MyCallback(void) {} - ~MyCallback(){}; - void Update(const double overallProgress, const double stageProgress, const double operationProgress, - const char* const stage, const char* const operation) - { - cout << setfill(' ') << setw(3) << (int)(overallProgress + 0.5) << "% " - << "[ " << stage << " " << setfill(' ') << setw(3) << (int)(stageProgress + 0.5) << "% ] " - << operation << " " << setfill(' ') << setw(3) << (int)(operationProgress + 0.5) << "%" << endl; - }; -}; -class MyLogger : public IVHACD::IUserLogger { -public: - MyLogger(void) {} - MyLogger(const string& fileName) { OpenFile(fileName); } - ~MyLogger(){}; - void Log(const char* const msg) - { - if (m_file.is_open()) { - m_file << msg; - m_file.flush(); - } - } - void OpenFile(const string& fileName) - { - m_file.open(fileName.c_str()); - } - -private: - ofstream m_file; -}; -struct Material { - - float m_diffuseColor[3]; - float m_ambientIntensity; - float m_specularColor[3]; - float m_emissiveColor[3]; - float m_shininess; - float m_transparency; - Material(void) - { - m_diffuseColor[0] = 0.5f; - m_diffuseColor[1] = 0.5f; - m_diffuseColor[2] = 0.5f; - m_specularColor[0] = 0.5f; - m_specularColor[1] = 0.5f; - m_specularColor[2] = 0.5f; - m_ambientIntensity = 0.4f; - m_emissiveColor[0] = 0.0f; - m_emissiveColor[1] = 0.0f; - m_emissiveColor[2] = 0.0f; - m_shininess = 0.4f; - m_transparency = 0.5f; - }; -}; -struct Parameters { - unsigned int m_oclPlatformID; - unsigned int m_oclDeviceID; - string m_fileNameIn; - string m_fileNameOut; - string m_fileNameLog; - bool m_run; - IVHACD::Parameters m_paramsVHACD; - Parameters(void) - { - m_run = true; - m_oclPlatformID = 0; - m_oclDeviceID = 0; - m_fileNameIn = ""; - m_fileNameOut = "output.wrl"; - m_fileNameLog = "log.txt"; - } -}; -bool LoadOFF(const string& fileName, vector& points, vector& triangles, IVHACD::IUserLogger& logger); -bool LoadOBJ(const string& fileName, vector& points, vector& triangles, IVHACD::IUserLogger& logger); -bool SaveOFF(const string& fileName, const float* const& points, const int* const& triangles, const unsigned int& nPoints, - const unsigned int& nTriangles, IVHACD::IUserLogger& logger); -bool SaveVRML2(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints, - const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger); -bool SaveOBJ(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints, - const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger, int convexPart, int vertexOffset); -void GetFileExtension(const string& fileName, string& fileExtension); -void ComputeRandomColor(Material& mat); -void Usage(const Parameters& params); -void ParseParameters(int argc, char* argv[], Parameters& params); - -#ifdef CL_VERSION_1_1 -bool InitOCL(const unsigned int oclPlatformID, const unsigned int oclDeviceID, OCLHelper& oclHelper, std::ostringstream& msg); -#endif // CL_VERSION_1_1 - -int main(int argc, char* argv[]) -{ - // --input camel.off --output camel_acd.wrl --log log.txt --resolution 1000000 --depth 20 --concavity 0.0025 --planeDownsampling 4 --convexhullDownsampling 4 --alpha 0.05 --beta 0.05 --gamma 0.00125 --pca 0 --mode 0 --maxNumVerticesPerCH 256 --minVolumePerCH 0.0001 --convexhullApproximation 1 --oclDeviceID 2 - { - // set parameters - Parameters params; - ParseParameters(argc, argv, params); - MyCallback myCallback; - MyLogger myLogger(params.m_fileNameLog); - params.m_paramsVHACD.m_logger = &myLogger; - params.m_paramsVHACD.m_callback = &myCallback; - Usage(params); - if (!params.m_run) { - return 0; - } - - std::ostringstream msg; -#ifdef CL_VERSION_1_1 - msg << "+ OpenCL (ON)" << std::endl; - OCLHelper oclHelper; - if (params.m_paramsVHACD.m_oclAcceleration) { - bool res = InitOCL(params.m_oclPlatformID, - params.m_oclDeviceID, - oclHelper, - msg); - if (!res) { - myLogger.Log(msg.str().c_str()); - return -1; - } - } -#else //CL_VERSION_1_1 - msg << "+ OpenCL (OFF)" << std::endl; -#endif //CL_VERSION_1_1 - -#ifdef _OPENMP - msg << "+ OpenMP (ON)" << std::endl; -#else - msg << "+ OpenMP (OFF)" << std::endl; -#endif - msg << "+ Parameters" << std::endl; - msg << "\t input " << params.m_fileNameIn << endl; - msg << "\t resolution " << params.m_paramsVHACD.m_resolution << endl; - msg << "\t max. concavity " << params.m_paramsVHACD.m_concavity << endl; - msg << "\t plane down-sampling " << params.m_paramsVHACD.m_planeDownsampling << endl; - msg << "\t convex-hull down-sampling " << params.m_paramsVHACD.m_convexhullDownsampling << endl; - msg << "\t alpha " << params.m_paramsVHACD.m_alpha << endl; - msg << "\t beta " << params.m_paramsVHACD.m_beta << endl; - msg << "\t maxhulls " << params.m_paramsVHACD.m_maxConvexHulls << endl; - msg << "\t pca " << params.m_paramsVHACD.m_pca << endl; - msg << "\t mode " << params.m_paramsVHACD.m_mode << endl; - msg << "\t max. vertices per convex-hull " << params.m_paramsVHACD.m_maxNumVerticesPerCH << endl; - msg << "\t min. volume to add vertices to convex-hulls " << params.m_paramsVHACD.m_minVolumePerCH << endl; - msg << "\t convex-hull approximation " << params.m_paramsVHACD.m_convexhullApproximation << endl; - msg << "\t OpenCL acceleration " << params.m_paramsVHACD.m_oclAcceleration << endl; - msg << "\t OpenCL platform ID " << params.m_oclPlatformID << endl; - msg << "\t OpenCL device ID " << params.m_oclDeviceID << endl; - msg << "\t output " << params.m_fileNameOut << endl; - msg << "\t log " << params.m_fileNameLog << endl; - msg << "+ Load mesh" << std::endl; - myLogger.Log(msg.str().c_str()); - - cout << msg.str().c_str(); - - // load mesh - vector points; - vector triangles; - string fileExtension; - GetFileExtension(params.m_fileNameIn, fileExtension); - if (fileExtension == ".OFF") { - if (!LoadOFF(params.m_fileNameIn, points, triangles, myLogger)) { - return -1; - } - } - else if (fileExtension == ".OBJ") { - if (!LoadOBJ(params.m_fileNameIn, points, triangles, myLogger)) { - return -1; - } - } - else { - myLogger.Log("Format not supported!\n"); - return -1; - } - - // run V-HACD - IVHACD* interfaceVHACD = CreateVHACD(); - -#ifdef CL_VERSION_1_1 - if (params.m_paramsVHACD.m_oclAcceleration) { - bool res = interfaceVHACD->OCLInit(oclHelper.GetDevice(), &myLogger); - if (!res) { - params.m_paramsVHACD.m_oclAcceleration = false; - } - } -#endif //CL_VERSION_1_1 - bool res = interfaceVHACD->Compute(&points[0], (unsigned int)points.size() / 3, - (const uint32_t *)&triangles[0], (unsigned int)triangles.size() / 3, params.m_paramsVHACD); - - - - - if (res) { - std::string ext; - if (params.m_fileNameOut.length() > 4) { - ext = params.m_fileNameOut.substr(params.m_fileNameOut.length()-4); - } - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - if (ext != ".obj") { - // save output - unsigned int nConvexHulls = interfaceVHACD->GetNConvexHulls(); - msg.str(""); - msg << "+ Generate output: " << nConvexHulls << " convex-hulls " << endl; - myLogger.Log(msg.str().c_str()); - ofstream foutCH(params.m_fileNameOut.c_str()); - IVHACD::ConvexHull ch; - if (foutCH.is_open()) { - Material mat; - for (unsigned int p = 0; p < nConvexHulls; ++p) { - interfaceVHACD->GetConvexHull(p, ch); - ComputeRandomColor(mat); - SaveVRML2(foutCH, ch.m_points, (const int *)ch.m_triangles, ch.m_nPoints, ch.m_nTriangles, mat, myLogger); - msg.str(""); - msg << "\t CH[" << setfill('0') << setw(5) << p << "] " << ch.m_nPoints << " V, " << ch.m_nTriangles << " T" << endl; - myLogger.Log(msg.str().c_str()); - } - foutCH.close(); - } - } - else { - unsigned int nConvexHulls = interfaceVHACD->GetNConvexHulls(); - msg.str(""); - msg << "+ Generate output: " << nConvexHulls << " convex-hulls " << endl; - myLogger.Log(msg.str().c_str()); - ofstream foutCH(params.m_fileNameOut.c_str()); - IVHACD::ConvexHull ch; - if (foutCH.is_open()) { - Material mat; - int vertexOffset = 1;//obj wavefront starts counting at 1... - for (unsigned int p = 0; p < nConvexHulls; ++p) { - interfaceVHACD->GetConvexHull(p, ch); - SaveOBJ(foutCH, ch.m_points, (const int *)ch.m_triangles, ch.m_nPoints, ch.m_nTriangles, mat, myLogger, p, vertexOffset); - vertexOffset+=ch.m_nPoints; - msg.str(""); - msg << "\t CH[" << setfill('0') << setw(5) << p << "] " << ch.m_nPoints << " V, " << ch.m_nTriangles << " T" << endl; - myLogger.Log(msg.str().c_str()); - } - foutCH.close(); - } - } - } - else { - myLogger.Log("Decomposition cancelled by user!\n"); - } - -#ifdef CL_VERSION_1_1 - if (params.m_paramsVHACD.m_oclAcceleration) { - bool res = interfaceVHACD->OCLRelease(&myLogger); - if (!res) { - assert(-1); - } - } -#endif //CL_VERSION_1_1 - - interfaceVHACD->Clean(); - interfaceVHACD->Release(); - } -#ifdef _CRTDBG_MAP_ALLOC - _CrtDumpMemoryLeaks(); -#endif // _CRTDBG_MAP_ALLOC - return 0; -} - -void Usage(const Parameters& params) -{ - std::ostringstream msg; - msg << "V-HACD V" << VHACD_VERSION_MAJOR << "." << VHACD_VERSION_MINOR << endl; - msg << "Syntax: testVHACD [options] --input infile.obj --output outfile.wrl --log logfile.txt" << endl - << endl; - msg << "Options:" << endl; - msg << " --input Wavefront .obj input file name" << endl; - msg << " --output VRML 2.0 output file name" << endl; - msg << " --log Log file name" << endl; - msg << " --resolution Maximum number of voxels generated during the voxelization stage (default=100,000, range=10,000-16,000,000)" << endl; - msg << " --maxhulls Maximum number of convex hulls to produce." << endl; - msg << " --concavity Maximum allowed concavity (default=0.0025, range=0.0-1.0)" << endl; - msg << " --planeDownsampling Controls the granularity of the search for the \"best\" clipping plane (default=4, range=1-16)" << endl; - msg << " --convexhullDownsampling Controls the precision of the convex-hull generation process during the clipping plane selection stage (default=4, range=1-16)" << endl; - msg << " --alpha Controls the bias toward clipping along symmetry planes (default=0.05, range=0.0-1.0)" << endl; - msg << " --beta Controls the bias toward clipping along revolution axes (default=0.05, range=0.0-1.0)" << endl; - msg << " --gamma Controls the maximum allowed concavity during the merge stage (default=0.00125, range=0.0-1.0)" << endl; - msg << " --delta Controls the bias toward maximaxing local concavity (default=0.05, range=0.0-1.0)" << endl; - msg << " --pca Enable/disable normalizing the mesh before applying the convex decomposition (default=0, range={0,1})" << endl; - msg << " --mode 0: voxel-based approximate convex decomposition, 1: tetrahedron-based approximate convex decomposition (default=0, range={0,1})" << endl; - msg << " --maxNumVerticesPerCH Controls the maximum number of triangles per convex-hull (default=64, range=4-1024)" << endl; - msg << " --minVolumePerCH Controls the adaptive sampling of the generated convex-hulls (default=0.0001, range=0.0-0.01)" << endl; - msg << " --convexhullApproximation Enable/disable approximation when computing convex-hulls (default=1, range={0,1})" << endl; - msg << " --oclAcceleration Enable/disable OpenCL acceleration (default=0, range={0,1})" << endl; - msg << " --oclPlatformID OpenCL platform id (default=0, range=0-# OCL platforms)" << endl; - msg << " --oclDeviceID OpenCL device id (default=0, range=0-# OCL devices)" << endl; - msg << " --help Print usage" << endl - << endl; - msg << "Examples:" << endl; - msg << " testVHACD.exe --input bunny.obj --output bunny_acd.wrl --log log.txt" << endl - << endl; - cout << msg.str(); - if (params.m_paramsVHACD.m_logger) { - params.m_paramsVHACD.m_logger->Log(msg.str().c_str()); - } -} -void ParseParameters(int argc, char* argv[], Parameters& params) -{ - for (int i = 1; i < argc; ++i) { - if (!strcmp(argv[i], "--input")) { - if (++i < argc) - params.m_fileNameIn = argv[i]; - } - else if (!strcmp(argv[i], "--output")) { - if (++i < argc) - params.m_fileNameOut = argv[i]; - } - else if (!strcmp(argv[i], "--log")) { - if (++i < argc) - params.m_fileNameLog = argv[i]; - } - else if (!strcmp(argv[i], "--resolution")) { - if (++i < argc) - params.m_paramsVHACD.m_resolution = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--concavity")) { - if (++i < argc) - params.m_paramsVHACD.m_concavity = atof(argv[i]); - } - else if (!strcmp(argv[i], "--planeDownsampling")) { - if (++i < argc) - params.m_paramsVHACD.m_planeDownsampling = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--convexhullDownsampling")) { - if (++i < argc) - params.m_paramsVHACD.m_convexhullDownsampling = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--alpha")) { - if (++i < argc) - params.m_paramsVHACD.m_alpha = atof(argv[i]); - } - else if (!strcmp(argv[i], "--beta")) { - if (++i < argc) - params.m_paramsVHACD.m_beta = atof(argv[i]); - } - else if (!strcmp(argv[i], "--maxhulls")) { - if (++i < argc) - params.m_paramsVHACD.m_maxConvexHulls = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--pca")) { - if (++i < argc) - params.m_paramsVHACD.m_pca = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--mode")) { - if (++i < argc) - params.m_paramsVHACD.m_mode = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--maxNumVerticesPerCH")) { - if (++i < argc) - params.m_paramsVHACD.m_maxNumVerticesPerCH = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--minVolumePerCH")) { - if (++i < argc) - params.m_paramsVHACD.m_minVolumePerCH = atof(argv[i]); - } - else if (!strcmp(argv[i], "--convexhullApproximation")) { - if (++i < argc) - params.m_paramsVHACD.m_convexhullApproximation = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--oclAcceleration")) { - if (++i < argc) - params.m_paramsVHACD.m_oclAcceleration = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--oclPlatformID")) { - if (++i < argc) - params.m_oclPlatformID = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--oclDeviceID")) { - if (++i < argc) - params.m_oclDeviceID = atoi(argv[i]); - } - else if (!strcmp(argv[i], "--help")) { - params.m_run = false; - } - } - params.m_paramsVHACD.m_resolution = (params.m_paramsVHACD.m_resolution < 64) ? 0 : params.m_paramsVHACD.m_resolution; - params.m_paramsVHACD.m_planeDownsampling = (params.m_paramsVHACD.m_planeDownsampling < 1) ? 1 : params.m_paramsVHACD.m_planeDownsampling; - params.m_paramsVHACD.m_convexhullDownsampling = (params.m_paramsVHACD.m_convexhullDownsampling < 1) ? 1 : params.m_paramsVHACD.m_convexhullDownsampling; -} - -#ifdef CL_VERSION_1_1 -bool InitOCL(const unsigned int oclPlatformID, const unsigned int oclDeviceID, OCLHelper& oclHelper, std::ostringstream& msg) -{ - - bool res = true; - vector info; - res = oclHelper.GetPlatformsInfo(info, "\t\t"); - if (!res) - return res; - - const size_t numPlatforms = info.size(); - msg << "\t Number of OpenCL platforms: " << numPlatforms << endl; - for (size_t i = 0; i < numPlatforms; ++i) { - msg << "\t OpenCL platform [" << i << "]" << endl; - msg << info[i]; - } - msg << "\t Using OpenCL platform [" << oclPlatformID << "]" << endl; - res = oclHelper.InitPlatform(oclPlatformID); - if (!res) - return res; - - info.clear(); - res = oclHelper.GetDevicesInfo(info, "\t\t"); - if (!res) - return res; - - const size_t numDevices = info.size(); - msg << "\t Number of OpenCL devices: " << numDevices << endl; - for (size_t i = 0; i < numDevices; ++i) { - msg << "\t OpenCL device [" << i << "]" << endl; - msg << info[i]; - } - msg << "\t Using OpenCL device [" << oclDeviceID << "]" << endl; - res = oclHelper.InitDevice(oclDeviceID); - return res; -} -#endif // CL_VERSION_1_1 -void GetFileExtension(const string& fileName, string& fileExtension) -{ - size_t lastDotPosition = fileName.find_last_of("."); - if (lastDotPosition == string::npos) { - fileExtension = ""; - } - else { - fileExtension = fileName.substr(lastDotPosition, fileName.size()); - transform(fileExtension.begin(), fileExtension.end(), fileExtension.begin(), ::toupper); - } -} -void ComputeRandomColor(Material& mat) -{ - mat.m_diffuseColor[0] = mat.m_diffuseColor[1] = mat.m_diffuseColor[2] = 0.0f; - while (mat.m_diffuseColor[0] == mat.m_diffuseColor[1] || mat.m_diffuseColor[2] == mat.m_diffuseColor[1] || mat.m_diffuseColor[2] == mat.m_diffuseColor[0]) { - mat.m_diffuseColor[0] = (rand() % 100) / 100.0f; - mat.m_diffuseColor[1] = (rand() % 100) / 100.0f; - mat.m_diffuseColor[2] = (rand() % 100) / 100.0f; - } -} -bool LoadOFF(const string& fileName, vector& points, vector& triangles, IVHACD::IUserLogger& logger) -{ - FILE* fid = fopen(fileName.c_str(), "r"); - if (fid) { - const string strOFF("OFF"); - char temp[1024]; - fscanf(fid, "%s", temp); - if (string(temp) != strOFF) { - logger.Log("Loading error: format not recognized \n"); - fclose(fid); - return false; - } - else { - int nv = 0; - int nf = 0; - int ne = 0; - fscanf(fid, "%i", &nv); - fscanf(fid, "%i", &nf); - fscanf(fid, "%i", &ne); - points.resize(nv * 3); - triangles.resize(nf * 3); - const int np = nv * 3; - for (int p = 0; p < np; p++) { - fscanf(fid, "%f", &(points[p])); - } - int s; - for (int t = 0, r = 0; t < nf; ++t) { - fscanf(fid, "%i", &s); - if (s == 3) { - fscanf(fid, "%i", &(triangles[r++])); - fscanf(fid, "%i", &(triangles[r++])); - fscanf(fid, "%i", &(triangles[r++])); - } - else // Fix me: support only triangular meshes - { - for (int h = 0; h < s; ++h) - fscanf(fid, "%i", &s); - } - } - fclose(fid); - } - } - else { - logger.Log("Loading error: file not found \n"); - return false; - } - return true; -} -bool LoadOBJ(const string& fileName, vector& points, vector& triangles, IVHACD::IUserLogger& logger) -{ - const unsigned int BufferSize = 1024; - FILE* fid = fopen(fileName.c_str(), "r"); - - if (fid) { - char buffer[BufferSize]; - int ip[4]; - float x[3]; - char* pch; - char* str; - while (!feof(fid)) { - if (!fgets(buffer, BufferSize, fid)) { - break; - } - else if (buffer[0] == 'v') { - if (buffer[1] == ' ') { - str = buffer + 2; - for (int k = 0; k < 3; ++k) { - pch = strtok(str, " "); - if (pch) - x[k] = (float)atof(pch); - else { - return false; - } - str = NULL; - } - points.push_back(x[0]); - points.push_back(x[1]); - points.push_back(x[2]); - } - } - else if (buffer[0] == 'f') { - - pch = str = buffer + 2; - int k = 0; - while (pch) { - pch = strtok(str, " "); - if (pch && *pch != '\n') { - ip[k++] = atoi(pch) - 1; - } - else { - break; - } - str = NULL; - } - if (k == 3) { - triangles.push_back(ip[0]); - triangles.push_back(ip[1]); - triangles.push_back(ip[2]); - } - else if (k == 4) { - triangles.push_back(ip[0]); - triangles.push_back(ip[1]); - triangles.push_back(ip[2]); - - triangles.push_back(ip[0]); - triangles.push_back(ip[2]); - triangles.push_back(ip[3]); - } - } - } - fclose(fid); - } - else { - logger.Log("File not found\n"); - return false; - } - return true; -} -bool SaveOFF(const string& fileName, const float* const& points, const int* const& triangles, const unsigned int& nPoints, - const unsigned int& nTriangles, IVHACD::IUserLogger& logger) -{ - ofstream fout(fileName.c_str()); - if (fout.is_open()) { - size_t nV = nPoints * 3; - size_t nT = nTriangles * 3; - fout << "OFF" << std::endl; - fout << nPoints << " " << nTriangles << " " << 0 << std::endl; - for (size_t v = 0; v < nV; v += 3) { - fout << points[v + 0] << " " - << points[v + 1] << " " - << points[v + 2] << std::endl; - } - for (size_t f = 0; f < nT; f += 3) { - fout << "3 " << triangles[f + 0] << " " - << triangles[f + 1] << " " - << triangles[f + 2] << std::endl; - } - fout.close(); - return true; - } - else { - logger.Log("Can't open file\n"); - return false; - } -} -bool SaveVRML2(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints, - const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger) -{ - if (fout.is_open()) { - fout.setf(std::ios::fixed, std::ios::floatfield); - fout.setf(std::ios::showpoint); - fout.precision(6); - size_t nV = nPoints * 3; - size_t nT = nTriangles * 3; - fout << "#VRML V2.0 utf8" << std::endl; - fout << "" << std::endl; - fout << "# Vertices: " << nPoints << std::endl; - fout << "# Triangles: " << nTriangles << std::endl; - fout << "" << std::endl; - fout << "Group {" << std::endl; - fout << " children [" << std::endl; - fout << " Shape {" << std::endl; - fout << " appearance Appearance {" << std::endl; - fout << " material Material {" << std::endl; - fout << " diffuseColor " << material.m_diffuseColor[0] << " " - << material.m_diffuseColor[1] << " " - << material.m_diffuseColor[2] << std::endl; - fout << " ambientIntensity " << material.m_ambientIntensity << std::endl; - fout << " specularColor " << material.m_specularColor[0] << " " - << material.m_specularColor[1] << " " - << material.m_specularColor[2] << std::endl; - fout << " emissiveColor " << material.m_emissiveColor[0] << " " - << material.m_emissiveColor[1] << " " - << material.m_emissiveColor[2] << std::endl; - fout << " shininess " << material.m_shininess << std::endl; - fout << " transparency " << material.m_transparency << std::endl; - fout << " }" << std::endl; - fout << " }" << std::endl; - fout << " geometry IndexedFaceSet {" << std::endl; - fout << " ccw TRUE" << std::endl; - fout << " solid TRUE" << std::endl; - fout << " convex TRUE" << std::endl; - if (nV > 0) { - fout << " coord DEF co Coordinate {" << std::endl; - fout << " point [" << std::endl; - for (size_t v = 0; v < nV; v += 3) { - fout << " " << points[v + 0] << " " - << points[v + 1] << " " - << points[v + 2] << "," << std::endl; - } - fout << " ]" << std::endl; - fout << " }" << std::endl; - } - if (nT > 0) { - fout << " coordIndex [ " << std::endl; - for (size_t f = 0; f < nT; f += 3) { - fout << " " << triangles[f + 0] << ", " - << triangles[f + 1] << ", " - << triangles[f + 2] << ", -1," << std::endl; - } - fout << " ]" << std::endl; - } - fout << " }" << std::endl; - fout << " }" << std::endl; - fout << " ]" << std::endl; - fout << "}" << std::endl; - return true; - } - else { - logger.Log("Can't open file\n"); - return false; - } -} - - -bool SaveOBJ(ofstream& fout, const double* const& points, const int* const& triangles, const unsigned int& nPoints, - const unsigned int& nTriangles, const Material& material, IVHACD::IUserLogger& logger, int convexPart, int vertexOffset) -{ - if (fout.is_open()) { - - fout.setf(std::ios::fixed, std::ios::floatfield); - fout.setf(std::ios::showpoint); - fout.precision(6); - size_t nV = nPoints * 3; - size_t nT = nTriangles * 3; - - fout << "o convex_" << convexPart << std::endl; - - if (nV > 0) { - for (size_t v = 0; v < nV; v += 3) { - fout << "v " << points[v + 0] << " " << points[v + 1] << " " << points[v + 2] << std::endl; - } - } - if (nT > 0) { - for (size_t f = 0; f < nT; f += 3) { - fout << "f " - << triangles[f + 0]+vertexOffset << " " - << triangles[f + 1]+vertexOffset << " " - << triangles[f + 2]+vertexOffset << " " << std::endl; - } - } - return true; - } - else { - logger.Log("Can't open file\n"); - return false; - } -} - diff --git a/src/test/src/oclHelper.cpp b/src/test/src/oclHelper.cpp deleted file mode 100644 index efaa9fa9..00000000 --- a/src/test/src/oclHelper.cpp +++ /dev/null @@ -1,329 +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. -*/ -#ifdef OPENCL_FOUND - -#include "oclHelper.h" -#include -#include - -bool OCLHelper::InitPlatform(const unsigned int platformIndex) -{ - cl_uint numPlatforms; - m_lastError = clGetPlatformIDs(1, NULL, &numPlatforms); - if (m_lastError != CL_SUCCESS || platformIndex >= numPlatforms) - return false; - - cl_platform_id* platforms = new cl_platform_id[numPlatforms]; - m_lastError = clGetPlatformIDs(numPlatforms, platforms, NULL); - if (m_lastError != CL_SUCCESS) { - delete[] platforms; - return false; - } - - m_platform = platforms[platformIndex]; - delete[] platforms; - return true; -} -bool OCLHelper::GetPlatformsInfo(std::vector& info, const std::string& indentation) -{ - const char* platformInfoParameters[] = { "CL_PLATFORM_NAME", - "CL_PLATFORM_VENDOR", - "CL_PLATFORM_VERSION", - "CL_PLATFORM_PROFILE", - "CL_PLATFORM_EXTENSIONS" }; - - cl_uint numPlatforms; - m_lastError = clGetPlatformIDs(1, NULL, &numPlatforms); - if (m_lastError != CL_SUCCESS) - return false; - - cl_platform_id* platforms = new cl_platform_id[numPlatforms]; - m_lastError = clGetPlatformIDs(numPlatforms, platforms, NULL); - if (m_lastError != CL_SUCCESS) { - delete[] platforms; - return false; - } - - size_t bufferSize = 4096; - char* buffer = new char[bufferSize]; - size_t size; - info.resize(numPlatforms); - for (cl_uint i = 0; i < numPlatforms; ++i) { - for (int j = CL_PLATFORM_PROFILE; j <= CL_PLATFORM_EXTENSIONS; ++j) { - info[i] += indentation + platformInfoParameters[j - CL_PLATFORM_PROFILE] + std::string(": "); - m_lastError = clGetPlatformInfo(platforms[i], j, 0, NULL, &size); - if (m_lastError != CL_SUCCESS) { - delete[] buffer; - delete[] platforms; - return false; - } - if (bufferSize < size) { - delete[] buffer; - bufferSize = size; - buffer = new char[bufferSize]; - } - m_lastError = clGetPlatformInfo(platforms[i], j, size, buffer, NULL); - if (m_lastError != CL_SUCCESS) { - delete[] buffer; - delete[] platforms; - return false; - } - info[i] += buffer + std::string("\n"); - } - } - delete[] platforms; - delete[] buffer; - return true; -} -bool OCLHelper::InitDevice(const unsigned int deviceIndex) -{ - cl_uint numDevices; - m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices); - if (m_lastError != CL_SUCCESS || deviceIndex >= numDevices) - return false; - - cl_device_id* devices = new cl_device_id[numDevices]; - m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, numDevices, devices, NULL); - if (m_lastError != CL_SUCCESS) { - delete[] devices; - return false; - } - m_device = devices[deviceIndex]; - delete[] devices; - return true; -} -bool OCLHelper::GetDevicesInfo(std::vector& info, const std::string& indentation) -{ - enum { - DATA_TYPE_CL_UINT, - DATA_TYPE_CL_BOOL, - DATA_TYPE_STRING, - DATA_TYPE_CL_ULONG, - DATA_TYPE_CL_DEVICE_FP_CONFIG, - DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES, - DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE, - DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE, - DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP, - DATA_TYPE_CL_DEVICE_TYPE, - DATA_TYPE_SIZE_T, - DATA_TYPE_SIZE_T_3, - }; - typedef struct - { - cl_device_info id; - const char* name; - int type; - } DeviceInfoParam; - const int numDeviceInfoParameters = 49; - const DeviceInfoParam deviceInfoParameters[numDeviceInfoParameters] = { - { CL_DEVICE_NAME, "CL_DEVICE_NAME", DATA_TYPE_STRING }, - { CL_DEVICE_PROFILE, "CL_DEVICE_PROFILE", DATA_TYPE_STRING }, - { CL_DEVICE_VENDOR, "CL_DEVICE_VENDOR", DATA_TYPE_STRING }, - { CL_DEVICE_VERSION, "CL_DEVICE_VERSION", DATA_TYPE_STRING }, - { CL_DRIVER_VERSION, "CL_DRIVER_VERSION", DATA_TYPE_STRING }, - { CL_DEVICE_EXTENSIONS, "CL_DEVICE_EXTENSIONS", DATA_TYPE_STRING }, - { CL_DEVICE_VERSION, "CL_DEVICE_VERSION", DATA_TYPE_STRING }, - { CL_DEVICE_ADDRESS_BITS, "CL_DEVICE_ADDRESS_BITS", DATA_TYPE_CL_UINT }, - { CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, "CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MAX_CLOCK_FREQUENCY, "CL_DEVICE_MAX_CLOCK_FREQUENCY", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MAX_COMPUTE_UNITS, "CL_DEVICE_MAX_COMPUTE_UNITS", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MAX_CONSTANT_ARGS, "CL_DEVICE_MAX_CONSTANT_ARGS", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MAX_READ_IMAGE_ARGS, "CL_DEVICE_MAX_READ_IMAGE_ARGS", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MAX_SAMPLERS, "CL_DEVICE_MAX_SAMPLERS", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, "CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MAX_WRITE_IMAGE_ARGS, "CL_DEVICE_MAX_WRITE_IMAGE_ARGS", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MEM_BASE_ADDR_ALIGN, "CL_DEVICE_MEM_BASE_ADDR_ALIGN", DATA_TYPE_CL_UINT }, - { CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, "CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE", DATA_TYPE_CL_UINT }, - { CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR", DATA_TYPE_CL_UINT }, - { CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT", DATA_TYPE_CL_UINT }, - { CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT", DATA_TYPE_CL_UINT }, - { CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG", DATA_TYPE_CL_UINT }, - { CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT", DATA_TYPE_CL_UINT }, - { CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, "CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE", DATA_TYPE_CL_UINT }, - { CL_DEVICE_VENDOR_ID, "CL_DEVICE_VENDOR_ID", DATA_TYPE_CL_UINT }, - { CL_DEVICE_AVAILABLE, "CL_DEVICE_AVAILABLE", DATA_TYPE_CL_BOOL }, - { CL_DEVICE_COMPILER_AVAILABLE, "CL_DEVICE_COMPILER_AVAILABLE", DATA_TYPE_CL_BOOL }, - { CL_DEVICE_ENDIAN_LITTLE, "CL_DEVICE_ENDIAN_LITTLE", DATA_TYPE_CL_BOOL }, - { CL_DEVICE_ERROR_CORRECTION_SUPPORT, "CL_DEVICE_ERROR_CORRECTION_SUPPORT", DATA_TYPE_CL_BOOL }, - { CL_DEVICE_IMAGE_SUPPORT, "CL_DEVICE_IMAGE_SUPPORT", DATA_TYPE_CL_BOOL }, - { CL_DEVICE_EXECUTION_CAPABILITIES, "CL_DEVICE_EXECUTION_CAPABILITIES", DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES }, - { CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, "CL_DEVICE_GLOBAL_MEM_CACHE_SIZE", DATA_TYPE_CL_ULONG }, - { CL_DEVICE_GLOBAL_MEM_SIZE, "CL_DEVICE_GLOBAL_MEM_SIZE", DATA_TYPE_CL_ULONG }, - { CL_DEVICE_LOCAL_MEM_SIZE, "CL_DEVICE_LOCAL_MEM_SIZE", DATA_TYPE_CL_ULONG }, - { CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, "CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE", DATA_TYPE_CL_ULONG }, - { CL_DEVICE_MAX_MEM_ALLOC_SIZE, "CL_DEVICE_MAX_MEM_ALLOC_SIZE", DATA_TYPE_CL_ULONG }, - { CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, "CL_DEVICE_GLOBAL_MEM_CACHE_TYPE", DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE }, - { CL_DEVICE_IMAGE2D_MAX_HEIGHT, "CL_DEVICE_IMAGE2D_MAX_HEIGHT", DATA_TYPE_SIZE_T }, - { CL_DEVICE_IMAGE2D_MAX_WIDTH, "CL_DEVICE_IMAGE2D_MAX_WIDTH", DATA_TYPE_SIZE_T }, - { CL_DEVICE_IMAGE3D_MAX_DEPTH, "CL_DEVICE_IMAGE3D_MAX_DEPTH", DATA_TYPE_SIZE_T }, - { CL_DEVICE_IMAGE3D_MAX_HEIGHT, "CL_DEVICE_IMAGE3D_MAX_HEIGHT", DATA_TYPE_SIZE_T }, - { CL_DEVICE_IMAGE3D_MAX_WIDTH, "CL_DEVICE_IMAGE3D_MAX_WIDTH", DATA_TYPE_SIZE_T }, - { CL_DEVICE_MAX_PARAMETER_SIZE, "CL_DEVICE_MAX_PARAMETER_SIZE", DATA_TYPE_SIZE_T }, - { CL_DEVICE_MAX_WORK_GROUP_SIZE, "CL_DEVICE_MAX_WORK_GROUP_SIZE", DATA_TYPE_SIZE_T }, - { CL_DEVICE_PROFILING_TIMER_RESOLUTION, "CL_DEVICE_PROFILING_TIMER_RESOLUTION", DATA_TYPE_SIZE_T }, - { CL_DEVICE_QUEUE_PROPERTIES, "CL_DEVICE_QUEUE_PROPERTIES", DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP }, - { CL_DEVICE_TYPE, "CL_DEVICE_TYPE", DATA_TYPE_CL_DEVICE_TYPE }, - { CL_DEVICE_LOCAL_MEM_TYPE, "CL_DEVICE_LOCAL_MEM_TYPE", DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE }, - { CL_DEVICE_MAX_WORK_ITEM_SIZES, "CL_DEVICE_MAX_WORK_ITEM_SIZES", DATA_TYPE_SIZE_T_3 } - // { CL_DEVICE_DOUBLE_FP_CONFIG, "CL_DEVICE_DOUBLE_FP_CONFIG", DATA_TYPE_CL_DEVICE_FP_CONFIG }, - // - }; - cl_uint numDevices; - m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, 0, NULL, &numDevices); - if (m_lastError != CL_SUCCESS) - return false; - - cl_device_id* devices = new cl_device_id[numDevices]; - m_lastError = clGetDeviceIDs(m_platform, CL_DEVICE_TYPE_ALL, numDevices, devices, NULL); - if (m_lastError != CL_SUCCESS) { - delete[] devices; - return false; - } - size_t bufferSize = 4096; - char* buffer = new char[bufferSize]; - size_t size; - info.resize(numDevices); - - for (cl_uint i = 0; i < numDevices; ++i) { - for (int j = 0; j < numDeviceInfoParameters; ++j) { - const DeviceInfoParam& infoParam = deviceInfoParameters[j]; - info[i] += indentation + infoParam.name + std::string(": "); - - if (infoParam.type == DATA_TYPE_STRING) { - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, 0, NULL, &size); - if (m_lastError == CL_SUCCESS) { - if (bufferSize < size) { - delete[] buffer; - bufferSize = size; - buffer = new char[bufferSize]; - } - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, size, buffer, NULL); - if (m_lastError != CL_SUCCESS) { - delete[] devices; - delete[] buffer; - return false; - } - info[i] += buffer + std::string("\n"); - } - } - else if (infoParam.type == DATA_TYPE_CL_UINT) { - cl_uint value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_uint), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_CL_BOOL) { - cl_bool value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_bool), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_CL_ULONG) { - cl_ulong value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_ulong), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_CL_DEVICE_FP_CONFIG) { - cl_device_fp_config value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_fp_config), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_CL_DEVICE_EXEC_CAPABILITIES) { - cl_device_exec_capabilities value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_exec_capabilities), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_CL_DEVICE_MEM_CACHE_TYPE) { - cl_device_mem_cache_type value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_mem_cache_type), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_CL_DEVICE_MEM_LOCAL_TYPE) { - cl_device_local_mem_type value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_local_mem_type), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_CL_DEVICE_CMD_QUEUE_PROP) { - cl_command_queue_properties value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_command_queue_properties), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_CL_DEVICE_TYPE) { - cl_device_type value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(cl_device_type), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_SIZE_T) { - size_t value; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, sizeof(size_t), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << value; - info[i] += svalue.str() + "\n"; - } - } - else if (infoParam.type == DATA_TYPE_SIZE_T_3) { - size_t value[3]; - m_lastError = clGetDeviceInfo(devices[i], infoParam.id, 3 * sizeof(size_t), &value, &size); - if (m_lastError == CL_SUCCESS) { - std::ostringstream svalue; - svalue << "(" << value[0] << ", " << value[1] << ", " << value[2] << ")"; - info[i] += svalue.str() + "\n"; - } - } - else { - assert(0); - } - } - } - delete[] devices; - delete[] buffer; - return true; -} -#endif // OPENCL_FOUND