From ffee956ddec909bb11433ec3e902ca9b7d750963 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Mon, 26 Sep 2016 07:16:18 -0700 Subject: [PATCH] add VHACD, with the addition of Wavefront .obj export --- Extras/VHACD/inc/btAlignedAllocator.h | 104 + Extras/VHACD/inc/btAlignedObjectArray.h | 448 ++++ Extras/VHACD/inc/btConvexHullComputer.h | 97 + Extras/VHACD/inc/btMinMax.h | 65 + Extras/VHACD/inc/btScalar.h | 532 +++++ Extras/VHACD/inc/btVector3.h | 715 ++++++ Extras/VHACD/inc/vhacdCircularList.h | 79 + Extras/VHACD/inc/vhacdCircularList.inl | 161 ++ Extras/VHACD/inc/vhacdICHull.h | 98 + Extras/VHACD/inc/vhacdManifoldMesh.h | 142 ++ Extras/VHACD/inc/vhacdMesh.h | 129 ++ Extras/VHACD/inc/vhacdMutex.h | 146 ++ Extras/VHACD/inc/vhacdSArray.h | 158 ++ Extras/VHACD/inc/vhacdTimer.h | 121 + Extras/VHACD/inc/vhacdVHACD.h | 350 +++ Extras/VHACD/inc/vhacdVector.h | 103 + Extras/VHACD/inc/vhacdVector.inl | 362 +++ Extras/VHACD/inc/vhacdVolume.h | 419 ++++ Extras/VHACD/premake4.lua | 4 + Extras/VHACD/public/VHACD.h | 121 + Extras/VHACD/src/VHACD.cpp | 1433 ++++++++++++ Extras/VHACD/src/btAlignedAllocator.cpp | 176 ++ Extras/VHACD/src/btConvexHullComputer.cpp | 2475 +++++++++++++++++++++ Extras/VHACD/src/premake4.lua | 10 + Extras/VHACD/src/vhacdICHull.cpp | 725 ++++++ Extras/VHACD/src/vhacdManifoldMesh.cpp | 202 ++ Extras/VHACD/src/vhacdMesh.cpp | 323 +++ Extras/VHACD/src/vhacdVolume.cpp | 1617 ++++++++++++++ Extras/VHACD/test/inc/oclHelper.h | 50 + Extras/VHACD/test/src/main.cpp | 648 ++++++ Extras/VHACD/test/src/oclHelper.cpp | 329 +++ Extras/VHACD/test/src/premake4.lua | 25 + Extras/premake4.lua | 1 + 33 files changed, 12368 insertions(+) create mode 100644 Extras/VHACD/inc/btAlignedAllocator.h create mode 100644 Extras/VHACD/inc/btAlignedObjectArray.h create mode 100644 Extras/VHACD/inc/btConvexHullComputer.h create mode 100644 Extras/VHACD/inc/btMinMax.h create mode 100644 Extras/VHACD/inc/btScalar.h create mode 100644 Extras/VHACD/inc/btVector3.h create mode 100644 Extras/VHACD/inc/vhacdCircularList.h create mode 100644 Extras/VHACD/inc/vhacdCircularList.inl create mode 100644 Extras/VHACD/inc/vhacdICHull.h create mode 100644 Extras/VHACD/inc/vhacdManifoldMesh.h create mode 100644 Extras/VHACD/inc/vhacdMesh.h create mode 100644 Extras/VHACD/inc/vhacdMutex.h create mode 100644 Extras/VHACD/inc/vhacdSArray.h create mode 100644 Extras/VHACD/inc/vhacdTimer.h create mode 100644 Extras/VHACD/inc/vhacdVHACD.h create mode 100644 Extras/VHACD/inc/vhacdVector.h create mode 100644 Extras/VHACD/inc/vhacdVector.inl create mode 100644 Extras/VHACD/inc/vhacdVolume.h create mode 100644 Extras/VHACD/premake4.lua create mode 100644 Extras/VHACD/public/VHACD.h create mode 100644 Extras/VHACD/src/VHACD.cpp create mode 100644 Extras/VHACD/src/btAlignedAllocator.cpp create mode 100644 Extras/VHACD/src/btConvexHullComputer.cpp create mode 100644 Extras/VHACD/src/premake4.lua create mode 100644 Extras/VHACD/src/vhacdICHull.cpp create mode 100644 Extras/VHACD/src/vhacdManifoldMesh.cpp create mode 100644 Extras/VHACD/src/vhacdMesh.cpp create mode 100644 Extras/VHACD/src/vhacdVolume.cpp create mode 100644 Extras/VHACD/test/inc/oclHelper.h create mode 100644 Extras/VHACD/test/src/main.cpp create mode 100644 Extras/VHACD/test/src/oclHelper.cpp create mode 100644 Extras/VHACD/test/src/premake4.lua diff --git a/Extras/VHACD/inc/btAlignedAllocator.h b/Extras/VHACD/inc/btAlignedAllocator.h new file mode 100644 index 000000000..4af0472af --- /dev/null +++ b/Extras/VHACD/inc/btAlignedAllocator.h @@ -0,0 +1,104 @@ +/* +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, int alignment, int line, char* filename); + +void btAlignedFreeInternal(void* ptr, int line, char* filename); + +#else +void* btAlignedAllocInternal(size_t size, int alignment); +void btAlignedFreeInternal(void* ptr); + +#define btAlignedAlloc(size, alignment) btAlignedAllocInternal(size, alignment) +#define btAlignedFree(ptr) btAlignedFreeInternal(ptr) + +#endif +typedef int size_type; + +typedef void*(btAlignedAllocFunc)(size_t size, int 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/Extras/VHACD/inc/btAlignedObjectArray.h b/Extras/VHACD/inc/btAlignedObjectArray.h new file mode 100644 index 000000000..49413c856 --- /dev/null +++ b/Extras/VHACD/inc/btAlignedObjectArray.h @@ -0,0 +1,448 @@ +/* +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; + + int m_size; + int 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 int allocSize(int size) + { + return (size ? size * 2 : 1); + } + SIMD_FORCE_INLINE void copy(int start, int end, T* dest) const + { + int 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(int first, int last) + { + int i; + for (i = first; i < last; i++) { + m_data[i].~T(); + } + } + + SIMD_FORCE_INLINE void* allocate(int 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(); + + int otherSize = otherArray.size(); + resize(otherSize); + otherArray.copy(0, otherSize, m_data); + } + + /// return the number of elements in the array + SIMD_FORCE_INLINE int size() const + { + return m_size; + } + + SIMD_FORCE_INLINE const T& at(int n) const + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE T& at(int n) + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE const T& operator[](int n) const + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE T& operator[](int 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(int newsize, const T& fillData = T()) + { + int curSize = size(); + + if (newsize < curSize) { + for (int i = newsize; i < curSize; i++) { + m_data[i].~T(); + } + } + else { + if (newsize > size()) { + reserve(newsize); + } +#ifdef BT_USE_PLACEMENT_NEW + for (int i = curSize; i < newsize; i++) { + new (&m_data[i]) T(fillData); + } +#endif //BT_USE_PLACEMENT_NEW + } + + m_size = newsize; + } + + SIMD_FORCE_INLINE T& expandNonInitializing() + { + int sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + m_size++; + + return m_data[sz]; + } + + SIMD_FORCE_INLINE T& expand(const T& fillValue = T()) + { + int 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) + { + int 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 int capacity() const + { + return m_capacity; + } + + SIMD_FORCE_INLINE void reserve(int _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, int lo, int hi) + { + // lo is the lower index, hi is the upper index + // of the region of array a that is to be sorted + int 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, int k, int 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) { + int 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(int index0, int 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 */ + int k; + int 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 + int findBinarySearch(const T& key) const + { + int first = 0; + int last = size() - 1; + + //assume sorted array + while (first <= last) { + int 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 + } + + int findLinearSearch(const T& key) const + { + int index = size(); + int i; + + for (i = 0; i < size(); i++) { + if (m_data[i] == key) { + index = i; + break; + } + } + return index; + } + + void remove(const T& key) + { + + int findIndex = findLinearSearch(key); + if (findIndex < size()) { + swap(findIndex, size() - 1); + pop_back(); + } + } + + //PCK: whole function + void initializeFromBuffer(void* buffer, int size, int capacity) + { + clear(); + m_ownsMemory = false; + m_data = (T*)buffer; + m_size = size; + m_capacity = capacity; + } + + void copyFromArray(const btAlignedObjectArray& otherArray) + { + int otherSize = otherArray.size(); + resize(otherSize); + otherArray.copy(0, otherSize, m_data); + } +}; + +#endif //BT_OBJECT_ARRAY__ diff --git a/Extras/VHACD/inc/btConvexHullComputer.h b/Extras/VHACD/inc/btConvexHullComputer.h new file mode 100644 index 000000000..d59b81e5d --- /dev/null +++ b/Extras/VHACD/inc/btConvexHullComputer.h @@ -0,0 +1,97 @@ +/* +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, int stride, int count, btScalar shrink, btScalar shrinkClamp); + +public: + class Edge { + private: + int next; + int reverse; + int targetVertex; + + friend class btConvexHullComputer; + + public: + int getSourceVertex() const + { + return (this + reverse)->targetVertex; + } + + int 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, int stride, int count, btScalar shrink, btScalar shrinkClamp) + { + return compute(coords, false, stride, count, shrink, shrinkClamp); + } + + // same as above, but double precision + btScalar compute(const double* coords, int stride, int count, btScalar shrink, btScalar shrinkClamp) + { + return compute(coords, true, stride, count, shrink, shrinkClamp); + } +}; + +#endif //BT_CONVEX_HULL_COMPUTER_H diff --git a/Extras/VHACD/inc/btMinMax.h b/Extras/VHACD/inc/btMinMax.h new file mode 100644 index 000000000..40b0ceb6e --- /dev/null +++ b/Extras/VHACD/inc/btMinMax.h @@ -0,0 +1,65 @@ +/* +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/Extras/VHACD/inc/btScalar.h b/Extras/VHACD/inc/btScalar.h new file mode 100644 index 000000000..ad3032497 --- /dev/null +++ b/Extras/VHACD/inc/btScalar.h @@ -0,0 +1,532 @@ +/* +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 + +/* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ +#define BT_BULLET_VERSION 279 + +inline int 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 int 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##__ { \ + int 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)(((int)condition | -(int)condition) >> 31); + unsigned testEqz = ~testNz; + return ((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE int btSelect(unsigned condition, int valueIfConditionNonZero, int valueIfConditionZero) +{ + unsigned testNz = (unsigned)(((int)condition | -(int)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(int 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 unsigned int btSwapEndianFloat(float d) +{ + unsigned int 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(unsigned int 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(int objectType) + : m_objectType(objectType) + { + } + int m_objectType; + inline int getObjectType() const + { + return m_objectType; + } +}; +#endif //BT_SCALAR_H diff --git a/Extras/VHACD/inc/btVector3.h b/Extras/VHACD/inc/btVector3.h new file mode 100644 index 000000000..159d67bdd --- /dev/null +++ b/Extras/VHACD/inc/btVector3.h @@ -0,0 +1,715 @@ +/* +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(); + int 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 int 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 int 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 int furthestAxis() const + { + return absolute().minAxis(); + } + + SIMD_FORCE_INLINE int 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[](int i) { return (&m_floats[0])[i]; } + //SIMD_FORCE_INLINE const btScalar& operator[](int 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 int maxAxis4() const + { + int 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 int minAxis4() const + { + int 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 int 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 (int 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 (int 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 (int i = 0; i < 4; i++) + dataOut.m_floats[i] = float(m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::deSerializeFloat(const struct btVector3FloatData& dataIn) +{ + for (int 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 (int i = 0; i < 4; i++) + dataOut.m_floats[i] = double(m_floats[i]); +} + +SIMD_FORCE_INLINE void btVector3::deSerializeDouble(const struct btVector3DoubleData& dataIn) +{ + for (int 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 (int i = 0; i < 4; i++) + dataOut.m_floats[i] = m_floats[i]; +} + +SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn) +{ + for (int i = 0; i < 4; i++) + m_floats[i] = dataIn.m_floats[i]; +} + +#endif //BT_VECTOR3_H diff --git a/Extras/VHACD/inc/vhacdCircularList.h b/Extras/VHACD/inc/vhacdCircularList.h new file mode 100644 index 000000000..0f5ddf9ec --- /dev/null +++ b/Extras/VHACD/inc/vhacdCircularList.h @@ -0,0 +1,79 @@ +/* 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/Extras/VHACD/inc/vhacdCircularList.inl b/Extras/VHACD/inc/vhacdCircularList.inl new file mode 100644 index 000000000..2be518052 --- /dev/null +++ b/Extras/VHACD/inc/vhacdCircularList.inl @@ -0,0 +1,161 @@ +#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/Extras/VHACD/inc/vhacdICHull.h b/Extras/VHACD/inc/vhacdICHull.h new file mode 100644 index 000000000..a3d941651 --- /dev/null +++ b/Extras/VHACD/inc/vhacdICHull.h @@ -0,0 +1,98 @@ +/* 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, int id); + //! Add points to the convex-hull + bool AddPoints(const Vec3* points, size_t nPoints); + //! + ICHullError Process(); + //! + ICHullError Process(const unsigned int 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(unsigned int& addedPoints); + //! + bool CleanTriangles(); + //! + bool CleanUp(unsigned int& addedPoints); + //! + bool MakeCCW(CircularListElement* f, + CircularListElement* e, + CircularListElement* v); + void Clear(); + +private: + static const int 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/Extras/VHACD/inc/vhacdManifoldMesh.h b/Extras/VHACD/inc/vhacdManifoldMesh.h new file mode 100644 index 000000000..f178ad3d5 --- /dev/null +++ b/Extras/VHACD/inc/vhacdManifoldMesh.h @@ -0,0 +1,142 @@ +/* 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; + int 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/Extras/VHACD/inc/vhacdMesh.h b/Extras/VHACD/inc/vhacdMesh.h new file mode 100644 index 000000000..0975d8e1f --- /dev/null +++ b/Extras/VHACD/inc/vhacdMesh.h @@ -0,0 +1,129 @@ +/* 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(); }; + int* GetTriangles() { return (int*)m_triangles.Data(); } // ugly + const int* const GetTriangles() const { return (int*)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(); + +#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/Extras/VHACD/inc/vhacdMutex.h b/Extras/VHACD/inc/vhacdMutex.h new file mode 100644 index 000000000..a28f6be61 --- /dev/null +++ b/Extras/VHACD/inc/vhacdMutex.h @@ -0,0 +1,146 @@ +/*! +** +** 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) + +//#define _WIN32_WINNT 0x400 +#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__) + int 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/Extras/VHACD/inc/vhacdSArray.h b/Extras/VHACD/inc/vhacdSArray.h new file mode 100644 index 000000000..4d84d1ae2 --- /dev/null +++ b/Extras/VHACD/inc/vhacdSArray.h @@ -0,0 +1,158 @@ +/* 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/Extras/VHACD/inc/vhacdTimer.h b/Extras/VHACD/inc/vhacdTimer.h new file mode 100644 index 000000000..ba0e2c336 --- /dev/null +++ b/Extras/VHACD/inc/vhacdTimer.h @@ -0,0 +1,121 @@ +/* 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/Extras/VHACD/inc/vhacdVHACD.h b/Extras/VHACD/inc/vhacdVHACD.h new file mode 100644 index 000000000..8c6c7fea1 --- /dev/null +++ b/Extras/VHACD/inc/vhacdVHACD.h @@ -0,0 +1,350 @@ +/* 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" + +#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) {} + unsigned int GetNConvexHulls() const + { + return (unsigned int)m_convexHulls.Size(); + } + void Cancel() + { + SetCancel(true); + } + void GetConvexHull(const unsigned int index, ConvexHull& ch) const + { + Mesh* mesh = m_convexHulls[index]; + ch.m_nPoints = (unsigned int)mesh->GetNPoints(); + ch.m_nTriangles = (unsigned int)mesh->GetNTriangles(); + ch.m_points = mesh->GetPoints(); + ch.m_triangles = mesh->GetTriangles(); + } + void Clean(void) + { + 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 unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int nTriangles, + const Parameters& params); + bool Compute(const double* const points, + const unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int nTriangles, + const Parameters& params); + bool OCLInit(void* const oclDevice, + IUserLogger* const logger = 0); + bool OCLRelease(IUserLogger* const logger = 0); + +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() + { + 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 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 int convexhullDownsampling, + const double progress0, + const double progress1, + Plane& bestPlane, + double& minConcavity, + const Parameters& params); + template + void AlignMesh(const T* const points, + const unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int 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 unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int 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; + int iteration = 0; + const int 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 unsigned int stridePoints, + const unsigned int nPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int nTriangles, + const Parameters& params) + { + Init(); + if (params.m_oclAcceleration) { + // build kernals + } + AlignMesh(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params); + VoxelizeMesh(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params); + ComputePrimitiveSet(params); + ComputeACD(params); + MergeConvexHulls(params); + SimplifyConvexHulls(params); + if (params.m_oclAcceleration) { + // Release kernals + } + if (GetCancel()) { + Clean(); + return false; + } + return true; + } + +private: + 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; + int 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/Extras/VHACD/inc/vhacdVector.h b/Extras/VHACD/inc/vhacdVector.h new file mode 100644 index 000000000..c9edfb1ae --- /dev/null +++ b/Extras/VHACD/inc/vhacdVector.h @@ -0,0 +1,103 @@ +/* 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); + +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/Extras/VHACD/inc/vhacdVector.inl b/Extras/VHACD/inc/vhacdVector.inl new file mode 100644 index 000000000..223c2ef17 --- /dev/null +++ b/Extras/VHACD/inc/vhacdVector.inl @@ -0,0 +1,362 @@ +#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/Extras/VHACD/inc/vhacdVolume.h b/Extras/VHACD/inc/vhacdVolume.h new file mode 100644 index 000000000..31377aa6c --- /dev/null +++ b/Extras/VHACD/inc/vhacdVolume.h @@ -0,0 +1,419 @@ +/* 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 + +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 int 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 unsigned int stridePoints, const unsigned int nPoints, + const int* const triangles, const unsigned int strideTriangles, const unsigned int 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 unsigned int stridePoints, const unsigned int 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; +}; +int 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 unsigned int idx, const Vec3& barycenter, + const double (&rot)[3][3], Vec3& pt){}; +template <> +inline void ComputeAlignedPoint(const float* const points, const unsigned int 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 unsigned int 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 unsigned int stridePoints, const unsigned int nPoints, + const Vec3& barycenter, const double (&rot)[3][3]) +{ + Vec3 pt; + ComputeAlignedPoint(points, 0, barycenter, rot, pt); + m_maxBB = pt; + m_minBB = pt; + for (unsigned int v = 1; v < nPoints; ++v) { + ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt); + for (int 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 unsigned int stridePoints, const unsigned int nPoints, + const int* const triangles, const unsigned int strideTriangles, const unsigned int 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 (int 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; + int 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(); +} +} +#endif // VHACD_VOLUME_H diff --git a/Extras/VHACD/premake4.lua b/Extras/VHACD/premake4.lua new file mode 100644 index 000000000..f66f160e2 --- /dev/null +++ b/Extras/VHACD/premake4.lua @@ -0,0 +1,4 @@ + +include "src" +include "test/src" + diff --git a/Extras/VHACD/public/VHACD.h b/Extras/VHACD/public/VHACD.h new file mode 100644 index 000000000..20f6aacd9 --- /dev/null +++ b/Extras/VHACD/public/VHACD.h @@ -0,0 +1,121 @@ +/* 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 2 + +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; + int* m_triangles; + unsigned int m_nPoints; + unsigned int m_nTriangles; + }; + + class Parameters { + public: + Parameters(void) { Init(); } + void Init(void) + { + m_resolution = 1000000; + m_depth = 20; + m_concavity = 0.001; + m_planeDownsampling = 4; + m_convexhullDownsampling = 4; + m_alpha = 0.05; + m_beta = 0.05; + m_gamma = 0.0005; + 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; + } + double m_concavity; + double m_alpha; + double m_beta; + double m_gamma; + double m_minVolumePerCH; + IUserCallback* m_callback; + IUserLogger* m_logger; + unsigned int m_resolution; + unsigned int m_maxNumVerticesPerCH; + int m_depth; + int m_planeDownsampling; + int m_convexhullDownsampling; + int m_pca; + int m_mode; + int m_convexhullApproximation; + int m_oclAcceleration; + }; + + virtual void Cancel() = 0; + virtual bool Compute(const float* const points, + const unsigned int stridePoints, + const unsigned int countPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int countTriangles, + const Parameters& params) + = 0; + virtual bool Compute(const double* const points, + const unsigned int stridePoints, + const unsigned int countPoints, + const int* const triangles, + const unsigned int strideTriangles, + const unsigned int countTriangles, + const Parameters& params) + = 0; + virtual unsigned int GetNConvexHulls() const = 0; + virtual void GetConvexHull(const unsigned int 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; + +protected: + virtual ~IVHACD(void) {} +}; +IVHACD* CreateVHACD(void); +} +#endif // VHACD_H \ No newline at end of file diff --git a/Extras/VHACD/src/VHACD.cpp b/Extras/VHACD/src/VHACD.cpp new file mode 100644 index 000000000..8f1d30903 --- /dev/null +++ b/Extras/VHACD/src/VHACD.cpp @@ -0,0 +1,1433 @@ +/* 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 +#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" + +#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 USE_SSE +#include + +const int SIMD_WIDTH = 4; +inline int FindMinimumElement(const float* const d, float* const m, const int n) +{ + // Min within vectors + __m128 min_i = _mm_set1_ps(-1.0f); + __m128 min_v = _mm_set1_ps(std::numeric_limits::max()); + for (int 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 int min_idx = __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4))); + int ret = min_i[min_idx] + min_idx; + + // Trailing elements + for (int 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 int FindMinimumElement(const float* const d, float* const m, const int begin, const int end) +{ + // Leading elements + int min_i = -1; + float min_d = std::numeric_limits::max(); + const int aligned = (begin & ~(SIMD_WIDTH - 1)) + ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0); + for (int 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 int n = end - aligned; + const int 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 int FindMinimumElement(const float* const d, float* const m, const int begin, const int end) +{ + int idx = -1; + float min = (std::numeric_limits::max)(); + for (int i = begin; i < 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 (int 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 (int 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 (int 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 (int 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 (int 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 unsigned int stridePoints, const unsigned int nPoints, + const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles, const Parameters& params) +{ + return ComputeACD(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params); +} +bool VHACD::Compute(const float* const points, const unsigned int stridePoints, const unsigned int nPoints, + const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles, const Parameters& params) +{ + return ComputeACD(points, stridePoints, nPoints, triangles, strideTriangles, 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 int 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; + int iBest = -1; + int nPlanes = static_cast(planes.Size()); + bool cancel = false; + int 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 (int 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 int nVoxels = (int)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 (int i = 0; i < m_ompNumProcessors; ++i) { + partialVolumes[i] = clCreateBuffer(m_oclContext, + CL_MEM_WRITE_ONLY, + sizeof(unsigned int) * 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(unsigned int), &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(unsigned int) * 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(unsigned int) * 4 * m_oclWorkGroupSize, NULL); + if (error != CL_SUCCESS) { + if (params.m_logger) { + params.m_logger->Log("Couldn't kernel atguments \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 (int x = 0; x < nPlanes; ++x) { + int 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); + } + int nValues = (int)nWorkGroups; + while (nValues > 1) { + error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, sizeof(int), &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 = (int)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 + unsigned int volumes[4]; + cl_int error = clEnqueueReadBuffer(m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE, + 0, sizeof(unsigned int) * 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 (int i = 0; i < m_ompNumProcessors; ++i) { + clReleaseMemObject(partialVolumes[i]); + } + delete[] partialVolumes; + } +#endif // CL_VERSION_1_1 + + if (psets) { + for (int 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; + int sub = 0; + bool firstIteration = true; + m_volumeCH0 = 1.0; + while (sub++ < params.m_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 int n = (int)mesh->GetNPoints(); + for (int 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), (int)pts.Size(), -1.0, -1.0); + combinedCH->ResizePoints(0); + combinedCH->ResizeTriangles(0); + for (int v = 0; v < ch.vertices.size(); v++) { + combinedCH->AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int nt = ch.faces.size(); + for (int t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int a = sourceEdge->getSourceVertex(); + int b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int 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()); + } + + size_t nConvexHulls = m_convexHulls.Size(); + int iteration = 0; + if (nConvexHulls > 1 && !m_cancel) { + const double threshold = params.m_gamma; + 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()); + + // Check if we should merge these hulls + if (bestCost >= threshold) { + break; + } + double nr = 1 + (8 * addr); + const size_t addrI = (static_cast(sqrt(nr)) - 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 SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume) +{ + if (nvertices <= 4) { + return; + } + ICHull icHull; + icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints()); + icHull.Process((unsigned int)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()); + } +} +} diff --git a/Extras/VHACD/src/btAlignedAllocator.cpp b/Extras/VHACD/src/btAlignedAllocator.cpp new file mode 100644 index 000000000..e69442f67 --- /dev/null +++ b/Extras/VHACD/src/btAlignedAllocator.cpp @@ -0,0 +1,176 @@ +/* +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" + +int gNumAlignedAllocs = 0; +int gNumAlignedFree = 0; +int 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, int 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, int alignment) +{ + return memalign(alignment, size); +} + +static inline void btAlignedFreeDefault(void* ptr) +{ + free(ptr); +} +#else +static inline void* btAlignedAllocDefault(size_t size, int 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, int alignment, int 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); + *((int*)(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); + + int* ptr = (int*)ret; + *ptr = 12; + return (ret); +} + +void btAlignedFreeInternal(void* ptr, int line, char* filename) +{ + + void* real; + gNumAlignedFree++; + + if (ptr) { + real = *((void**)(ptr)-1); + int size = *((int*)(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, int 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/Extras/VHACD/src/btConvexHullComputer.cpp b/Extras/VHACD/src/btConvexHullComputer.cpp new file mode 100644 index 000000000..a84f7c02a --- /dev/null +++ b/Extras/VHACD/src/btConvexHullComputer.cpp @@ -0,0 +1,2475 @@ +/* +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 int int32_t; +typedef long long int int64_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#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; + int 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(); + } + + int 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)); + } + + int 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; + int 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); + } + + int 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; + int 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; + } + int dsign = denominator.getSign(); + if (dsign >= 0) { + this->denominator = denominator; + } + else { + sign = -sign; + this->denominator = -denominator; + } + isInt64 = false; + } + + int compare(const Rational128& b) const; + + int 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; + int 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; + int 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; + int size; + + public: + PoolArray* next; + + PoolArray(int size) + : size(size) + , next(NULL) + { + array = (T*)btAlignedAlloc(sizeof(T) * size, 16); + } + + ~PoolArray() + { + btAlignedFree(array); + } + + T* init() + { + T* o = array; + for (int 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; + int 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(int 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; + int mergeStamp; + int minAxis; + int medAxis; + int maxAxis; + int usedEdgePairs; + int 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(int start, int 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, int stride, int 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; +} + +int 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 + + int 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 +} + +int 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); + + int cmp = nbdHigh.ucmp(dbnHigh); + if (cmp) { + return cmp * sign; + } + return nbdLow.ucmp(dbnLow) * sign; +} + +int 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 (int 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(int start, int end, IntermediateHull& result) +{ + int 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; + } + } + + int split0 = start + n / 2; + Point32 p = originalVertices[split0 - 1]->point; + int 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()), (int)cot.isNaN()); + e->print(); +#endif + if (cot.isNaN()) { + btAssert(ccw ? (t.dot(s) < 0) : (t.dot(s) > 0)); + } + else { + int 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 { + int 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, int stride, int 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 (int 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 (int 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 (int 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 (int 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 (int 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; + } + int 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; + + int faceCount = faces.size(); + + if (clampAmount > 0) { + btScalar minDist = SIMD_INFINITY; + for (int 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); + } + + unsigned int seed = 243703; + for (int i = 0; i < faceCount; i++, seed = 1664525 * seed + 1013904223) { + btSwap(faces[i], faces[seed % faceCount]); + } + + for (int 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); + int cmp = optDot.compare(shiftedDot); +#ifdef SHOW_ITERATIONS + int 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) { + int 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 + int 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; + } + + int 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 + int pos = 0; + while (pos < stack.size()) { + int 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 int getVertexCopy(btConvexHullInternal::Vertex* vertex, btAlignedObjectArray& vertices) +{ + int 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, int stride, int 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); + int copied = 0; + while (copied < oldVertices.size()) { + btConvexHullInternal::Vertex* v = oldVertices[copied]; + vertices.push_back(hull.getCoordinates(v)); + btConvexHullInternal::Edge* firstEdge = v->edges; + if (firstEdge) { + int firstCopy = -1; + int prevCopy = -1; + btConvexHullInternal::Edge* e = firstEdge; + do { + if (e->copy < 0) { + int 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 (int 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/Extras/VHACD/src/premake4.lua b/Extras/VHACD/src/premake4.lua new file mode 100644 index 000000000..3566943be --- /dev/null +++ b/Extras/VHACD/src/premake4.lua @@ -0,0 +1,10 @@ + project "vhacd" + + kind "StaticLib" + includedirs { + "../inc","../public", + } + files { + "*.cpp", + "*.h" + } diff --git a/Extras/VHACD/src/vhacdICHull.cpp b/Extras/VHACD/src/vhacdICHull.cpp new file mode 100644 index 000000000..4216f0599 --- /dev/null +++ b/Extras/VHACD/src/vhacdICHull.cpp @@ -0,0 +1,725 @@ +/* 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 +namespace VHACD { +const double ICHull::sc_eps = 1.0e-15; +const int 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, int id) +{ + if (AddPoints(&point, 1)) { + m_mesh.m_vertices.GetData().m_name = id; + return true; + } + return false; +} + +ICHullError ICHull::Process() +{ + unsigned int 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 (int k = 0; k < 3; k++) { + for (int 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 unsigned int nPointsCH, + const double minVolume) +{ + unsigned int 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 (int k = 0; k < 3; k++) { + for (int 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; + int 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; + int 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 (int 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 (int i = 0; i < 2; ++i) { + for (int 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; + int nvisible = 0; + m_edgesToDelete.Resize(0); + m_edgesToUpdate.Resize(0); + do { + tmp = e->GetNext(); + nvisible = 0; + for (int 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. + int 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(unsigned int& 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(unsigned int& 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/Extras/VHACD/src/vhacdManifoldMesh.cpp b/Extras/VHACD/src/vhacdManifoldMesh.cpp new file mode 100644 index 000000000..dffa0244e --- /dev/null +++ b/Extras/VHACD/src/vhacdManifoldMesh.cpp @@ -0,0 +1,202 @@ +/* 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 (int 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 (int 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 (int 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 (int 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 (int 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 (int 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 (int e = 0; e < 3; e++) { + int found = 0; + for (int 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/Extras/VHACD/src/vhacdMesh.cpp b/Extras/VHACD/src/vhacdMesh.cpp new file mode 100644 index 000000000..542ce430b --- /dev/null +++ b/Extras/VHACD/src/vhacdMesh.cpp @@ -0,0 +1,323 @@ +/* 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 "btConvexHullComputer.h" +#include "vhacdMesh.h" +#include +#include +#include +#include +#include +#include + +namespace VHACD { +Mesh::Mesh() +{ + m_diag = 1.0; +} +Mesh::~Mesh() +{ +} +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 (int t = 0; 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), (int)nPts, -1.0, -1.0); + for (int v = 0; v < ch.vertices.size(); v++) { + AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int nt = ch.faces.size(); + for (int t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int a = sourceEdge->getSourceVertex(); + int b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int 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 (int t = 0; 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 { + int nv = 0; + int nf = 0; + int 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 (int 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; + } + int i, j, k, s; + for (int 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 (int h = 0; h < s; ++h) + fscanf(fid, "%i", &s); + } + } + fclose(fid); + } + } + else { + return false; + } + return true; +} +#endif // VHACD_DEBUG_MESH +} diff --git a/Extras/VHACD/src/vhacdVolume.cpp b/Extras/VHACD/src/vhacdVolume.cpp new file mode 100644 index 000000000..a3cf60360 --- /dev/null +++ b/Extras/VHACD/src/vhacdVolume.cpp @@ -0,0 +1,1617 @@ +/* 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 "btConvexHullComputer.h" +#include "vhacdVolume.h" +#include +#include +#include +#include +#include + +namespace VHACD { +/********************************************************/ +/* AABB-triangle overlap test code */ +/* by Tomas Akenine-Möller */ +/* Function: int 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; + +int PlaneBoxOverlap(const Vec3& normal, + const Vec3& vert, + const Vec3& maxbox) +{ + int 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; +} + +int 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 int maxsteps = 24; // certainly wont need that many. + int 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 (int 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 (int 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 (int 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 (int 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), (int)q, -1.0, -1.0); + for (int 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), (int)cpoints.Size(), -1.0, -1.0); + meshCH.ResizePoints(0); + meshCH.ResizeTriangles(0); + for (int v = 0; v < ch.vertices.size(); v++) { + meshCH.AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int nt = ch.faces.size(); + for (int t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int a = sourceEdge->getSourceVertex(); + int b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int 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 (int k = 0; k < 8; ++k) { + positivePts->PushBack(pts[k]); + } + } + else { + if (++sp == sampling) { + // positivePts->PushBack(pt); + GetPoints(voxel, pts); + for (int k = 0; k < 8; ++k) { + positivePts->PushBack(pts[k]); + } + sp = 0; + } + } + } + else { + if (-d <= d0) { + GetPoints(voxel, pts); + for (int k = 0; k < 8; ++k) { + negativePts->PushBack(pts[k]); + } + } + else { + if (++sn == sampling) { + // negativePts->PushBack(pt); + GetPoints(voxel, pts); + for (int 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 (int 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 (int 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 (int 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); + int s = (int)mesh.GetNPoints(); + for (int 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 (int 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 >= (int)m_dim[0] || b < 0 || b >= (int)m_dim[1] || c < 0 || c >= (int)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); + int s = (int)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 (int 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 (int 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 (int i = 0; i < 4; ++i) { + for (int 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 (int a = 0; a < 4; ++a) { + points[q++] = m_tetrahedra[p].m_pts[a]; + for (int 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), (int)q, -1.0, -1.0); + for (int 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), (int)cpoints.Size(), -1.0, -1.0); + meshCH.ResizePoints(0); + meshCH.ResizeTriangles(0); + for (int v = 0; v < ch.vertices.size(); v++) { + meshCH.AddPoint(Vec3(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ())); + } + const int nt = ch.faces.size(); + for (int t = 0; t < nt; ++t) { + const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]); + int a = sourceEdge->getSourceVertex(); + int b = sourceEdge->getTargetVertex(); + const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace(); + int 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 (int a = 0; a < 4; ++a) { + for (int 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 int nPts) +{ + const int 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 int tet[15][4] = { + { 0, 1, 2, 3 }, { 1, 2, 3, 4 }, { 0, 2, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 2, 4 }, + }; + const int rem[5] = { 4, 0, 1, 2, 3 }; + double maxVol = 0.0; + int h0 = -1; + Tetrahedron tetrahedron0; + tetrahedron0.m_data = PRIMITIVE_ON_SURFACE; + for (int 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; + } + int a = rem[h0]; + maxVol = 0.0; + int h1 = -1; + Tetrahedron tetrahedron1; + tetrahedron1.m_data = PRIMITIVE_ON_SURFACE; + for (int 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 int 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 int 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; + int h0 = -1; + Tetrahedron tetrahedron0; + tetrahedron0.m_data = PRIMITIVE_ON_SURFACE; + for (int 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; + } + + int a0 = rem[h0][0]; + int a1 = rem[h0][1]; + int h1 = -1; + Tetrahedron tetrahedron1; + tetrahedron1.m_data = PRIMITIVE_ON_SURFACE; + maxVol = 0.0; + for (int 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; + int h2 = -1; + Tetrahedron tetrahedron2; + tetrahedron2.m_data = PRIMITIVE_ON_SURFACE; + for (int 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 (int 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 (int i = 0; i < 3; ++i) { + for (int 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 (int i = 0; i < 3; ++i) { + for (int 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; + int sign[4]; + int 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 int 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 (int 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 { + int nnew = 0; + for (int 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 (int 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) { + int s = (int)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 (int 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 (int 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 (int 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/Extras/VHACD/test/inc/oclHelper.h b/Extras/VHACD/test/inc/oclHelper.h new file mode 100644 index 000000000..ff63c3006 --- /dev/null +++ b/Extras/VHACD/test/inc/oclHelper.h @@ -0,0 +1,50 @@ +/* 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/Extras/VHACD/test/src/main.cpp b/Extras/VHACD/test/src/main.cpp new file mode 100644 index 000000000..6b1204726 --- /dev/null +++ b/Extras/VHACD/test/src/main.cpp @@ -0,0 +1,648 @@ +/* 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" + + +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.obj"; + 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 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); + +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); +void GetFileExtension(const string& fileName, string& fileExtension); +void ComputeRandomColor(Material& mat); +void Usage(const Parameters& params); +void ParseParameters(int argc, char* argv[], Parameters& params); + + +int main(int argc, char* argv[]) +{ + // --input camel.off --output camel_acd.obj --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; + + msg << "+ OpenCL (OFF)" << std::endl; + + msg << "+ Parameters" << std::endl; + msg << "\t input " << params.m_fileNameIn << endl; + msg << "\t resolution " << params.m_paramsVHACD.m_resolution << endl; + msg << "\t max. depth " << params.m_paramsVHACD.m_depth << 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 gamma " << params.m_paramsVHACD.m_gamma << 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], 3, (unsigned int)points.size() / 3, + &triangles[0], 3, (unsigned int)triangles.size() / 3, params.m_paramsVHACD); + if (res) { + // 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; + 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, 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.obj --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 << " --depth Maximum number of clipping stages. During each split stage, parts with a concavity higher than the user defined threshold are clipped according the \"best\" clipping plane (default=20, range=1-32)" << 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.obj --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], "--depth")) { + if (++i < argc) + params.m_paramsVHACD.m_depth = 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], "--gamma")) { + if (++i < argc) + params.m_paramsVHACD.m_gamma = atof(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; +} + +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) { + 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 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; + } +} + + + +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; + } +} diff --git a/Extras/VHACD/test/src/oclHelper.cpp b/Extras/VHACD/test/src/oclHelper.cpp new file mode 100644 index 000000000..efaa9fa99 --- /dev/null +++ b/Extras/VHACD/test/src/oclHelper.cpp @@ -0,0 +1,329 @@ +/* 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 diff --git a/Extras/VHACD/test/src/premake4.lua b/Extras/VHACD/test/src/premake4.lua new file mode 100644 index 000000000..a329f5439 --- /dev/null +++ b/Extras/VHACD/test/src/premake4.lua @@ -0,0 +1,25 @@ + +project "test_vhacd" + +if _OPTIONS["ios"] then + kind "WindowedApp" +else + kind "ConsoleApp" +end + +includedirs {"../../public"} + +links { + "vhacd" +} + +language "C++" + +files { + "main.cpp", +} + + +if os.is("Linux") then + links {"pthread"} +end \ No newline at end of file diff --git a/Extras/premake4.lua b/Extras/premake4.lua index dd4853f6e..c3660783a 100644 --- a/Extras/premake4.lua +++ b/Extras/premake4.lua @@ -1,5 +1,6 @@ include "HACD" +include "VHACD" include "ConvexDecomposition" include "InverseDynamics" include "Serialize/BulletFileLoader"